OSDN Git Service

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