OSDN Git Service

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