OSDN Git Service

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