OSDN Git Service

Fix issue #202: Switching between "Find Next" and "Find Prev" need to click twice
[winmerge-jp/winmerge-jp.git] / Externals / crystaledit / editlib / ccrystaltextview.cpp
1 ////////////////////////////////////////////////////////////////////////////
2 //  File:       ccrystaltextview.cpp
3 //  Version:    1.2.0.5
4 //  Created:    29-Dec-1998
5 //
6 //  Author:     Stcherbatchenko Andrei
7 //  E-mail:     windfall@gmx.de
8 //
9 //  Implementation of the CCrystalTextView class, a part of Crystal Edit -
10 //  syntax coloring text editor.
11 //
12 //  You are free to use or modify this code to the following restrictions:
13 //  - Acknowledge me somewhere in your about box, simple "Parts of code by.."
14 //  will be enough. If you can't (or don't want to), contact me personally.
15 //  - LEAVE THIS HEADER INTACT
16 ////////////////////////////////////////////////////////////////////////////
17
18 ////////////////////////////////////////////////////////////////////////////
19 //  17-Feb-99
20 //  FIX:    missing UpdateCaret() in CCrystalTextView::SetFont
21 //  FIX:    missing UpdateCaret() in CCrystalTextView::RecalcVertScrollBar
22 //  FIX:    mistype in CCrystalTextView::RecalcPageLayouts + instead of +=
23 //  FIX:    removed condition 'm_nLineHeight < 20' in
24 //      CCrystalTextView::CalcLineCharDim(). This caused painting defects
25 //      when using very small fonts.
26 //
27 //  FEATURE:    Some experiments with smooth scrolling, controlled by
28 //      m_bSmoothScroll member variable, by default turned off.
29 //      See ScrollToLine function for implementation details.
30 ////////////////////////////////////////////////////////////////////////////
31
32 ////////////////////////////////////////////////////////////////////////////
33 //  21-Feb-99
34 //      Paul Selormey, James R. Twine
35 //  +   FEATURE: description for Undo/Redo actions
36 //  +   FEATURE: multiple MSVC-like bookmarks
37 //  +   FEATURE: 'Disable backspace at beginning of line' option
38 //  +   FEATURE: 'Disable drag-n-drop editing' option
39 //
40 //  +   FIX:  ResetView() now virtual
41 //  +   FEATURE: Added OnEditOperation() virtual: base for auto-indent,
42 //      smart indent etc.
43 ////////////////////////////////////////////////////////////////////////////
44
45 ////////////////////////////////////////////////////////////////////////////
46 //  19-Jul-99
47 //      Ferdinand Prantl:
48 //  +   FEATURE: regular expressions, go to line and things ...
49 //  +   FEATURE: plenty of syntax highlighting definitions
50 //  +   FEATURE: corrected bug in syntax highlighting C comments
51 //  +   FEATURE: extended registry support for saving settings
52 //  +   FEATURE: some other things I've forgotten ...
53 //
54 //  ... it's being edited very rapidly so sorry for non-commented
55 //        and maybe "ugly" code ...
56 ////////////////////////////////////////////////////////////////////////////
57
58 ////////////////////////////////////////////////////////////////////////////
59 //  01-Jun-99 to 31-Aug-99
60 //      Sven Wiegand (search for "//BEGIN SW" to find my changes):
61 //
62 //  + FEATURE: support for language switching on the fly with class 
63 //          CCrystalParser
64 //  +   FEATURE: word wrapping
65 //  + FIX:  Setting m_nIdealCharPos, when choosing cursor position by mouse
66 //  + FIX:  Backward search
67 //  + FEATURE: incremental search
68 ////////////////////////////////////////////////////////////////////////////
69
70 ////////////////////////////////////////////////////////////////////////////
71 //  24-Oct-99
72 //      Sven Wiegand
73 //
74 //  + FIX: Opening large files won't crash anymore and will go very fast
75 //         (removed call to RecalcVertScrollBar() in WrapLineCached())
76 //  + FIX: Problems with repainting and cursor-position by resizing window 
77 //         fixed by adding call to ScrollToSubLine() in OnSize().
78 //  + FEATURE: Supporting [Return] to exit incremental-search-mode
79 //           (see OnChar())
80 ///////////////////////////////////////////////////////////////////////////////
81
82 /**
83  * @file  ccrystaltextview.cpp
84  *
85  * @brief Implementation of the CCrystalTextView class
86  */
87 // ID line follows -- this is updated by SVN
88 // $Id: ccrystaltextview.cpp 7117 2010-02-01 14:24:51Z sdottaka $
89
90 #include "StdAfx.h"
91 #include <vector>
92 #include <algorithm>
93 #include <malloc.h>
94 #include <imm.h> /* IME */
95 #include "editcmd.h"
96 #include "editreg.h"
97 #include "ccrystaltextview.h"
98 #include "ccrystaltextbuffer.h"
99 #include "cfindtextdlg.h"
100 #include "ctextmarkerdlg.h"
101 #include "fpattern.h"
102 #include "filesup.h"
103 #include "registry.h"
104 #include "gotodlg.h"
105 #include "ViewableWhitespace.h"
106 #include "SyntaxColors.h"
107 #include "ccrystaltextmarkers.h"
108 #include "string_util.h"
109 #include "wcwidth.h"
110 #include "icu.hpp"
111
112 using std::vector;
113 using CrystalLineParser::TEXTBLOCK;
114
115 // Escaped character constants in range 0x80-0xFF are interpreted in current codepage
116 // Using C locale gets us direct mapping to Unicode codepoints
117 #pragma setlocale("C")
118
119 #ifndef __AFXPRIV_H__
120 #pragma message("Include <afxpriv.h> in your stdafx.h to avoid this message")
121 #include <afxpriv.h>
122 #endif
123
124 #ifdef _DEBUG
125 #define new DEBUG_NEW
126 #endif
127
128
129 // The vcruntime.h version of _countf() gives syntax errors starting with VS 15.7.2,
130 // but only with `CCrystalTextView::m_SourceDefs` (which is local to this .cpp file), 
131 // and only for X64 compilations (Win32 is ok, probably because no alignment issues
132 // are involved).  I think that this could be related to C++17 compliance issues.
133 // This patch reverts to a 'traditional' definition of _countf(), a pre-existing 
134 // part of the CCrystalTextView package.
135 #undef _countof
136 #ifndef _countof
137 #define _countof(array) (sizeof(array)/sizeof(array[0]))
138 #endif
139
140 #define DEFAULT_PRINT_MARGIN        1000    //  10 millimeters
141
142 /** @brief Maximum tab-char width. */
143 const UINT MAX_TAB_LEN = 64;
144 /** @brief Width of revision marks. */
145 const UINT MARGIN_REV_WIDTH = 3;
146 /** @brief Width of icons printed in the margin. */
147 const UINT MARGIN_ICON_WIDTH = 12;
148 /** @brief Height of icons printed in the margin. */
149 const UINT MARGIN_ICON_HEIGHT = 12;
150
151 /** @brief Color of unsaved line revision mark (dark yellow). */
152 const COLORREF UNSAVED_REVMARK_CLR = RGB(0xD7, 0xD7, 0x00);
153 /** @brief Color of saved line revision mark (green). */
154 const COLORREF SAVED_REVMARK_CLR = RGB(0x00, 0xFF, 0x00);
155
156 #define SMOOTH_SCROLL_FACTOR        6
157
158 #define ICON_INDEX_WRAPLINE         15
159
160 ////////////////////////////////////////////////////////////////////////////
161 // CCrystalTextView
162
163 LOGFONT CCrystalTextView::m_LogFont;
164
165 IMPLEMENT_DYNCREATE (CCrystalTextView, CView)
166
167 HINSTANCE CCrystalTextView::s_hResourceInst = nullptr;
168
169 static ptrdiff_t FindStringHelper(LPCTSTR pszLineBegin, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch);
170
171 BEGIN_MESSAGE_MAP (CCrystalTextView, CView)
172 //{{AFX_MSG_MAP(CCrystalTextView)
173 ON_WM_DESTROY ()
174 ON_WM_ERASEBKGND ()
175 ON_WM_SIZE ()
176 ON_WM_VSCROLL ()
177 ON_WM_SETCURSOR ()
178 ON_WM_LBUTTONDOWN ()
179 ON_WM_SETFOCUS ()
180 ON_WM_HSCROLL ()
181 ON_WM_LBUTTONUP ()
182 ON_WM_MOUSEMOVE ()
183 ON_WM_TIMER ()
184 ON_WM_KILLFOCUS ()
185 ON_WM_LBUTTONDBLCLK ()
186 ON_COMMAND (ID_EDIT_COPY, OnEditCopy)
187 ON_UPDATE_COMMAND_UI (ID_EDIT_COPY, OnUpdateEditCopy)
188 ON_COMMAND (ID_EDIT_SELECT_ALL, OnEditSelectAll)
189 ON_WM_RBUTTONDOWN ()
190 ON_WM_SYSCOLORCHANGE ()
191 ON_WM_CREATE ()
192 ON_COMMAND (ID_EDIT_FIND, OnEditFind)
193 ON_COMMAND (ID_EDIT_REPEAT, OnEditRepeat)
194 ON_UPDATE_COMMAND_UI (ID_EDIT_REPEAT, OnUpdateEditRepeat)
195 ON_COMMAND (ID_EDIT_MARK, OnEditMark)
196 ON_WM_MOUSEWHEEL ()
197 ON_MESSAGE (WM_IME_STARTCOMPOSITION, OnImeStartComposition) /* IME */
198 //}}AFX_MSG_MAP
199 ON_COMMAND (ID_EDIT_CHAR_LEFT, OnCharLeft)
200 ON_COMMAND (ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
201 ON_COMMAND (ID_EDIT_CHAR_RIGHT, OnCharRight)
202 ON_COMMAND (ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
203 ON_COMMAND (ID_EDIT_WORD_LEFT, OnWordLeft)
204 ON_COMMAND (ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
205 ON_COMMAND (ID_EDIT_WORD_RIGHT, OnWordRight)
206 ON_COMMAND (ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
207 ON_COMMAND (ID_EDIT_LINE_UP, OnLineUp)
208 ON_COMMAND (ID_EDIT_EXT_LINE_UP, OnExtLineUp)
209 ON_COMMAND (ID_EDIT_LINE_DOWN, OnLineDown)
210 ON_COMMAND (ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
211 ON_COMMAND (ID_EDIT_SCROLL_UP, ScrollUp)
212 ON_COMMAND (ID_EDIT_SCROLL_DOWN, ScrollDown)
213 ON_COMMAND (ID_EDIT_PAGE_UP, OnPageUp)
214 ON_COMMAND (ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
215 ON_COMMAND (ID_EDIT_PAGE_DOWN, OnPageDown)
216 ON_COMMAND (ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
217 ON_COMMAND (ID_EDIT_LINE_END, OnLineEnd)
218 ON_COMMAND (ID_EDIT_EXT_LINE_END, OnExtLineEnd)
219 ON_COMMAND (ID_EDIT_HOME, OnHome)
220 ON_COMMAND (ID_EDIT_EXT_HOME, OnExtHome)
221 ON_COMMAND (ID_EDIT_TEXT_BEGIN, OnTextBegin)
222 ON_COMMAND (ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
223 ON_COMMAND (ID_EDIT_TEXT_END, OnTextEnd)
224 ON_COMMAND (ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
225 //  Standard printing commands
226 ON_COMMAND (ID_FILE_PAGE_SETUP, OnFilePageSetup)
227 ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint)
228 ON_COMMAND (ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
229 ON_COMMAND (ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
230 //  Status
231 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
232 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
233 //  Bookmarks
234 ON_COMMAND_RANGE (ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
235 ON_COMMAND_RANGE (ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
236 ON_COMMAND (ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
237 // More Bookmarks
238 ON_COMMAND (ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
239 ON_COMMAND (ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
240 ON_COMMAND (ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
241 ON_COMMAND (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
242 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
243 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
244 ON_UPDATE_COMMAND_UI (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
245 // Ferdi's source type chnages
246 ON_COMMAND_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnSourceType)
247 ON_UPDATE_COMMAND_UI_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnUpdateSourceType)
248 ON_COMMAND (ID_EDIT_MATCHBRACE, OnMatchBrace)
249 ON_UPDATE_COMMAND_UI (ID_EDIT_MATCHBRACE, OnUpdateMatchBrace)
250 ON_COMMAND (ID_EDIT_GOTO, OnEditGoTo)
251 ON_UPDATE_COMMAND_UI (ID_VIEW_TOGGLE_SRC_HDR, OnUpdateToggleSourceHeader)
252 ON_COMMAND (ID_VIEW_TOGGLE_SRC_HDR, OnToggleSourceHeader)
253 ON_UPDATE_COMMAND_UI (ID_VIEW_SELMARGIN, OnUpdateSelMargin)
254 ON_COMMAND (ID_VIEW_SELMARGIN, OnSelMargin)
255 ON_UPDATE_COMMAND_UI (ID_VIEW_WORDWRAP, OnUpdateWordWrap)
256 ON_COMMAND (ID_VIEW_WORDWRAP, OnWordWrap)
257 ON_COMMAND (ID_FORCE_REDRAW, OnForceRedraw)
258   //BEGIN SW
259   // incremental search
260   ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnEditFindIncrementalForward)
261   ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnEditFindIncrementalBackward)
262   ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnUpdateEditFindIncrementalForward)
263   ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnUpdateEditFindIncrementalBackward)
264   //END SW
265 ON_COMMAND (ID_EDIT_TOGGLE_COLUMNSELECTION, OnToggleColumnSelection)
266 END_MESSAGE_MAP ()
267
268 #define EXPAND_PRIMITIVE(impl, func)    \
269 void CCrystalTextView::On##func() { m_bColumnSelection = false; impl(false); }  \
270 void CCrystalTextView::OnExt##func() { impl(true); }
271 EXPAND_PRIMITIVE (MoveLeft, CharLeft)
272 EXPAND_PRIMITIVE (MoveRight, CharRight)
273 EXPAND_PRIMITIVE (MoveWordLeft, WordLeft)
274 EXPAND_PRIMITIVE (MoveWordRight, WordRight)
275 EXPAND_PRIMITIVE (MoveUp, LineUp)
276 EXPAND_PRIMITIVE (MoveDown, LineDown)
277 EXPAND_PRIMITIVE (MovePgUp, PageUp)
278 EXPAND_PRIMITIVE (MovePgDn, PageDown)
279 EXPAND_PRIMITIVE (MoveHome, Home)
280 EXPAND_PRIMITIVE (MoveEnd, LineEnd)
281 EXPAND_PRIMITIVE (MoveCtrlHome, TextBegin)
282 EXPAND_PRIMITIVE (MoveCtrlEnd, TextEnd)
283 #undef EXPAND_PRIMITIVE
284
285 // Tabsize is commented out since we have only GUI setting for it now.
286 // Not removed because we may later want to have per-filetype settings again.
287 // See ccrystaltextview.h for table declaration.
288 CCrystalTextView::TextDefinition CCrystalTextView::m_SourceDefs[] =
289   {
290     CCrystalTextView::SRC_PLAIN, _T ("Plain"), _T ("txt,doc,diz"), &CrystalLineParser::ParseLinePlain, SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T (""), (DWORD)-1,
291     CCrystalTextView::SRC_ASP, _T ("ASP"), _T ("asp,ascx"), &CrystalLineParser::ParseLineAsp, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (""), _T (""), _T ("'"), (DWORD)-1,
292     CCrystalTextView::SRC_BASIC, _T ("Basic"), _T ("bas,vb,vbs,frm,dsm,cls,ctl,pag,dsr"), &CrystalLineParser::ParseLineBasic, SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T ("\'"), (DWORD)-1,
293     CCrystalTextView::SRC_BATCH, _T ("Batch"), _T ("bat,btm,cmd"), &CrystalLineParser::ParseLineBatch, SRCOPT_INSERTTABS|SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T ("rem "), (DWORD)-1,
294     CCrystalTextView::SRC_C, _T ("C"), _T ("c,cc,cpp,cxx,h,hpp,hxx,hm,inl,rh,tlh,tli,xs"), &CrystalLineParser::ParseLineC, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
295     CCrystalTextView::SRC_CSHARP, _T ("C#"), _T ("cs"), &CrystalLineParser::ParseLineCSharp, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
296     CCrystalTextView::SRC_CSS, _T ("CSS"), _T ("css"), &CrystalLineParser::ParseLineCss, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T (""), (DWORD)-1,
297     CCrystalTextView::SRC_DCL, _T ("DCL"), _T ("dcl,dcc"), &CrystalLineParser::ParseLineDcl, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
298     CCrystalTextView::SRC_FORTRAN, _T ("Fortran"), _T ("f,f90,f9p,fpp,for,f77"), &CrystalLineParser::ParseLineFortran, SRCOPT_INSERTTABS|SRCOPT_AUTOINDENT, /*8,*/ _T (""), _T (""), _T ("!"), (DWORD)-1,
299     CCrystalTextView::SRC_GO, _T ("Go"), _T ("go"), &CrystalLineParser::ParseLineGo, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
300     CCrystalTextView::SRC_HTML, _T ("HTML"), _T ("html,htm,shtml,ihtml,ssi,stm,stml,jsp"), &CrystalLineParser::ParseLineHtml, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("<!--"), _T ("-->"), _T (""), (DWORD)-1,
301     CCrystalTextView::SRC_INI, _T ("INI"), _T ("ini,reg,vbp,isl"), &CrystalLineParser::ParseLineIni, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU|SRCOPT_EOLNUNIX, /*2,*/ _T (""), _T (""), _T (";"), (DWORD)-1,
302     CCrystalTextView::SRC_INNOSETUP, _T ("InnoSetup"), _T ("iss"), &CrystalLineParser::ParseLineInnoSetup, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("{"), _T ("}"), _T (";"), (DWORD)-1,
303     CCrystalTextView::SRC_INSTALLSHIELD, _T ("InstallShield"), _T ("rul"), &CrystalLineParser::ParseLineIS, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
304     CCrystalTextView::SRC_JAVA, _T ("Java"), _T ("java,jav,js"), &CrystalLineParser::ParseLineJava, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
305     CCrystalTextView::SRC_LISP, _T ("AutoLISP"), _T ("lsp,dsl"), &CrystalLineParser::ParseLineLisp, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (";|"), _T ("|;"), _T (";"), (DWORD)-1,
306     CCrystalTextView::SRC_LUA, _T ("Lua"), _T ("lua"), &CrystalLineParser::ParseLineLua, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("--[["), _T ("]]"), _T ("--"), (DWORD)-1,
307     CCrystalTextView::SRC_NSIS, _T ("NSIS"), _T ("nsi,nsh"), &CrystalLineParser::ParseLineNsis, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T (";"), (DWORD)-1,
308     CCrystalTextView::SRC_PASCAL, _T ("Pascal"), _T ("pas"), &CrystalLineParser::ParseLinePascal, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("{"), _T ("}"), _T (""), (DWORD)-1,
309     CCrystalTextView::SRC_PERL, _T ("Perl"), _T ("pl,pm,plx"), &CrystalLineParser::ParseLinePerl, SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
310     CCrystalTextView::SRC_PHP, _T ("PHP"), _T ("php,php3,php4,php5,phtml"), &CrystalLineParser::ParseLinePhp, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
311     CCrystalTextView::SRC_PO, _T ("PO"), _T ("po,pot"), &CrystalLineParser::ParseLinePo, SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
312     CCrystalTextView::SRC_POWERSHELL, _T ("PowerShell"), _T ("ps1"), &CrystalLineParser::ParseLinePowerShell, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
313     CCrystalTextView::SRC_PYTHON, _T ("Python"), _T ("py"), &CrystalLineParser::ParseLinePython, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
314     CCrystalTextView::SRC_REXX, _T ("REXX"), _T ("rex,rexx"), &CrystalLineParser::ParseLineRexx, SRCOPT_AUTOINDENT, /*4,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
315     CCrystalTextView::SRC_RSRC, _T ("Resources"), _T ("rc,dlg,r16,r32,rc2"), &CrystalLineParser::ParseLineRsrc, SRCOPT_AUTOINDENT, /*4,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
316     CCrystalTextView::SRC_RUBY, _T ("Ruby"), _T ("rb,rbw,rake,gemspec"), &CrystalLineParser::ParseLineRuby, SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
317     CCrystalTextView::SRC_RUST, _T ("Rust"), _T ("rs"), &CrystalLineParser::ParseLineRust, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
318     CCrystalTextView::SRC_SGML, _T ("Sgml"), _T ("sgml"), &CrystalLineParser::ParseLineSgml, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("<!--"), _T ("-->"), _T (""), (DWORD)-1,
319     CCrystalTextView::SRC_SH, _T ("Shell"), _T ("sh,conf"), &CrystalLineParser::ParseLineSh, SRCOPT_INSERTTABS|SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
320     CCrystalTextView::SRC_SIOD, _T ("SIOD"), _T ("scm"), &CrystalLineParser::ParseLineSiod, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T (";|"), _T ("|;"), _T (";"), (DWORD)-1,
321     CCrystalTextView::SRC_SQL, _T ("SQL"), _T ("sql"), &CrystalLineParser::ParseLineSql, SRCOPT_AUTOINDENT, /*4,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
322     CCrystalTextView::SRC_TCL, _T ("TCL"), _T ("tcl"), &CrystalLineParser::ParseLineTcl, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU|SRCOPT_EOLNUNIX, /*2,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
323     CCrystalTextView::SRC_TEX, _T ("TEX"), _T ("tex,sty,clo,ltx,fd,dtx"), &CrystalLineParser::ParseLineTex, SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T ("%"), (DWORD)-1,
324     CCrystalTextView::SRC_VERILOG, _T ("Verilog"), _T ("v,vh"), &CrystalLineParser::ParseLineVerilog, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
325     CCrystalTextView::SRC_VHDL, _T ("VHDL"), _T ("vhd,vhdl,vho"), &CrystalLineParser::ParseLineVhdl, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (""), _T (""), _T ("--"), (DWORD)-1,
326     CCrystalTextView::SRC_XML, _T ("XML"), _T ("xml"), &CrystalLineParser::ParseLineXml, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("<!--"), _T ("-->"), _T (""), (DWORD)-1
327   };
328
329 /////////////////////////////////////////////////////////////////////////////
330 // CCrystalTextView construction/destruction
331
332 bool
333 MatchType (CString pattern, LPCTSTR lpszExt)
334 {
335   CString part;
336   int pos, len = pattern.GetLength ();
337
338   while ((pos = pattern.Find (_T (','))) != -1)
339     {
340       part = pattern.Left (pos);
341       if (!part.IsEmpty () && fpattern_isvalid (part))
342         {
343           if (fpattern_matchn (part, lpszExt))
344             {
345               return true;
346             }
347         }
348       len -= pos + 1;
349       pattern = pattern.Right (len);
350     }
351   if (!pattern.IsEmpty () && fpattern_isvalid (pattern))
352     {
353       if (fpattern_matchn (pattern, lpszExt))
354         {
355           return true;
356         }
357     }
358   return false;
359 }
360
361 bool CCrystalTextView::
362 DoSetTextType (TextDefinition *def)
363 {
364   m_CurSourceDef = def;
365   SetFlags (def->flags);
366
367 // Do not set these
368 // EOL is determined from file, tabsize and viewtabs are
369 // global WinMerge settings, selection margin is not needed
370 // and wordwrapping must be false always
371 #if 0
372   SetWordWrapping ((def->flags & SRCOPT_WORDWRAP) != false);
373   SetSelectionMargin ((def->flags & SRCOPT_SELMARGIN) != false);
374   SetTabSize (def->tabsize);
375   SetViewTabs ((def->flags & SRCOPT_SHOWTABS) != false);
376   int nEoln;
377   if (def->flags & SRCOPT_EOLNDOS)
378     {
379       nEoln = 0;
380     }
381   else if (def->flags & SRCOPT_EOLNUNIX)
382     {
383       nEoln = 1;
384     }
385   else if (def->flags & SRCOPT_EOLNMAC)
386     {
387       nEoln = 2;
388     }
389   else /* eoln auto */
390     {
391       nEoln = -1;
392     }
393   SetCRLFMode (nEoln);
394 #endif
395   return true;
396 }
397
398 CCrystalTextView::TextDefinition* CCrystalTextView::
399 GetTextType (LPCTSTR pszExt)
400 {
401   TextDefinition *def;
402   CString sExt = pszExt;
403
404   def = CCrystalTextView::m_SourceDefs;
405   sExt.MakeLower ();
406   for (int i = 0; i < _countof (CCrystalTextView::m_SourceDefs); i++, def++)
407     if (MatchType (def->exts, sExt))
408       return def;
409   return nullptr;
410 }
411
412 bool CCrystalTextView::
413 SetTextType (LPCTSTR pszExt)
414 {
415   m_CurSourceDef = m_SourceDefs;
416
417   TextDefinition *def = GetTextType (pszExt);
418
419   return SetTextType (def);
420 }
421
422 bool CCrystalTextView::
423 SetTextType (CCrystalTextView::TextType enuType)
424 {
425   TextDefinition *def;
426
427   m_CurSourceDef = def = m_SourceDefs;
428   for (int i = 0; i < _countof (m_SourceDefs); i++, def++)
429     {
430       if (def->type == enuType)
431         {
432           return SetTextType (def);
433         }
434     }
435   return false;
436 }
437
438 bool CCrystalTextView::
439 SetTextType (CCrystalTextView::TextDefinition *def)
440 {
441   if (def)
442     if (m_CurSourceDef != def)
443       return DoSetTextType (def);
444     else
445       return true;
446   return false;
447 }
448
449 void CCrystalTextView::
450 LoadSettings ()
451 {
452   TextDefinition *def = m_SourceDefs;
453   bool bFontLoaded;
454   CReg reg;
455   if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
456     {
457       reg.LoadNumber (_T ("DefaultEncoding"), (DWORD*) &CCrystalTextBuffer::m_nDefaultEncoding);
458       for (int i = 0; i < _countof (m_SourceDefs); i++, def++)
459         {
460           CReg reg1;
461           if (reg1.Open (reg.hKey, def->name, KEY_READ))
462             {
463               reg1.LoadString (_T ("Extensions"), def->exts, _countof (def->exts));
464               reg1.LoadNumber (_T ("Flags"), &def->flags);
465 //              reg1.LoadNumber (_T ("TabSize"), &def->tabsize);
466               reg1.LoadString (_T ("OpenComment"), def->opencomment, _countof (def->opencomment));
467               reg1.LoadString (_T ("CloseComment"), def->closecomment, _countof (def->closecomment));
468               reg1.LoadString (_T ("CommentLine"), def->commentline, _countof (def->commentline));
469               reg1.LoadNumber (_T ("DefaultEncoding"), &def->encoding);
470             }
471         }
472       bFontLoaded = reg.LoadBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont));
473     }
474   else
475     bFontLoaded = false;
476   if (!bFontLoaded)
477     {
478       CWindowDC dc (CWnd::GetDesktopWindow ());
479       NONCLIENTMETRICS info;
480       info.cbSize = sizeof(info);
481       SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
482       memcpy (&m_LogFont, &info.lfMessageFont, sizeof (LOGFONT));
483       m_LogFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
484       m_LogFont.lfWeight = FW_NORMAL;
485       m_LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
486       m_LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
487       m_LogFont.lfQuality = DEFAULT_QUALITY;
488       m_LogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
489       _tcscpy_s (m_LogFont.lfFaceName, _T ("Courier New"));
490     }
491 }
492
493 void CCrystalTextView::
494 SaveSettings ()
495 {
496   TextDefinition *def = m_SourceDefs;
497   CReg reg;
498   if (reg.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
499     {
500       VERIFY (reg.SaveNumber (_T ("DefaultEncoding"), (DWORD) CCrystalTextBuffer::m_nDefaultEncoding));
501       for (int i = 0; i < _countof (m_SourceDefs); i++, def++)
502         {
503           CReg reg1;
504           if (reg1.Create (reg.hKey, def->name, KEY_WRITE))
505             {
506               VERIFY (reg1.SaveString (_T ("Extensions"), def->exts));
507               VERIFY (reg1.SaveNumber (_T ("Flags"), def->flags));
508 //              VERIFY (reg1.SaveNumber (_T ("TabSize"), def->tabsize));
509               VERIFY (reg1.SaveString (_T ("OpenComment"), def->opencomment));
510               VERIFY (reg1.SaveString (_T ("CloseComment"), def->closecomment));
511               VERIFY (reg1.SaveString (_T ("CommentLine"), def->commentline));
512               VERIFY (reg1.SaveNumber (_T ("DefaultEncoding"), def->encoding));
513             }
514         }
515       VERIFY (reg.SaveBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont)));
516     }
517 }
518
519 CCrystalTextView::CCrystalTextView ()
520 : m_nScreenChars(-1)
521 , m_pFindTextDlg(nullptr)
522 , m_CurSourceDef(nullptr)
523 , m_dwLastDblClickTime(0)
524 , m_iterChar(UBRK_CHARACTER, "en", nullptr, 0)
525 , m_iterWord(UBRK_WORD, "en", nullptr, 0)
526 , m_rxnode(nullptr)
527 , m_pszMatched(nullptr)
528 , m_bSelMargin(true)
529 , m_bViewLineNumbers(false)
530 , m_bWordWrap(false)
531 , m_bHideLines(false)
532 , m_bLastSearch(false)
533 , m_bBookmarkExist(false)
534 , m_bSingle(false) // needed to be set in descendat classes
535 , m_bRememberLastPos(false)
536 , m_pColors(nullptr)
537 , m_nLastLineIndexCalculatedSubLineIndex(-1)
538 , m_pIcons(nullptr)
539 , m_apFonts{}
540 , m_hAccel(nullptr)
541 , m_pTextBuffer(nullptr)
542 , m_pCacheBitmap(nullptr)
543 , m_pszLastFindWhat(nullptr)
544 , m_dwLastSearchFlags(0)
545 , m_bMultipleSearch(false)
546 , m_bCursorHidden(false)
547 , m_nLineHeight(0)
548 , m_nCharWidth(0)
549 , m_bViewTabs(false)
550 , m_bViewEols(false)
551 , m_bDistinguishEols(false)
552 , m_dwFlags(0)
553 , m_nScreenLines(0)
554 , m_pMarkers(nullptr)
555 , m_panSubLines(new CArray<int, int>())
556 , m_panSubLineIndexCache(new CArray<int, int>())
557 , m_pstrIncrementalSearchString(new CString)
558 , m_pstrIncrementalSearchStringOld(new CString)
559 , m_ParseCookies(new vector<DWORD>)
560 , m_pnActualLineLength(new vector<int>)
561 , m_nIdealCharPos(0)
562 , m_bFocused(false)
563 , m_lfBaseFont{}
564 , m_lfSavedBaseFont{}
565 , m_pParser(nullptr)
566 , m_pPrintFont(nullptr)
567 , m_bChWidthsCalculated{}
568 , m_iChDoubleWidthFlags{}
569 , m_bPreparingToDrag(false)
570 , m_bDraggingText(false)
571 , m_bDragSelection(false)
572 , m_bWordSelection(false)
573 , m_bLineSelection(false)
574 , m_bColumnSelection(false)
575 , m_nDragSelTimer(0)
576 , m_bOverrideCaret(false)
577 , m_nLastFindWhatLen(0)
578 , m_nPrintPages(0)
579 , m_nPrintLineHeight(0)
580 , m_bPrintFooter(false)
581 , m_bPrintHeader(false)
582 , m_bPrinting(false)
583 , m_nTopLine(0)
584 , m_nOffsetChar(0)
585 , m_nTopSubLine(0)
586 , m_bSmoothScroll(false)
587 , m_bVertScrollBarLocked(false)
588 , m_bHorzScrollBarLocked(false)
589 , m_bShowInactiveSelection(false)
590 , m_bDisableDragAndDrop(false)
591 , m_bIncrementalSearchForward(false)
592 , m_bIncrementalSearchBackward(false)
593 , m_bIncrementalFound(false)
594 , m_rxmatch{}
595 {
596   m_panSubLines->SetSize( 0, 4096 );
597   m_panSubLineIndexCache->SetSize( 0, 4096 );
598
599   //END SW
600   ResetView ();
601   SetTextType (SRC_PLAIN);
602 }
603
604 CCrystalTextView::~CCrystalTextView ()
605 {
606   ASSERT (m_hAccel == nullptr);
607   ASSERT (m_pCacheBitmap == nullptr);
608   ASSERT (m_pTextBuffer == nullptr);   //  Must be correctly detached
609
610   delete m_pFindTextDlg;
611
612   if (m_pszLastFindWhat != nullptr)
613     {
614       free (m_pszLastFindWhat);
615       m_pszLastFindWhat=nullptr;
616     }
617   if (m_rxnode != nullptr)
618     {
619       RxFree (m_rxnode);
620       m_rxnode = nullptr;
621     }
622   if (m_pszMatched != nullptr)
623     {
624       free(m_pszMatched); // Allocated by _tcsdup()
625       m_pszMatched = nullptr;
626     }
627   //BEGIN SW
628   if( m_panSubLines != nullptr )
629     {
630       delete m_panSubLines;
631       m_panSubLines = nullptr;
632     }
633   if( m_panSubLineIndexCache != nullptr )
634     {
635       delete m_panSubLineIndexCache;
636       m_panSubLineIndexCache = nullptr;
637     }
638   if( m_pstrIncrementalSearchString != nullptr )
639     {
640       delete m_pstrIncrementalSearchString;
641       m_pstrIncrementalSearchString = nullptr;
642     }
643   if( m_pstrIncrementalSearchStringOld != nullptr )
644     {
645       delete m_pstrIncrementalSearchStringOld;
646       m_pstrIncrementalSearchStringOld = nullptr;
647     }
648   //END SW
649   ASSERT(m_ParseCookies != nullptr);
650   delete m_ParseCookies;
651   m_ParseCookies = nullptr;
652   ASSERT(m_pnActualLineLength != nullptr);
653   delete m_pnActualLineLength;
654   m_pnActualLineLength = nullptr;
655   delete m_pIcons;
656   if (m_pMarkers != nullptr)
657       m_pMarkers->DeleteView(this);
658 }
659
660 BOOL CCrystalTextView::
661 PreCreateWindow (CREATESTRUCT & cs)
662 {
663   CWnd *pParentWnd = CWnd::FromHandlePermanent (cs.hwndParent);
664   if (pParentWnd == nullptr || !pParentWnd->IsKindOf (RUNTIME_CLASS (CSplitterWnd)))
665     {
666       //  View must always create its own scrollbars,
667       //  if only it's not used within splitter
668     //BEGIN SW
669     if( m_bWordWrap )
670       // we do not need a horizontal scroll bar, if we wrap the lines
671       cs.style|= WS_VSCROLL;
672     else
673       cs.style |= (WS_HSCROLL | WS_VSCROLL);
674     /*ORIGINAL
675     cs.style |= (WS_HSCROLL | WS_VSCROLL);
676     */
677     //END SW
678     }
679   cs.lpszClass = AfxRegisterWndClass (CS_DBLCLKS);
680   return CView::PreCreateWindow (cs);
681 }
682
683
684 /////////////////////////////////////////////////////////////////////////////
685 // CCrystalTextView drawing
686
687 void CCrystalTextView::
688 GetSelection (CPoint & ptStart, CPoint & ptEnd)
689 {
690   PrepareSelBounds ();
691   ptStart = m_ptDrawSelStart;
692   ptEnd = m_ptDrawSelEnd;
693 }
694
695 bool CCrystalTextView::
696 GetColumnSelection (int nLineIndex, int & nSelBegin, int & nSelEnd)
697 {
698   int nSelTop, nSelBottom;
699   if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
700     {
701       nSelTop = m_ptDrawSelStart.y;
702       nSelBottom = m_ptDrawSelEnd.y;
703     }
704   else
705     {
706       nSelTop = m_ptDrawSelEnd.y;
707       nSelBottom = m_ptDrawSelStart.y;
708     }
709
710   if (nSelTop > nLineIndex || nLineIndex > nSelBottom)
711     {
712       nSelBegin = 0;
713       nSelEnd = 0;
714       return false;
715     }
716   else
717     {
718       int nStartCharPos = CalculateActualOffset (m_ptDrawSelStart.y, m_ptDrawSelStart.x, true);
719       int nEndCharPos = CalculateActualOffset (m_ptDrawSelEnd.y, m_ptDrawSelEnd.x, true);
720       int nLeftCharPos, nRightCharPos;
721       if (nStartCharPos > nEndCharPos)
722         {
723           nLeftCharPos = nEndCharPos;
724           nRightCharPos = nStartCharPos;
725         }
726       else
727         {
728           nLeftCharPos = nStartCharPos;
729           nRightCharPos = nEndCharPos;
730         }
731       if (nRightCharPos < m_nIdealCharPos)
732         nRightCharPos = m_nIdealCharPos;
733       nSelBegin = ApproxActualOffset (nLineIndex, nLeftCharPos);
734       nSelEnd = ApproxActualOffset (nLineIndex, nRightCharPos);
735       return true;
736     }
737 }
738
739 void CCrystalTextView::
740 GetFullySelectedLines(int & firstLine, int & lastLine)
741 {
742   CPoint ptStart;
743   CPoint ptEnd;
744   GetSelection(ptStart, ptEnd);
745
746   if (ptStart.x == 0)
747     firstLine = ptStart.y;
748   else
749     firstLine = ptStart.y + 1;
750   if (ptEnd.x == GetLineLength(ptEnd.y))
751     lastLine = ptEnd.y;
752   else
753     lastLine = ptEnd.y-1;
754 }
755
756 CCrystalTextBuffer *CCrystalTextView::
757 LocateTextBuffer ()
758 {
759   return nullptr;
760 }
761
762 /**
763  * @brief : Get the line length, for cursor movement 
764  *
765  * @note : there are at least 4 line lengths :
766  * - number of characters (memory, no EOL)
767  * - number of characters (memory, with EOL)
768  * - number of characters for cursor position (tabs are expanded, no EOL)
769  * - number of displayed characters (tabs are expanded, with EOL)
770  * Corresponding functions :
771  * - GetLineLength
772  * - GetFullLineLength
773  * - GetLineActualLength
774  * - ExpandChars (returns the line to be displayed as a CString)
775  */
776 int CCrystalTextView::
777 GetLineActualLength (int nLineIndex)
778 {
779   const int nLineCount = GetLineCount ();
780   ASSERT (nLineCount > 0);
781   ASSERT (nLineIndex >= 0 && nLineIndex < nLineCount);
782   if (!m_pnActualLineLength->size())
783     {
784       m_pnActualLineLength->assign(nLineCount, -1);
785     }
786
787   if ((*m_pnActualLineLength)[nLineIndex] != - 1)
788       return (*m_pnActualLineLength)[nLineIndex];
789
790   //  Actual line length is not determined yet, let's calculate a little
791   int nActualLength = 0;
792   int nLength = GetLineLength (nLineIndex);
793   if (nLength > 0)
794     {
795       LPCTSTR pszChars = GetLineChars (nLineIndex);
796       const int nTabSize = GetTabSize ();
797       m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
798       for (int i = 0; i < nLength; i = m_iterChar.next())
799         {
800           TCHAR c = pszChars[i];
801           if (c == _T('\t'))
802             nActualLength += (nTabSize - nActualLength % nTabSize);
803           else
804             nActualLength += GetCharCellCountFromChar(pszChars + i);
805         }
806     }
807
808   (*m_pnActualLineLength)[nLineIndex] = nActualLength;
809   return nActualLength;
810 }
811
812 void CCrystalTextView::
813 ScrollToChar (int nNewOffsetChar, bool bNoSmoothScroll /*= false*/ , bool bTrackScrollBar /*= true*/ )
814 {
815   //BEGIN SW
816   // no horizontal scrolling, when word wrapping is enabled
817   if( m_bWordWrap )
818     return;
819   //END SW
820
821   //  For now, ignoring bNoSmoothScroll and m_bSmoothScroll
822   if (m_nOffsetChar != nNewOffsetChar)
823     {
824       int nScrollChars = m_nOffsetChar - nNewOffsetChar;
825       m_nOffsetChar = nNewOffsetChar;
826       CRect rcScroll;
827       GetClientRect (&rcScroll);
828       rcScroll.left += GetMarginWidth ();
829       ScrollWindow (nScrollChars * GetCharWidth (), 0, &rcScroll, &rcScroll);
830       UpdateWindow ();
831       if (bTrackScrollBar)
832         RecalcHorzScrollBar (true);
833     }
834 }
835
836 /**
837  * @brief Scroll view to given line.
838  * Scrolls view so that given line is first line in the view. We limit
839  * scrolling so that there is only one empty line visible after the last
840  * line at max. So we don't allow user to scroll last line being at top or
841  * even at middle of the screen. This is how many editors behave and I
842  * (Kimmo) think it is good for us too.
843  * @param [in] nNewTopSubLine New top line for view.
844  * @param [in] bNoSmoothScroll if true don't use smooth scrolling.
845  * @param [in] bTrackScrollBar if true scrollbar is updated after scroll.
846  */
847 void CCrystalTextView::ScrollToSubLine( int nNewTopSubLine, 
848                   bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
849 {
850   if (m_nTopSubLine != nNewTopSubLine)
851     {
852       if (bNoSmoothScroll || ! m_bSmoothScroll)
853         {
854           // Limit scrolling so that we show one empty line at end of file
855           const int nScreenLines = GetScreenLines();
856           const int nLineCount = GetSubLineCount();
857           if (nNewTopSubLine > (nLineCount - nScreenLines))
858             {
859               nNewTopSubLine = nLineCount - nScreenLines;
860               if (nNewTopSubLine < 0)
861                 nNewTopSubLine = 0;
862             }
863
864           const int nScrollLines = m_nTopSubLine - nNewTopSubLine;
865           m_nTopSubLine = nNewTopSubLine;
866           // OnDraw() uses m_nTopLine to determine topline
867           int dummy;
868           GetLineBySubLine(m_nTopSubLine, m_nTopLine, dummy);
869           ScrollWindow(0, nScrollLines * GetLineHeight());
870           UpdateWindow();
871           if (bTrackScrollBar)
872             {
873               RecalcVertScrollBar(true);
874               RecalcHorzScrollBar();
875             }
876         }
877       else
878         {
879           // Do smooth scrolling
880           int nLineHeight = GetLineHeight();
881           if (m_nTopSubLine > nNewTopSubLine)
882             {
883               int nIncrement = (m_nTopSubLine - nNewTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
884               while (m_nTopSubLine != nNewTopSubLine)
885                 {
886                   int nTopSubLine = m_nTopSubLine - nIncrement;
887                   if (nTopSubLine < nNewTopSubLine)
888                     nTopSubLine = nNewTopSubLine;
889                   const int nScrollLines = nTopSubLine - m_nTopSubLine;
890                   m_nTopSubLine = nTopSubLine;
891                   ScrollWindow(0, - nLineHeight * nScrollLines);
892                   UpdateWindow();
893                   if (bTrackScrollBar)
894                     {
895                       RecalcVertScrollBar(true);
896                       RecalcHorzScrollBar();
897                     }
898                 }
899             }
900           else
901             {
902               int nIncrement = (nNewTopSubLine - m_nTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
903               while (m_nTopSubLine != nNewTopSubLine)
904                 {
905                   int nTopSubLine = m_nTopSubLine + nIncrement;
906                   if (nTopSubLine > nNewTopSubLine)
907                     nTopSubLine = nNewTopSubLine;
908                   const int nScrollLines = nTopSubLine - m_nTopSubLine;
909                   m_nTopSubLine = nTopSubLine;
910                   ScrollWindow(0, - nLineHeight * nScrollLines);
911                   UpdateWindow();
912                   if (bTrackScrollBar)
913                     {
914                       RecalcVertScrollBar(true);
915                       RecalcHorzScrollBar();
916                     }
917                 }
918             }
919         }
920       int nDummy;
921       GetLineBySubLine( m_nTopSubLine, m_nTopLine, nDummy );
922     }
923 }
924
925 void CCrystalTextView::
926 ScrollToLine (int nNewTopLine, bool bNoSmoothScroll /*= false*/ , bool bTrackScrollBar /*= true*/ )
927 {
928   if( m_nTopLine != nNewTopLine )
929     ScrollToSubLine( GetSubLineIndex( nNewTopLine ), bNoSmoothScroll, bTrackScrollBar );
930 }
931
932 /** Append szadd to string str, and advance position curpos */
933 static void AppendStringAdv(CString & str, int & curpos, LPCTSTR szadd)
934 {
935   str += szadd;
936   curpos += (int) _tcslen(szadd);
937 }
938
939 /** Append escaped control char to string str, and advance position curpos */
940 static void AppendEscapeAdv(CString & str, int & curpos, TCHAR c)
941 {
942   int curlen = str.GetLength();
943   LPTSTR szadd = str.GetBufferSetLength(curlen + 3) + curlen;
944   curpos += wsprintf(szadd, _T("\t%02X"), static_cast<int>(c));
945 }
946
947 int CCrystalTextView::
948 ExpandChars (LPCTSTR pszChars, int nOffset, int nCount, CString & line, int nActualOffset)
949 {
950   line.Empty();
951   // Request whitespace characters for codepage ACP
952   // because that is the codepage used by ExtTextOut
953   const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP());
954
955   if (nCount <= 0)
956     {
957       return 0;
958     }
959
960   const int nTabSize = GetTabSize ();
961
962   pszChars += nOffset;
963   int nLength = nCount;
964
965   for (int i = 0; i < nLength; i++)
966     {
967       TCHAR c = pszChars[i];
968       if (c == _T('\t'))
969         nCount += nTabSize - 1;
970       else if (c >= _T('\x00') && c <= _T('\x1F') && c != _T('\r') && c != _T('\n'))
971         nCount += 2;
972     }
973
974   // Preallocate line buffer, to avoid reallocations as we add characters
975   line.GetBuffer(nCount + 1); // at least this many characters
976   line.ReleaseBuffer(0);
977   int nCurPos = 0;
978
979   if (nCount > nLength || m_bViewTabs || m_bViewEols)
980     {
981       m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
982       for (int i = 0, next = 0; i < nLength; i = next)
983         {
984           next = m_iterChar.next();
985           if (pszChars[i] == _T('\t'))
986             {
987               int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
988               if (m_bViewTabs)
989                 {
990                   AppendStringAdv(line, nCurPos, lpspc->c_tab);
991                   nSpaces--;
992                 }
993               while (nSpaces > 0)
994                 {
995                   line += _T(' ');
996                   nCurPos++;
997                   nSpaces--;
998                 }
999             }
1000           else  if (pszChars[i] == ' ' && m_bViewTabs)
1001             AppendStringAdv(line, nCurPos, lpspc->c_space);
1002           else if (pszChars[i] == '\r' || pszChars[i] == '\n')
1003             {
1004               if (m_bViewEols)
1005                 {
1006                   if (pszChars[i] == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
1007                     {
1008                       // Ignore \n after \r
1009                     }
1010                   else
1011                     {
1012                       if (pszChars[i] == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
1013                         {
1014                           AppendStringAdv(line, nCurPos, lpspc->c_eol);
1015                           i++;
1016                         }
1017                       else if (pszChars[i] == '\r' && m_bDistinguishEols)
1018                         AppendStringAdv(line, nCurPos, lpspc->c_cr);
1019                       else if (pszChars[i] == '\n' && m_bDistinguishEols)
1020                         AppendStringAdv(line, nCurPos, lpspc->c_lf);
1021                       else
1022                         {
1023                           AppendStringAdv(line, nCurPos, lpspc->c_eol);
1024                         }
1025                     }
1026                  }
1027             }
1028           else if (pszChars[i] >= _T('\x00') && pszChars[i] <= _T('\x1F'))
1029             {
1030               AppendEscapeAdv(line, nCurPos, pszChars[i]);
1031             }
1032           else
1033             {
1034               nCurPos += GetCharCellCountFromChar(pszChars + i);
1035               for (; i < next; ++i)
1036                 line += pszChars[i];
1037             }
1038         }
1039     }
1040   else
1041     {
1042       m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
1043       for (int i1=0, next=0; i1<nLength; i1 = next)
1044       {
1045         next = m_iterChar.next();
1046         nCurPos += GetCharCellCountFromChar(pszChars + i1);
1047         for (; i1 < next; ++i1)
1048           line += pszChars[i1];
1049       }
1050     }
1051   return nCurPos;
1052 }
1053
1054
1055
1056 /**
1057  * @brief Draw a chunk of text (one color, one line, full or part of line)
1058  *
1059  * @note In ANSI build, this routine is buggy for multibytes or double-width characters
1060  */
1061 void CCrystalTextView::
1062 DrawLineHelperImpl (CDC * pdc, CPoint & ptOrigin, const CRect & rcClip,
1063                     int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd, LPCTSTR pszChars, int nOffset, int nCount, int &nActualOffset)
1064 {
1065   ASSERT (nCount >= 0);
1066   if (nCount > 0)
1067     {
1068       CString line;
1069       nActualOffset += ExpandChars (pszChars, nOffset, nCount, line, nActualOffset);
1070       const int lineLen = line.GetLength();
1071       const int nCharWidth = GetCharWidth();
1072       const int nCharWidthNarrowed = nCharWidth / 2;
1073       const int nCharWidthWidened = nCharWidth * 2 - nCharWidthNarrowed;
1074       const int nLineHeight = GetLineHeight();
1075       m_iterChar.setText(reinterpret_cast<const UChar *>((LPCTSTR)line), lineLen);
1076
1077       // i the character index, from 0 to lineLen-1
1078       int i = 0;
1079
1080       // Pass if the text begins after the right end of the clipping region
1081       if (ptOrigin.x < rcClip.right)
1082         {
1083           // Because ExtTextOut is buggy when ptOrigin.x < - 4095 * charWidth
1084           // or when nCount >= 4095
1085           // and because this is not well documented,
1086           // we decide to do the left & right clipping here
1087           
1088           // Update the position after the left clipped characters
1089           // stop for i = first visible character, at least partly
1090           const int clipLeft = rcClip.left - nCharWidth * 2;
1091           for ( ; i < lineLen; i = m_iterChar.next())
1092           {
1093             int pnWidthsCurrent = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1094             ptOrigin.x += pnWidthsCurrent;
1095             if (ptOrigin.x >= clipLeft)
1096             {
1097               ptOrigin.x -= pnWidthsCurrent;
1098               break;
1099             }
1100           }
1101         
1102           // 
1103 #ifdef _DEBUG
1104           //CSize sz = pdc->GetTextExtent(line, nCount);
1105           //ASSERT(sz.cx == m_nCharWidth * nCount);
1106 #endif
1107            
1108           if (i < lineLen)
1109             {
1110               // We have to draw some characters
1111               int ibegin = i;
1112               int nSumWidth = 0;
1113
1114               // A raw estimate of the number of characters to display
1115               // For wide characters, nCountFit may be overvalued
1116               int nWidth = rcClip.right - ptOrigin.x;
1117               int nCount1 = lineLen - ibegin;
1118               int nCountFit = nWidth / nCharWidth + 2/* wide char */;
1119               if (nCount1 > nCountFit) {
1120 #ifndef _UNICODE
1121                 if (_ismbslead((unsigned char *)(LPCSTR)line, (unsigned char *)(LPCSTR)line + nCountFit - 1))
1122                   nCountFit++;
1123 #endif
1124                 nCount1 = nCountFit;
1125               }
1126
1127               // Table of charwidths as CCrystalEditor thinks they are
1128               // Seems that CrystalEditor's and ExtTextOut()'s charwidths aren't
1129               // same with some fonts and text is drawn only partially
1130               // if this table is not used.
1131               vector<int> nWidths(nCount1 + 2);
1132               bool bdisphex = false;
1133               for (int next = i; i < nCount1 + ibegin ; i = next)
1134                 {
1135                   if (line[i] == '\t') // Escape sequence leadin?
1136                   {
1137                     bdisphex = true;
1138                     // Substitute a space narrowed to half the width of a character cell.
1139                     line.SetAt(i, ' ');
1140                     nSumWidth += nWidths[i - ibegin] = nCharWidthNarrowed;
1141                     // 1st hex digit has normal width.
1142                     nSumWidth += nWidths[m_iterChar.next() - ibegin] = nCharWidth;
1143                     // 2nd hex digit is padded by half the width of a character cell.
1144                     nSumWidth += nWidths[m_iterChar.next() - ibegin] = nCharWidthWidened;
1145                   }
1146                   else
1147                   {
1148                     nSumWidth += nWidths[i - ibegin] = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1149                   }
1150                    next = m_iterChar.next();
1151                 }
1152
1153               if (ptOrigin.x + nSumWidth > rcClip.left)
1154                 {
1155                   if (crText == CLR_NONE || nColorIndex & COLORINDEX_APPLYFORCE)
1156                     pdc->SetTextColor(GetColor(nColorIndex));
1157                   else
1158                     pdc->SetTextColor(crText);
1159                   if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1160                     pdc->SetBkColor(GetColor(nBgColorIndex));
1161                   else
1162                     pdc->SetBkColor(crBkgnd);
1163
1164                   pdc->SelectObject(GetFont(GetItalic(nColorIndex),
1165                       GetBold(nColorIndex)));
1166                   // we are sure to have less than 4095 characters because all the chars are visible
1167                   RECT rcIntersect;
1168                   RECT rcTextBlock = {ptOrigin.x, ptOrigin.y, ptOrigin.x + nSumWidth + 2, ptOrigin.y + nLineHeight};
1169                   IntersectRect(&rcIntersect, &rcClip, &rcTextBlock);
1170                   VERIFY(pdc->ExtTextOut(ptOrigin.x, ptOrigin.y, ETO_CLIPPED | ETO_OPAQUE,
1171                       &rcIntersect, LPCTSTR(line) + ibegin, nCount1, &nWidths[0]));
1172                   if (bdisphex)
1173                     {
1174                      // Draw rounded rectangles around control characters
1175                      pdc->SaveDC();
1176                      pdc->IntersectClipRect(&rcClip);
1177                      HDC hDC = pdc->m_hDC;
1178                      HGDIOBJ hBrush = ::GetStockObject(NULL_BRUSH);
1179                      hBrush = ::SelectObject(hDC, hBrush);
1180                      HGDIOBJ hPen = ::CreatePen(PS_SOLID, 1, ::GetTextColor(hDC));
1181                      hPen = ::SelectObject(hDC, hPen);
1182                      int x = ptOrigin.x;
1183                      for (int j = 0 ; j < nCount1 ; ++j)
1184                      {
1185                        // Assume narrowed space is converted escape sequence leadin.
1186                        if (line[ibegin + j] == ' ' && nWidths[j] < nCharWidth)
1187                        {
1188                          ::RoundRect(hDC, x + 2, ptOrigin.y + 1,
1189                            x + 3 * nCharWidth - 2, ptOrigin.y + nLineHeight - 1,
1190                            nCharWidth / 2, nLineHeight / 2);
1191                        }
1192                        x += nWidths[j];
1193                      }
1194                      hPen = ::SelectObject(hDC, hPen);
1195                      ::DeleteObject(hPen);
1196                      hBrush = ::SelectObject(hDC, hBrush);
1197                      pdc->RestoreDC(-1);
1198                    }
1199                 }
1200
1201               // Update the final position after the visible characters
1202               ptOrigin.x += nSumWidth;
1203
1204             }
1205         }
1206       // Update the final position after the right clipped characters
1207       for ( ; i < lineLen; i = m_iterChar.next())
1208         {
1209           ptOrigin.x += GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1210         }
1211     }
1212 }
1213
1214 void CCrystalTextView::
1215 DrawLineHelper (CDC * pdc, CPoint & ptOrigin, const CRect & rcClip, int nColorIndex, int nBgColorIndex, 
1216                 COLORREF crText, COLORREF crBkgnd, LPCTSTR pszChars, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos)
1217 {
1218   if (nCount > 0)
1219     {
1220       if (m_bFocused || m_bShowInactiveSelection)
1221         {
1222           int nSelBegin = 0, nSelEnd = 0;
1223           if ( !m_bColumnSelection )
1224             {
1225               if (m_ptDrawSelStart.y > ptTextPos.y)
1226                 {
1227                   nSelBegin = nCount;
1228                 }
1229               else if (m_ptDrawSelStart.y == ptTextPos.y)
1230                 {
1231                   nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
1232                   if (nSelBegin < 0)
1233                     nSelBegin = 0;
1234                   if (nSelBegin > nCount)
1235                     nSelBegin = nCount;
1236                 }
1237               if (m_ptDrawSelEnd.y > ptTextPos.y)
1238                 {
1239                   nSelEnd = nCount;
1240                 }
1241               else if (m_ptDrawSelEnd.y == ptTextPos.y)
1242                 {
1243                   nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
1244                   if (nSelEnd < 0)
1245                     nSelEnd = 0;
1246                   if (nSelEnd > nCount)
1247                     nSelEnd = nCount;
1248                 }
1249             }
1250           else
1251             {
1252               int nSelLeft, nSelRight;
1253               GetColumnSelection (ptTextPos.y, nSelLeft, nSelRight);
1254               nSelBegin = nSelLeft - ptTextPos.x;
1255               nSelEnd = nSelRight - ptTextPos.x;
1256               if (nSelBegin < 0) nSelBegin = 0;
1257               if (nSelBegin > nCount) nSelBegin = nCount;
1258               if (nSelEnd < 0) nSelEnd = 0;
1259               if (nSelEnd > nCount) nSelEnd = nCount;
1260             }
1261
1262           ASSERT (nSelBegin >= 0 && nSelBegin <= nCount);
1263           ASSERT (nSelEnd >= 0 && nSelEnd <= nCount);
1264           ASSERT (nSelBegin <= nSelEnd);
1265
1266           //  Draw part of the text before selection
1267           if (nSelBegin > 0)
1268             {
1269               DrawLineHelperImpl (pdc, ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, pszChars, nOffset, nSelBegin, nActualOffset);
1270             }
1271           if (nSelBegin < nSelEnd)
1272             {
1273               DrawLineHelperImpl (pdc, ptOrigin, rcClip,
1274                   nColorIndex & ~COLORINDEX_APPLYFORCE, nBgColorIndex & ~COLORINDEX_APPLYFORCE, 
1275                   GetColor (COLORINDEX_SELTEXT),
1276                   GetColor (COLORINDEX_SELBKGND),
1277                   pszChars, nOffset + nSelBegin, nSelEnd - nSelBegin, nActualOffset);
1278             }
1279           if (nSelEnd < nCount)
1280             {
1281               DrawLineHelperImpl (pdc, ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, pszChars, nOffset + nSelEnd, nCount - nSelEnd, nActualOffset);
1282             }
1283         }
1284       else
1285         {
1286           DrawLineHelperImpl (pdc, ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, pszChars, nOffset, nCount, nActualOffset);
1287         }
1288     }
1289 }
1290
1291 void CCrystalTextView::
1292 GetLineColors (int nLineIndex, COLORREF & crBkgnd,
1293                COLORREF & crText, bool & bDrawWhitespace)
1294 {
1295   DWORD dwLineFlags = GetLineFlags (nLineIndex);
1296   bDrawWhitespace = true;
1297   crText = RGB (255, 255, 255);
1298   if (dwLineFlags & LF_EXECUTION)
1299     {
1300       crBkgnd = RGB (0, 128, 0);
1301       return;
1302     }
1303   if (dwLineFlags & LF_BREAKPOINT)
1304     {
1305       crBkgnd = RGB (255, 0, 0);
1306       return;
1307     }
1308   if (dwLineFlags & LF_INVALID_BREAKPOINT)
1309     {
1310       crBkgnd = RGB (128, 128, 0);
1311       return;
1312     }
1313   crBkgnd = CLR_NONE;
1314   crText = CLR_NONE;
1315   bDrawWhitespace = false;
1316 }
1317
1318 DWORD CCrystalTextView::
1319 GetParseCookie (int nLineIndex)
1320 {
1321   const int nLineCount = GetLineCount ();
1322   if (m_ParseCookies->size() == 0)
1323     {
1324       // must be initialized to invalid value (DWORD) -1
1325       m_ParseCookies->assign(nLineCount, static_cast<DWORD>(-1));
1326     }
1327
1328   if (nLineIndex < 0)
1329     return 0;
1330   if ((*m_ParseCookies)[nLineIndex] != - 1)
1331     return (*m_ParseCookies)[nLineIndex];
1332
1333   int L = nLineIndex;
1334   while (L >= 0 && (*m_ParseCookies)[L] == - 1)
1335     L--;
1336   L++;
1337
1338   int nBlocks;
1339   while (L <= nLineIndex)
1340     {
1341       DWORD dwCookie = 0;
1342       if (L > 0)
1343         dwCookie = (*m_ParseCookies)[L - 1];
1344       ASSERT (dwCookie != - 1);
1345       (*m_ParseCookies)[L] = ParseLine (dwCookie, GetLineChars(L), GetLineLength(L), nullptr, nBlocks);
1346       ASSERT ((*m_ParseCookies)[L] != - 1);
1347       L++;
1348     }
1349
1350   return (*m_ParseCookies)[nLineIndex];
1351 }
1352
1353 std::vector<TEXTBLOCK> CCrystalTextView::
1354 GetAdditionalTextBlocks (int nLineIndex)
1355 {
1356   return {};
1357 }
1358
1359 //BEGIN SW
1360 void CCrystalTextView::WrapLine( int nLineIndex, int nMaxLineWidth, int *anBreaks, int &nBreaks )
1361 {
1362   // There must be a parser attached to this view
1363   if( m_pParser == nullptr )
1364     return;
1365
1366   m_pParser->WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1367 }
1368
1369
1370 void CCrystalTextView::WrapLineCached( 
1371                     int nLineIndex, int nMaxLineWidth, int *anBreaks, int &nBreaks )
1372 {
1373   if( !GetLineVisible (nLineIndex) )
1374   {
1375     nBreaks = -1;
1376     return;
1377   }
1378
1379   // If the word wrap is not active, there is no breaks in the line
1380   if( !m_bWordWrap )
1381   {
1382     nBreaks = 0;
1383     return;
1384   }
1385
1386   // word wrap is active
1387   if( nLineIndex < m_panSubLines->GetSize() && !anBreaks && (*m_panSubLines)[nLineIndex] > -1 )
1388     // return cached data
1389     nBreaks = (*m_panSubLines)[nLineIndex] - 1;
1390   else
1391   {
1392     // recompute line wrap
1393     nBreaks = 0;
1394     WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1395
1396     // cache data
1397     ASSERT( nBreaks > -1 );
1398     m_panSubLines->SetAtGrow( nLineIndex, nBreaks + 1 );
1399
1400     // RecalcVertScrollBar();
1401   }
1402 }
1403
1404
1405 void CCrystalTextView::InvalidateLineCache( int nLineIndex1, int nLineIndex2 /*= -1*/ )
1406 {
1407   // invalidate cached sub line index
1408   InvalidateSubLineIndexCache( nLineIndex1 );
1409
1410   // invalidate cached sub line count
1411
1412   if( nLineIndex2 == -1 && nLineIndex1 < m_panSubLines->GetSize() )
1413     for( int i = nLineIndex1; i < m_panSubLines->GetSize(); i++ )
1414       (*m_panSubLines)[i] = -1;
1415   else
1416     {
1417       if( nLineIndex1 > nLineIndex2 )
1418         {
1419           int   nStorage = nLineIndex1;
1420           nLineIndex1 = nLineIndex2;
1421           nLineIndex2 = nStorage;
1422         }
1423
1424     if( nLineIndex1 >= m_panSubLines->GetSize() )
1425       return;
1426
1427     if( nLineIndex2 >= m_panSubLines->GetSize() )
1428       nLineIndex2 = (int) m_panSubLines->GetUpperBound();
1429
1430     for( int i = nLineIndex1; i <= nLineIndex2; i++ )
1431       if( i >= 0 && i < m_panSubLines->GetSize() )
1432         (*m_panSubLines)[i] = -1;
1433   }
1434 }
1435
1436 /**
1437  * @brief Invalidate sub line index cache from the specified index to the end of file.
1438  * @param [in] nLineIndex Index of the first line to invalidate 
1439  */
1440 void CCrystalTextView::InvalidateSubLineIndexCache( int nLineIndex )
1441 {
1442   if (m_nLastLineIndexCalculatedSubLineIndex > nLineIndex)
1443     m_nLastLineIndexCalculatedSubLineIndex = nLineIndex - 1;
1444 }
1445
1446 /**
1447  * @brief Invalidate items related screen size.
1448  */
1449 void CCrystalTextView::InvalidateScreenRect(bool bInvalidateView)
1450 {
1451   if (m_pCacheBitmap != nullptr)
1452     {
1453       delete m_pCacheBitmap;
1454       m_pCacheBitmap = nullptr;
1455     }
1456   m_nScreenChars = -1;
1457   m_nScreenLines = -1;
1458   InvalidateLineCache(0, -1);
1459   if (bInvalidateView)
1460     {
1461       Invalidate();
1462       m_nTopSubLine = GetSubLineIndex(m_nTopLine);
1463       RecalcVertScrollBar ();
1464       RecalcHorzScrollBar ();
1465       UpdateCaret ();
1466     }
1467 }
1468
1469 void CCrystalTextView::DrawScreenLine( CDC *pdc, CPoint &ptOrigin, const CRect &rcClip,
1470          const std::vector<TEXTBLOCK>& blocks, int &nActualItem, 
1471          COLORREF crText, COLORREF crBkgnd, bool bDrawWhitespace,
1472          LPCTSTR pszChars, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos )
1473 {
1474   CPoint        originalOrigin = ptOrigin;
1475   CPoint        ptOriginZeroWidthBlock;
1476   CRect         frect = rcClip;
1477   const int nLineLength = GetViewableLineLength( ptTextPos.y );
1478   const int nLineHeight = GetLineHeight();
1479   int nBgColorIndexZeorWidthBlock = COLORINDEX_NONE;
1480   bool bPrevZeroWidthBlock = false;
1481   static const int ZEROWIDTHBLOCK_WIDTH = 2;
1482
1483   frect.top = ptOrigin.y;
1484   frect.bottom = frect.top + nLineHeight;
1485
1486   int nBlockSize = static_cast<int>(blocks.size());
1487   ASSERT( nActualItem < nBlockSize );
1488
1489   if( nBlockSize > 0 && nActualItem < nBlockSize - 1 && 
1490     blocks[nActualItem + 1].m_nCharPos >= nOffset && 
1491     blocks[nActualItem + 1].m_nCharPos <= nOffset + nCount )
1492     {
1493       ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1494          blocks[nActualItem].m_nCharPos <= nLineLength);
1495
1496       size_t I=0;
1497       for (I = nActualItem; I < blocks.size() - 1 &&
1498         blocks[I + 1].m_nCharPos <= nOffset + nCount; I ++)
1499         {
1500           const TEXTBLOCK& blk = blocks[I];
1501           ASSERT(blk.m_nCharPos >= 0 && blk.m_nCharPos <= nLineLength);
1502
1503           int nOffsetToUse = (nOffset > blk.m_nCharPos) ?
1504              nOffset : blk.m_nCharPos;
1505           if (blocks[I + 1].m_nCharPos - nOffsetToUse > 0)
1506             {
1507               int nOldActualOffset = nActualOffset;
1508               DrawLineHelper(pdc, ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex, crText, crBkgnd, pszChars,
1509                 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos, 
1510                 blocks[I + 1].m_nCharPos - nOffsetToUse,
1511                 nActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1512               if (bPrevZeroWidthBlock)
1513                 {
1514                   CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1515                   DrawLineHelper(pdc, ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock, crText, crBkgnd, pszChars,
1516                       (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos, 
1517                       blocks[I + 1].m_nCharPos - nOffsetToUse,
1518                       nOldActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1519                   bPrevZeroWidthBlock = false;
1520                 }
1521             }
1522           else
1523             {
1524               if (!bPrevZeroWidthBlock)
1525                 {
1526                   int nBgColorIndex = blk.m_nBgColorIndex;
1527                   COLORREF clrBkColor;
1528                   if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1529                     clrBkColor = GetColor(nBgColorIndex);
1530                   else
1531                     clrBkColor = crBkgnd;
1532                   pdc->FillSolidRect(ptOrigin.x, ptOrigin.y, ZEROWIDTHBLOCK_WIDTH, GetLineHeight(), clrBkColor);
1533                   ptOriginZeroWidthBlock = ptOrigin;
1534                   nBgColorIndexZeorWidthBlock = blk.m_nBgColorIndex;
1535                   bPrevZeroWidthBlock = true;
1536                 }
1537             }
1538           if (ptOrigin.x > rcClip.right)
1539             break;
1540         }
1541
1542       nActualItem = static_cast<int>(I);
1543
1544       ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1545         blocks[nActualItem].m_nCharPos <= nLineLength);
1546
1547       if (nOffset + nCount - blocks[nActualItem].m_nCharPos > 0)
1548         {
1549           int nOldActualOffset = nActualOffset;
1550           DrawLineHelper(pdc, ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1551                   crText, crBkgnd, pszChars, blocks[nActualItem].m_nCharPos,
1552                   nOffset + nCount - blocks[nActualItem].m_nCharPos,
1553                   nActualOffset, CPoint(blocks[nActualItem].m_nCharPos, ptTextPos.y));
1554           if (bPrevZeroWidthBlock)
1555             {
1556               CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1557               DrawLineHelper(pdc, ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blocks[nActualItem].m_nColorIndex, nBgColorIndexZeorWidthBlock,
1558                   crText, crBkgnd, pszChars, blocks[nActualItem].m_nCharPos,
1559                   nOffset + nCount - blocks[nActualItem].m_nCharPos,
1560                   nOldActualOffset, CPoint(blocks[nActualItem].m_nCharPos, ptTextPos.y));
1561               bPrevZeroWidthBlock = false;
1562             }
1563         }
1564       else
1565         {
1566           if (!bPrevZeroWidthBlock)
1567             {
1568               int nBgColorIndex = blocks[nActualItem].m_nBgColorIndex;
1569               COLORREF clrBkColor;
1570               if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1571                 clrBkColor = GetColor(nBgColorIndex);
1572               else
1573                 clrBkColor = crBkgnd;
1574               pdc->FillSolidRect(ptOrigin.x, ptOrigin.y, ZEROWIDTHBLOCK_WIDTH, GetLineHeight(), clrBkColor);
1575               bPrevZeroWidthBlock = true;
1576             }
1577         }
1578     }
1579   else
1580     {
1581       DrawLineHelper(
1582               pdc, ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1583               crText, crBkgnd, pszChars, nOffset, nCount, nActualOffset, ptTextPos);
1584     }
1585
1586   // Draw space on the right of the text
1587
1588   frect.left = ptOrigin.x + (bPrevZeroWidthBlock ? ZEROWIDTHBLOCK_WIDTH : 0);
1589
1590   if ((m_bFocused || m_bShowInactiveSelection) 
1591     && !m_bColumnSelection
1592     && IsInsideSelBlock(CPoint(nLineLength, ptTextPos.y)) 
1593     && (nOffset + nCount) == nLineLength )
1594     {
1595       if (frect.left >= rcClip.left)
1596         {
1597           const int nCharWidth = GetCharWidth();
1598           pdc->FillSolidRect(frect.left, frect.top, nCharWidth, frect.Height(),
1599               GetColor(COLORINDEX_SELBKGND));
1600           frect.left += nCharWidth;
1601         }
1602     }
1603   if (frect.left < rcClip.left)
1604     frect.left = rcClip.left;
1605
1606   if (frect.right > frect.left)
1607     pdc->FillSolidRect(frect, bDrawWhitespace ?
1608       crBkgnd : GetColor(COLORINDEX_WHITESPACE));
1609
1610   // set origin to beginning of next screen line
1611   ptOrigin.x = originalOrigin.x;
1612   ptOrigin.y+= nLineHeight;
1613 }
1614 //END SW
1615
1616 class IntArray : public CArray<int, int>
1617 {
1618 public:
1619   explicit IntArray(int len) { SetSize(len); }
1620 };
1621
1622 std::vector<TEXTBLOCK> CCrystalTextView::
1623 MergeTextBlocks (const std::vector<TEXTBLOCK>& blocks1, const std::vector<TEXTBLOCK>& blocks2) const
1624 {
1625   size_t i, j, k;
1626
1627   std::vector<TEXTBLOCK> mergedBlocks(blocks1.size() + blocks2.size());
1628
1629   for (i = 0, j = 0, k = 0; ; k++)
1630     {
1631       if (i >= blocks1.size() && j >= blocks2.size())
1632         {
1633           break;
1634         }
1635       else if ((i < blocks1.size()&& j < blocks2.size()) &&
1636           (blocks1[i].m_nCharPos == blocks2[j].m_nCharPos))
1637         {
1638           mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1639           if (blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1640             mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1641           else
1642             mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1643           if (blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1644             mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1645           else
1646             mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1647           i++;
1648           j++;
1649         }
1650       else if (j >= blocks2.size() || (i < blocks1.size() &&
1651           blocks1[i].m_nCharPos < blocks2[j].m_nCharPos))
1652         {
1653           mergedBlocks[k].m_nCharPos = blocks1[i].m_nCharPos;
1654           if (blocks2.size() == 0 || blocks2[j - 1].m_nColorIndex == COLORINDEX_NONE)
1655             mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1656           else
1657             mergedBlocks[k].m_nColorIndex = blocks2[j - 1].m_nColorIndex;
1658           if (blocks2.size() == 0 || blocks2[j - 1].m_nBgColorIndex == COLORINDEX_NONE)
1659             mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1660           else
1661             mergedBlocks[k].m_nBgColorIndex = blocks2[j - 1].m_nBgColorIndex;
1662           i++;
1663         }
1664       else if (i >= blocks1.size() || (j < blocks2.size() && blocks1[i].m_nCharPos > blocks2[j].m_nCharPos))
1665         {
1666           mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1667           if (i > 0 && blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1668             mergedBlocks[k].m_nColorIndex = blocks1[i - 1].m_nColorIndex;
1669           else
1670             mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1671           if (i > 0 && blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1672             mergedBlocks[k].m_nBgColorIndex = blocks1[i - 1].m_nBgColorIndex;
1673           else
1674             mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1675           j++;
1676         }
1677     }
1678
1679   j = 0;
1680   for (i = 0; i < k; ++i)
1681     {
1682       if (i == 0 ||
1683           (mergedBlocks[i - 1].m_nColorIndex   != mergedBlocks[i].m_nColorIndex ||
1684            mergedBlocks[i - 1].m_nBgColorIndex != mergedBlocks[i].m_nBgColorIndex))
1685         {
1686           mergedBlocks[j] = mergedBlocks[i];
1687           ++j;
1688         }
1689     }
1690
1691   mergedBlocks.resize(j);
1692   return mergedBlocks;
1693 }
1694
1695 std::vector<TEXTBLOCK>
1696 CCrystalTextView::GetMarkerTextBlocks(int nLineIndex) const
1697 {
1698   std::vector<TEXTBLOCK> allblocks;
1699   if (!m_pMarkers->GetEnabled())
1700     return allblocks;
1701
1702   int nLength = GetViewableLineLength (nLineIndex);
1703
1704   for (const auto& marker : m_pMarkers->GetMarkers())
1705     {
1706       if (!marker.second.bVisible)
1707           continue;
1708       int nBlocks = 0;
1709       std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1710       blocks[0].m_nCharPos = 0;
1711       blocks[0].m_nColorIndex = COLORINDEX_NONE;
1712       blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1713       ++nBlocks;
1714       const TCHAR *pszChars = GetLineChars(nLineIndex);
1715       int nLineLength = GetLineLength(nLineIndex);
1716       if (pszChars != nullptr)
1717         {
1718           for (const TCHAR *p = pszChars; p < pszChars + nLineLength; )
1719             {
1720               RxNode *node = nullptr;
1721               RxMatchRes matches;
1722               int nMatchLen = 0;
1723               size_t nPos = ::FindStringHelper(pszChars, p, marker.second.sFindWhat, marker.second.dwFlags | FIND_NO_WRAP, nMatchLen, node, &matches);
1724               if (nPos == -1)
1725                   break;
1726               if (nLineLength < static_cast<int>((p - pszChars) + nPos) + nMatchLen)
1727                   nMatchLen = static_cast<int>(nLineLength - (p - pszChars));
1728               ASSERT(((p - pszChars) + nPos) < INT_MAX);
1729               blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos);
1730               blocks[nBlocks].m_nBgColorIndex = marker.second.nBgColorIndex | COLORINDEX_APPLYFORCE;
1731               blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1732               ++nBlocks;
1733               ASSERT(((p - pszChars) + nPos + nMatchLen) < INT_MAX);
1734               blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos + nMatchLen);
1735               blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1736               blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1737               ++nBlocks;
1738               p += nPos + (nMatchLen == 0 ? 1 : nMatchLen);
1739             }
1740           blocks.resize(nBlocks);
1741           allblocks = MergeTextBlocks(allblocks, blocks);
1742         }
1743     }
1744
1745   return allblocks;
1746 }
1747
1748 std::vector<TEXTBLOCK>
1749 CCrystalTextView::GetTextBlocks(int nLineIndex)
1750 {
1751   int nLength = GetViewableLineLength (nLineIndex);
1752
1753   //  Parse the line
1754   DWORD dwCookie = GetParseCookie(nLineIndex - 1);
1755   std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1756   int nBlocks = 0;
1757   // insert at least one textblock of normal color at the beginning
1758   blocks[0].m_nCharPos = 0;
1759   blocks[0].m_nColorIndex = COLORINDEX_NORMALTEXT;
1760   blocks[0].m_nBgColorIndex = COLORINDEX_BKGND;
1761   nBlocks++;
1762   (*m_ParseCookies)[nLineIndex] = ParseLine(dwCookie, GetLineChars(nLineIndex), GetLineLength(nLineIndex), blocks.data(), nBlocks);
1763   ASSERT((*m_ParseCookies)[nLineIndex] != -1);
1764   blocks.resize(nBlocks);
1765   
1766   return MergeTextBlocks(blocks, 
1767           (m_pMarkers && m_pMarkers->GetEnabled() && m_pMarkers->GetMarkers().size() > 0) ?
1768           MergeTextBlocks(GetAdditionalTextBlocks(nLineIndex), GetMarkerTextBlocks(nLineIndex)) :
1769           GetAdditionalTextBlocks(nLineIndex));
1770 }
1771
1772 void CCrystalTextView::
1773 DrawSingleLine (CDC * pdc, const CRect & rc, int nLineIndex)
1774 {
1775   const int nCharWidth = GetCharWidth();
1776   ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
1777
1778   if (nLineIndex == -1)
1779     {
1780       //  Draw line beyond the text
1781       pdc->FillSolidRect (rc, GetColor (COLORINDEX_WHITESPACE));
1782       return;
1783     }
1784
1785   //  Acquire the background color for the current line
1786   bool bDrawWhitespace = false;
1787   COLORREF crBkgnd, crText;
1788   GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
1789
1790   int nLength = GetViewableLineLength (nLineIndex);
1791   LPCTSTR pszChars = GetLineChars (nLineIndex);
1792
1793   std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
1794
1795   int nActualItem = 0;
1796   int nActualOffset = 0;
1797   // Wrap the line
1798   IntArray anBreaks(nLength);
1799   int nBreaks = 0;
1800
1801   WrapLineCached( nLineIndex, GetScreenChars(), anBreaks.GetData(), nBreaks );
1802
1803   //  Draw the line text
1804   CPoint origin (rc.left - m_nOffsetChar * nCharWidth, rc.top);
1805   if (crBkgnd != CLR_NONE)
1806     pdc->SetBkColor (crBkgnd);
1807   if (crText != CLR_NONE)
1808     pdc->SetTextColor (crText);
1809
1810   if( nBreaks > 0 )
1811     {
1812       // Draw all the screen lines of the wrapped line
1813       ASSERT( anBreaks[0] < nLength );
1814
1815       // draw start of line to first break
1816       DrawScreenLine(
1817         pdc, origin, rc,
1818         blocks, nActualItem,
1819         crText, crBkgnd, bDrawWhitespace,
1820         pszChars, 0, anBreaks[0], nActualOffset, CPoint( 0, nLineIndex ) );
1821
1822       // draw from first break to last break
1823       int i=0;
1824       for( i = 0; i < nBreaks - 1; i++ )
1825         {
1826           ASSERT( anBreaks[i] >= 0 && anBreaks[i] < nLength );
1827           DrawScreenLine(
1828             pdc, origin, rc,
1829             blocks, nActualItem,
1830             crText, crBkgnd, bDrawWhitespace,
1831             pszChars, anBreaks[i], anBreaks[i + 1] - anBreaks[i],
1832             nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
1833         }
1834
1835       // draw from last break till end of line
1836       DrawScreenLine(
1837         pdc, origin, rc,
1838         blocks, nActualItem,
1839         crText, crBkgnd, bDrawWhitespace,
1840         pszChars, anBreaks[i], nLength - anBreaks[i],
1841         nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
1842     }
1843   else
1844       DrawScreenLine(
1845         pdc, origin, rc,
1846         blocks, nActualItem,
1847         crText, crBkgnd, bDrawWhitespace,
1848         pszChars, 0, nLength, nActualOffset, CPoint(0, nLineIndex));
1849
1850   // Draw empty sublines
1851   int nEmptySubLines = GetEmptySubLines(nLineIndex);
1852   if (nEmptySubLines > 0)
1853     {
1854       CRect frect = rc;
1855       frect.top = frect.bottom - nEmptySubLines * GetLineHeight();
1856       pdc->FillSolidRect(frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
1857     }
1858 }
1859
1860 /**
1861  * @brief Escape special characters
1862  * @param [in]      strText The text to escape
1863  * @param [in, out] bLastCharSpace Whether last char processed was white space
1864  * @param [in, out] nNonbreakChars The number of non-break characters in the text
1865  * @param [in]      nScreenChars   The maximum number of characters to display per line
1866  * @return The escaped text
1867  */
1868 static CString
1869 EscapeHTML (const CString & strText, bool & bLastCharSpace, int & nNonbreakChars, int nScreenChars)
1870 {
1871   CString strHTML;
1872   int len = strText.GetLength ();
1873   for (int i = 0; i < len; ++i)
1874     {
1875       TCHAR ch = strText[i];
1876       switch (ch)
1877         {
1878           case '&':
1879             strHTML += _T("&amp;");
1880             bLastCharSpace = false;
1881             nNonbreakChars++;
1882             break;
1883           case '<':
1884             strHTML += _T("&lt;");
1885             bLastCharSpace = false;
1886             nNonbreakChars++;
1887             break;
1888           case '>':
1889             strHTML += _T("&gt;");
1890             bLastCharSpace = false;
1891             nNonbreakChars++;
1892             break;
1893           case 0xB7:
1894           case 0xBB:
1895             strHTML += ch;
1896             strHTML += _T("<wbr>");
1897             bLastCharSpace = false;
1898             nNonbreakChars = 0;
1899             break;
1900           case ' ':
1901             if (bLastCharSpace)
1902               {
1903                 strHTML += _T("&nbsp;");
1904                 bLastCharSpace = false;
1905               }
1906             else
1907               {
1908                 strHTML += _T(" ");
1909                 bLastCharSpace = true;
1910               }
1911             nNonbreakChars = 0;
1912             break;
1913           default:
1914             strHTML += ch;
1915             bLastCharSpace = false;
1916             nNonbreakChars++;
1917         }
1918 #ifndef _UNICODE
1919       if (IsDBCSLeadByte (ch))
1920         strHTML += strText[++i];
1921 #endif
1922       if ((nNonbreakChars % nScreenChars) == nScreenChars - 1)
1923         {
1924           strHTML += _T("<wbr>");
1925           nNonbreakChars = 0;
1926         }
1927     }
1928
1929   return strHTML;
1930 }
1931
1932 // Make a CString from printf-style args (single call version of CString::Format)
1933 static CString Fmt(LPCTSTR fmt, ...)
1934 {
1935   CString str;
1936   va_list args;
1937   va_start(args, fmt);
1938   str.FormatV(fmt, args);
1939   va_end(args);
1940   return str;
1941 }
1942
1943 /**
1944  * @brief Return all the styles necessary to render this view as HTML code
1945  * @return The HTML styles
1946  */
1947 CString CCrystalTextView::
1948 GetHTMLStyles ()
1949 {
1950   int arColorIndices[] = {
1951     COLORINDEX_NORMALTEXT,
1952     COLORINDEX_SELTEXT,
1953     COLORINDEX_KEYWORD,
1954     COLORINDEX_FUNCNAME,
1955     COLORINDEX_COMMENT,
1956     COLORINDEX_NUMBER,
1957     COLORINDEX_OPERATOR,
1958     COLORINDEX_STRING,
1959     COLORINDEX_PREPROCESSOR,
1960     COLORINDEX_HIGHLIGHTTEXT1,
1961     COLORINDEX_HIGHLIGHTTEXT2,
1962     COLORINDEX_USER1,
1963     COLORINDEX_USER2,
1964   };
1965   int arBgColorIndices[] = {
1966     COLORINDEX_BKGND,
1967     COLORINDEX_SELBKGND,
1968     COLORINDEX_HIGHLIGHTBKGND1,
1969     COLORINDEX_HIGHLIGHTBKGND2,
1970     COLORINDEX_HIGHLIGHTBKGND3,
1971     COLORINDEX_HIGHLIGHTBKGND4,
1972   };
1973
1974   CString strStyles;
1975   for (int f = 0; f < sizeof(arColorIndices)/sizeof(int); f++)
1976     {
1977       int nColorIndex = arColorIndices[f];
1978       for (int b = 0; b < sizeof(arBgColorIndices)/sizeof(int); b++)
1979         {
1980           int nBgColorIndex = arBgColorIndices[b];
1981           COLORREF clr;
1982
1983           strStyles += Fmt (_T(".sf%db%d {"), nColorIndex, nBgColorIndex);
1984           clr = GetColor (nColorIndex);
1985           strStyles += Fmt (_T("color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
1986           clr = GetColor (nBgColorIndex);
1987           strStyles += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
1988           if (GetBold (nColorIndex))
1989             strStyles += _T("font-weight: bold; ");
1990           if (GetItalic (nColorIndex))
1991             strStyles += _T("font-style: italic; ");
1992           strStyles += _T("}\n");
1993         }
1994     }
1995   return strStyles;
1996 }
1997
1998 /**
1999  * @brief Return the HTML attribute associated with the specified colors
2000  * @param [in] nColorIndex   Index of text color
2001  * @param [in] nBgColorIndex Index of background color
2002  * @param [in] crText        Base text color
2003  * @param [in] crBkgnd       Base background color
2004  * @return The HTML attribute
2005  */
2006 CString CCrystalTextView::
2007 GetHTMLAttribute (int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd)
2008 {
2009   CString strAttr;
2010   COLORREF clr;
2011
2012   if ((crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE)) && 
2013       (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE)))
2014     return Fmt(_T("class=\"sf%db%d\""), nColorIndex & ~COLORINDEX_APPLYFORCE, nBgColorIndex & ~COLORINDEX_APPLYFORCE);
2015
2016   if (crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE))
2017     clr = GetColor (nColorIndex);
2018   else
2019     clr = crText;
2020   strAttr += Fmt (_T("style=\"color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2021
2022   if (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE))
2023     clr = GetColor (nBgColorIndex);
2024   else
2025     clr = crBkgnd;
2026   strAttr += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2027
2028   if (GetBold (nColorIndex))
2029     strAttr += _T("font-weight: bold; ");
2030   if (GetItalic (nColorIndex))
2031     strAttr += _T("font-style: italic; ");
2032
2033   strAttr += _T("\"");
2034
2035   return strAttr;
2036 }
2037
2038 /**
2039  * @brief Retrieve the html version of the line
2040  * @param [in] nLineIndex  Index of line in view
2041  * @param [in] pszTag      The HTML tag to enclose the line
2042  * @return The html version of the line
2043  */
2044 CString CCrystalTextView::
2045 GetHTMLLine (int nLineIndex, LPCTSTR pszTag)
2046 {
2047   ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2048
2049   int nLength = GetViewableLineLength (nLineIndex);
2050   LPCTSTR pszChars = GetLineChars (nLineIndex);
2051
2052   //  Acquire the background color for the current line
2053   bool bDrawWhitespace = false;
2054   COLORREF crBkgnd, crText;
2055   GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2056
2057   std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2058
2059   CString strHTML;
2060   CString strExpanded;
2061   size_t i;
2062   int nNonbreakChars = 0;
2063   bool bLastCharSpace = false;
2064   const int nScreenChars = 40; //  GetScreenChars();
2065
2066   strHTML += _T("<");
2067   strHTML += pszTag;
2068   strHTML += _T(" ");
2069   strHTML += GetHTMLAttribute (COLORINDEX_NORMALTEXT, COLORINDEX_BKGND, crText, crBkgnd);
2070   strHTML += _T("><code>");
2071
2072   for (i = 0; i < blocks.size() - 1; i++)
2073     {
2074       ExpandChars (pszChars, blocks[i].m_nCharPos, blocks[i + 1].m_nCharPos - blocks[i].m_nCharPos, strExpanded, 0);
2075       if (!strExpanded.IsEmpty())
2076         {
2077           strHTML += _T("<span ");
2078           strHTML += GetHTMLAttribute (blocks[i].m_nColorIndex, blocks[i].m_nBgColorIndex, crText, crBkgnd);
2079           strHTML += _T(">");
2080           strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2081           strHTML += _T("</span>");
2082         }
2083     }
2084   if (blocks.size() > 0)
2085   {
2086     ExpandChars (pszChars, blocks[i].m_nCharPos, nLength - blocks[i].m_nCharPos, strExpanded, 0);
2087     if (!strExpanded.IsEmpty())
2088       {
2089         strHTML += _T("<span ");
2090         strHTML += GetHTMLAttribute (blocks[i].m_nColorIndex, blocks[i].m_nBgColorIndex, crText, crBkgnd);
2091         strHTML += _T(">");
2092         strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2093         strHTML += _T("</span>");
2094       }
2095     if (strExpanded.Compare (CString (' ', strExpanded.GetLength())) == 0)
2096       strHTML += _T("&nbsp;");
2097   }
2098   strHTML += _T("</code></");
2099   strHTML += pszTag;
2100   strHTML += _T(">");
2101
2102   return strHTML;
2103 }
2104
2105 COLORREF CCrystalTextView::
2106 GetColor (int nColorIndex)
2107 {
2108   if (m_pColors != nullptr)
2109     {
2110       nColorIndex &= ~COLORINDEX_APPLYFORCE;
2111       return m_pColors->GetColor(nColorIndex);
2112     }
2113   else
2114     return RGB(0, 0, 0);
2115 }
2116
2117 DWORD CCrystalTextView::
2118 GetLineFlags (int nLineIndex) const
2119 {
2120   if (m_pTextBuffer == nullptr)
2121     return 0;
2122   return m_pTextBuffer->GetLineFlags (nLineIndex);
2123 }
2124
2125 /**
2126  * @brief Draw selection margin.
2127  * @param [in] pdc         Pointer to draw context.
2128  * @param [in] rect        The rectangle to draw.
2129  * @param [in] nLineIndex  Index of line in view.
2130  * @param [in] nLineNumber Line number to display. if -1, it's not displayed.
2131  */
2132 void CCrystalTextView::
2133 DrawMargin (CDC * pdc, const CRect & rect, int nLineIndex, int nLineNumber)
2134 {
2135   if (!m_bSelMargin && !m_bViewLineNumbers)
2136     pdc->FillSolidRect (rect, GetColor (COLORINDEX_BKGND));
2137   else
2138     pdc->FillSolidRect (rect, GetColor (COLORINDEX_SELMARGIN));
2139
2140   if (m_bViewLineNumbers && nLineNumber > 0)
2141     {
2142       TCHAR szNumbers[32];
2143       wsprintf(szNumbers, _T("%d"), nLineNumber);
2144       CFont *pOldFont = pdc->SelectObject(GetFont());
2145       COLORREF clrOldColor = pdc->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
2146       UINT uiOldAlign = pdc->SetTextAlign(TA_RIGHT);
2147       pdc->TextOut(rect.right - (pdc->IsPrinting() ? 0 : 4), rect.top, szNumbers, lstrlen(szNumbers));
2148       pdc->SetTextAlign(uiOldAlign);
2149       pdc->SelectObject(pOldFont);
2150       pdc->SetTextColor(clrOldColor);
2151     }
2152
2153   // Draw line revision mark (or background) whenever we have valid lineindex
2154   COLORREF clrRevisionMark = GetColor(COLORINDEX_WHITESPACE);
2155   if (nLineIndex >= 0 && m_pTextBuffer != nullptr)
2156     {
2157       // get line revision marks color
2158       DWORD dwRevisionNumber = m_pTextBuffer->GetLineRevisionNumber(nLineIndex);
2159       if (dwRevisionNumber > 0)
2160         {
2161           if (m_pTextBuffer->m_dwRevisionNumberOnSave < dwRevisionNumber)
2162             clrRevisionMark = UNSAVED_REVMARK_CLR;
2163           else
2164             clrRevisionMark = SAVED_REVMARK_CLR;
2165         }
2166     }
2167
2168   // draw line revision marks
2169   CRect rc(rect.right - (pdc->IsPrinting () ? 0 : MARGIN_REV_WIDTH), rect.top, rect.right, rect.bottom);
2170   pdc->FillSolidRect (rc, clrRevisionMark);
2171
2172   if (!m_bSelMargin || pdc->IsPrinting ())
2173     return;
2174
2175   int nImageIndex = -1;
2176   if (nLineIndex >= 0)
2177     {
2178       DWORD dwLineFlags = GetLineFlags (nLineIndex);
2179       static const DWORD adwFlags[] =
2180         {
2181           LF_EXECUTION,
2182           LF_BREAKPOINT,
2183           LF_COMPILATION_ERROR,
2184           LF_BOOKMARK (1),
2185           LF_BOOKMARK (2),
2186           LF_BOOKMARK (3),
2187           LF_BOOKMARK (4),
2188           LF_BOOKMARK (5),
2189           LF_BOOKMARK (6),
2190           LF_BOOKMARK (7),
2191           LF_BOOKMARK (8),
2192           LF_BOOKMARK (9),
2193           LF_BOOKMARK (0),
2194           LF_BOOKMARKS,
2195           LF_INVALID_BREAKPOINT
2196         };
2197       for (int I = 0; I < sizeof (adwFlags) / sizeof (adwFlags[0]); I++)
2198         {
2199           if ((dwLineFlags & adwFlags[I]) != 0)
2200             {
2201               nImageIndex = I;
2202               break;
2203             }
2204         }
2205     }
2206   if (m_pIcons == nullptr)
2207     {
2208       m_pIcons = new CImageList;
2209       VERIFY (m_pIcons->Create(MARGIN_ICON_WIDTH, MARGIN_ICON_HEIGHT,
2210           ILC_COLOR32 | ILC_MASK, 0, 1));
2211       CBitmap bmp;
2212       bmp.LoadBitmap(IDR_MARGIN_ICONS);
2213       m_pIcons->Add(&bmp, RGB(255, 255, 255));
2214     }
2215   if (nImageIndex >= 0)
2216     {
2217       CPoint pt(rect.left + 2, rect.top + (GetLineHeight() - MARGIN_ICON_HEIGHT) / 2);
2218       VERIFY(m_pIcons->Draw(pdc, nImageIndex, pt, ILD_TRANSPARENT));
2219       VERIFY (m_pIcons->Draw (pdc, nImageIndex, pt, ILD_TRANSPARENT));
2220     }
2221
2222   // draw wrapped-line-icon
2223   if (nLineNumber > 0)
2224     {
2225       int nBreaks = 0;
2226       WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2227       for (int i = 0; i < nBreaks; i++)
2228         {
2229           CPoint pt(rect.right - MARGIN_ICON_WIDTH, rect.top + (GetLineHeight()
2230               - MARGIN_ICON_WIDTH) / 2 + (i+1) * GetLineHeight());
2231           m_pIcons->Draw (pdc, ICON_INDEX_WRAPLINE, pt, ILD_TRANSPARENT);
2232         }
2233     }
2234 }
2235
2236 void CCrystalTextView::
2237 DrawBoundaryLine (CDC * pdc, int nLeft, int nRight, int y)
2238 {
2239   CPen *pOldPen = (CPen *)pdc->SelectStockObject (BLACK_PEN);
2240   pdc->MoveTo (nLeft, y);
2241   pdc->LineTo (nRight, y);
2242   pdc->SelectObject (pOldPen);
2243 }
2244
2245 void CCrystalTextView::
2246 DrawLineCursor (CDC * pdc, int nLeft, int nRight, int y, int nHeight)
2247 {
2248   CDC  dcMem;
2249   dcMem.CreateCompatibleDC (pdc);
2250   CBitmap bitmap;
2251   bitmap.CreateCompatibleBitmap (pdc, nRight - nLeft, nHeight);
2252   CBitmap *pOldBitmap = dcMem.SelectObject (&bitmap);
2253   dcMem.SetBkColor(RGB(0, 255, 0));
2254   BLENDFUNCTION blend = {0};
2255   blend.BlendOp = AC_SRC_OVER;
2256   blend.SourceConstantAlpha = 24;
2257   pdc->AlphaBlend (nLeft, y, nRight - nLeft, nHeight, &dcMem, 0, 0, nRight - nLeft, nHeight, blend);
2258   dcMem.SelectObject (pOldBitmap);
2259 }
2260
2261 bool CCrystalTextView::
2262 IsInsideSelBlock (CPoint ptTextPos)
2263 {
2264   PrepareSelBounds();
2265   ASSERT_VALIDTEXTPOS (ptTextPos);
2266   if (ptTextPos.y < m_ptDrawSelStart.y)
2267     return false;
2268   if (ptTextPos.y > m_ptDrawSelEnd.y)
2269     return false;
2270   if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
2271     return true;
2272   if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
2273     {
2274       if (ptTextPos.y == m_ptDrawSelEnd.y)
2275         return ptTextPos.x < m_ptDrawSelEnd.x;
2276       ASSERT (ptTextPos.y == m_ptDrawSelStart.y);
2277       return ptTextPos.x >= m_ptDrawSelStart.x;
2278     }
2279   ASSERT (m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
2280   return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2281 }
2282
2283 bool CCrystalTextView::
2284 IsInsideSelection (const CPoint & ptTextPos)
2285 {
2286   PrepareSelBounds ();
2287   return IsInsideSelBlock (ptTextPos);
2288 }
2289
2290 /**
2291  * @brief : class the selection extremities in ascending order
2292  *
2293  * @note : Updates m_ptDrawSelStart and m_ptDrawSelEnd
2294  * This function must be called before reading these values
2295  */
2296 void CCrystalTextView::
2297 PrepareSelBounds ()
2298 {
2299   if (m_ptSelStart.y < m_ptSelEnd.y ||
2300         (m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
2301     {
2302       m_ptDrawSelStart = m_ptSelStart;
2303       m_ptDrawSelEnd = m_ptSelEnd;
2304     }
2305   else
2306     {
2307       m_ptDrawSelStart = m_ptSelEnd;
2308       m_ptDrawSelEnd = m_ptSelStart;
2309     }
2310 }
2311
2312 void CCrystalTextView::
2313 OnDraw (CDC * pdc)
2314 {
2315   // We use the same GDI objects for both drawing and printing.
2316   // So when printing is in progress, we do nothing in this function.
2317   if (m_bPrinting)
2318     return;
2319
2320   CRect rcClient;
2321   GetClientRect (rcClient);
2322
2323   if (m_pTextBuffer == nullptr)
2324     {
2325       pdc->FillSolidRect(&rcClient, GetSysColor(COLOR_WINDOW));
2326       return;
2327     }
2328
2329   const int nLineCount = GetLineCount ();
2330   const int nLineHeight = GetLineHeight ();
2331   PrepareSelBounds ();
2332
2333   // if the private arrays (m_ParseCookies and m_pnActualLineLength) 
2334   // are defined, check they are in phase with the text buffer
2335   if (m_ParseCookies->size())
2336     ASSERT(m_ParseCookies->size() == static_cast<size_t>(nLineCount));
2337   if (m_pnActualLineLength->size())
2338     ASSERT(m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
2339
2340   CDC cacheDC;
2341   VERIFY (cacheDC.CreateCompatibleDC (pdc));
2342   if (m_pCacheBitmap == nullptr)
2343     {
2344       m_pCacheBitmap = new CBitmap;
2345       VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height()));
2346     }
2347   CBitmap *pOldBitmap = cacheDC.SelectObject (m_pCacheBitmap);
2348
2349   CRect rcLine;
2350   rcLine = rcClient;
2351   rcLine.bottom = rcLine.top + nLineHeight;
2352   CRect rcCacheMargin (0, 0, GetMarginWidth (), nLineHeight);
2353   CRect rcCacheLine (GetMarginWidth (), 0, rcLine.Width (), nLineHeight);
2354   //BEGIN SW
2355   // initialize rects
2356   int           nSubLineOffset = GetSubLineIndex( m_nTopLine ) - m_nTopSubLine;
2357   if( nSubLineOffset < 0 )
2358   {
2359     rcCacheMargin.OffsetRect( 0, nSubLineOffset * nLineHeight );
2360     rcCacheLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
2361   }
2362
2363   int nCursorY = TextToClient (m_ptCursorPos).y;
2364
2365   //END SW
2366
2367   int nCurrentLine = m_nTopLine;
2368   while (rcLine.top < rcClient.bottom)
2369     {
2370       //BEGIN SW
2371       int nSubLines = 1;
2372       if( nCurrentLine < nLineCount /*&& GetLineLength( nCurrentLine ) > nMaxLineChars*/ )
2373          nSubLines = GetSubLines(nCurrentLine);
2374
2375       rcLine.bottom = rcLine.top + nSubLines * nLineHeight;
2376       rcCacheLine.bottom = rcCacheLine.top + rcLine.Height();
2377       rcCacheMargin.bottom = rcCacheMargin.top + rcLine.Height();
2378
2379       if( rcCacheLine.top < 0 )
2380         rcLine.bottom+= rcCacheLine.top;
2381       //END SW
2382       if (pdc->RectVisible(rcLine))
2383         {
2384           if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
2385             {
2386               DrawMargin (&cacheDC, rcCacheMargin, nCurrentLine, nCurrentLine + 1);
2387               DrawSingleLine (&cacheDC, rcCacheLine, nCurrentLine);
2388               if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
2389                 DrawBoundaryLine (&cacheDC, rcCacheMargin.left, rcCacheLine.right, rcCacheMargin.bottom-1);
2390               if (nCurrentLine == m_ptCursorPos.y)
2391                 DrawLineCursor (&cacheDC, rcCacheMargin.left, rcCacheLine.right, 
2392                   nCursorY - rcLine.top + nLineHeight - 1, 1);
2393             }
2394           else
2395             {
2396               DrawMargin (&cacheDC, rcCacheMargin, -1, -1);
2397               DrawSingleLine (&cacheDC, rcCacheLine, -1);
2398             }
2399
2400           VERIFY (pdc->BitBlt (rcLine.left, rcLine.top, rcLine.Width (),
2401               rcLine.Height (), &cacheDC, 0, 0, SRCCOPY));
2402         }
2403
2404       nCurrentLine++;
2405       //BEGIN SW
2406       rcLine.top = rcLine.bottom;
2407       rcCacheLine.top = 0;
2408       rcCacheMargin.top = 0;
2409       /*ORIGINAL
2410       rcLine.OffsetRect(0, nLineHeight);
2411       */
2412       //END SW
2413     }
2414
2415   cacheDC.SelectObject (pOldBitmap);
2416   cacheDC.DeleteDC ();
2417 }
2418
2419 void CCrystalTextView::
2420 ResetView ()
2421 {
2422   // m_bWordWrap = false;
2423   m_nTopLine = 0;
2424   m_nTopSubLine = 0;
2425   m_nOffsetChar = 0;
2426   m_nLineHeight = -1;
2427   m_nCharWidth = -1;
2428   m_nScreenLines = -1;
2429   m_nScreenChars = -1;
2430   m_nIdealCharPos = -1;
2431   m_ptAnchor.x = 0;
2432   m_ptAnchor.y = 0;
2433   if (m_pIcons != nullptr)
2434     {
2435       delete m_pIcons;
2436       m_pIcons = nullptr;
2437     }
2438   for (int I = 0; I < 4; I++)
2439     {
2440       if (m_apFonts[I] != nullptr)
2441         {
2442           delete m_apFonts[I];
2443           m_apFonts[I] = nullptr;
2444         }
2445     }
2446   InvalidateLineCache( 0, -1 );
2447   m_ParseCookies->clear();
2448   m_pnActualLineLength->clear();
2449   m_ptCursorPos.x = 0;
2450   m_ptCursorPos.y = 0;
2451   m_ptSelStart = m_ptSelEnd = m_ptCursorPos;
2452   if (m_bDragSelection)
2453     {
2454       ReleaseCapture ();
2455       KillTimer (m_nDragSelTimer);
2456     }
2457   m_bDragSelection = false;
2458   m_bVertScrollBarLocked = false;
2459   m_bHorzScrollBarLocked = false;
2460   if (::IsWindow (m_hWnd))
2461     UpdateCaret ();
2462   m_bShowInactiveSelection = true; // FP: reverted because I like it
2463   m_bPrintHeader = false;
2464   m_bPrintFooter = true;
2465
2466   m_bMultipleSearch = false;    // More search
2467
2468 }
2469
2470 void CCrystalTextView::
2471 UpdateCaret ()
2472 {
2473   ASSERT_VALIDTEXTPOS (m_ptCursorPos);
2474   if (m_bFocused && !m_bCursorHidden &&
2475         CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x) >= m_nOffsetChar)
2476     {
2477       int nCaretHeight = GetLineVisible(m_ptCursorPos.y) ? GetLineHeight () : 0;
2478       if (m_bOverrideCaret)  //UPDATE
2479         CreateSolidCaret(GetCharWidth(), nCaretHeight);
2480       else
2481         CreateSolidCaret (2, nCaretHeight);
2482
2483       SetCaretPos (TextToClient (m_ptCursorPos));
2484       ShowCaret ();
2485       UpdateCompositionWindowPos(); /* IME */
2486     }
2487   else
2488     {
2489       HideCaret ();
2490     }
2491   OnUpdateCaret();
2492 }
2493
2494 void CCrystalTextView::
2495 OnUpdateCaret()
2496 {
2497 }
2498
2499 int CCrystalTextView::
2500 GetCRLFMode ()
2501 {
2502   if (m_pTextBuffer != nullptr)
2503     {
2504       return m_pTextBuffer->GetCRLFMode ();
2505     }
2506   return -1;
2507 }
2508
2509 void CCrystalTextView::
2510 SetCRLFMode (CRLFSTYLE nCRLFMode)
2511 {
2512   if (m_pTextBuffer != nullptr)
2513     {
2514       m_pTextBuffer->SetCRLFMode (nCRLFMode);
2515     }
2516 }
2517
2518 int CCrystalTextView::
2519 GetTabSize ()
2520 {
2521   if (m_pTextBuffer == nullptr)
2522     return 4;
2523
2524   return m_pTextBuffer->GetTabSize();
2525
2526 }
2527
2528 void CCrystalTextView::
2529 SetTabSize (int nTabSize)
2530 {
2531   ASSERT (nTabSize >= 0 && nTabSize <= 64);
2532   if (m_pTextBuffer == nullptr)
2533     return;
2534
2535   if (m_pTextBuffer->GetTabSize() != nTabSize)
2536     {
2537       m_pTextBuffer->SetTabSize( nTabSize );
2538
2539       m_pnActualLineLength->clear();
2540       RecalcHorzScrollBar ();
2541       Invalidate ();
2542       UpdateCaret ();
2543     }
2544 }
2545
2546 CFont *CCrystalTextView::
2547 GetFont (bool bItalic /*= false*/ , bool bBold /*= false*/ )
2548 {
2549   int nIndex = 0;
2550   if (bBold)
2551     nIndex |= 1;
2552   if (bItalic)
2553     nIndex |= 2;
2554
2555   if (m_apFonts[nIndex] == nullptr)
2556     {
2557       m_apFonts[nIndex] = new CFont;
2558       if (!m_lfBaseFont.lfHeight)
2559         {
2560           CClientDC dc (GetDesktopWindow ());
2561           m_lfBaseFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
2562         }
2563       m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
2564       m_lfBaseFont.lfItalic = (BYTE) bItalic;
2565       if (!m_apFonts[nIndex]->CreateFontIndirect (&m_lfBaseFont))
2566         {
2567           delete m_apFonts[nIndex];
2568           m_apFonts[nIndex] = nullptr;
2569           return CView::GetFont ();
2570         }
2571     }
2572   return m_apFonts[nIndex];
2573 }
2574
2575 void CCrystalTextView::
2576 CalcLineCharDim ()
2577 {
2578   CDC *pdc = GetDC ();
2579   CFont *pOldFont = pdc->SelectObject (GetFont ());
2580   CSize szCharExt = pdc->GetTextExtent (_T ("X"));
2581   m_nLineHeight = szCharExt.cy;
2582   if (m_nLineHeight < 1)
2583     m_nLineHeight = 1;
2584   m_nCharWidth = szCharExt.cx;
2585   /*
2586      TEXTMETRIC tm;
2587      if (pdc->GetTextMetrics(&tm))
2588      m_nCharWidth -= tm.tmOverhang;
2589    */
2590   pdc->SelectObject (GetFont (false, true));
2591   szCharExt = pdc->GetTextExtent (_T ("X"));
2592   if (m_nLineHeight < szCharExt.cy)
2593     m_nLineHeight = szCharExt.cy;
2594   pdc->SelectObject (pOldFont);
2595   ReleaseDC (pdc);
2596 }
2597
2598 int CCrystalTextView::
2599 GetLineHeight ()
2600 {
2601   if (m_nLineHeight == -1)
2602     CalcLineCharDim ();
2603   return m_nLineHeight;
2604 }
2605
2606 int CCrystalTextView::GetSubLines( int nLineIndex )
2607 {
2608   // get a number of lines this wrapped lines contains
2609   int nBreaks = 0;
2610   WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2611
2612   return GetEmptySubLines(nLineIndex) + nBreaks + 1;
2613 }
2614
2615 int CCrystalTextView::GetEmptySubLines( int nLineIndex )
2616 {
2617   return 0;
2618 }
2619
2620 bool CCrystalTextView::IsEmptySubLineIndex( int nSubLineIndex )
2621 {
2622   int nLineIndex;
2623   int dummy;
2624   GetLineBySubLine(nSubLineIndex, nLineIndex, dummy);
2625   int nSubLineIndexNextLine = GetSubLineIndex(nLineIndex) + GetSubLines(nLineIndex);
2626   if (nSubLineIndexNextLine - GetEmptySubLines(nLineIndex) <= nSubLineIndex && nSubLineIndex < nSubLineIndexNextLine)
2627     return true;
2628   else
2629     return false;
2630 }
2631
2632 int CCrystalTextView::CharPosToPoint( int nLineIndex, int nCharPos, CPoint &charPoint )
2633 {
2634   // if we do not wrap lines, y is allways 0 and x is equl to nCharPos
2635   if (!m_bWordWrap)
2636     {
2637       charPoint.x = nCharPos;
2638       charPoint.y = 0;
2639     }
2640
2641   // line is wrapped
2642   vector<int> anBreaks(GetLineLength (nLineIndex) + 1);
2643   int nBreaks = 0;
2644
2645   WrapLineCached (nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks);
2646
2647   int i = (nBreaks <= 0) ? -1 : nBreaks - 1;
2648   for (; i >= 0 && nCharPos < anBreaks[i]; i--)
2649     ; // Empty loop!
2650
2651   charPoint.x = (i >= 0)? nCharPos - anBreaks[i] : nCharPos;
2652   charPoint.y = i + 1;
2653
2654   int nReturnVal = (i >= 0)? anBreaks[i] : 0;
2655
2656   return nReturnVal;
2657 }
2658
2659 /** Does character introduce a multicharacter character? */
2660 static inline bool IsLeadByte(TCHAR ch)
2661 {
2662 #ifdef UNICODE
2663   return false;
2664 #else
2665   return _getmbcp() && IsDBCSLeadByte(ch);
2666 #endif
2667 }
2668
2669 int CCrystalTextView::CursorPointToCharPos( int nLineIndex, const CPoint &curPoint )
2670 {
2671   // calculate char pos out of point
2672   const int nLength = GetLineLength( nLineIndex );
2673   const int nScreenChars = GetScreenChars();
2674   LPCTSTR       szLine = GetLineChars( nLineIndex );
2675
2676   // wrap line
2677   vector<int> anBreaks(nLength + 1);
2678   int   nBreaks = 0;
2679
2680   WrapLineCached( nLineIndex, nScreenChars, &anBreaks[0], nBreaks );
2681
2682   // find char pos that matches cursor position
2683   int nXPos = 0;
2684   int nYPos = 0;
2685   int   nCurPos = 0;
2686   const int nTabSize = GetTabSize();
2687
2688   int nIndex=0, nPrevIndex = 0;
2689   m_iterChar.setText(reinterpret_cast<const UChar *>(szLine), nLength);
2690   for( nIndex = 0; nIndex < nLength; nIndex = m_iterChar.next())
2691     {
2692       if( nBreaks > 0 && nIndex == anBreaks[nYPos] )
2693         {
2694           nXPos = 0;
2695           nYPos++;
2696         }
2697
2698       int nOffset;
2699       if (szLine[nIndex] == _T('\t'))
2700         nOffset = nTabSize - nCurPos % nTabSize;
2701       else
2702         nOffset = GetCharCellCountFromChar(szLine + nIndex);
2703       nXPos += nOffset;
2704       nCurPos += nOffset;
2705
2706       if( nXPos > curPoint.x && nYPos == curPoint.y )
2707         break;
2708       else if( nYPos > curPoint.y )
2709         {
2710           nIndex = nPrevIndex;
2711           break;
2712         }
2713
2714       nPrevIndex = nIndex;
2715     }
2716
2717   return nIndex;
2718 }
2719
2720 void CCrystalTextView::SubLineCursorPosToTextPos( const CPoint &subLineCurPos, CPoint &textPos )
2721 {
2722   // Get line breaks
2723   int   nSubLineOffset, nLine;
2724
2725   GetLineBySubLine( subLineCurPos.y, nLine, nSubLineOffset );
2726
2727   // compute cursor-position
2728   textPos.x = CursorPointToCharPos( nLine, CPoint( subLineCurPos.x, nSubLineOffset ) );
2729   textPos.y = nLine;
2730 }
2731
2732 /**
2733  * @brief Calculate last character position in (sub)line.
2734  * @param [in] nLineIndex Linenumber to check.
2735  * @param [in] nSublineOffset Subline index in wrapped line.
2736  * @return Position of the last character.
2737  */
2738 int CCrystalTextView::SubLineEndToCharPos(int nLineIndex, int nSubLineOffset)
2739 {
2740   const int nLength = GetLineLength(nLineIndex);
2741
2742   // if word wrapping is disabled, the end is equal to the length of the line -1
2743   if (!m_bWordWrap)
2744     return nLength;
2745
2746   // wrap line
2747   vector<int> anBreaks(nLength + 1);
2748   int nBreaks = 0;
2749
2750   WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks);
2751
2752   // if there is no break inside the line or the given subline is the last
2753   // one in this line...
2754   if (nBreaks <= 0 || nSubLineOffset == nBreaks)
2755     {
2756       return nLength;
2757     }
2758
2759   // compute character position for end of subline
2760   ASSERT(nSubLineOffset >= 0 && nSubLineOffset <= nBreaks);
2761
2762   int nReturnVal = anBreaks[nSubLineOffset] - 1;
2763
2764   return nReturnVal;
2765 }
2766
2767 /** 
2768  * @brief Calculate first character position in (sub)line.
2769  * @param [in] nLineIndex Linenumber to check.
2770  * @param [in] nSublineOffset Subline index in wrapped line.
2771  * @return Position of the first character.
2772  */
2773 int CCrystalTextView::SubLineHomeToCharPos(int nLineIndex, int nSubLineOffset)
2774 {
2775   // if word wrapping is disabled, the start is 0
2776   if (!m_bWordWrap || nSubLineOffset == 0)
2777     return 0;
2778
2779   // wrap line
2780   int nLength = GetLineLength(nLineIndex);
2781   vector<int> anBreaks(nLength + 1);
2782   int nBreaks = 0;
2783
2784   WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks);
2785
2786   // if there is no break inside the line...
2787   if (nBreaks == 0)
2788     {
2789       return 0;
2790     }
2791
2792   // compute character position for end of subline
2793   ASSERT(nSubLineOffset > 0 && nSubLineOffset <= nBreaks);
2794
2795   int nReturnVal = anBreaks[nSubLineOffset - 1];
2796
2797   return nReturnVal;
2798 }
2799 //END SW
2800
2801 int CCrystalTextView::
2802 GetCharWidth ()
2803 {
2804   if (m_nCharWidth == -1)
2805     CalcLineCharDim ();
2806   return m_nCharWidth;
2807 }
2808
2809 int CCrystalTextView::
2810 GetMaxLineLength (int nTopLine, int nLines)
2811 {
2812   int nMaxLineLength = 0;
2813   const int nLineCount = (std::min)(nTopLine + nLines, GetLineCount ());
2814   for (int I = nTopLine; I < nLineCount; I++)
2815     {
2816       int nActualLength = GetLineActualLength (I);
2817       if (nMaxLineLength < nActualLength)
2818         nMaxLineLength = nActualLength;
2819     }
2820   return nMaxLineLength;
2821 }
2822
2823 CCrystalTextView *CCrystalTextView::
2824 GetSiblingView (int nRow, int nCol)
2825 {
2826   CSplitterWnd *pSplitter = GetParentSplitter (this, false);
2827   if (pSplitter == nullptr)
2828     return nullptr;
2829   CWnd *pWnd = CWnd::FromHandlePermanent (
2830                  ::GetDlgItem (pSplitter->m_hWnd, pSplitter->IdFromRowCol (nRow, nCol)));
2831   if (pWnd == nullptr || !pWnd->IsKindOf (RUNTIME_CLASS (CCrystalTextView)))
2832     return nullptr;
2833   return (CCrystalTextView *) pWnd;
2834 }
2835
2836 void CCrystalTextView::
2837 GoToLine (int nLine, bool bRelative)
2838 {
2839   int nLines = m_pTextBuffer->GetLineCount () - 1;
2840   CPoint ptCursorPos = GetCursorPos ();
2841   if (bRelative)
2842     {
2843       nLine += ptCursorPos.y;
2844     }
2845   if (nLine)
2846     {
2847       nLine--;
2848     }
2849   if (nLine > nLines)
2850     {
2851       nLine = nLines;
2852     }
2853   if (nLine >= 0)
2854     {
2855       int nChars = m_pTextBuffer->GetLineLength (nLine);
2856       if (nChars)
2857         {
2858           nChars--;
2859         }
2860       if (ptCursorPos.x > nChars)
2861         {
2862           ptCursorPos.x = nChars;
2863         }
2864       if (ptCursorPos.x >= 0)
2865         {
2866           ptCursorPos.y = nLine;
2867           ASSERT_VALIDTEXTPOS (ptCursorPos);
2868           SetAnchor (ptCursorPos);
2869           SetSelection (ptCursorPos, ptCursorPos);
2870           SetCursorPos (ptCursorPos);
2871           EnsureVisible (ptCursorPos);
2872         }
2873     }
2874 }
2875
2876 void CCrystalTextView::
2877 OnInitialUpdate ()
2878 {
2879   CView::OnInitialUpdate ();
2880   CString sDoc = GetDocument ()->GetPathName (), sExt = GetExt (sDoc);
2881   if (!sExt.IsEmpty())
2882       SetTextType (sExt);
2883   AttachToBuffer (nullptr);
2884
2885   CSplitterWnd *pSplitter = GetParentSplitter (this, false);
2886   if (pSplitter != nullptr)
2887     {
2888       //  See CSplitterWnd::IdFromRowCol() implementation
2889       int nRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
2890       int nCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
2891       ASSERT (nRow >= 0 && nRow < pSplitter->GetRowCount ());
2892       ASSERT (nCol >= 0 && nCol < pSplitter->GetColumnCount ());
2893
2894       if (nRow > 0)
2895         {
2896           CCrystalTextView *pSiblingView = GetSiblingView (0, nCol);
2897           if (pSiblingView != nullptr && pSiblingView != this)
2898             {
2899               m_nOffsetChar = pSiblingView->m_nOffsetChar;
2900               ASSERT (m_nOffsetChar >= 0 && m_nOffsetChar <= GetMaxLineLength (m_nTopLine, GetScreenLines()));
2901             }
2902         }
2903
2904       if (nCol > 0)
2905         {
2906           CCrystalTextView *pSiblingView = GetSiblingView (nRow, 0);
2907           if (pSiblingView != nullptr && pSiblingView != this)
2908             {
2909               m_nTopLine = pSiblingView->m_nTopLine;
2910               ASSERT (m_nTopLine >= 0 && m_nTopLine < GetLineCount ());
2911             }
2912         }
2913     }
2914   SetFont (m_LogFont);
2915   if (m_bRememberLastPos && !sDoc.IsEmpty ())
2916     {
2917       DWORD dwLastPos[3];
2918       CString sKey = REG_EDITPAD;
2919       sKey += _T ("\\Remembered");
2920       CReg reg;
2921       if (reg.Open (HKEY_CURRENT_USER, sKey, KEY_READ) &&
2922         reg.LoadBinary (sDoc, (LPBYTE) dwLastPos, sizeof (dwLastPos)))
2923         {
2924           CPoint ptCursorPos;
2925           ptCursorPos.x = dwLastPos[1];
2926           ptCursorPos.y = dwLastPos[2];
2927           if (IsValidTextPosY (ptCursorPos))
2928             {
2929               if (!IsValidTextPosX (ptCursorPos))
2930                 ptCursorPos.x = 0;
2931               ASSERT_VALIDTEXTPOS (ptCursorPos);
2932               SetCursorPos (ptCursorPos);
2933               SetSelection (ptCursorPos, ptCursorPos);
2934               SetAnchor (ptCursorPos);
2935               EnsureVisible (ptCursorPos);
2936             }
2937         }
2938     }
2939 }
2940
2941 /////////////////////////////////////////////////////////////////////////////
2942 // CCrystalTextView printing
2943
2944 void CCrystalTextView::
2945 OnPrepareDC (CDC * pDC, CPrintInfo * pInfo /*= nullptr*/)
2946 {
2947   CView::OnPrepareDC (pDC, pInfo);
2948
2949   if (pInfo != nullptr)
2950     {
2951       pInfo->m_bContinuePrinting = true;
2952       if (m_nPrintPages != 0 && (int) pInfo->m_nCurPage > m_nPrintPages)
2953         pInfo->m_bContinuePrinting = false;
2954     }
2955 }
2956
2957 BOOL CCrystalTextView::
2958 OnPreparePrinting (CPrintInfo * pInfo)
2959 {
2960   return DoPreparePrinting (pInfo);
2961 }
2962
2963 void CCrystalTextView::
2964 GetPrintHeaderText (int nPageNum, CString & text)
2965 {
2966   ASSERT (m_bPrintHeader);
2967   text = _T ("");
2968 }
2969
2970 void CCrystalTextView::
2971 GetPrintFooterText (int nPageNum, CString & text)
2972 {
2973   ASSERT (m_bPrintFooter);
2974   text.Format (_T ("Page %d/%d"), nPageNum, m_nPrintPages);
2975 }
2976
2977 void CCrystalTextView::
2978 PrintHeader (CDC * pdc, int nPageNum)
2979 {
2980   CRect rcHeader = m_rcPrintArea;
2981   rcHeader.bottom = rcHeader.top;
2982   rcHeader.top -= (m_nPrintLineHeight + m_nPrintLineHeight / 2);
2983
2984   CString text;
2985   GetPrintHeaderText (nPageNum, text);
2986   if (!text.IsEmpty ())
2987     pdc->DrawText (text, &rcHeader, DT_CENTER | DT_NOPREFIX | DT_TOP | DT_SINGLELINE);
2988 }
2989
2990 void CCrystalTextView::
2991 PrintFooter (CDC * pdc, int nPageNum)
2992 {
2993   CRect rcFooter = m_rcPrintArea;
2994   rcFooter.top = rcFooter.bottom;
2995   rcFooter.bottom += (m_nPrintLineHeight + m_nPrintLineHeight / 2);
2996
2997   CString text;
2998   GetPrintFooterText (nPageNum, text);
2999   if (!text.IsEmpty ())
3000     pdc->DrawText (text, &rcFooter, DT_CENTER | DT_NOPREFIX | DT_BOTTOM | DT_SINGLELINE);
3001 }
3002
3003 /**
3004 * @brief Retrieves the print margins
3005 * @param nLeft   [out] Left margin
3006 * @param nTop    [out] Top margin
3007 * @param nRight  [out] right margin
3008 * @param nBottom [out] Bottom margin
3009 */
3010 void CCrystalTextView::
3011 GetPrintMargins (long & nLeft, long & nTop, long & nRight, long & nBottom)
3012 {
3013   nLeft = DEFAULT_PRINT_MARGIN;
3014   nTop = DEFAULT_PRINT_MARGIN;
3015   nRight = DEFAULT_PRINT_MARGIN;
3016   nBottom = DEFAULT_PRINT_MARGIN;
3017   CReg reg;
3018   if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
3019     {
3020       DWORD dwTemp;
3021       if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
3022         nLeft = dwTemp;
3023       if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
3024         nRight = dwTemp;
3025       if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
3026         nTop = dwTemp;
3027       if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
3028         nBottom = dwTemp;
3029     }
3030 }
3031
3032 void CCrystalTextView::
3033 RecalcPageLayouts (CDC * pdc, CPrintInfo * pInfo)
3034 {
3035   m_ptPageArea = pInfo->m_rectDraw;
3036   m_ptPageArea.NormalizeRect ();
3037
3038   m_nPrintLineHeight = pdc->GetTextExtent (_T ("X")).cy;
3039
3040   m_rcPrintArea = m_ptPageArea;
3041   CSize szTopLeft, szBottomRight;
3042   CWinApp *pApp = AfxGetApp ();
3043   ASSERT (pApp != nullptr);
3044   GetPrintMargins (szTopLeft.cx, szTopLeft.cy, szBottomRight.cx, szBottomRight.cy);
3045   pdc->HIMETRICtoLP (&szTopLeft);
3046   pdc->HIMETRICtoLP (&szBottomRight);
3047   m_rcPrintArea.left += szTopLeft.cx;
3048   m_rcPrintArea.right -= szBottomRight.cx;
3049   m_rcPrintArea.top += szTopLeft.cy;
3050   m_rcPrintArea.bottom -= szBottomRight.cy;
3051   if (m_bPrintHeader)
3052     m_rcPrintArea.top += m_nPrintLineHeight + m_nPrintLineHeight / 2;
3053   if (m_bPrintFooter)
3054     m_rcPrintArea.bottom -= m_nPrintLineHeight + m_nPrintLineHeight / 2;
3055
3056   InvalidateLineCache (0, -1);
3057
3058   m_nScreenChars = (m_rcPrintArea.Width () - GetMarginWidth (pdc)) / GetCharWidth ();
3059   m_nScreenLines = m_rcPrintArea.Height () / GetLineHeight ();
3060 }
3061
3062 void CCrystalTextView::
3063 OnBeginPrinting (CDC * pdc, CPrintInfo * pInfo)
3064 {
3065   ASSERT (m_pPrintFont == nullptr);
3066   CFont *pDisplayFont = GetFont ();
3067
3068   LOGFONT lf;
3069   CDC *pDisplayDC = GetDC ();
3070   pDisplayFont->GetLogFont (&lf);
3071   lf.lfHeight = MulDiv (lf.lfHeight, pdc->GetDeviceCaps (LOGPIXELSY), pDisplayDC->GetDeviceCaps (LOGPIXELSY));
3072   lf.lfWidth = MulDiv (lf.lfWidth, pdc->GetDeviceCaps (LOGPIXELSX), pDisplayDC->GetDeviceCaps (LOGPIXELSX));
3073   ReleaseDC (pDisplayDC);
3074
3075   m_pPrintFont = new CFont;
3076   if (!m_pPrintFont->CreateFontIndirect (&lf))
3077     {
3078       delete m_pPrintFont;
3079       m_pPrintFont = nullptr;
3080       return;
3081     }
3082
3083   GetFont (m_lfSavedBaseFont);
3084   m_pPrintFont->GetLogFont (&lf);
3085   SetFont(lf);
3086
3087   m_nPrintPages = 0;
3088   m_bPrinting = true;
3089 }
3090
3091 void CCrystalTextView::
3092 OnEndPrinting (CDC * pdc, CPrintInfo * pInfo)
3093 {
3094   if (m_pPrintFont != nullptr)
3095     {
3096       delete m_pPrintFont;
3097       m_pPrintFont = nullptr;
3098       SetFont(m_lfSavedBaseFont);
3099     }
3100   m_nPrintPages = 0;
3101   m_nPrintLineHeight = 0;
3102   m_bPrinting = false;
3103 }
3104
3105 void CCrystalTextView::
3106 OnPrint (CDC * pdc, CPrintInfo * pInfo)
3107 {
3108   pdc->SelectObject (m_pPrintFont);
3109
3110   const COLORREF defaultLineColor = RGB(0,0,0);
3111   const COLORREF defaultBgColor = RGB(255,255,255);
3112
3113   RecalcPageLayouts (pdc, pInfo);
3114
3115   m_nPrintPages = (GetSubLineCount () + GetScreenLines () - 1) / GetScreenLines ();
3116
3117   ASSERT (pInfo->m_nCurPage >= 1 && (int) pInfo->m_nCurPage <= m_nPrintPages);
3118
3119   int nTopSubLine = (pInfo->m_nCurPage - 1) * GetScreenLines ();
3120   int nEndSubLine = nTopSubLine + GetScreenLines () - 1;
3121   if (nEndSubLine >= GetSubLineCount ())
3122     nEndSubLine = GetSubLineCount () - 1;
3123   int nSubLines;
3124   int nTopLine, nEndLine;
3125   GetLineBySubLine (nTopSubLine, nTopLine, nSubLines);
3126   GetLineBySubLine (nEndSubLine, nEndLine, nSubLines);
3127
3128   TRACE (_T ("Printing page %d of %d, lines %d - %d\n"), 
3129         pInfo->m_nCurPage, m_nPrintPages, nTopLine, nEndLine);
3130
3131   pdc->SetTextColor(defaultLineColor);
3132   pdc->SetBkColor(defaultBgColor);
3133
3134   if (m_bPrintHeader)
3135     {
3136       PrintHeader (pdc, pInfo->m_nCurPage);
3137     }
3138
3139   if (m_bPrintFooter)
3140     {
3141       PrintFooter (pdc, pInfo->m_nCurPage);
3142     }
3143
3144   // set clipping region
3145   // see http://support.microsoft.com/kb/128334
3146   CRect rectClip = m_rcPrintArea;
3147   rectClip.right = rectClip.left + GetMarginWidth (pdc) + GetScreenChars () * GetCharWidth ();
3148   rectClip.bottom = rectClip.top + GetScreenLines () * GetLineHeight ();
3149   if (!!pdc->IsKindOf (RUNTIME_CLASS (CPreviewDC)))
3150     {
3151       CPreviewDC *pPrevDC = (CPreviewDC *)pdc;
3152
3153       pPrevDC->PrinterDPtoScreenDP (&rectClip.TopLeft ());
3154       pPrevDC->PrinterDPtoScreenDP (&rectClip.BottomRight ());
3155
3156       CPoint ptOrg;
3157       ::GetViewportOrgEx (pdc->m_hDC,&ptOrg);
3158       rectClip += ptOrg;
3159     }
3160   CRgn rgn;
3161   rgn.CreateRectRgnIndirect (&rectClip);
3162   pdc->SelectClipRgn (&rgn);
3163
3164   // print lines
3165   CRect rcMargin;
3166   CRect rcLine = m_rcPrintArea;
3167   int nLineHeight = GetLineHeight ();
3168   rcLine.bottom = rcLine.top + nLineHeight;
3169   rcMargin = rcLine;
3170   rcMargin.right = rcMargin.left + GetMarginWidth (pdc);
3171   rcLine.left = rcMargin.right;
3172
3173   int nSubLineOffset = GetSubLineIndex (nTopLine) - nTopSubLine;
3174   if( nSubLineOffset < 0 )
3175   {
3176     rcLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
3177   }
3178
3179   int nLineCount = GetLineCount();
3180   int nCurrentLine;
3181   for (nCurrentLine = nTopLine; nCurrentLine <= nEndLine; nCurrentLine++)
3182     {
3183       rcLine.bottom = rcLine.top + GetSubLines (nCurrentLine) * nLineHeight;
3184       rcMargin.bottom = rcLine.bottom;
3185
3186       if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
3187         {
3188           DrawMargin (pdc, rcMargin, nCurrentLine, nCurrentLine + 1);
3189           DrawSingleLine (pdc, rcLine, nCurrentLine);
3190           if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
3191             DrawBoundaryLine (pdc, rcMargin.left, rcLine.right, rcMargin.bottom-1);
3192         }
3193
3194       rcLine.top = rcLine.bottom;
3195       rcMargin.top = rcLine.bottom;
3196     }
3197
3198   pdc->SelectClipRgn (nullptr);
3199 }
3200
3201
3202 /////////////////////////////////////////////////////////////////////////////
3203 // CCrystalTextView message handlers
3204
3205 int CCrystalTextView::
3206 GetLineCount ()
3207 {
3208   if (m_pTextBuffer == nullptr)
3209     return 1;                   //  Single empty line
3210
3211   int nLineCount = m_pTextBuffer->GetLineCount ();
3212   ASSERT (nLineCount > 0);
3213   return nLineCount;
3214 }
3215
3216 //BEGIN SW
3217 int CCrystalTextView::GetSubLineCount()
3218 {
3219   const int nLineCount = GetLineCount();
3220
3221   // if we do not wrap words, number of sub lines is
3222   // equal to number of lines
3223   if( !m_bWordWrap && !m_bHideLines )
3224     return nLineCount;
3225
3226   // calculate number of sub lines
3227   if (nLineCount <= 0)
3228       return 0;
3229   return CCrystalTextView::GetSubLineIndex( nLineCount - 1 ) + GetSubLines( nLineCount - 1 );
3230 }
3231
3232 int CCrystalTextView::GetSubLineIndex( int nLineIndex )
3233 {
3234   // if we do not wrap words, subline index of this line is equal to its index
3235   if( !m_bWordWrap && !m_bHideLines )
3236     return nLineIndex;
3237
3238   // calculate subline index of the line
3239   int   nSubLineCount = 0;
3240   int nLineCount = GetLineCount();
3241
3242   if( nLineIndex >= nLineCount )
3243     nLineIndex = nLineCount - 1;
3244
3245   // return cached subline index of the line if it is already cached.
3246   if (nLineIndex <= m_nLastLineIndexCalculatedSubLineIndex)
3247     return (*m_panSubLineIndexCache)[nLineIndex];
3248
3249   // calculate subline index of the line and cache it.
3250   if (m_nLastLineIndexCalculatedSubLineIndex >= 0)
3251     nSubLineCount = (*m_panSubLineIndexCache)[m_nLastLineIndexCalculatedSubLineIndex];
3252   else
3253     {
3254       m_nLastLineIndexCalculatedSubLineIndex = 0;
3255       m_panSubLineIndexCache->SetAtGrow( 0, 0 );
3256     }
3257
3258 // TODO: Rethink this, it is very time consuming
3259   for( int i = m_nLastLineIndexCalculatedSubLineIndex; i < nLineIndex; i++ )
3260     {
3261       m_panSubLineIndexCache->SetAtGrow( i, nSubLineCount);
3262       nSubLineCount+= GetSubLines( i );
3263     }
3264   m_panSubLineIndexCache->SetAtGrow( nLineIndex, nSubLineCount);
3265   m_nLastLineIndexCalculatedSubLineIndex = nLineIndex;
3266
3267   return nSubLineCount;
3268 }
3269
3270 // See comment in the header file
3271 void CCrystalTextView::GetLineBySubLine(int nSubLineIndex, int &nLine, int &nSubLine)
3272 {
3273   if (GetSubLineCount() == 0)
3274     {
3275       nLine = 0;
3276       nSubLine = 0;
3277       return;
3278     }
3279
3280   ASSERT( nSubLineIndex < GetSubLineCount() );
3281
3282   // if we do not wrap words, nLine is equal to nSubLineIndex and nSubLine is allways 0
3283   if ( !m_bWordWrap && !m_bHideLines )
3284     {
3285       nLine = nSubLineIndex;
3286       nSubLine = 0;
3287       return;
3288     }
3289
3290   // compute result
3291   const int nLineCount = GetLineCount();
3292
3293   // binary search
3294   int base = 0, i = 0, nSubLineIndex2 = 0;
3295   for (int lim = nLineCount; lim != 0; lim >>= 1)
3296     {
3297       i = base + (lim >> 1);
3298       nSubLineIndex2 = GetSubLineIndex(i);
3299       if (nSubLineIndex >= nSubLineIndex2 && nSubLineIndex < nSubLineIndex2 + GetSubLines(i))
3300         break;
3301       else if (nSubLineIndex2 <= nSubLineIndex) /* key > p: move right */
3302         {
3303           base = i + 1;
3304           lim--;
3305         } /* else move left */
3306     }
3307
3308   ASSERT(i < nLineCount);
3309   nLine = i;
3310   nSubLine = nSubLineIndex - nSubLineIndex2;
3311 }
3312
3313 int CCrystalTextView::
3314 GetLineLength (int nLineIndex) const
3315 {
3316   if (m_pTextBuffer == nullptr)
3317     return 0;
3318   return m_pTextBuffer->GetLineLength (nLineIndex);
3319 }
3320
3321 int CCrystalTextView::
3322 GetFullLineLength (int nLineIndex) const
3323 {
3324   if (m_pTextBuffer == nullptr)
3325     return 0;
3326   return m_pTextBuffer->GetFullLineLength (nLineIndex);
3327 }
3328
3329 // How many bytes of line are displayed on-screen?
3330 int CCrystalTextView::
3331 GetViewableLineLength (int nLineIndex) const
3332 {
3333   if (m_bViewEols)
3334     return GetFullLineLength(nLineIndex);
3335   else
3336     return GetLineLength(nLineIndex);
3337 }
3338
3339 LPCTSTR CCrystalTextView::
3340 GetLineChars (int nLineIndex) const
3341 {
3342   if (m_pTextBuffer == nullptr)
3343     return nullptr;
3344   return m_pTextBuffer->GetLineChars (nLineIndex);
3345 }
3346
3347 /** 
3348  * @brief Reattach buffer after deleting/inserting ghost lines :
3349  * 
3350  * @note no need to reinitialize the horizontal scrollbar
3351  * no need to reset the editor options (m_bOvrMode, m_bLastReplace)
3352  */
3353 void CCrystalTextView::
3354 ReAttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3355 {
3356   if (m_pTextBuffer != nullptr)
3357     m_pTextBuffer->RemoveView (this);
3358   if (pBuf == nullptr)
3359     {
3360       pBuf = LocateTextBuffer ();
3361       //  ...
3362     }
3363   m_pTextBuffer = pBuf;
3364   if (m_pTextBuffer != nullptr)
3365     m_pTextBuffer->AddView (this);
3366   // don't reset CCrystalEditView options
3367   CCrystalTextView::ResetView ();
3368
3369   //  Init scrollbars arrows
3370   CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3371   if (pVertScrollBarCtrl != nullptr)
3372     pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3373                                          ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3374   //  Update vertical scrollbar only
3375   RecalcVertScrollBar ();
3376 }
3377
3378 /** 
3379  * @brief Attach buffer (maybe for the first time)
3380  * initialize the view and initialize both scrollbars
3381  */
3382 void CCrystalTextView::
3383 AttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3384 {
3385   if (m_pTextBuffer != nullptr)
3386     m_pTextBuffer->RemoveView (this);
3387   if (pBuf == nullptr)
3388     {
3389       pBuf = LocateTextBuffer ();
3390       //  ...
3391     }
3392   m_pTextBuffer = pBuf;
3393   if (m_pTextBuffer != nullptr)
3394     m_pTextBuffer->AddView (this);
3395   ResetView ();
3396
3397   //  Init scrollbars
3398   CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3399   if (pVertScrollBarCtrl != nullptr)
3400     pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3401                                          ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3402   CScrollBar *pHorzScrollBarCtrl = GetScrollBarCtrl (SB_HORZ);
3403   if (pHorzScrollBarCtrl != nullptr)
3404     pHorzScrollBarCtrl->EnableScrollBar (GetScreenChars () >= GetMaxLineLength (m_nTopLine, GetScreenLines())?
3405                                          ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3406
3407   //  Update scrollbars
3408   RecalcVertScrollBar ();
3409   RecalcHorzScrollBar ();
3410 }
3411
3412 void CCrystalTextView::
3413 DetachFromBuffer ()
3414 {
3415   if (m_pTextBuffer != nullptr)
3416     {
3417       m_pTextBuffer->RemoveView (this);
3418       m_pTextBuffer = nullptr;
3419       // don't reset CCrystalEditView options
3420       CCrystalTextView::ResetView ();
3421     }
3422 }
3423
3424 int CCrystalTextView::
3425 GetScreenLines ()
3426 {
3427   if (m_nScreenLines == -1)
3428     {
3429       CRect rect;
3430       GetClientRect (&rect);
3431       m_nScreenLines = rect.Height () / GetLineHeight ();
3432     }
3433   return m_nScreenLines;
3434 }
3435
3436 bool CCrystalTextView::
3437 GetItalic (int nColorIndex)
3438 {
3439   // WINMERGE - since italic text has problems,
3440   // lets disable it. E.g. "_" chars disappear and last
3441   // char may be cropped.
3442   return false;
3443
3444   // return nColorIndex == COLORINDEX_COMMENT;
3445 }
3446
3447 bool CCrystalTextView::
3448 GetBold (int nColorIndex)
3449 {
3450   if (m_pColors  != nullptr)
3451     {
3452       nColorIndex &= ~COLORINDEX_APPLYFORCE;
3453       return m_pColors->GetBold(nColorIndex);
3454     }
3455   else
3456     return false;
3457 }
3458
3459 int CCrystalTextView::
3460 GetScreenChars ()
3461 {
3462   if (m_nScreenChars == -1)
3463     {
3464       CRect rect;
3465       GetClientRect (&rect);
3466       m_nScreenChars = (rect.Width () - GetMarginWidth ()) / GetCharWidth ();
3467     }
3468   return m_nScreenChars;
3469 }
3470
3471 void CCrystalTextView::
3472 OnDestroy ()
3473 {
3474   GetFont ()->GetLogFont (&m_lfBaseFont);
3475   DetachFromBuffer ();
3476   m_hAccel = nullptr;
3477
3478   CView::OnDestroy ();
3479
3480   for (int I = 0; I < 4; I++)
3481     {
3482       if (m_apFonts[I] != nullptr)
3483         {
3484           delete m_apFonts[I];
3485           m_apFonts[I] = nullptr;
3486         }
3487     }
3488   if (m_pCacheBitmap != nullptr)
3489     {
3490       delete m_pCacheBitmap;
3491       m_pCacheBitmap = nullptr;
3492     }
3493 }
3494
3495 BOOL CCrystalTextView::
3496 OnEraseBkgnd (CDC * pdc)
3497 {
3498   UNREFERENCED_PARAMETER(pdc);
3499   return true;
3500 }
3501
3502 void CCrystalTextView::
3503 OnSize (UINT nType, int cx, int cy)
3504 {
3505   CView::OnSize (nType, cx, cy);
3506
3507   //BEGIN SW
3508   // get char position of top left visible character with old cached word wrap
3509   CPoint        topPos;
3510   SubLineCursorPosToTextPos( CPoint( 0, m_nTopSubLine ), topPos );
3511   //END SW
3512
3513   //BEGIN SW
3514   // we have to recompute the line wrapping
3515   InvalidateScreenRect(false);
3516
3517   // compute new top sub line
3518   CPoint        topSubLine;
3519   CharPosToPoint( topPos.y, topPos.x, topSubLine );
3520   m_nTopSubLine = GetSubLineIndex(topPos.y) + topSubLine.y;
3521
3522   ScrollToSubLine( m_nTopSubLine );
3523
3524   // set caret to right position
3525   UpdateCaret();
3526   //END SW
3527
3528   RecalcVertScrollBar (false, false);
3529   RecalcHorzScrollBar (false, false);
3530 }
3531
3532 void CCrystalTextView::
3533 UpdateSiblingScrollPos (bool bHorz)
3534 {
3535   CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
3536   if (pSplitterWnd != nullptr)
3537     {
3538       //  See CSplitterWnd::IdFromRowCol() implementation for details
3539       int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3540       int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3541       ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
3542       ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
3543
3544       if (bHorz)
3545         {
3546           int nCols = pSplitterWnd->GetColumnCount ();
3547           for (int nCol = 0; nCol < nCols; nCol++)
3548             {
3549               if (nCol != nCurrentCol)  //  We don't need to update ourselves
3550                 {
3551                   CCrystalTextView *pSiblingView = GetSiblingView (nCurrentRow, nCol);
3552                   if (pSiblingView != nullptr)
3553                     pSiblingView->OnUpdateSibling (this, false);
3554                 }
3555             }
3556         }
3557       else
3558         {
3559           int nRows = pSplitterWnd->GetRowCount ();
3560           for (int nRow = 0; nRow < nRows; nRow++)
3561             {
3562               if (nRow != nCurrentRow)  //  We don't need to update ourselves
3563                 {
3564                   CCrystalTextView *pSiblingView = GetSiblingView (nRow, nCurrentCol);
3565                   if (pSiblingView != nullptr)
3566                     pSiblingView->OnUpdateSibling (this, false);
3567                 }
3568             }
3569         }
3570     }
3571 }
3572
3573 void CCrystalTextView::
3574 OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
3575 {
3576   if (pUpdateSource != this)
3577     {
3578       ASSERT (pUpdateSource != nullptr);
3579       ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
3580       if (bHorz)
3581         {
3582           ASSERT (pUpdateSource->m_nTopLine >= 0);
3583           ASSERT (pUpdateSource->m_nTopLine < GetLineCount ());
3584           if (pUpdateSource->m_nTopLine != m_nTopLine)
3585             {
3586               ScrollToLine (pUpdateSource->m_nTopLine, true, false);
3587               UpdateCaret ();
3588             }
3589         }
3590       else
3591         {
3592           ASSERT (pUpdateSource->m_nOffsetChar >= 0);
3593           ASSERT (pUpdateSource->m_nOffsetChar < GetMaxLineLength (m_nTopLine, GetScreenLines()));
3594           if (pUpdateSource->m_nOffsetChar != m_nOffsetChar)
3595             {
3596               ScrollToChar (pUpdateSource->m_nOffsetChar, true, false);
3597               UpdateCaret ();
3598             }
3599         }
3600     }
3601 }
3602
3603 void CCrystalTextView::
3604 RecalcVertScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
3605 {
3606   SCROLLINFO si = {0};
3607   si.cbSize = sizeof (si);
3608   if (bPositionOnly)
3609     {
3610       si.fMask = SIF_POS;
3611       si.nPos = m_nTopSubLine;
3612     }
3613   else
3614     {
3615       const int nScreenLines = GetScreenLines();
3616       if( m_nTopSubLine > 0 && nScreenLines >= GetSubLineCount() )
3617         {
3618           m_nTopLine = 0;
3619           Invalidate ();
3620           UpdateCaret ();
3621         }
3622       si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3623       si.nMin = 0;
3624       si.nMax = GetSubLineCount() - 1;
3625       si.nPage = nScreenLines;
3626       si.nPos = m_nTopSubLine;
3627     }
3628   VERIFY (SetScrollInfo (SB_VERT, &si, bRedraw));
3629 }
3630
3631 void CCrystalTextView::
3632 OnVScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
3633 {
3634   CView::OnVScroll (nSBCode, nPos, pScrollBar);
3635
3636   // Note we cannot use nPos because of its 16-bit nature
3637   SCROLLINFO si = {0};
3638   si.cbSize = sizeof (si);
3639   si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3640   VERIFY (GetScrollInfo (SB_VERT, &si));
3641
3642   // Get the minimum and maximum scroll-bar positions.
3643   int nMinPos = si.nMin;
3644   int nMaxPos = si.nMax;
3645
3646   // Get the current position of scroll box.
3647   int nCurPos = si.nPos;
3648
3649   bool bDisableSmooth = true;
3650   switch (nSBCode)
3651     {
3652     case SB_TOP:                        // Scroll to top.
3653       nCurPos = nMinPos;
3654       bDisableSmooth = false;
3655       break;
3656
3657     case SB_BOTTOM:                     // Scroll to bottom.
3658       nCurPos = nMaxPos;
3659       bDisableSmooth = false;
3660       break;
3661
3662     case SB_LINEUP:                     // Scroll one line up.
3663       if (nCurPos > nMinPos)
3664         nCurPos--;
3665       break;
3666
3667     case SB_LINEDOWN:           // Scroll one line down.
3668       if (nCurPos < nMaxPos)
3669         nCurPos++;
3670       break;
3671
3672     case SB_PAGEUP:                     // Scroll one page up.
3673       nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
3674       bDisableSmooth = false;
3675       break;
3676
3677     case SB_PAGEDOWN:           // Scroll one page down.
3678       nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
3679       bDisableSmooth = false;
3680       break;
3681
3682     case SB_THUMBPOSITION:              // Scroll to absolute position. nPos is the position
3683       nCurPos = si.nTrackPos;   // of the scroll box at the end of the drag operation.
3684       break;
3685
3686     case SB_THUMBTRACK:                 // Drag scroll box to specified position. nPos is the
3687       nCurPos = si.nTrackPos;   // position that the scroll box has been dragged to.
3688       break;
3689     }
3690   ScrollToSubLine(nCurPos, bDisableSmooth);
3691 }
3692
3693 void CCrystalTextView::
3694 RecalcHorzScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
3695 {
3696   SCROLLINFO si = {0};
3697   si.cbSize = sizeof (si);
3698
3699   const int nScreenChars = GetScreenChars();
3700   
3701   if (m_bWordWrap)
3702     {
3703        if (m_nOffsetChar > nScreenChars)
3704          {
3705            m_nOffsetChar = 0;
3706            UpdateCaret ();
3707          }
3708
3709       // Disable horizontal scroll bar
3710       si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3711       SetScrollInfo (SB_HORZ, &si);
3712       return;
3713     }
3714
3715   const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
3716
3717   if (bPositionOnly)
3718     {
3719       si.fMask = SIF_POS;
3720       si.nPos = m_nOffsetChar;
3721     }
3722   else
3723     {
3724       if (nScreenChars >= nMaxLineLen && m_nOffsetChar > 0)
3725         {
3726           m_nOffsetChar = 0;
3727           Invalidate ();
3728           UpdateCaret ();
3729         }
3730       si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3731       si.nMin = 0;
3732
3733       // Horiz scroll limit to longest line + one screenwidth 
3734       si.nMax = nMaxLineLen + nScreenChars;
3735       si.nPage = nScreenChars;
3736       si.nPos = m_nOffsetChar;
3737     }
3738   VERIFY (SetScrollInfo (SB_HORZ, &si, bRedraw));
3739 }
3740
3741 void CCrystalTextView::
3742 OnHScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
3743 {
3744   // Default handler not needed
3745   //CView::OnHScroll (nSBCode, nPos, pScrollBar);
3746
3747   //  Again, we cannot use nPos because it's 16-bit
3748   SCROLLINFO si = {0};
3749   si.cbSize = sizeof (si);
3750   si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3751   VERIFY (GetScrollInfo (SB_HORZ, &si));
3752
3753   // Get the minimum and maximum scroll-bar positions.
3754   int nMinPos = si.nMin;
3755   int nMaxPos = si.nMax;
3756
3757   // Get the current position of scroll box.
3758   int nCurPos = si.nPos;
3759
3760   switch (nSBCode)
3761     {
3762     case SB_LEFT:             // Scroll to far left.
3763       nCurPos = nMinPos;
3764       break;
3765
3766     case SB_RIGHT:            // Scroll to far right.
3767       nCurPos = nMaxPos;
3768       break;
3769
3770     case SB_ENDSCROLL:        // End scroll.
3771       break;
3772
3773     case SB_LINELEFT:         // Scroll left.
3774       if (nCurPos > nMinPos)
3775         nCurPos--;
3776       break;
3777
3778     case SB_LINERIGHT:        // Scroll right.
3779       if (nCurPos < nMaxPos)
3780         nCurPos++;
3781       break;
3782
3783     case SB_PAGELEFT:         // Scroll one page left.
3784       nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
3785       break;
3786
3787     case SB_PAGERIGHT:        // Scroll one page right.
3788       nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
3789       break;
3790
3791     case SB_THUMBPOSITION:    // Scroll to absolute position. The current position is 
3792       nCurPos = si.nTrackPos; // specified by the nPos parameter.
3793       break;
3794
3795     case SB_THUMBTRACK:       // Drag scroll box to specified position. The current 
3796       nCurPos = si.nTrackPos; // position is specified by the nPos parameter
3797                               // The SB_THUMBTRACK scroll-bar code typically is used by applications that give 
3798                               // some feedback while the scroll box is being dragged.
3799       break;
3800     }
3801   ScrollToChar (nCurPos, true);
3802   // This is needed, but why ? OnVScroll don't need to call UpdateCaret
3803   UpdateCaret ();
3804 }
3805
3806 BOOL CCrystalTextView::
3807 OnSetCursor (CWnd * pWnd, UINT nHitTest, UINT message)
3808 {
3809   if (nHitTest == HTCLIENT)
3810     {
3811       CPoint pt;
3812       ::GetCursorPos (&pt);
3813       ScreenToClient (&pt);
3814       if (pt.x < GetMarginWidth ())
3815         {
3816           ::SetCursor (::LoadCursor (AfxGetInstanceHandle (), MAKEINTRESOURCE (IDR_MARGIN_CURSOR)));
3817         }
3818       else
3819         {
3820           CPoint ptText = ClientToText (pt);
3821           PrepareSelBounds ();
3822           if (IsInsideSelBlock (ptText))
3823             {
3824               //  [JRT]:  Support For Disabling Drag and Drop...
3825               if (!m_bDisableDragAndDrop)   // If Drag And Drop Not Disabled
3826
3827                 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW));     // Set To Arrow Cursor
3828
3829             }
3830           else
3831             ::SetCursor (::LoadCursor (nullptr, IDC_IBEAM));
3832         }
3833       return true;
3834     }
3835   return CView::OnSetCursor (pWnd, nHitTest, message);
3836 }
3837
3838 /** 
3839  * @brief Converts client area point to text position.
3840  * @param [in] point Client area point.
3841  * @return Text position (line index, char index in line).
3842  * @note For gray selection area char index is 0.
3843  */
3844 CPoint CCrystalTextView::
3845 ClientToText (const CPoint & point)
3846 {
3847   //BEGIN SW
3848   const int nSubLineCount = GetSubLineCount();
3849   const int nLineCount = GetLineCount();
3850
3851   CPoint pt;
3852   pt.y = m_nTopSubLine + point.y / GetLineHeight();
3853   if (pt.y >= nSubLineCount)
3854     pt.y = nSubLineCount - 1;
3855   if (pt.y < 0)
3856     pt.y = 0;
3857
3858   int nLine;
3859   int nSubLineOffset;
3860   int nOffsetChar = m_nOffsetChar;
3861
3862   GetLineBySubLine( pt.y, nLine, nSubLineOffset );
3863   pt.y = nLine;
3864
3865   LPCTSTR pszLine = nullptr;
3866   int nLength = 0;
3867   vector<int> anBreaks(1);
3868   int nBreaks = 0;
3869
3870   if (pt.y >= 0 && pt.y < nLineCount)
3871     {
3872       nLength = GetLineLength( pt.y );
3873       anBreaks.resize(nLength + 1);
3874       pszLine = GetLineChars(pt.y);
3875       WrapLineCached( pt.y, GetScreenChars(), &anBreaks[0], nBreaks );
3876
3877       if (nSubLineOffset > 0)
3878         {
3879           if (nSubLineOffset < nBreaks)
3880             nOffsetChar = anBreaks[nSubLineOffset - 1];
3881           else if (nBreaks > 0)
3882             nOffsetChar = anBreaks[nBreaks - 1];
3883         }
3884       if (nBreaks > nSubLineOffset)
3885         nLength = anBreaks[nSubLineOffset] - 1;
3886     }
3887
3888   int nPos = 0;
3889   // Char index for margin area is 0
3890   if (point.x > GetMarginWidth())
3891     nPos = nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth();
3892   if (nPos < 0)
3893     nPos = 0;
3894
3895   int nIndex = 0, nPrevIndex = 0;
3896   int nCurPos = 0;
3897   int n = 0;
3898   int i = 0;
3899   const int nTabSize = GetTabSize();
3900
3901   m_iterChar.setText(reinterpret_cast<const UChar *>(pszLine), nLength);
3902   while (nIndex < nLength)
3903     {
3904       if (nBreaks && nIndex == anBreaks[i])
3905         {
3906           n = nIndex;
3907           i++;
3908         }
3909
3910       int nOffset;
3911       if (pszLine[nIndex] == '\t')
3912         nOffset = nTabSize - nCurPos % nTabSize;
3913       else
3914         nOffset = GetCharCellCountFromChar(pszLine + nIndex);
3915       n += nOffset;
3916       nCurPos += nOffset;
3917
3918       if (n > nPos && i == nSubLineOffset)
3919         break;
3920
3921       nPrevIndex = nIndex;
3922
3923       nIndex = m_iterChar.next();
3924     }
3925
3926   ASSERT(nIndex >= 0 && nIndex <= nLength);
3927   pt.x = nIndex;
3928   return pt;
3929 }
3930
3931 #ifdef _DEBUG
3932 void CCrystalTextView::
3933 AssertValidTextPos (const CPoint & point)
3934 {
3935   if (GetLineCount () > 0)
3936     {
3937       ASSERT (m_nTopLine >= 0 && m_nOffsetChar >= 0);
3938       ASSERT (point.y >= 0 && point.y < GetLineCount ());
3939       ASSERT (point.x >= 0 && point.x <= GetViewableLineLength (point.y));
3940     }
3941 }
3942 #endif
3943
3944 bool CCrystalTextView::
3945 IsValidTextPos (const CPoint &point)
3946 {
3947   return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
3948     point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
3949 }
3950
3951 bool CCrystalTextView::
3952 IsValidTextPosX (const CPoint &point)
3953 {
3954   return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
3955     point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
3956 }
3957
3958 bool CCrystalTextView::
3959 IsValidTextPosY (const CPoint &point)
3960 {
3961   return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
3962     point.y >= 0 && point.y < GetLineCount ();
3963 }
3964
3965 CPoint CCrystalTextView::
3966 TextToClient (const CPoint & point)
3967 {
3968   ASSERT_VALIDTEXTPOS (point);
3969   LPCTSTR pszLine = GetLineChars (point.y);
3970
3971   CPoint pt;
3972   //BEGIN SW
3973   CPoint        charPoint;
3974   int                   nSubLineStart = CharPosToPoint( point.y, point.x, charPoint );
3975   charPoint.y+= GetSubLineIndex( point.y );
3976
3977   // compute y-position
3978   pt.y = (charPoint.y - m_nTopSubLine) * GetLineHeight();
3979
3980   // if pt.x is null, we know the result
3981   if( charPoint.x == 0 )
3982     {
3983       pt.x = GetMarginWidth();
3984       return pt;
3985     }
3986
3987   // we have to calculate x-position
3988   int   nPreOffset = 0;
3989   /*ORIGINAL
3990   pt.y = (point.y - m_nTopLine) * GetLineHeight();
3991   */
3992   //END SW
3993   pt.x = 0;
3994   int nTabSize = GetTabSize ();
3995   m_iterChar.setText(reinterpret_cast<const UChar *>(pszLine), point.x);
3996   for (int nIndex = 0; nIndex < point.x; nIndex = m_iterChar.next())
3997     {
3998       //BEGIN SW
3999       if( nIndex == nSubLineStart )
4000         nPreOffset = pt.x;
4001       //END SW
4002       if (pszLine[nIndex] == _T ('\t'))
4003         pt.x += (nTabSize - pt.x % nTabSize);
4004       else
4005         pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4006     }
4007   //BEGIN SW
4008   pt.x-= nPreOffset;
4009   //END SW
4010
4011   pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4012   return pt;
4013 }
4014
4015 void CCrystalTextView::
4016 InvalidateLines (int nLine1, int nLine2, bool bInvalidateMargin /*= false*/ )
4017 {
4018   bInvalidateMargin = true;
4019   const int nLineHeight = GetLineHeight();
4020   if (nLine2 == -1)
4021     {
4022       CRect rcInvalid;
4023       GetClientRect (&rcInvalid);
4024       if (!bInvalidateMargin)
4025         rcInvalid.left += GetMarginWidth ();
4026       //BEGIN SW
4027       rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight;
4028       /*ORIGINAL
4029       rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4030       */
4031       //END SW
4032       InvalidateRect (&rcInvalid, false);
4033     }
4034   else
4035     {
4036       if (nLine2 < nLine1)
4037         {
4038           int nTemp = nLine1;
4039           nLine1 = nLine2;
4040           nLine2 = nTemp;
4041         }
4042       CRect rcInvalid;
4043       GetClientRect (&rcInvalid);
4044       if (!bInvalidateMargin)
4045         rcInvalid.left += GetMarginWidth ();
4046       //BEGIN SW
4047       rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight;
4048       rcInvalid.bottom = (GetSubLineIndex( nLine2 ) - m_nTopSubLine + GetSubLines( nLine2 )) * nLineHeight;
4049       /*ORIGINAL
4050       rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4051       rcInvalid.bottom = (nLine2 - m_nTopLine + 1) * GetLineHeight();
4052       */
4053       //END SW
4054       InvalidateRect (&rcInvalid, false);
4055     }
4056 }
4057
4058 void CCrystalTextView::
4059 SetSelection (const CPoint & ptStart, const CPoint & ptEnd, bool bUpdateView /* = true */)
4060 {
4061   ASSERT_VALIDTEXTPOS (ptStart);
4062   ASSERT_VALIDTEXTPOS (ptEnd);
4063   if (m_ptSelStart == ptStart && !m_bColumnSelection)
4064     {
4065       if (m_ptSelEnd != ptEnd)
4066         InvalidateLines (ptEnd.y, m_ptSelEnd.y);
4067     }
4068   else
4069     {
4070       InvalidateLines (ptStart.y, ptEnd.y);
4071       InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4072     }
4073   m_ptSelStart = ptStart;
4074   m_ptSelEnd = ptEnd;
4075 }
4076
4077 void CCrystalTextView::
4078 AdjustTextPoint (CPoint & point)
4079 {
4080   point.x += GetCharWidth () / 2;   //todo
4081
4082 }
4083
4084 void CCrystalTextView::
4085 OnSetFocus (CWnd * pOldWnd)
4086 {
4087   CView::OnSetFocus (pOldWnd);
4088
4089   m_bFocused = true;
4090   if (m_ptSelStart != m_ptSelEnd)
4091     InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4092   UpdateCaret ();
4093 }
4094
4095 DWORD CCrystalTextView::
4096 ParseLine (DWORD dwCookie, const TCHAR *pszChars, int nLength, TEXTBLOCK * pBuf, int &nActualItems)
4097 {
4098   return m_CurSourceDef->ParseLineX (dwCookie, pszChars, nLength, pBuf, nActualItems);
4099 }
4100
4101 int CCrystalTextView::
4102 CalculateActualOffset (int nLineIndex, int nCharIndex, bool bAccumulate)
4103 {
4104   const int nLength = GetLineLength (nLineIndex);
4105   ASSERT (nCharIndex >= 0 && nCharIndex <= nLength);
4106   LPCTSTR pszChars = GetLineChars (nLineIndex);
4107   int nOffset = 0;
4108   const int nTabSize = GetTabSize ();
4109   //BEGIN SW
4110   vector<int>   anBreaks(nLength + 1);
4111   int                   nBreaks = 0;
4112
4113   /*if( nLength > GetScreenChars() )*/
4114   WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks );
4115
4116   int   nPreOffset = 0;
4117   int   nPreBreak = 0;
4118
4119   if( nBreaks > 0 )
4120   {
4121     int J=0;
4122     for( J = nBreaks - 1; J >= 0 && nCharIndex < anBreaks[J]; J-- );
4123     nPreBreak = (J >= 0) ? anBreaks[J] : 0;
4124   }
4125   //END SW
4126   m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nCharIndex);
4127   int I=0;
4128   for (I = 0; I < nCharIndex; I = m_iterChar.next())
4129     {
4130       //BEGIN SW
4131       if( nPreBreak == I && nBreaks )
4132       nPreOffset = nOffset;
4133       //END SW
4134       if (pszChars[I] == _T ('\t'))
4135         nOffset += (nTabSize - nOffset % nTabSize);
4136       else
4137         nOffset += GetCharCellCountFromChar(pszChars + I);
4138     }
4139   if (bAccumulate)
4140     return nOffset;
4141   //BEGIN SW
4142   if( nPreBreak == I && nBreaks > 0)
4143     return 0;
4144   else
4145     return nOffset - nPreOffset;
4146   /*ORIGINAL
4147   return nOffset;
4148   *///END SW
4149 }
4150
4151 int CCrystalTextView::
4152 ApproxActualOffset (int nLineIndex, int nOffset)
4153 {
4154   if (nOffset == 0)
4155     return 0;
4156
4157   int nLength = GetLineLength (nLineIndex);
4158   LPCTSTR pszChars = GetLineChars (nLineIndex);
4159   int nCurrentOffset = 0;
4160   int nTabSize = GetTabSize ();
4161   m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
4162   for (int I = 0; I < nLength; I = m_iterChar.next())
4163     {
4164       if (pszChars[I] == _T ('\t'))
4165         nCurrentOffset += (nTabSize - nCurrentOffset % nTabSize);
4166       else
4167         {
4168           nCurrentOffset += GetCharCellCountFromChar(pszChars + I);
4169         }
4170       if (nCurrentOffset >= nOffset)
4171         {
4172           if (nOffset <= nCurrentOffset - nTabSize / 2)
4173             return I;
4174           return m_iterChar.next();
4175         }
4176     }
4177   return nLength;
4178 }
4179
4180 void CCrystalTextView::
4181 EnsureVisible (CPoint pt)
4182 {
4183   //  Scroll vertically
4184   int                   nSubLineCount = GetSubLineCount();
4185   int                   nNewTopSubLine = m_nTopSubLine;
4186   CPoint        subLinePos;
4187
4188   CharPosToPoint( pt.y, pt.x, subLinePos );
4189   subLinePos.y+= GetSubLineIndex( pt.y );
4190
4191   if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
4192     nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
4193   if( subLinePos.y < nNewTopSubLine )
4194     nNewTopSubLine = subLinePos.y;
4195
4196   if( nNewTopSubLine < 0 )
4197     nNewTopSubLine = 0;
4198   if( nNewTopSubLine >= nSubLineCount )
4199     nNewTopSubLine = nSubLineCount - 1;
4200
4201   if ( !m_bWordWrap && !m_bHideLines )
4202     {
4203       // WINMERGE: This line fixes (cursor) slowdown after merges!
4204       // I don't know exactly why, but propably we are setting
4205       // m_nTopLine to zero in ResetView() and are not setting to
4206       // valid value again. Maybe this is a good place to set it?
4207       m_nTopLine = nNewTopSubLine;
4208     }
4209   else
4210     {
4211       int dummy;
4212       GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
4213     }
4214
4215   if( nNewTopSubLine != m_nTopSubLine )
4216     {
4217       ScrollToSubLine( nNewTopSubLine );
4218       UpdateCaret();
4219       UpdateSiblingScrollPos( false );
4220     }
4221
4222   //  Scroll horizontally
4223   // we do not need horizontally scrolling, if we wrap the words
4224   if( m_bWordWrap )
4225     return;
4226   int nActualPos = CalculateActualOffset (pt.y, pt.x);
4227   int nNewOffset = m_nOffsetChar;
4228   const int nScreenChars = GetScreenChars ();
4229   
4230   // Keep 5 chars visible right to cursor
4231   if (nActualPos > nNewOffset + nScreenChars - 5)
4232     {
4233       // Add 10 chars width space after line
4234       nNewOffset = nActualPos - nScreenChars + 10;
4235     }
4236   // Keep 5 chars visible left to cursor
4237   if (nActualPos < nNewOffset + 5)
4238     {
4239       // Jump by 10 char steps, so user sees previous letters too
4240       nNewOffset = nActualPos - 10;
4241     }
4242
4243   // Horiz scroll limit to longest line + one screenwidth
4244   const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
4245   if (nNewOffset >= nMaxLineLen + nScreenChars)
4246     nNewOffset = nMaxLineLen + nScreenChars - 1;
4247   if (nNewOffset < 0)
4248     nNewOffset = 0;
4249
4250   if (m_nOffsetChar != nNewOffset)
4251     {
4252       ScrollToChar (nNewOffset);
4253       UpdateCaret ();
4254       UpdateSiblingScrollPos (true);
4255     }
4256 }
4257
4258 void CCrystalTextView::
4259 OnKillFocus (CWnd * pNewWnd)
4260 {
4261   CView::OnKillFocus (pNewWnd);
4262
4263   m_bFocused = false;
4264   UpdateCaret ();
4265   if (m_ptSelStart != m_ptSelEnd)
4266     InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4267   if (m_bDragSelection)
4268     {
4269       ReleaseCapture ();
4270       KillTimer (m_nDragSelTimer);
4271       m_bDragSelection = false;
4272     }
4273 }
4274
4275 void CCrystalTextView::
4276 OnSysColorChange ()
4277 {
4278   CView::OnSysColorChange ();
4279   Invalidate ();
4280 }
4281
4282 void CCrystalTextView::
4283 GetText (const CPoint & ptStart, const CPoint & ptEnd, CString & text, bool bExcludeInvisibleLines /*= true*/)
4284 {
4285   if (m_pTextBuffer != nullptr)
4286     m_pTextBuffer->GetText (ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, text);
4287   else
4288     text = _T ("");
4289 }
4290
4291 void CCrystalTextView::
4292 GetTextInColumnSelection (CString & text, bool bExcludeInvisibleLines /*= true*/)
4293 {
4294   if (m_pTextBuffer == nullptr)
4295     {
4296       text = _T ("");
4297       return;
4298     }
4299
4300   PrepareSelBounds ();
4301
4302   CString sEol = m_pTextBuffer->GetStringEol (CRLF_STYLE_DOS);
4303
4304   int nBufSize = 1;
4305   for (int L = m_ptDrawSelStart.y; L <= m_ptDrawSelEnd.y; L++)
4306       nBufSize += GetLineLength (L) + sEol.GetLength ();
4307   LPTSTR pszBuf = text.GetBuffer (nBufSize);
4308
4309   for (int I = m_ptDrawSelStart.y; I <= m_ptDrawSelEnd.y; I++)
4310     {
4311       if (bExcludeInvisibleLines && (GetLineFlags (I) & LF_INVISIBLE))
4312         continue;
4313       int nSelLeft, nSelRight;
4314       GetColumnSelection (I, nSelLeft, nSelRight);
4315       memcpy (pszBuf, GetLineChars (I) + nSelLeft, sizeof (TCHAR) * (nSelRight - nSelLeft));
4316       pszBuf += (nSelRight - nSelLeft);
4317       memcpy (pszBuf, sEol, sizeof (TCHAR) * sEol.GetLength ());
4318       pszBuf += sEol.GetLength ();
4319     }
4320   pszBuf[0] = 0;
4321   text.ReleaseBuffer ();
4322   text.FreeExtra ();
4323 }
4324
4325 void CCrystalTextView::
4326 UpdateView (CCrystalTextView * pSource, CUpdateContext * pContext,
4327             DWORD dwFlags, int nLineIndex /*= -1*/ )
4328 {
4329   // SetTextType (GetExt (GetDocument ()->GetPathName ()));
4330   if (dwFlags & UPDATE_RESET)
4331     {
4332       ResetView ();
4333       RecalcVertScrollBar ();
4334       RecalcHorzScrollBar ();
4335       return;
4336     }
4337
4338   int nLineCount = GetLineCount ();
4339   ASSERT (nLineCount > 0);
4340   ASSERT (nLineIndex >= -1 && nLineIndex < nLineCount);
4341   if ((dwFlags & UPDATE_SINGLELINE) != 0)
4342     {
4343       ASSERT (nLineIndex != -1);
4344       //  All text below this line should be reparsed
4345       const int cookiesSize = (int) m_ParseCookies->size();
4346       if (cookiesSize > 0)
4347         {
4348           ASSERT (cookiesSize == nLineCount);
4349           // must be reinitialized to invalid value (DWORD) - 1
4350           for (int i = nLineIndex; i < cookiesSize; ++i)
4351             (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4352         }
4353       //  This line'th actual length must be recalculated
4354       if (m_pnActualLineLength->size())
4355         {
4356           ASSERT (m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
4357           // must be initialized to invalid code -1
4358           (*m_pnActualLineLength)[nLineIndex] = -1;
4359       //BEGIN SW
4360       InvalidateLineCache( nLineIndex, nLineIndex );
4361       //END SW
4362         }
4363       //  Repaint the lines
4364       InvalidateLines (nLineIndex, -1, true);
4365     }
4366   else
4367     {
4368       if (m_bViewLineNumbers)
4369         // if enabling linenumber, we must invalidate all line-cache in visible area because selection margin width changes dynamically.
4370         nLineIndex = m_nTopLine < nLineIndex ? m_nTopLine : nLineIndex;
4371
4372       if (nLineIndex == -1)
4373         nLineIndex = 0;         //  Refresh all text
4374
4375       //  All text below this line should be reparsed
4376       if (m_ParseCookies->size())
4377         {
4378           size_t arrSize = m_ParseCookies->size();
4379           if (arrSize != static_cast<size_t>(nLineCount))
4380             {
4381               size_t oldsize = arrSize; 
4382               m_ParseCookies->resize(nLineCount);
4383               arrSize = nLineCount;
4384               // must be initialized to invalid value (DWORD) - 1
4385               for (size_t i = oldsize; i < arrSize; ++i)
4386                 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4387             }
4388           for (size_t i = nLineIndex; i < arrSize; ++i)
4389             (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4390         }
4391
4392       //  Recalculate actual length for all lines below this
4393       if (m_pnActualLineLength->size())
4394         {
4395             size_t arrsize = m_pnActualLineLength->size();
4396             if (arrsize != static_cast<size_t>(nLineCount))
4397             {
4398               //  Reallocate actual length array
4399               size_t oldsize = arrsize; 
4400               m_pnActualLineLength->resize(nLineCount);
4401               arrsize = nLineCount;
4402               // must be initialized to invalid code -1
4403               for (size_t i = oldsize; i < arrsize; ++i)
4404                 (*m_pnActualLineLength)[i] = -1;
4405             }
4406           for (size_t i = nLineIndex; i < arrsize; ++i)
4407             (*m_pnActualLineLength)[i] = -1;
4408         }
4409     //BEGIN SW
4410     InvalidateLineCache( nLineIndex, -1 );
4411     //END SW
4412       //  Repaint the lines
4413       InvalidateLines (nLineIndex, -1, true);
4414     }
4415
4416   //  All those points must be recalculated and validated
4417   if (pContext != nullptr)
4418     {
4419       pContext->RecalcPoint (m_ptCursorPos);
4420       pContext->RecalcPoint (m_ptSelStart);
4421       pContext->RecalcPoint (m_ptSelEnd);
4422       pContext->RecalcPoint (m_ptAnchor);
4423       ASSERT_VALIDTEXTPOS (m_ptCursorPos);
4424       ASSERT_VALIDTEXTPOS (m_ptSelStart);
4425       ASSERT_VALIDTEXTPOS (m_ptSelEnd);
4426       ASSERT_VALIDTEXTPOS (m_ptAnchor);
4427       if (m_bDraggingText)
4428         {
4429           pContext->RecalcPoint (m_ptDraggedTextBegin);
4430           pContext->RecalcPoint (m_ptDraggedTextEnd);
4431           ASSERT_VALIDTEXTPOS (m_ptDraggedTextBegin);
4432           ASSERT_VALIDTEXTPOS (m_ptDraggedTextEnd);
4433         }
4434       CPoint ptTopLine (0, m_nTopLine);
4435       pContext->RecalcPoint (ptTopLine);
4436       ASSERT_VALIDTEXTPOS (ptTopLine);
4437       m_nTopLine = ptTopLine.y;
4438       UpdateCaret ();
4439     }
4440
4441   //  Recalculate vertical scrollbar, if needed
4442   if ((dwFlags & UPDATE_VERTRANGE) != 0)
4443     {
4444       if (!m_bVertScrollBarLocked)
4445         RecalcVertScrollBar ();
4446     }
4447
4448   //  Recalculate horizontal scrollbar, if needed
4449   if ((dwFlags & UPDATE_HORZRANGE) != 0)
4450     {
4451       if (!m_bHorzScrollBarLocked)
4452         RecalcHorzScrollBar ();
4453     }
4454 }
4455
4456 HINSTANCE CCrystalTextView::
4457 GetResourceHandle ()
4458 {
4459 #ifdef CRYSEDIT_RES_HANDLE
4460   return CRYSEDIT_RES_HANDLE;
4461 #else
4462   if (s_hResourceInst != nullptr)
4463     return s_hResourceInst;
4464   return AfxGetResourceHandle ();
4465 #endif
4466 }
4467
4468 int CCrystalTextView::
4469 OnCreate (LPCREATESTRUCT lpCreateStruct)
4470 {
4471   m_lfBaseFont = {};
4472   _tcscpy_s (m_lfBaseFont.lfFaceName, _T ("FixedSys"));
4473   m_lfBaseFont.lfHeight = 0;
4474   m_lfBaseFont.lfWeight = FW_NORMAL;
4475   m_lfBaseFont.lfItalic = false;
4476   m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
4477   m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
4478   m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4479   m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
4480   m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
4481
4482   if (CView::OnCreate (lpCreateStruct) == -1)
4483     return -1;
4484
4485   ASSERT (m_hAccel == nullptr);
4486              // vvv GetResourceHandle () ???
4487   HINSTANCE hInst = AfxFindResourceHandle (MAKEINTRESOURCE(IDR_DEFAULT_ACCEL), RT_ACCELERATOR);
4488   ASSERT (hInst != nullptr);
4489   m_hAccel =::LoadAccelerators (hInst, MAKEINTRESOURCE (IDR_DEFAULT_ACCEL));
4490   ASSERT (m_hAccel != nullptr);
4491   return 0;
4492 }
4493
4494 void CCrystalTextView::
4495 SetAnchor (const CPoint & ptNewAnchor)
4496 {
4497   ASSERT_VALIDTEXTPOS (ptNewAnchor);
4498   m_ptAnchor = ptNewAnchor;
4499 }
4500
4501 void CCrystalTextView::
4502 OnEditOperation (int nAction, LPCTSTR pszText, size_t cchText)
4503 {
4504 }
4505
4506 BOOL CCrystalTextView::
4507 PreTranslateMessage (MSG * pMsg)
4508 {
4509   if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
4510     {
4511       if (m_hAccel != nullptr)
4512         {
4513           if (::TranslateAccelerator (m_hWnd, m_hAccel, pMsg))
4514             return true;
4515         }
4516     }
4517   else if (pMsg->message == WM_LBUTTONDBLCLK)
4518     m_dwLastDblClickTime = GetTickCount();
4519   else if (pMsg->message == WM_LBUTTONDOWN && (GetTickCount() - GetDoubleClickTime()) < m_dwLastDblClickTime)
4520     {
4521       m_dwLastDblClickTime = 0;
4522       OnLButtonTrippleClk(static_cast<UINT>(pMsg->wParam), { GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam) });
4523       return true;
4524     }
4525   return CView::PreTranslateMessage (pMsg);
4526 }
4527
4528 CPoint CCrystalTextView::
4529 GetCursorPos () const
4530 {
4531   return m_ptCursorPos;
4532 }
4533
4534 void CCrystalTextView::
4535 SetCursorPos (const CPoint & ptCursorPos)
4536 {
4537   ASSERT_VALIDTEXTPOS (ptCursorPos);
4538   m_ptCursorPos = ptCursorPos;
4539   m_nIdealCharPos = CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x);
4540   UpdateCaret ();
4541 }
4542
4543 void CCrystalTextView::
4544 UpdateCompositionWindowPos() /* IME */
4545 {
4546   HIMC hIMC = ImmGetContext(m_hWnd);
4547   COMPOSITIONFORM compform;
4548
4549   compform.dwStyle = CFS_FORCE_POSITION;
4550   compform.ptCurrentPos = GetCaretPos();
4551   ImmSetCompositionWindow(hIMC, &compform);
4552
4553   ImmReleaseContext(m_hWnd, hIMC);
4554 }
4555
4556 void CCrystalTextView::
4557 UpdateCompositionWindowFont() /* IME */
4558 {
4559   HIMC hIMC = ImmGetContext(m_hWnd);
4560   LOGFONT logfont;
4561
4562   GetFont()->GetLogFont(&logfont);
4563   ImmSetCompositionFont(hIMC, &logfont);
4564
4565   ImmReleaseContext(m_hWnd, hIMC);
4566 }
4567
4568 void CCrystalTextView::
4569 SetSelectionMargin (bool bSelMargin)
4570 {
4571   if (m_bSelMargin != bSelMargin)
4572     {
4573       m_bSelMargin = bSelMargin;
4574       if (::IsWindow (m_hWnd))
4575         {
4576           InvalidateScreenRect ();
4577           m_nTopSubLine = GetSubLineIndex(m_nTopLine);
4578           RecalcHorzScrollBar ();
4579           UpdateCaret ();
4580         }
4581     }
4582 }
4583
4584 void CCrystalTextView::
4585 SetViewLineNumbers (bool bViewLineNumbers)
4586 {
4587   if (m_bViewLineNumbers != bViewLineNumbers)
4588     {
4589       m_bViewLineNumbers = bViewLineNumbers;
4590       if (::IsWindow (m_hWnd))
4591         {
4592           InvalidateScreenRect ();
4593           m_nTopSubLine = GetSubLineIndex(m_nTopLine);
4594           RecalcHorzScrollBar ();
4595           UpdateCaret ();
4596         }
4597     }
4598 }
4599
4600 void CCrystalTextView::
4601 GetFont (LOGFONT & lf)
4602 {
4603   lf = m_lfBaseFont;
4604 }
4605
4606 void CCrystalTextView::
4607 SetFont (const LOGFONT & lf)
4608 {
4609   m_lfBaseFont = lf;
4610   m_nCharWidth = -1;
4611   m_nLineHeight = -1;
4612   for (int I = 0; I < 4; I++)
4613     {
4614       if (m_apFonts[I] != nullptr)
4615         {
4616           delete m_apFonts[I];
4617           m_apFonts[I] = nullptr;
4618         }
4619     }
4620   if (::IsWindow (m_hWnd))
4621     {
4622       InvalidateScreenRect();
4623       m_nTopSubLine = GetSubLineIndex(m_nTopLine);
4624       RecalcVertScrollBar ();
4625       RecalcHorzScrollBar ();
4626       UpdateCaret ();
4627     }
4628 #ifdef _UNICODE
4629   ResetCharWidths();
4630 #endif
4631 }
4632
4633 void CCrystalTextView::
4634 OnUpdateIndicatorPosition (CCmdUI * pCmdUI)
4635 {
4636   ASSERT_VALIDTEXTPOS (m_ptCursorPos);
4637   CString stat;
4638                                                    // VVV m_ptCursorPos.x + 1 ???
4639   stat.Format (_T ("Ln %d, Col %d"), m_ptCursorPos.y + 1, m_nIdealCharPos + 1);
4640   pCmdUI->SetText (stat);
4641   //BEGIN SW
4642   if( pCmdUI->m_pOther != nullptr && !!pCmdUI->m_pOther->IsKindOf( RUNTIME_CLASS(CStatusBar) ) )
4643     OnUpdateStatusMessage( (CStatusBar*)pCmdUI->m_pOther );
4644   //END SW
4645 }
4646
4647 void CCrystalTextView::
4648 OnUpdateIndicatorCRLF (CCmdUI * pCmdUI)
4649 {
4650   if (m_pTextBuffer != nullptr)
4651     {
4652       std::basic_string<TCHAR> eol;
4653       CRLFSTYLE crlfMode = m_pTextBuffer->GetCRLFMode ();
4654       switch (crlfMode)
4655         {
4656         case CRLF_STYLE_DOS:
4657           eol = LoadResString (IDS_EOL_DOS);
4658           pCmdUI->SetText (eol.c_str());
4659           pCmdUI->Enable (true);
4660           break;
4661         case CRLF_STYLE_UNIX:
4662           eol = LoadResString (IDS_EOL_UNIX);
4663           pCmdUI->SetText (eol.c_str());
4664           pCmdUI->Enable (true);
4665           break;
4666         case CRLF_STYLE_MAC:
4667           eol = LoadResString (IDS_EOL_MAC);
4668           pCmdUI->SetText (eol.c_str());
4669           pCmdUI->Enable (true);
4670           break;
4671         case CRLF_STYLE_MIXED:
4672           eol = LoadResString (IDS_EOL_MIXED);
4673           pCmdUI->SetText (eol.c_str());
4674           pCmdUI->Enable (true);
4675           break;
4676         default:
4677           pCmdUI->SetText (nullptr);
4678           pCmdUI->Enable (false);
4679         }
4680     }
4681   else
4682     {
4683       pCmdUI->SetText (nullptr);
4684       pCmdUI->Enable (false);
4685     }
4686 }
4687
4688 void CCrystalTextView::
4689 OnToggleBookmark (UINT nCmdID)
4690 {
4691   int nBookmarkID = nCmdID - ID_EDIT_TOGGLE_BOOKMARK0;
4692   ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
4693   if (m_pTextBuffer != nullptr)
4694     {
4695       DWORD dwFlags = GetLineFlags (m_ptCursorPos.y);
4696       DWORD dwMask = LF_BOOKMARK (nBookmarkID);
4697       m_pTextBuffer->SetLineFlag (m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0);
4698     }
4699 }
4700
4701 void CCrystalTextView::
4702 OnGoBookmark (UINT nCmdID)
4703 {
4704   int nBookmarkID = nCmdID - ID_EDIT_GO_BOOKMARK0;
4705   ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
4706   if (m_pTextBuffer != nullptr)
4707     {
4708       int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
4709       if (nLine >= 0)
4710         {
4711           CPoint pt (0, nLine);
4712           ASSERT_VALIDTEXTPOS (pt);
4713           SetCursorPos (pt);
4714           SetSelection (pt, pt);
4715           SetAnchor (pt);
4716           EnsureVisible (pt);
4717         }
4718     }
4719 }
4720
4721 void CCrystalTextView::
4722 OnClearBookmarks ()
4723 {
4724   if (m_pTextBuffer != nullptr)
4725     {
4726       for (int nBookmarkID = 0; nBookmarkID <= 9; nBookmarkID++)
4727         {
4728           int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
4729           if (nLine >= 0)
4730             {
4731               m_pTextBuffer->SetLineFlag (nLine, LF_BOOKMARK (nBookmarkID), false);
4732             }
4733         }
4734
4735     }
4736 }
4737
4738 void CCrystalTextView::
4739 ShowCursor ()
4740 {
4741   m_bCursorHidden = false;
4742   UpdateCaret ();
4743 }
4744
4745 void CCrystalTextView::
4746 HideCursor ()
4747 {
4748   m_bCursorHidden = true;
4749   UpdateCaret ();
4750 }
4751
4752 DROPEFFECT CCrystalTextView::
4753 GetDropEffect ()
4754 {
4755   return DROPEFFECT_COPY;
4756 }
4757
4758 void CCrystalTextView::
4759 OnDropSource (DROPEFFECT de)
4760 {
4761   ASSERT (de == DROPEFFECT_COPY);
4762 }
4763
4764 HGLOBAL CCrystalTextView::
4765 PrepareDragData ()
4766 {
4767   PrepareSelBounds ();
4768   if (m_ptDrawSelStart == m_ptDrawSelEnd)
4769     return nullptr;
4770
4771   CString text;
4772   GetText (m_ptDrawSelStart, m_ptDrawSelEnd, text);
4773   int cchText = text.GetLength();
4774   SIZE_T cbData = (cchText + 1) * sizeof(TCHAR);
4775   HGLOBAL hData =::GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, cbData);
4776   if (hData == nullptr)
4777     return nullptr;
4778
4779   LPTSTR pszData = (LPTSTR)::GlobalLock (hData);
4780   if (pszData == nullptr)
4781     {
4782       ::GlobalFree(hData);
4783       return nullptr;
4784     }
4785   memcpy (pszData, text, cbData);
4786   ::GlobalUnlock (hData);
4787
4788   m_ptDraggedTextBegin = m_ptDrawSelStart;
4789   m_ptDraggedTextEnd = m_ptDrawSelEnd;
4790   return hData;
4791 }
4792
4793 static ptrdiff_t
4794 FindStringHelper (LPCTSTR pszLineBegin, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch)
4795 {
4796   if (dwFlags & FIND_REGEXP)
4797     {
4798       ptrdiff_t pos = -1;
4799
4800       if (rxnode)
4801         RxFree (rxnode);
4802       rxnode = nullptr;
4803       if (pszFindWhat[0] == '^' && pszLineBegin != pszFindWhere)
4804         return pos;
4805       rxnode = RxCompile (pszFindWhat, (dwFlags & FIND_MATCH_CASE) != 0 ? RX_CASE : 0);
4806       if (rxnode && RxExec (rxnode, pszFindWhere, _tcslen (pszFindWhere), pszFindWhere, rxmatch))
4807         {
4808           pos = rxmatch->Open[0];
4809           ASSERT((rxmatch->Close[0] - rxmatch->Open[0]) < INT_MAX);
4810           nLen = static_cast<int>(rxmatch->Close[0] - rxmatch->Open[0]);
4811         }
4812       return pos;
4813     }
4814   else
4815     {
4816       ASSERT (pszFindWhere != nullptr);
4817       ASSERT (pszFindWhat != nullptr);
4818       int nCur = 0;
4819       int nLength = (int) _tcslen (pszFindWhat);
4820       LPCTSTR pszFindWhereOrig = pszFindWhere;
4821       nLen = nLength;
4822       for (;;)
4823         {
4824           LPCTSTR pszPos;
4825           if (dwFlags & FIND_MATCH_CASE)
4826             pszPos = _tcsstr(pszFindWhere, pszFindWhat);
4827           else
4828             pszPos = StrStrI(pszFindWhere, pszFindWhat);
4829           if (pszPos == nullptr)
4830             return -1;
4831           if ((dwFlags & FIND_WHOLE_WORD) == 0)
4832             return nCur + (int) (pszPos - pszFindWhere);
4833           if (pszPos > pszFindWhereOrig && xisalnum (pszPos[-1]))
4834             {
4835               nCur += (int) (pszPos - pszFindWhere + 1);
4836               pszFindWhere = pszPos + 1;
4837               continue;
4838             }
4839           if (xisalnum (pszPos[nLength]))
4840             {
4841               nCur += (int) (pszPos - pszFindWhere + 1);
4842               pszFindWhere = pszPos + 1;
4843               continue;
4844             }
4845           return nCur + (int) (pszPos - pszFindWhere);
4846         }
4847     }
4848 //~  ASSERT (false);               // Unreachable
4849 }
4850
4851 /** 
4852  * @brief Select text in editor.
4853  * @param [in] ptStartPos Star position for highlight.
4854  * @param [in] nLength Count of characters to highlight.
4855  * @param [in] bCursorToLeft If true cursor is positioned to Left-end of text
4856  *  selection, if false cursor is positioned to right-end.
4857  */
4858 bool CCrystalTextView::
4859 HighlightText (const CPoint & ptStartPos, int nLength,
4860     bool bCursorToLeft /*= false*/)
4861 {
4862   ASSERT_VALIDTEXTPOS (ptStartPos);
4863   CPoint ptEndPos = ptStartPos;
4864   int nCount = GetLineLength (ptEndPos.y) - ptEndPos.x;
4865   if (nLength <= nCount)
4866     {
4867       ptEndPos.x += nLength;
4868     }
4869   else
4870     {
4871       while (nLength > nCount)
4872         {
4873           nLength -= nCount + 1;
4874           nCount = GetLineLength (++ptEndPos.y);
4875         }
4876       ptEndPos.x = nLength;
4877     }
4878   ASSERT_VALIDTEXTPOS (m_ptCursorPos);  //  Probably 'nLength' is bigger than expected...
4879
4880   m_ptCursorPos = bCursorToLeft ? ptStartPos : ptEndPos;
4881   m_ptAnchor = bCursorToLeft ? ptEndPos : ptStartPos;
4882   SetSelection (ptStartPos, ptEndPos);
4883   UpdateCaret ();
4884   
4885   // Scrolls found text to middle of screen if out-of-screen
4886   int nScreenLines = GetScreenLines();
4887   if (ptStartPos.y < m_nTopLine || ptEndPos.y > m_nTopLine + nScreenLines)
4888     {
4889       if (ptStartPos.y > nScreenLines / 2)
4890         ScrollToLine(ptStartPos.y - nScreenLines / 2);
4891       else
4892         ScrollToLine(ptStartPos.y);
4893       UpdateSiblingScrollPos (false);
4894     }
4895   EnsureVisible (ptStartPos, ptEndPos);
4896   return true;
4897 }
4898
4899 bool CCrystalTextView::
4900 FindText (LPCTSTR pszText, const CPoint & ptStartPos, DWORD dwFlags,
4901           bool bWrapSearch, CPoint * pptFoundPos)
4902 {
4903   if (m_pMarkers != nullptr)
4904     {
4905       m_pMarkers->SetMarker(_T("EDITOR_MARKER"), pszText, dwFlags, COLORINDEX_MARKERBKGND0, false);
4906       if (m_pMarkers->GetEnabled())
4907         m_pMarkers->UpdateViews();
4908     }
4909   int nLineCount = GetLineCount ();
4910   return FindTextInBlock (pszText, ptStartPos, CPoint (0, 0),
4911                           CPoint (GetLineLength (nLineCount - 1), nLineCount - 1),
4912                           dwFlags, bWrapSearch, pptFoundPos);
4913 }
4914
4915 int HowManyStr (LPCTSTR s, LPCTSTR m)
4916 {
4917   LPCTSTR p = s;
4918   int n = 0;
4919   const int l = (int) _tcslen (m);
4920   while ((p = _tcsstr (p, m)) != nullptr)
4921     {
4922       n++;
4923       p += l;
4924     }
4925   return n;
4926 }
4927
4928 int HowManyStr (LPCTSTR s, TCHAR c)
4929 {
4930   LPCTSTR p = s;
4931   int n = 0;
4932   while ((p = _tcschr (p, c)) != nullptr)
4933     {
4934       n++;
4935       p++;
4936     }
4937   return n;
4938 }
4939
4940 bool CCrystalTextView::
4941 FindTextInBlock (LPCTSTR pszText, const CPoint & ptStartPosition,
4942                  const CPoint & ptBlockBegin, const CPoint & ptBlockEnd,
4943                  DWORD dwFlags, bool bWrapSearch, CPoint * pptFoundPos)
4944 {
4945   CPoint ptCurrentPos = ptStartPosition;
4946
4947   ASSERT (pszText != nullptr && _tcslen (pszText) > 0);
4948   ASSERT_VALIDTEXTPOS (ptCurrentPos);
4949   ASSERT_VALIDTEXTPOS (ptBlockBegin);
4950   ASSERT_VALIDTEXTPOS (ptBlockEnd);
4951   ASSERT (ptBlockBegin.y < ptBlockEnd.y || ptBlockBegin.y == ptBlockEnd.y &&
4952           ptBlockBegin.x <= ptBlockEnd.x);
4953   if (ptBlockBegin == ptBlockEnd)
4954     return false;
4955   CWaitCursor waitCursor;
4956   if (ptCurrentPos.y < ptBlockBegin.y || ptCurrentPos.y == ptBlockBegin.y &&
4957         ptCurrentPos.x < ptBlockBegin.x)
4958     ptCurrentPos = ptBlockBegin;
4959
4960   CString what = pszText;
4961   int nEolns;
4962   if (dwFlags & FIND_REGEXP)
4963     {
4964       nEolns = HowManyStr (what, _T("\\n"));
4965     }
4966   else
4967     {
4968       nEolns = 0;
4969     }
4970   if (dwFlags & FIND_DIRECTION_UP)
4971     {
4972       //  Let's check if we deal with whole text.
4973       //  At this point, we cannot search *up* in selection
4974       ASSERT (ptBlockBegin.x == 0 && ptBlockBegin.y == 0);
4975       ASSERT (ptBlockEnd.x == GetLineLength (GetLineCount () - 1) &&
4976               ptBlockEnd.y == GetLineCount () - 1);
4977
4978       //  Proceed as if we have whole text search.
4979       for (;;)
4980         {
4981           while (ptCurrentPos.y >= 0)
4982             {
4983               int nLineLength;
4984               CString line;
4985               if (dwFlags & FIND_REGEXP)
4986                 {
4987                   for (int i = 0; i <= nEolns && ptCurrentPos.y >= i; i++)
4988                     {
4989                       CString item;
4990                       LPCTSTR pszChars = GetLineChars (ptCurrentPos.y - i);
4991                       if (i)
4992                         {
4993                           nLineLength = GetLineLength (ptCurrentPos.y - i);
4994                           ptCurrentPos.x = 0;
4995                           line = _T ('\n') + line;
4996                         }
4997                       else
4998                         {
4999                           nLineLength = ptCurrentPos.x != -1 ? ptCurrentPos.x : GetLineLength (ptCurrentPos.y - i);
5000                         }
5001                       if (nLineLength > 0)
5002                         {
5003                           LPTSTR pszBuf = item.GetBuffer (nLineLength + 1);
5004                           _tcsncpy_s (pszBuf, nLineLength+1, pszChars, nLineLength);
5005                           item.ReleaseBuffer (nLineLength);
5006                           line = item + line;
5007                         }
5008                     }
5009                   nLineLength = line.GetLength ();
5010                   if (ptCurrentPos.x == -1)
5011                     ptCurrentPos.x = 0;
5012                 }
5013               else
5014                 {
5015                   nLineLength = GetLineLength(ptCurrentPos.y);
5016                   if (ptCurrentPos.x == -1)
5017                     {
5018                       ptCurrentPos.x = nLineLength;
5019                     }
5020                   else
5021                     if( ptCurrentPos.x > nLineLength )
5022                       ptCurrentPos.x = nLineLength;
5023                   if (ptCurrentPos.x == -1)
5024                     ptCurrentPos.x = 0;
5025
5026                   LPCTSTR pszChars = GetLineChars (ptCurrentPos.y);
5027                   _tcsncpy_s (line.GetBuffer(ptCurrentPos.x + 1), ptCurrentPos.x + 1, pszChars, ptCurrentPos.x);
5028                   line.ReleaseBuffer (ptCurrentPos.x);
5029                 }
5030
5031               ptrdiff_t nFoundPos = -1;
5032               int nMatchLen = what.GetLength();
5033               int nLineLen = line.GetLength();
5034               size_t nPos = 0;
5035               for (;;)
5036                 {
5037                   size_t nPosRel = ::FindStringHelper(line, static_cast<LPCTSTR>(line) + nPos, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5038                   if (nPosRel == -1)
5039                     break;
5040                   nFoundPos = nPos + nPosRel;
5041                   nMatchLen = m_nLastFindWhatLen;
5042                   nPos += nMatchLen == 0 ? 1 : nMatchLen;
5043                 }
5044
5045               if( nFoundPos != -1 )     // Found text!
5046                 {
5047                   ptCurrentPos.x = static_cast<int>(nFoundPos);
5048                   *pptFoundPos = ptCurrentPos;
5049                   return true;
5050                 }
5051
5052               ptCurrentPos.y--;
5053               if( ptCurrentPos.y >= 0 )
5054                 ptCurrentPos.x = GetLineLength( ptCurrentPos.y );
5055             }
5056
5057           //  Beginning of text reached
5058           if (!bWrapSearch)
5059             return false;
5060
5061           //  Start again from the end of text
5062           bWrapSearch = false;
5063           ptCurrentPos = CPoint (GetLineLength (GetLineCount () - 1), GetLineCount () - 1);
5064         }
5065     }
5066   else
5067     {
5068       for (;;)
5069         {
5070           while (ptCurrentPos.y <= ptBlockEnd.y)
5071             {
5072               int nLineLength;
5073               CString line;
5074               if (dwFlags & FIND_REGEXP)
5075                 {
5076                   int nLines = m_pTextBuffer->GetLineCount ();
5077                   for (int i = 0; i <= nEolns && ptCurrentPos.y + i < nLines; i++)
5078                     {
5079                       CString item;
5080                       LPCTSTR pszChars = GetLineChars (ptCurrentPos.y + i);
5081                       nLineLength = GetLineLength (ptCurrentPos.y + i);
5082                       if (i)
5083                         {
5084                           line += _T ('\n');
5085                         }
5086                       if (nLineLength > 0)
5087                         {
5088                           LPTSTR pszBuf = item.GetBuffer (nLineLength + 1);
5089                           _tcsncpy_s (pszBuf, nLineLength + 1, pszChars, nLineLength);
5090                           item.ReleaseBuffer (nLineLength);
5091                           line += item;
5092                         }
5093                     }
5094                   nLineLength = line.GetLength ();
5095                 }
5096               else
5097                 {
5098                   nLineLength = GetLineLength (ptCurrentPos.y) - ptCurrentPos.x;
5099                   if (nLineLength <= 0)
5100                     {
5101                       ptCurrentPos.x = 0;
5102                       ptCurrentPos.y++;
5103                       continue;
5104                     }
5105
5106                   line = GetLineChars (ptCurrentPos.y);
5107                 }
5108
5109               //  Perform search in the line
5110               size_t nPos = ::FindStringHelper (line, static_cast<LPCTSTR>(line) + ptCurrentPos.x, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5111               if (nPos != -1)
5112                 {
5113                   if (m_pszMatched != nullptr)
5114                     free(m_pszMatched);
5115                   m_pszMatched = _tcsdup (line);
5116                   if (nEolns)
5117                     {
5118                       CString item = line.Left (static_cast<LONG>(nPos));
5119                       LPCTSTR current = _tcsrchr (item, _T('\n'));
5120                       if (current)
5121                         current++;
5122                       else
5123                         current = item;
5124                       nEolns = HowManyStr (item, _T('\n'));
5125                       if (nEolns)
5126                         {
5127                           ptCurrentPos.y += nEolns;
5128                           ptCurrentPos.x = static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5129                         }
5130                       else
5131                         {
5132                           ptCurrentPos.x += static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5133                         }
5134                       if (ptCurrentPos.x < 0)
5135                         ptCurrentPos.x = 0;
5136                     }
5137                   else
5138                     {
5139                       ptCurrentPos.x += static_cast<LONG>(nPos);
5140                     }
5141                   //  Check of the text found is outside the block.
5142                   if (ptCurrentPos.y == ptBlockEnd.y && ptCurrentPos.x >= ptBlockEnd.x)
5143                     break;
5144
5145                   *pptFoundPos = ptCurrentPos;
5146                   return true;
5147                 }
5148               else
5149                 {
5150                   if (m_pszMatched != nullptr)
5151                     free(m_pszMatched);
5152                   m_pszMatched = nullptr;
5153                 }
5154
5155               //  Go further, text was not found
5156               ptCurrentPos.x = 0;
5157               ptCurrentPos.y++;
5158             }
5159
5160           //  End of text reached
5161           if (!bWrapSearch)
5162             return false;
5163
5164           //  Start from the beginning
5165           bWrapSearch = false;
5166           ptCurrentPos = ptBlockBegin;
5167         }
5168     }
5169
5170  //~ ASSERT (false);               // Unreachable
5171 }
5172
5173 static DWORD ConvertSearchInfosToSearchFlags(const LastSearchInfos *lastSearch)
5174 {
5175   DWORD dwSearchFlags = 0;
5176   if (lastSearch->m_bMatchCase)
5177     dwSearchFlags |= FIND_MATCH_CASE;
5178   if (lastSearch->m_bWholeWord)
5179     dwSearchFlags |= FIND_WHOLE_WORD;
5180   if (lastSearch->m_bRegExp)
5181     dwSearchFlags |= FIND_REGEXP;
5182   if (lastSearch->m_nDirection == 0)
5183     dwSearchFlags |= FIND_DIRECTION_UP;
5184   if (lastSearch->m_bNoWrap)
5185     dwSearchFlags |= FIND_NO_WRAP;
5186   if (lastSearch->m_bNoClose)
5187     dwSearchFlags |= FIND_NO_CLOSE;
5188   return dwSearchFlags;
5189 }
5190
5191 static void ConvertSearchFlagsToLastSearchInfos(LastSearchInfos *lastSearch, DWORD dwFlags)
5192 {
5193   lastSearch->m_bMatchCase = (dwFlags & FIND_MATCH_CASE) != 0;
5194   lastSearch->m_bWholeWord = (dwFlags & FIND_WHOLE_WORD) != 0;
5195   lastSearch->m_bRegExp = (dwFlags & FIND_REGEXP) != 0;
5196   lastSearch->m_nDirection = (dwFlags & FIND_DIRECTION_UP) == 0;
5197   lastSearch->m_bNoWrap = (dwFlags & FIND_NO_WRAP) != 0;
5198   lastSearch->m_bNoClose = (dwFlags & FIND_NO_CLOSE) != 0;
5199 }
5200
5201 CPoint CCrystalTextView::
5202 GetSearchPos(DWORD dwSearchFlags)
5203 {
5204   CPoint ptSearchPos;
5205   if (IsSelection())
5206     {
5207       CPoint ptStart, ptEnd;
5208       GetSelection(ptStart, ptEnd);
5209       if( dwSearchFlags & FIND_DIRECTION_UP)
5210         ptSearchPos = ptStart;
5211       else
5212         ptSearchPos = ptEnd;
5213     }
5214   else
5215     ptSearchPos = m_ptCursorPos;
5216   return ptSearchPos;
5217 }
5218
5219 bool CCrystalTextView::
5220 FindText (const LastSearchInfos * lastSearch)
5221 {
5222   CPoint ptTextPos;
5223   DWORD dwSearchFlags = ConvertSearchInfosToSearchFlags(lastSearch);
5224   if (!FindText (lastSearch->m_sText, GetSearchPos(dwSearchFlags), dwSearchFlags, !lastSearch->m_bNoWrap,
5225       &ptTextPos))
5226     {
5227       return false;
5228     }
5229
5230   bool bCursorToLeft = (lastSearch->m_nDirection == 0);
5231   HighlightText (ptTextPos, m_nLastFindWhatLen, bCursorToLeft);
5232
5233   //  Save search parameters for 'F3' command
5234   m_bLastSearch = true;
5235   if (m_pszLastFindWhat != nullptr)
5236     free (m_pszLastFindWhat);
5237   m_pszLastFindWhat = _tcsdup (lastSearch->m_sText);
5238   m_dwLastSearchFlags = dwSearchFlags;
5239
5240   //  Save search parameters to registry
5241   VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), m_dwLastSearchFlags));
5242
5243   return true;
5244 }
5245
5246 void CCrystalTextView::
5247 OnEditFind ()
5248 {
5249   CWinApp *pApp = AfxGetApp ();
5250   ASSERT (pApp != nullptr);
5251
5252   if (m_pFindTextDlg == nullptr)
5253     m_pFindTextDlg = new CFindTextDlg (this);
5254
5255   LastSearchInfos * lastSearch = m_pFindTextDlg->GetLastSearchInfos();
5256
5257   if (m_bLastSearch)
5258     {
5259       //  Get the latest search parameters
5260       ConvertSearchFlagsToLastSearchInfos(lastSearch, m_dwLastSearchFlags);
5261       if (m_pszLastFindWhat != nullptr)
5262         lastSearch->m_sText = m_pszLastFindWhat;
5263     }
5264   else
5265     {
5266       DWORD dwFlags;
5267       if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), &dwFlags))
5268         dwFlags = 0;
5269       ConvertSearchFlagsToLastSearchInfos(lastSearch, dwFlags);
5270     }
5271   m_pFindTextDlg->UseLastSearch ();
5272
5273   //  Take the current selection, if any
5274   if (IsSelection ())
5275     {
5276       CPoint ptSelStart, ptSelEnd;
5277       GetSelection (ptSelStart, ptSelEnd);
5278       if (ptSelStart.y == ptSelEnd.y)
5279         GetText (ptSelStart, ptSelEnd, m_pFindTextDlg->m_sText);
5280     }
5281   else
5282     {
5283       CPoint ptCursorPos = GetCursorPos ();
5284       CPoint ptStart = WordToLeft (ptCursorPos);
5285       CPoint ptEnd = WordToRight (ptCursorPos);
5286       if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5287         GetText (ptStart, ptEnd, m_pFindTextDlg->m_sText);
5288     }
5289
5290   //  Execute Find dialog
5291
5292   // m_bShowInactiveSelection = true; // FP: removed because I like it
5293   m_pFindTextDlg->UpdateData(FALSE);
5294   m_pFindTextDlg->ShowWindow(SW_SHOW);
5295   // m_bShowInactiveSelection = false; // FP: removed because I like it
5296
5297 }
5298
5299 void CCrystalTextView::
5300 OnEditRepeat ()
5301 {
5302   bool bEnable = m_bLastSearch;
5303   // Show dialog if no last find text
5304   if (m_pszLastFindWhat == nullptr || _tcslen(m_pszLastFindWhat) == 0)
5305     bEnable = false;
5306   CString sText;
5307   if (bEnable)
5308     sText = m_pszLastFindWhat;
5309   else
5310     {
5311       // If last find-text exists, cut it to first line
5312       bEnable = !sText.IsEmpty ();
5313       if (bEnable)
5314         {
5315           int pos = sText.FindOneOf (_T("\r\n"));
5316           if (pos >= 0)
5317             sText = sText.Left (pos);
5318         }
5319     }
5320
5321   // CTRL-F3 will find selected text..
5322   bool bControlKey = (::GetAsyncKeyState(VK_CONTROL)& 0x8000) != 0;
5323   // CTRL-SHIFT-F3 will find selected text, but opposite direction
5324   bool bShiftKey = (::GetAsyncKeyState(VK_SHIFT)& 0x8000) != 0;
5325   if (bControlKey)
5326     {
5327       if (IsSelection())
5328         {
5329           CPoint ptSelStart, ptSelEnd;
5330           GetSelection (ptSelStart, ptSelEnd);
5331           GetText (ptSelStart, ptSelEnd, sText);
5332         }
5333       else
5334         {
5335           CPoint ptCursorPos = GetCursorPos ();
5336           CPoint ptStart = WordToLeft (ptCursorPos);
5337           CPoint ptEnd = WordToRight (ptCursorPos);
5338           if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5339             GetText (ptStart, ptEnd, sText);
5340         }
5341       if (!sText.IsEmpty())
5342         {
5343           bEnable = true;
5344           free(m_pszLastFindWhat);
5345           m_pszLastFindWhat = _tcsdup (sText);
5346           m_bLastSearch = true;
5347         }
5348     }
5349   if (bShiftKey)
5350     m_dwLastSearchFlags |= FIND_DIRECTION_UP;
5351   else
5352     m_dwLastSearchFlags &= ~FIND_DIRECTION_UP;
5353   if (bEnable)
5354     {
5355       CPoint ptFoundPos;
5356       //BEGIN SW
5357       // for correct backward search we need some changes:
5358       if (! FindText(sText, GetSearchPos(m_dwLastSearchFlags), m_dwLastSearchFlags,
5359             (m_dwLastSearchFlags & FIND_NO_WRAP) == 0, &ptFoundPos))
5360         {
5361           CString prompt;
5362           prompt.Format (LoadResString(IDS_EDIT_TEXT_NOT_FOUND).c_str(), (LPCTSTR)sText);
5363           AfxMessageBox (prompt, MB_ICONINFORMATION);
5364           return;
5365         }
5366       HighlightText (ptFoundPos, m_nLastFindWhatLen, (m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0);
5367       m_bMultipleSearch = true; // More search
5368     }
5369   else
5370     OnEditFind(); // No previous find, open Find-dialog
5371 }
5372
5373 void CCrystalTextView::
5374 OnUpdateEditRepeat (CCmdUI * pCmdUI)
5375 {
5376   pCmdUI->Enable (true);
5377 }
5378
5379 void CCrystalTextView::
5380 OnEditMark ()
5381 {
5382   CString sText;
5383   DWORD dwFlags;
5384   if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), &dwFlags))
5385     dwFlags = 0;
5386
5387   //  Take the current selection, if any
5388   if (IsSelection ())
5389     {
5390       CPoint ptSelStart, ptSelEnd;
5391       GetSelection (ptSelStart, ptSelEnd);
5392       if (ptSelStart.y == ptSelEnd.y)
5393         GetText (ptSelStart, ptSelEnd, sText);
5394     }
5395   else
5396     {
5397       CPoint ptCursorPos = GetCursorPos ();
5398       CPoint ptStart = WordToLeft (ptCursorPos);
5399       CPoint ptEnd = WordToRight (ptCursorPos);
5400       if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5401         GetText (ptStart, ptEnd, sText);
5402     }
5403
5404   CTextMarkerDlg markerDlg(*m_pMarkers, sText, dwFlags);
5405
5406   if (markerDlg.DoModal() == IDOK)
5407     {
5408       //  Save search parameters to registry
5409       VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), markerDlg.GetLastSearchFlags()));
5410       m_pMarkers->SaveToRegistry();
5411     }
5412 }
5413
5414 void CCrystalTextView::
5415 OnFilePageSetup ()
5416 {
5417   CWinApp *pApp = AfxGetApp ();
5418   ASSERT (pApp != nullptr);
5419
5420   CPageSetupDialog dlg;
5421   PRINTDLG pd;
5422   if (!pApp->GetPrinterDeviceDefaults (&pd))
5423     return;
5424
5425   dlg.m_psd.hDevMode = pd.hDevMode;
5426   dlg.m_psd.hDevNames = pd.hDevNames;
5427   dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS|PSD_MARGINS;
5428   dlg.m_psd.rtMargin.left = DEFAULT_PRINT_MARGIN;
5429   dlg.m_psd.rtMargin.right = DEFAULT_PRINT_MARGIN;
5430   dlg.m_psd.rtMargin.top = DEFAULT_PRINT_MARGIN;
5431   dlg.m_psd.rtMargin.bottom = DEFAULT_PRINT_MARGIN;
5432   CReg reg;
5433   if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
5434     {
5435       DWORD dwTemp;
5436       if (reg.LoadNumber (_T ("PageWidth"), &dwTemp))
5437         dlg.m_psd.ptPaperSize.x = dwTemp;
5438       if (reg.LoadNumber (_T ("PageHeight"), &dwTemp))
5439         dlg.m_psd.ptPaperSize.y = dwTemp;
5440       if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
5441         dlg.m_psd.rtMargin.left = dwTemp;
5442       if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
5443         dlg.m_psd.rtMargin.right = dwTemp;
5444       if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
5445         dlg.m_psd.rtMargin.top = dwTemp;
5446       if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
5447         dlg.m_psd.rtMargin.bottom = dwTemp;
5448     }
5449   if (dlg.DoModal () == IDOK)
5450     {
5451       CReg reg1;
5452       if (reg1.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
5453         {
5454           VERIFY (reg1.SaveNumber (_T ("PageWidth"), dlg.m_psd.ptPaperSize.x));
5455           VERIFY (reg1.SaveNumber (_T ("PageHeight"), dlg.m_psd.ptPaperSize.y));
5456           VERIFY (reg1.SaveNumber (_T ("PageLeft"), dlg.m_psd.rtMargin.left));
5457           VERIFY (reg1.SaveNumber (_T ("PageRight"), dlg.m_psd.rtMargin.right));
5458           VERIFY (reg1.SaveNumber (_T ("PageTop"), dlg.m_psd.rtMargin.top));
5459           VERIFY (reg1.SaveNumber (_T ("PageBottom"), dlg.m_psd.rtMargin.bottom));
5460         }
5461       pApp->SelectPrinter (dlg.m_psd.hDevNames, dlg.m_psd.hDevMode, false);
5462     }
5463 }
5464
5465 /** 
5466  * @brief Adds/removes bookmark on given line.
5467  * This functions adds bookmark or removes bookmark on given line.
5468  * @param [in] Index (0-based) of line to add/remove bookmark.
5469  */
5470 void CCrystalTextView::ToggleBookmark(int nLine)
5471 {
5472   ASSERT(nLine >= 0 && nLine < GetLineCount());
5473   if (m_pTextBuffer != nullptr)
5474     {
5475       DWORD dwFlags = GetLineFlags (nLine);
5476       DWORD dwMask = LF_BOOKMARKS;
5477       m_pTextBuffer->SetLineFlag (nLine, dwMask, (dwFlags & dwMask) == 0, false);
5478       const int nBookmarkLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARKS);
5479       if (nBookmarkLine >= 0)
5480         m_bBookmarkExist = true;
5481       else
5482         m_bBookmarkExist = false;
5483     }  
5484 }
5485 /** 
5486  * @brief Called when Toggle Bookmark is selected from the GUI.
5487  */
5488 void CCrystalTextView::
5489 OnToggleBookmark ()
5490 {
5491   ToggleBookmark(m_ptCursorPos.y);
5492 }
5493
5494 void CCrystalTextView::
5495 OnNextBookmark ()
5496 {
5497   if (m_pTextBuffer != nullptr)
5498     {
5499       int nLine = m_pTextBuffer->FindNextBookmarkLine (m_ptCursorPos.y);
5500       if (nLine >= 0)
5501         {
5502           CPoint pt (0, nLine);
5503           ASSERT_VALIDTEXTPOS (pt);
5504           SetCursorPos (pt);
5505           SetSelection (pt, pt);
5506           SetAnchor (pt);
5507           EnsureVisible (pt);
5508         }
5509     }
5510 }
5511
5512 void CCrystalTextView::
5513 OnPrevBookmark ()
5514 {
5515   if (m_pTextBuffer != nullptr)
5516     {
5517       int nLine = m_pTextBuffer->FindPrevBookmarkLine (m_ptCursorPos.y);
5518       if (nLine >= 0)
5519         {
5520           CPoint pt (0, nLine);
5521           ASSERT_VALIDTEXTPOS (pt);
5522           SetCursorPos (pt);
5523           SetSelection (pt, pt);
5524           SetAnchor (pt);
5525           EnsureVisible (pt);
5526         }
5527     }
5528 }
5529
5530 void CCrystalTextView::
5531 OnClearAllBookmarks ()
5532 {
5533   if (m_pTextBuffer != nullptr)
5534     {
5535       int nLineCount = GetLineCount ();
5536       for (int I = 0; I < nLineCount; I++)
5537         {
5538           if (m_pTextBuffer->GetLineFlags (I) & LF_BOOKMARKS)
5539             m_pTextBuffer->SetLineFlag (I, LF_BOOKMARKS, false);
5540         }
5541       m_bBookmarkExist = false;
5542     }
5543 }
5544
5545 void CCrystalTextView::
5546 OnUpdateNextBookmark (CCmdUI * pCmdUI)
5547 {
5548   pCmdUI->Enable (m_bBookmarkExist);
5549 }
5550
5551 void CCrystalTextView::
5552 OnUpdatePrevBookmark (CCmdUI * pCmdUI)
5553 {
5554   pCmdUI->Enable (m_bBookmarkExist);
5555 }
5556
5557 void CCrystalTextView::
5558 OnUpdateClearAllBookmarks (CCmdUI * pCmdUI)
5559 {
5560   pCmdUI->Enable (m_bBookmarkExist);
5561 }
5562
5563 bool CCrystalTextView::
5564 GetViewTabs ()
5565 {
5566   return m_bViewTabs;
5567 }
5568
5569 void CCrystalTextView::
5570 SetViewTabs (bool bViewTabs)
5571 {
5572   if (bViewTabs != m_bViewTabs)
5573     {
5574       m_bViewTabs = bViewTabs;
5575       if (::IsWindow (m_hWnd))
5576         Invalidate ();
5577     }
5578 }
5579
5580 void CCrystalTextView::
5581 SetViewEols (bool bViewEols, bool bDistinguishEols)
5582 {
5583   if (bViewEols != m_bViewEols || bDistinguishEols != m_bDistinguishEols)
5584     {
5585       m_bViewEols = bViewEols;
5586       m_bDistinguishEols = bDistinguishEols;
5587       if (::IsWindow (m_hWnd))
5588         Invalidate ();
5589     }
5590 }
5591
5592 DWORD CCrystalTextView::
5593 GetFlags ()
5594 {
5595   return m_dwFlags;
5596 }
5597
5598 void CCrystalTextView::
5599 SetFlags (DWORD dwFlags)
5600 {
5601   if (m_dwFlags != dwFlags)
5602     {
5603       m_dwFlags = dwFlags;
5604       if (::IsWindow (m_hWnd))
5605         Invalidate ();
5606     }
5607 }
5608
5609 bool CCrystalTextView::
5610 GetSelectionMargin ()
5611 {
5612   return m_bSelMargin;
5613 }
5614
5615 bool CCrystalTextView::
5616 GetViewLineNumbers () const
5617 {
5618   return m_bViewLineNumbers;
5619 }
5620
5621 /**
5622  * @brief Calculate margin area width.
5623  * This function calculates needed margin width. Needed width is (approx.)
5624  * one char-width for bookmark etc markers and rest to linenumbers (if
5625  * visible). If we have linenumbers visible we need to adjust width so that
5626  * biggest number fits.
5627  * @return Margin area width in pixels.
5628  */
5629 int CCrystalTextView::
5630 GetMarginWidth (CDC *pdc /*= nullptr*/)
5631 {
5632   int nMarginWidth = 0;
5633
5634   if (m_bViewLineNumbers)
5635     {
5636       const int nLines = GetLineCount();
5637       int nNumbers = 0;
5638       int n = 1;
5639       for (n = 1; n <= nLines; n *= 10)
5640         ++nNumbers;
5641       nMarginWidth += GetCharWidth () * nNumbers;
5642       if (!m_bSelMargin)
5643         nMarginWidth += 2; // Small gap when symbol part disabled
5644     }
5645
5646   if (m_bSelMargin)
5647     {
5648       if (pdc == nullptr || !pdc->IsPrinting ())
5649         nMarginWidth += MARGIN_ICON_WIDTH  + 7;  // Width for icon markers and some margin
5650     }
5651   else
5652     {
5653       if (pdc == nullptr || !pdc->IsPrinting ())
5654         nMarginWidth += MARGIN_REV_WIDTH; // Space for revision marks
5655     }
5656
5657   return nMarginWidth;
5658 }
5659
5660 bool CCrystalTextView::
5661 GetSmoothScroll ()
5662 const
5663 {
5664   return m_bSmoothScroll;
5665 }
5666
5667 void CCrystalTextView::SetSmoothScroll (bool bSmoothScroll)
5668 {
5669   m_bSmoothScroll = bSmoothScroll;
5670 }
5671
5672 //  [JRT]
5673 bool CCrystalTextView::
5674 GetDisableDragAndDrop ()
5675 const
5676 {
5677   return m_bDisableDragAndDrop;
5678 }
5679
5680 //  [JRT]
5681 void CCrystalTextView::SetDisableDragAndDrop (bool bDDAD)
5682 {
5683   m_bDisableDragAndDrop = bDDAD;
5684 }
5685
5686 void CCrystalTextView::CopyProperties (CCrystalTextView *pSource)
5687 {
5688   m_nTopLine = pSource->m_nTopLine;
5689   m_nTopSubLine = pSource->m_nTopSubLine;
5690   m_bViewTabs = pSource->m_bViewTabs;
5691   m_bViewEols = pSource->m_bViewEols;
5692   m_bDistinguishEols = pSource->m_bDistinguishEols;
5693   m_bSelMargin = pSource->m_bSelMargin;
5694   m_bViewLineNumbers = pSource->m_bViewLineNumbers;
5695   m_bSmoothScroll = pSource->m_bSmoothScroll;
5696   m_bWordWrap = pSource->m_bWordWrap;
5697   m_pColors = pSource->m_pColors;
5698   m_pMarkers = pSource->m_pMarkers;
5699   m_bDisableDragAndDrop = pSource->m_bDisableDragAndDrop;
5700   SetTextType(pSource->m_CurSourceDef);
5701   SetFont (pSource->m_lfBaseFont);
5702 }
5703
5704 //
5705 // Mouse wheel event.  zDelta is in multiples of 120.
5706 // Divide by 40 so each click is 3 lines.  I know some
5707 // drivers let you set the ammount of scroll, but I
5708 // don't know how to retrieve this or if they just
5709 // adjust the zDelta you get here.
5710 BOOL CCrystalTextView::
5711 OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
5712 {
5713   SCROLLINFO si = {0};
5714   si.cbSize = sizeof (si);
5715   si.fMask = SIF_PAGE | SIF_RANGE;
5716   VERIFY (GetScrollInfo (SB_VERT, &si));
5717
5718   int nNewTopSubLine= m_nTopSubLine - zDelta / 40;
5719
5720   if (nNewTopSubLine < 0)
5721     nNewTopSubLine = 0;
5722   if (nNewTopSubLine > (si.nMax - (signed int)si.nPage + 1))
5723     nNewTopSubLine = si.nMax - si.nPage + 1;
5724
5725   ScrollToSubLine(nNewTopSubLine, true);
5726   UpdateSiblingScrollPos(false);
5727
5728   return CView::OnMouseWheel (nFlags, zDelta, pt);
5729 }
5730
5731 void CCrystalTextView::
5732 OnSourceType (UINT nId)
5733 {
5734   SetTextType ((CCrystalTextView::TextType) (nId - ID_SOURCE_PLAIN));
5735   Invalidate ();
5736 }
5737
5738 void CCrystalTextView::
5739 OnUpdateSourceType (CCmdUI * pCmdUI)
5740 {
5741   pCmdUI->SetRadio (m_SourceDefs + (pCmdUI->m_nID - ID_SOURCE_PLAIN) == m_CurSourceDef);
5742 }
5743
5744 int
5745 bracetype (TCHAR c)
5746 {
5747   static LPCTSTR braces = _T("{}()[]<>");
5748   LPCTSTR pos = _tcschr (braces, c);
5749   return pos != nullptr ? (int) (pos - braces) + 1 : 0;
5750 }
5751
5752 int
5753 bracetype (LPCTSTR s)
5754 {
5755   if (s[1])
5756     return 0;
5757   return bracetype (*s);
5758 }
5759
5760 void CCrystalTextView::
5761 OnMatchBrace ()
5762 {
5763   CPoint ptCursorPos = GetCursorPos ();
5764   int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
5765   LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y), pszEnd = pszText + ptCursorPos.x;
5766   bool bAfter = false;
5767   int nType = 0;
5768   if (ptCursorPos.x < nLength)
5769     {
5770       nType = bracetype (*pszEnd);
5771       if (nType)
5772         {
5773           bAfter = false;
5774         }
5775       else if (!nType && ptCursorPos.x > 0)
5776         {
5777           nType = bracetype (pszEnd[-1]);
5778           bAfter = true;
5779         }
5780     }
5781   else if (ptCursorPos.x > 0)
5782     {
5783       nType = bracetype (pszEnd[-1]);
5784       bAfter = true;
5785     }
5786   if (nType)
5787     {
5788       int nOther, nCount = 0, nComment = 0;
5789       if (bAfter)
5790         {
5791           nOther = ((nType - 1) ^ 1) + 1;
5792           if (nOther & 1)
5793             pszEnd--;
5794         }
5795       else
5796         {
5797           nOther = ((nType - 1) ^ 1) + 1;
5798           if (!(nOther & 1))
5799             pszEnd++;
5800         }
5801       LPCTSTR pszOpenComment = m_CurSourceDef->opencomment,
5802         pszCloseComment = m_CurSourceDef->closecomment,
5803         pszCommentLine = m_CurSourceDef->commentline, pszTest;
5804       int nOpenComment = (int) _tcslen (pszOpenComment),
5805         nCloseComment = (int) _tcslen (pszCloseComment),
5806         nCommentLine = (int) _tcslen (pszCommentLine);
5807       if (nOther & 1)
5808         {
5809           for (;;)
5810             {
5811               while (--pszEnd >= pszText)
5812                 {
5813                   pszTest = pszEnd - nOpenComment + 1;
5814                   if (pszTest >= pszText && !_tcsnicmp (pszTest, pszOpenComment, nOpenComment))
5815                     {
5816                       nComment--;
5817                       pszEnd = pszTest;
5818                       if (--pszEnd < pszText)
5819                         {
5820                           break;
5821                         }
5822                     }
5823                   pszTest = pszEnd - nCloseComment + 1;
5824                   if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCloseComment, nCloseComment))
5825                     {
5826                       nComment++;
5827                       pszEnd = pszTest;
5828                       if (--pszEnd < pszText)
5829                         {
5830                           break;
5831                         }
5832                     }
5833                   if (!nComment)
5834                     {
5835                       pszTest = pszEnd - nCommentLine + 1;
5836                       if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCommentLine, nCommentLine))
5837                         {
5838                           break;
5839                         }
5840                       if (bracetype (*pszEnd) == nType)
5841                         {
5842                           nCount++;
5843                         }
5844                       else if (bracetype (*pszEnd) == nOther)
5845                         {
5846                           if (!nCount--)
5847                             {
5848                               ptCursorPos.x = (LONG) (pszEnd - pszText);
5849                               if (bAfter)
5850                                 ptCursorPos.x++;
5851                               SetCursorPos (ptCursorPos);
5852                               SetSelection (ptCursorPos, ptCursorPos);
5853                               SetAnchor (ptCursorPos);
5854                               EnsureVisible (ptCursorPos);
5855                               return;
5856                             }
5857                         }
5858                     }
5859                 }
5860               if (ptCursorPos.y)
5861                 {
5862                   ptCursorPos.x = m_pTextBuffer->GetLineLength (--ptCursorPos.y);
5863                   pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
5864                   pszEnd = pszText + ptCursorPos.x;
5865                 }
5866               else
5867                 break;
5868             }
5869         }
5870       else
5871         {
5872           LPCTSTR pszBegin = pszText;
5873           pszText = pszEnd;
5874           pszEnd = pszBegin + nLength;
5875           int nLines = m_pTextBuffer->GetLineCount ();
5876           for (;;)
5877             {
5878               while (pszText < pszEnd)
5879                 {
5880                   pszTest = pszText + nCloseComment;
5881                   if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCloseComment, nCloseComment))
5882                     {
5883                       nComment--;
5884                       pszText = pszTest;
5885                       if (pszText > pszEnd)
5886                         {
5887                           break;
5888                         }
5889                     }
5890                   pszTest = pszText + nOpenComment;
5891                   if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszOpenComment, nOpenComment))
5892                     {
5893                       nComment++;
5894                       pszText = pszTest;
5895                       if (pszText > pszEnd)
5896                         {
5897                           break;
5898                         }
5899                     }
5900                   if (!nComment)
5901                     {
5902                       pszTest = pszText + nCommentLine;
5903                       if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCommentLine, nCommentLine))
5904                         {
5905                           break;
5906                         }
5907                       if (bracetype (*pszText) == nType)
5908                         {
5909                           nCount++;
5910                         }
5911                       else if (bracetype (*pszText) == nOther)
5912                         {
5913                           if (!nCount--)
5914                             {
5915                               ptCursorPos.x = (LONG) (pszText - pszBegin);
5916                               if (bAfter)
5917                                 ptCursorPos.x++;
5918                               SetCursorPos (ptCursorPos);
5919                               SetSelection (ptCursorPos, ptCursorPos);
5920                               SetAnchor (ptCursorPos);
5921                               EnsureVisible (ptCursorPos);
5922                               return;
5923                             }
5924                         }
5925                     }
5926                   pszText++;
5927                 }
5928               if (ptCursorPos.y < nLines)
5929                 {
5930                   ptCursorPos.x = 0;
5931                   nLength = m_pTextBuffer->GetLineLength (++ptCursorPos.y);
5932                   pszBegin = pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
5933                   pszEnd = pszBegin + nLength;
5934                 }
5935               else
5936                 break;
5937             }
5938         }
5939     }
5940 }
5941
5942 void CCrystalTextView::
5943 OnUpdateMatchBrace (CCmdUI * pCmdUI)
5944 {
5945   CPoint ptCursorPos = GetCursorPos ();
5946   int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
5947   LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y) + ptCursorPos.x;
5948   pCmdUI->Enable (ptCursorPos.x < nLength && (bracetype (*pszText) || ptCursorPos.x > 0 && bracetype (pszText[-1])) || ptCursorPos.x > 0 && bracetype (pszText[-1]));
5949 }
5950
5951 void CCrystalTextView::
5952 OnEditGoTo ()
5953 {
5954   CGotoDlg dlg (this);
5955   dlg.DoModal ();
5956 }
5957
5958 void CCrystalTextView::
5959 OnUpdateToggleSourceHeader (CCmdUI * pCmdUI)
5960 {
5961   pCmdUI->Enable (m_CurSourceDef->type == SRC_C);
5962 }
5963
5964 void CCrystalTextView::
5965 OnToggleSourceHeader ()
5966 {
5967   if (m_CurSourceDef->type == SRC_C)
5968     {
5969       CDocument *pDoc = GetDocument ();
5970       ASSERT (pDoc != nullptr);
5971       CString sFilePath = pDoc->GetPathName (), sOriginalPath = sFilePath;
5972       if (!_tcsicmp (sFilePath.Right (2), _T (".c")))
5973         {
5974           sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ('h');
5975         }
5976       else if (!_tcsicmp (sFilePath.Right (4), _T (".cpp")))
5977         {
5978           sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('h');
5979         }
5980       else if (!_tcsicmp (sFilePath.Right (4), _T (".inl")))
5981         {
5982           sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
5983           if (!FileExist(sFilePath))
5984             {
5985               sFilePath = sFilePath + _T ("pp");
5986             }
5987         }
5988       else if (!_tcsicmp (sFilePath.Right (4), _T (".hpp")))
5989         {
5990           sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
5991           if (!FileExist(sFilePath))
5992             {
5993               sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
5994               if (!FileExist(sFilePath))
5995                 {
5996                   sFilePath = sFilePath + _T ("pp");
5997                 }
5998             }
5999         }
6000       else if (!_tcsicmp (sFilePath.Right (2), _T (".h")))
6001         {
6002           sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ("hpp");
6003           if (!FileExist(sFilePath))
6004             {
6005               sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6006               if (!FileExist(sFilePath))
6007                 {
6008                   sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6009                   if (!FileExist(sFilePath))
6010                     {
6011                       sFilePath = sFilePath + _T ("pp");
6012                     }
6013                 }
6014             }
6015         }
6016       if (FileExist(sFilePath))
6017         {
6018           if (!m_bSingle || !pDoc->IsModified () || pDoc->DoSave (sOriginalPath))
6019             {
6020               AfxGetApp ()->OpenDocumentFile (sFilePath);
6021               if (m_bSingle)
6022                 {
6023                   m_ptCursorLast.x = m_ptCursorLast.y = 0;
6024                   ASSERT_VALIDTEXTPOS (m_ptCursorLast);
6025                   CPoint ptCursorPos = m_ptCursorLast;
6026                   SetCursorPos (ptCursorPos);
6027                   SetSelection (ptCursorPos, ptCursorPos);
6028                   SetAnchor (ptCursorPos);
6029                   EnsureVisible (ptCursorPos);
6030                   Invalidate ();
6031                 }
6032             }
6033         }
6034     }
6035 }
6036
6037 void CCrystalTextView::
6038 OnUpdateSelMargin (CCmdUI * pCmdUI)
6039 {
6040   pCmdUI->SetCheck (m_bSelMargin);
6041 }
6042
6043 void CCrystalTextView::
6044 OnSelMargin ()
6045 {
6046   ASSERT (m_CurSourceDef != nullptr);
6047   if (m_bSelMargin)
6048     {
6049       m_CurSourceDef->flags &= ~SRCOPT_SELMARGIN;
6050       SetSelectionMargin (false);
6051     }
6052   else
6053     {
6054       m_CurSourceDef->flags |= SRCOPT_SELMARGIN;
6055       SetSelectionMargin (true);
6056     }
6057 }
6058
6059 void CCrystalTextView::
6060 OnUpdateWordWrap (CCmdUI * pCmdUI)
6061 {
6062   pCmdUI->SetCheck (m_bWordWrap);
6063 }
6064
6065 void CCrystalTextView::
6066 OnWordWrap ()
6067 {
6068   ASSERT (m_CurSourceDef != nullptr);
6069   if (m_bWordWrap)
6070     {
6071       m_CurSourceDef->flags &= ~SRCOPT_WORDWRAP;
6072       SetWordWrapping (false);
6073     }
6074   else
6075     {
6076       m_CurSourceDef->flags |= SRCOPT_WORDWRAP;
6077       SetWordWrapping (true);
6078     }
6079 }
6080
6081 void CCrystalTextView::
6082 OnForceRedraw ()
6083 {
6084   //Invalidate ();
6085   RedrawWindow (nullptr, nullptr, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ERASENOW);
6086 }
6087
6088 void CCrystalTextView::
6089 OnToggleColumnSelection ()
6090 {
6091   m_bColumnSelection = !m_bColumnSelection;
6092   Invalidate ();
6093 }
6094
6095 //BEGIN SW
6096 bool CCrystalTextView::GetWordWrapping() const
6097 {
6098   return m_bWordWrap;
6099 }
6100
6101 void CCrystalTextView::SetWordWrapping( bool bWordWrap )
6102 {
6103   m_bWordWrap = bWordWrap;
6104
6105   if( IsWindow( m_hWnd ) )
6106     {
6107       m_nOffsetChar = 0;
6108       InvalidateScreenRect();
6109     }
6110 }
6111
6112 CCrystalParser *CCrystalTextView::SetParser( CCrystalParser *pParser )
6113 {
6114   CCrystalParser        *pOldParser = m_pParser;
6115
6116   m_pParser = pParser;
6117
6118   if( pParser != nullptr )
6119     pParser->m_pTextView = this;
6120
6121   return pOldParser;
6122 }
6123 //END SW
6124
6125 bool CCrystalTextView::GetEnableHideLines () const
6126 {
6127   return m_bHideLines;
6128 }
6129
6130 void CCrystalTextView::SetEnableHideLines (bool bHideLines)
6131 {
6132   m_bHideLines = bHideLines;
6133 }
6134
6135 /**
6136  * @brief Return whether a line is visible.
6137  */
6138 bool CCrystalTextView::GetLineVisible (int nLineIndex) const
6139 {
6140   return !m_bHideLines || !(GetLineFlags (nLineIndex) & LF_INVISIBLE);
6141 }
6142
6143 //BEGIN SW
6144 // incremental search imlementation
6145 BOOL CCrystalTextView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO *pHandlerInfo )
6146 {
6147   // just look for commands
6148   if( nCode != CN_COMMAND || pExtra != nullptr )
6149     return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6150
6151   // handle code:
6152   // each command that is not related to incremental search
6153   // ends the incremental search
6154   if( nID == ID_EDIT_FIND_INCREMENTAL_FORWARD || 
6155     nID == ID_EDIT_FIND_INCREMENTAL_BACKWARD || 
6156     nID == ID_EDIT_DELETE_BACK )
6157     return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6158
6159   if( nID >= ID_EDIT_FIRST && nID <= ID_EDIT_LAST )
6160     m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6161
6162   return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6163 }
6164
6165 void CCrystalTextView::OnChar( wchar_t nChar, UINT nRepCnt, UINT nFlags )
6166 {
6167   CView::OnChar( nChar, nRepCnt, nFlags );
6168
6169   // we only have to handle character-input, if we are in incremental search mode
6170   if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6171     return;
6172
6173   // exit incremental search, when Escape is pressed
6174   if( nChar == VK_ESCAPE )
6175     {
6176       // if not end incremental search
6177       m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6178       SetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6179       SetCursorPos( m_cursorPosBeforeIncrementalSearch );
6180       EnsureVisible( m_cursorPosBeforeIncrementalSearch );
6181       return;
6182     }
6183
6184   // exit incremental search without destroying selection
6185   if( nChar == VK_RETURN )
6186     {
6187       m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6188       return;
6189     }
6190
6191   // is the character valid for incremental search?
6192   if( !_istgraph( nChar ) && !(nChar == _T(' ')) && !(nChar == _T('\t')) )
6193     {
6194       // if not end incremental search
6195       m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6196       return;
6197     }
6198
6199   // if last search was not successfull do not add a new character
6200   if( !m_bIncrementalFound )
6201     {
6202       MessageBeep( MB_OK );
6203       return;
6204     }
6205
6206   // add character to incremental search string and search
6207   *m_pstrIncrementalSearchString += (TCHAR) nChar;
6208   OnEditFindIncremental();
6209 }
6210
6211 LRESULT CCrystalTextView::OnImeStartComposition(WPARAM wParam, LPARAM lParam) /* IME */
6212 {
6213   UpdateCompositionWindowFont();
6214   UpdateCompositionWindowPos();
6215
6216   return DefWindowProc(WM_IME_STARTCOMPOSITION, wParam, lParam);
6217 }
6218
6219 void CCrystalTextView::OnEditDeleteBack() 
6220 {
6221   if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6222     return;
6223
6224   // remove last character from search string
6225   if( m_pstrIncrementalSearchString->IsEmpty() )
6226     return;
6227
6228   *m_pstrIncrementalSearchString = m_pstrIncrementalSearchString->Left( m_pstrIncrementalSearchString->GetLength() - 1 );
6229   OnEditFindIncremental();
6230 }
6231
6232
6233 void CCrystalTextView::OnEditFindIncremental( bool bFindNextOccurence /*= false*/ )
6234 {
6235   // when string is empty, then goto position where the search starts
6236   if( m_pstrIncrementalSearchString->IsEmpty() )
6237     {
6238       SetSelection( m_incrementalSearchStartPos, m_incrementalSearchStartPos );
6239       SetCursorPos( m_incrementalSearchStartPos );
6240       EnsureVisible( m_incrementalSearchStartPos );
6241       return;
6242     }
6243
6244   // otherwise search next occurence of search string, 
6245   // starting at current cursor position
6246   CPoint        matchStart, matchEnd;
6247
6248   // calculate start point for search
6249   if( bFindNextOccurence )
6250     {
6251       CPoint    selStart, selEnd;
6252       GetSelection( selStart, selEnd );
6253       m_incrementalSearchStartPos = (m_bIncrementalSearchBackward)? selStart : selEnd;
6254     }
6255
6256   m_bIncrementalFound = FindText( 
6257     *m_pstrIncrementalSearchString,
6258     m_incrementalSearchStartPos,
6259     m_bIncrementalSearchBackward? FIND_DIRECTION_UP : 0,
6260     true,
6261     &matchStart );
6262
6263   if( !m_bIncrementalFound )
6264     {
6265       MessageBeep( MB_OK );
6266       return;
6267     }
6268
6269   // select found text and set cursor to end of match
6270   matchEnd = matchStart;
6271   matchEnd.x+= m_pstrIncrementalSearchString->GetLength();
6272   SetSelection( matchStart, matchEnd );
6273   SetCursorPos( matchEnd );
6274   EnsureVisible( matchEnd );
6275 }
6276
6277
6278
6279 void CCrystalTextView::OnEditFindIncrementalForward()
6280 {
6281   if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6282     {
6283       // initialize
6284       if( !m_pstrIncrementalSearchString->IsEmpty() )
6285         *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6286       m_pstrIncrementalSearchString->Empty();
6287       m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6288       GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6289     }
6290   else if( m_bIncrementalSearchForward )
6291     {
6292       if( m_pstrIncrementalSearchString->IsEmpty() )
6293         {
6294           *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6295           m_pstrIncrementalSearchStringOld->Empty();
6296           OnEditFindIncremental();
6297         }
6298       else
6299         OnEditFindIncremental( true );
6300
6301       return;
6302     }
6303
6304   m_bIncrementalSearchForward = true;
6305   m_bIncrementalSearchBackward = false;
6306   m_bIncrementalFound = true;
6307   OnEditFindIncremental();
6308 }
6309
6310 void CCrystalTextView::OnEditFindIncrementalBackward()
6311 {
6312   if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6313     {
6314       // initialize
6315       if( !m_pstrIncrementalSearchString->IsEmpty() )
6316         *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6317       m_pstrIncrementalSearchString->Empty();
6318       GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6319       m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6320     }
6321   else if( m_bIncrementalSearchBackward )
6322     {
6323       if( m_pstrIncrementalSearchString->IsEmpty() )
6324         {
6325           *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6326           m_pstrIncrementalSearchStringOld->Empty();
6327           OnEditFindIncremental();
6328         }
6329       else
6330         OnEditFindIncremental( true );
6331
6332       return;
6333     }
6334
6335   m_bIncrementalSearchForward = false;
6336   m_bIncrementalSearchBackward = true;
6337   m_bIncrementalFound = true;
6338   OnEditFindIncremental();
6339 }
6340
6341 void CCrystalTextView::OnUpdateEditFindIncrementalForward(CCmdUI* pCmdUI)
6342 {
6343   if (m_pTextBuffer != nullptr)
6344     {
6345       int nLines = m_pTextBuffer->GetLineCount ();
6346       int nChars = m_pTextBuffer->GetLineLength (m_ptCursorPos.y);
6347       pCmdUI->Enable(m_ptCursorPos.y < nLines - 1 || m_ptCursorPos.x < nChars);
6348       return;
6349     }
6350   pCmdUI->Enable(false);
6351 }
6352
6353 void CCrystalTextView::OnUpdateEditFindIncrementalBackward(CCmdUI* pCmdUI)
6354 {
6355   if (m_pTextBuffer != nullptr)
6356     {
6357       pCmdUI->Enable(m_ptCursorPos.y > 0 || m_ptCursorPos.x > 0);
6358       return;
6359     }
6360   pCmdUI->Enable(false);
6361 }
6362
6363 void CCrystalTextView::OnUpdateStatusMessage( CStatusBar *pStatusBar )
6364 {
6365   static bool   bUpdatedAtLastCall = false;
6366
6367   ASSERT( pStatusBar != nullptr && IsWindow( pStatusBar->m_hWnd ) );
6368   if( pStatusBar == nullptr || !IsWindow( pStatusBar->m_hWnd ) )
6369     return;
6370
6371   if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6372     {
6373       if( bUpdatedAtLastCall )
6374         pStatusBar->SetPaneText( 0, LoadResString(AFX_IDS_IDLEMESSAGE).c_str() );
6375
6376       bUpdatedAtLastCall = false;
6377
6378       return;
6379     }
6380
6381   CString       strFormat;
6382   UINT formatid = 0;
6383   if( !m_bIncrementalFound )
6384     formatid = IDS_FIND_INCREMENTAL_FAILED;
6385   else if( m_bIncrementalSearchForward )
6386     formatid = IDS_FIND_INCREMENTAL_FORWARD;
6387   else if( m_bIncrementalSearchBackward )
6388     formatid = IDS_FIND_INCREMENTAL_BACKWARD;
6389   else
6390     return;
6391   strFormat.Format( LoadResString(formatid).c_str(), (LPCTSTR)*m_pstrIncrementalSearchString );
6392
6393   pStatusBar->SetPaneText( 0, strFormat );
6394   bUpdatedAtLastCall = false;
6395 }
6396 //END SW
6397
6398 bool CCrystalTextView::IsTextBufferInitialized () const
6399 {
6400   return m_pTextBuffer && m_pTextBuffer->IsTextBufferInitialized(); 
6401 }
6402
6403 CString CCrystalTextView::GetTextBufferEol(int nLine) const
6404 {
6405   return m_pTextBuffer->GetLineEol(nLine); 
6406 }
6407
6408 void CCrystalTextView::SetMarkersContext(CCrystalTextMarkers * pMarkers)
6409 {
6410   pMarkers->AddView(this);
6411   m_pMarkers = pMarkers;
6412 }
6413
6414 #ifdef _UNICODE
6415 int CCrystalTextView::GetCharCellCountUnicodeChar(const wchar_t *pch)
6416 {  
6417   wchar_t ch = *pch;
6418   if (!m_bChWidthsCalculated[ch/256])
6419     {
6420       if (U16_IS_SURROGATE(ch) && U16_IS_SURROGATE_LEAD(ch))
6421         {
6422           return wcwidth(U16_GET_SUPPLEMENTARY(ch, pch[1]));
6423         }
6424       else
6425         {
6426           int nWidthArray[256];
6427           wchar_t nStart = ch/256*256;
6428           wchar_t nEnd = nStart + 255;
6429           CDC *pdc = GetDC();
6430           CFont *pOldFont = pdc->SelectObject(GetFont());
6431           GetCharWidth32(pdc->m_hDC, nStart, nEnd, nWidthArray);
6432           int nCharWidth = GetCharWidth();
6433           for (int i = 0; i < 256; i++) 
6434             {
6435               if (nCharWidth * 15 < nWidthArray[i] * 10)
6436                 m_iChDoubleWidthFlags[(nStart+i)/32] |= 1 << (i % 32);
6437               else
6438                 {
6439                   wchar_t ch2 = static_cast<wchar_t>(nStart + i);
6440                   if (wcwidth(ch2) > 1)
6441                     m_iChDoubleWidthFlags[(nStart + i) / 32] |= 1 << (i % 32);
6442                 }
6443             }
6444           m_bChWidthsCalculated[ch / 256] = true;
6445           pdc->SelectObject(pOldFont);
6446         }
6447     }
6448   if (m_iChDoubleWidthFlags[ch / 32] & (1 << (ch % 32)))
6449     return 2;
6450   else
6451     return 1;
6452 }
6453 #endif
6454
6455 /** @brief Reset computed unicode character widths. */
6456 void CCrystalTextView::ResetCharWidths ()
6457 {
6458 #ifdef _UNICODE
6459   ZeroMemory(m_bChWidthsCalculated, sizeof(m_bChWidthsCalculated));
6460   ZeroMemory(m_iChDoubleWidthFlags, sizeof(m_iChDoubleWidthFlags));
6461 #endif
6462 }
6463
6464 // This function assumes selection is in one line
6465 void CCrystalTextView::EnsureVisible (CPoint ptStart, CPoint ptEnd)
6466 {
6467   //  Scroll vertically
6468   //BEGIN SW
6469   int nSubLineCount = GetSubLineCount();
6470   int nNewTopSubLine = m_nTopSubLine;
6471   CPoint subLinePos;
6472   CPoint subLinePosEnd;
6473
6474   CharPosToPoint( ptStart.y, ptStart.x, subLinePos );
6475   subLinePos.y += GetSubLineIndex( ptStart.y );
6476   CharPosToPoint( ptEnd.y, ptEnd.x, subLinePosEnd );
6477   subLinePosEnd.y += GetSubLineIndex( ptEnd.y );
6478
6479   if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
6480     nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
6481   if( subLinePos.y < nNewTopSubLine )
6482     nNewTopSubLine = subLinePos.y;
6483
6484   if( nNewTopSubLine < 0 )
6485     nNewTopSubLine = 0;
6486   if( nNewTopSubLine >= nSubLineCount )
6487     nNewTopSubLine = nSubLineCount - 1;
6488
6489   if ( !m_bWordWrap && !m_bHideLines )
6490     {
6491       // WINMERGE: This line fixes (cursor) slowdown after merges!
6492       // I don't know exactly why, but propably we are setting
6493       // m_nTopLine to zero in ResetView() and are not setting to
6494       // valid value again. Maybe this is a good place to set it?
6495       m_nTopLine = nNewTopSubLine;
6496     }
6497   else
6498     {
6499       int dummy;
6500       GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
6501     }
6502
6503   if( nNewTopSubLine != m_nTopSubLine )
6504     {
6505       ScrollToSubLine( nNewTopSubLine );
6506       UpdateCaret();
6507       UpdateSiblingScrollPos( false );
6508     }
6509
6510   //  Scroll horizontally
6511   //BEGIN SW
6512   // we do not need horizontally scrolling, if we wrap the words
6513   if( m_bWordWrap )
6514     return;
6515   //END SW
6516   int nActualPos = CalculateActualOffset (ptStart.y, ptStart.x);
6517   int nActualEndPos = CalculateActualOffset (ptEnd.y, ptEnd.x);
6518   int nNewOffset = m_nOffsetChar;
6519   const int nScreenChars = GetScreenChars ();
6520   const int nBeginOffset = nActualPos - m_nOffsetChar;
6521   const int nEndOffset = nActualEndPos - m_nOffsetChar;
6522   const int nSelLen = nActualEndPos - nActualPos;
6523
6524   // Selection fits to screen, scroll whole selection visible
6525   if (nSelLen < nScreenChars)
6526     {
6527       // Begin of selection not visible 
6528       if (nBeginOffset > nScreenChars)
6529         {
6530           // Scroll so that there is max 5 chars margin at end
6531           if (nScreenChars - nSelLen > 5)
6532             nNewOffset = nActualPos + 5 - nScreenChars + nSelLen;
6533           else
6534             nNewOffset = nActualPos - 5;
6535         }
6536       else if (nBeginOffset < 0)
6537         {
6538           // Scroll so that there is max 5 chars margin at begin
6539           if (nScreenChars - nSelLen >= 5)
6540             nNewOffset = nActualPos - 5;
6541           else
6542             nNewOffset = nActualPos - 5 - nScreenChars + nSelLen;
6543         }
6544       // End of selection not visible
6545       else if (nEndOffset > nScreenChars ||
6546           nEndOffset < 0)
6547         {
6548           nNewOffset = nActualPos - 5;
6549         }
6550      }
6551   else // Selection does not fit screen so scroll to begin of selection
6552     {
6553       nNewOffset = nActualPos - 5;
6554     }
6555
6556   // Horiz scroll limit to longest line + one screenwidth
6557   const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
6558   if (nNewOffset >= nMaxLineLen + nScreenChars)
6559     nNewOffset = nMaxLineLen + nScreenChars - 1;
6560   if (nNewOffset < 0)
6561     nNewOffset = 0;
6562
6563   if (m_nOffsetChar != nNewOffset)
6564     {
6565       ScrollToChar (nNewOffset);
6566       UpdateCaret ();
6567       UpdateSiblingScrollPos (true);
6568     }
6569 }
6570
6571 // Analyze the first line of file to detect its type
6572 // Mainly it works for xml files
6573 bool CCrystalTextView::
6574 SetTextTypeByContent (LPCTSTR pszContent)
6575 {
6576   RxNode *rxnode = nullptr;
6577   RxMatchRes rxmatch;
6578   int nLen;
6579   if (::FindStringHelper(pszContent, pszContent, _T("^\\s*\\<\\?xml\\s+.+?\\?\\>\\s*$"),
6580       FIND_REGEXP, nLen, rxnode, &rxmatch) == 0)
6581     {
6582       if (rxnode)
6583         RxFree (rxnode);
6584       return SetTextType(CCrystalTextView::SRC_XML);
6585     }
6586   if (rxnode)
6587     RxFree (rxnode);
6588   return false;
6589 }
6590
6591 ////////////////////////////////////////////////////////////////////////////