OSDN Git Service

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