1 ////////////////////////////////////////////////////////////////////////////
2 // File: ccrystaltextview.cpp
4 // Created: 29-Dec-1998
6 // Author: Stcherbatchenko Andrei
7 // E-mail: windfall@gmx.de
9 // Implementation of the CCrystalTextView class, a part of Crystal Edit -
10 // syntax coloring text editor.
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 ////////////////////////////////////////////////////////////////////////////
18 ////////////////////////////////////////////////////////////////////////////
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.
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 ////////////////////////////////////////////////////////////////////////////
32 ////////////////////////////////////////////////////////////////////////////
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
40 // + FIX: ResetView() now virtual
41 // + FEATURE: Added OnEditOperation() virtual: base for auto-indent,
43 ////////////////////////////////////////////////////////////////////////////
45 ////////////////////////////////////////////////////////////////////////////
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 ...
54 // ... it's being edited very rapidly so sorry for non-commented
55 // and maybe "ugly" code ...
56 ////////////////////////////////////////////////////////////////////////////
58 ////////////////////////////////////////////////////////////////////////////
59 // 01-Jun-99 to 31-Aug-99
60 // Sven Wiegand (search for "//BEGIN SW" to find my changes):
62 // + FEATURE: support for language switching on the fly with class
64 // + FEATURE: word wrapping
65 // + FIX: Setting m_nIdealCharPos, when choosing cursor position by mouse
66 // + FIX: Backward search
67 // + FEATURE: incremental search
68 ////////////////////////////////////////////////////////////////////////////
70 ////////////////////////////////////////////////////////////////////////////
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
80 ///////////////////////////////////////////////////////////////////////////////
83 * @file ccrystaltextview.cpp
85 * @brief Implementation of the CCrystalTextView class
92 #include <imm.h> /* IME */
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"
113 using CrystalLineParser::TEXTBLOCK;
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")
119 #ifndef __AFXPRIV_H__
120 #pragma message("Include <afxpriv.h> in your stdafx.h to avoid this message")
125 #define new DEBUG_NEW
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.
137 #define _countof(array) (sizeof(array)/sizeof(array[0]))
140 #define DEFAULT_PRINT_MARGIN 1000 // 10 millimeters
142 #ifndef WM_MOUSEHWHEEL
143 # define WM_MOUSEHWHEEL 0x20e
146 /** @brief Width of revision marks. */
147 const UINT MARGIN_REV_WIDTH = 3;
149 /** @brief Color of unsaved line revision mark (dark yellow). */
150 const COLORREF UNSAVED_REVMARK_CLR = RGB(0xD7, 0xD7, 0x00);
151 /** @brief Color of saved line revision mark (green). */
152 const COLORREF SAVED_REVMARK_CLR = RGB(0x00, 0xFF, 0x00);
154 #define SMOOTH_SCROLL_FACTOR 6
156 #define ICON_INDEX_WRAPLINE 15
158 ////////////////////////////////////////////////////////////////////////////
161 LOGFONT CCrystalTextView::m_LogFont;
163 IMPLEMENT_DYNCREATE (CCrystalTextView, CView)
165 HINSTANCE CCrystalTextView::s_hResourceInst = nullptr;
166 CCrystalTextView::RENDERING_MODE CCrystalTextView::s_nRenderingModeDefault = RENDERING_MODE::GDI;
168 static ptrdiff_t FindStringHelper(LPCTSTR pszLineBegin, size_t nLineLength, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch);
170 BEGIN_MESSAGE_MAP (CCrystalTextView, CView)
171 //{{AFX_MSG_MAP(CCrystalTextView)
184 ON_WM_LBUTTONDBLCLK ()
185 ON_COMMAND (ID_EDIT_COPY, OnEditCopy)
186 ON_UPDATE_COMMAND_UI (ID_EDIT_COPY, OnUpdateEditCopy)
187 ON_COMMAND (ID_EDIT_SELECT_ALL, OnEditSelectAll)
189 ON_WM_SYSCOLORCHANGE ()
191 ON_COMMAND (ID_EDIT_FIND, OnEditFind)
192 ON_COMMAND (ID_EDIT_REPEAT, OnEditRepeat)
193 ON_UPDATE_COMMAND_UI (ID_EDIT_REPEAT, OnUpdateEditRepeat)
194 ON_COMMAND (ID_EDIT_MARK, OnEditMark)
197 ON_MESSAGE (WM_IME_STARTCOMPOSITION, OnImeStartComposition) /* IME */
199 ON_COMMAND (ID_EDIT_CHAR_LEFT, OnCharLeft)
200 ON_COMMAND (ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
201 ON_COMMAND (ID_EDIT_CHAR_RIGHT, OnCharRight)
202 ON_COMMAND (ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
203 ON_COMMAND (ID_EDIT_WORD_LEFT, OnWordLeft)
204 ON_COMMAND (ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
205 ON_COMMAND (ID_EDIT_WORD_RIGHT, OnWordRight)
206 ON_COMMAND (ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
207 ON_COMMAND (ID_EDIT_LINE_UP, OnLineUp)
208 ON_COMMAND (ID_EDIT_EXT_LINE_UP, OnExtLineUp)
209 ON_COMMAND (ID_EDIT_LINE_DOWN, OnLineDown)
210 ON_COMMAND (ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
211 ON_COMMAND (ID_EDIT_SCROLL_UP, ScrollUp)
212 ON_COMMAND (ID_EDIT_SCROLL_DOWN, ScrollDown)
213 ON_COMMAND (ID_EDIT_PAGE_UP, OnPageUp)
214 ON_COMMAND (ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
215 ON_COMMAND (ID_EDIT_PAGE_DOWN, OnPageDown)
216 ON_COMMAND (ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
217 ON_COMMAND (ID_EDIT_LINE_END, OnLineEnd)
218 ON_COMMAND (ID_EDIT_EXT_LINE_END, OnExtLineEnd)
219 ON_COMMAND (ID_EDIT_HOME, OnHome)
220 ON_COMMAND (ID_EDIT_EXT_HOME, OnExtHome)
221 ON_COMMAND (ID_EDIT_TEXT_BEGIN, OnTextBegin)
222 ON_COMMAND (ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
223 ON_COMMAND (ID_EDIT_TEXT_END, OnTextEnd)
224 ON_COMMAND (ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
225 // Standard printing commands
226 ON_COMMAND (ID_FILE_PAGE_SETUP, OnFilePageSetup)
227 ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint)
228 ON_COMMAND (ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
229 ON_COMMAND (ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
231 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
232 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
234 ON_COMMAND_RANGE (ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
235 ON_COMMAND_RANGE (ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
236 ON_COMMAND (ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
238 ON_COMMAND (ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
239 ON_COMMAND (ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
240 ON_COMMAND (ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
241 ON_COMMAND (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
242 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
243 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
244 ON_UPDATE_COMMAND_UI (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
245 // Ferdi's source type chnages
246 ON_COMMAND_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnSourceType)
247 ON_UPDATE_COMMAND_UI_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnUpdateSourceType)
248 ON_COMMAND (ID_EDIT_MATCHBRACE, OnMatchBrace)
249 ON_UPDATE_COMMAND_UI (ID_EDIT_MATCHBRACE, OnUpdateMatchBrace)
250 ON_COMMAND (ID_EDIT_GOTO, OnEditGoTo)
251 ON_UPDATE_COMMAND_UI (ID_VIEW_TOGGLE_SRC_HDR, OnUpdateToggleSourceHeader)
252 ON_COMMAND (ID_VIEW_TOGGLE_SRC_HDR, OnToggleSourceHeader)
253 ON_UPDATE_COMMAND_UI (ID_VIEW_TOPMARGIN, OnUpdateTopMargin)
254 ON_COMMAND (ID_VIEW_TOPMARGIN, OnTopMargin)
255 ON_UPDATE_COMMAND_UI (ID_VIEW_SELMARGIN, OnUpdateSelMargin)
256 ON_COMMAND (ID_VIEW_SELMARGIN, OnSelMargin)
257 ON_UPDATE_COMMAND_UI (ID_VIEW_WORDWRAP, OnUpdateWordWrap)
258 ON_COMMAND (ID_VIEW_WORDWRAP, OnWordWrap)
259 ON_COMMAND (ID_FORCE_REDRAW, OnForceRedraw)
261 // incremental search
262 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnEditFindIncrementalForward)
263 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnEditFindIncrementalBackward)
264 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnUpdateEditFindIncrementalForward)
265 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnUpdateEditFindIncrementalBackward)
267 ON_COMMAND (ID_EDIT_TOGGLE_COLUMNSELECTION, OnToggleColumnSelection)
270 #define EXPAND_PRIMITIVE(impl, func) \
271 void CCrystalTextView::On##func() { m_bRectangularSelection = false; impl(false); } \
272 void CCrystalTextView::OnExt##func() { impl(true); }
273 EXPAND_PRIMITIVE (MoveLeft, CharLeft)
274 EXPAND_PRIMITIVE (MoveRight, CharRight)
275 EXPAND_PRIMITIVE (MoveWordLeft, WordLeft)
276 EXPAND_PRIMITIVE (MoveWordRight, WordRight)
277 EXPAND_PRIMITIVE (MoveUp, LineUp)
278 EXPAND_PRIMITIVE (MoveDown, LineDown)
279 EXPAND_PRIMITIVE (MovePgUp, PageUp)
280 EXPAND_PRIMITIVE (MovePgDn, PageDown)
281 EXPAND_PRIMITIVE (MoveHome, Home)
282 EXPAND_PRIMITIVE (MoveEnd, LineEnd)
283 EXPAND_PRIMITIVE (MoveCtrlHome, TextBegin)
284 EXPAND_PRIMITIVE (MoveCtrlEnd, TextEnd)
285 #undef EXPAND_PRIMITIVE
287 /////////////////////////////////////////////////////////////////////////////
288 // CCrystalTextView construction/destruction
290 bool CCrystalTextView::
291 DoSetTextType (CrystalLineParser::TextDefinition *def)
293 m_CurSourceDef = def;
294 SetFlags (def->flags);
297 // EOL is determined from file, tabsize and viewtabs are
298 // global WinMerge settings, selection margin is not needed
299 // and wordwrapping must be false always
301 SetWordWrapping ((def->flags & SRCOPT_WORDWRAP) != false);
302 SetSelectionMargin ((def->flags & SRCOPT_SELMARGIN) != false);
303 SetTabSize (def->tabsize);
304 SetViewTabs ((def->flags & SRCOPT_SHOWTABS) != false);
306 if (def->flags & SRCOPT_EOLNDOS)
310 else if (def->flags & SRCOPT_EOLNUNIX)
314 else if (def->flags & SRCOPT_EOLNMAC)
327 bool CCrystalTextView::
328 SetTextType (LPCTSTR pszExt)
330 m_CurSourceDef = CrystalLineParser::m_SourceDefs;
332 CrystalLineParser::TextDefinition *def = CrystalLineParser::GetTextType (pszExt);
334 return SetTextType (def);
337 bool CCrystalTextView::
338 SetTextType (CrystalLineParser::TextType enuType)
340 CrystalLineParser::TextDefinition *def;
342 m_CurSourceDef = def = CrystalLineParser::m_SourceDefs;
343 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
345 if (def->type == enuType)
347 return SetTextType (def);
353 bool CCrystalTextView::
354 SetTextType (CrystalLineParser::TextDefinition *def)
357 if (m_CurSourceDef != def)
358 return DoSetTextType (def);
364 void CCrystalTextView::
367 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
370 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
372 reg.LoadNumber (_T ("DefaultEncoding"), (DWORD*) &CCrystalTextBuffer::m_nDefaultEncoding);
373 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
376 if (reg1.Open (reg.hKey, def->name, KEY_READ))
378 reg1.LoadString (_T ("Extensions"), def->exts, _countof (def->exts));
379 reg1.LoadNumber (_T ("Flags"), reinterpret_cast<DWORD *>(&def->flags));
380 // reg1.LoadNumber (_T ("TabSize"), &def->tabsize);
381 reg1.LoadString (_T ("OpenComment"), def->opencomment, _countof (def->opencomment));
382 reg1.LoadString (_T ("CloseComment"), def->closecomment, _countof (def->closecomment));
383 reg1.LoadString (_T ("CommentLine"), def->commentline, _countof (def->commentline));
384 reg1.LoadNumber (_T ("DefaultEncoding"), reinterpret_cast<DWORD *>(&def->encoding));
387 bFontLoaded = reg.LoadBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont));
393 CWindowDC dc (CWnd::GetDesktopWindow ());
394 NONCLIENTMETRICS info;
395 info.cbSize = sizeof(info);
396 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
397 memcpy (&m_LogFont, &info.lfMessageFont, sizeof (LOGFONT));
398 m_LogFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
399 m_LogFont.lfWeight = FW_NORMAL;
400 m_LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
401 m_LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
402 m_LogFont.lfQuality = DEFAULT_QUALITY;
403 m_LogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
404 _tcscpy_s (m_LogFont.lfFaceName, _T ("Courier New"));
408 void CCrystalTextView::
411 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
413 if (reg.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
415 VERIFY (reg.SaveNumber (_T ("DefaultEncoding"), (DWORD) CCrystalTextBuffer::m_nDefaultEncoding));
416 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
419 if (reg1.Create (reg.hKey, def->name, KEY_WRITE))
421 VERIFY (reg1.SaveString (_T ("Extensions"), def->exts));
422 VERIFY (reg1.SaveNumber (_T ("Flags"), def->flags));
423 // VERIFY (reg1.SaveNumber (_T ("TabSize"), def->tabsize));
424 VERIFY (reg1.SaveString (_T ("OpenComment"), def->opencomment));
425 VERIFY (reg1.SaveString (_T ("CloseComment"), def->closecomment));
426 VERIFY (reg1.SaveString (_T ("CommentLine"), def->commentline));
427 VERIFY (reg1.SaveNumber (_T ("DefaultEncoding"), def->encoding));
430 VERIFY (reg.SaveBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont)));
434 CCrystalTextView::CCrystalTextView ()
436 , m_pFindTextDlg(nullptr)
437 , m_CurSourceDef(nullptr)
438 , m_dwLastDblClickTime(0)
440 , m_pszMatched(nullptr)
441 , m_bTopMargin(false)
443 , m_bViewLineNumbers(false)
445 , m_bHideLines(false)
446 , m_bLastSearch(false)
447 , m_bBookmarkExist(false)
448 , m_bSingle(false) // needed to be set in descendat classes
449 , m_bRememberLastPos(false)
451 , m_nLastLineIndexCalculatedSubLineIndex(-1)
453 , m_pTextBuffer(nullptr)
454 , m_pCacheBitmap(nullptr)
455 , m_pszLastFindWhat(nullptr)
456 , m_dwLastSearchFlags(0)
457 , m_bMultipleSearch(false)
458 , m_bCursorHidden(false)
463 , m_bDistinguishEols(false)
466 , m_pMarkers(nullptr)
467 , m_panSubLines(new CArray<int, int>())
468 , m_panSubLineIndexCache(new CArray<int, int>())
469 , m_pstrIncrementalSearchString(new CString)
470 , m_pstrIncrementalSearchStringOld(new CString)
471 , m_ParseCookies(new vector<DWORD>)
472 , m_pnActualLineLength(new vector<int>)
476 , m_lfSavedBaseFont{}
478 , m_pPrintFont(nullptr)
480 , m_bChWidthsCalculated{}
481 , m_iChDoubleWidthFlags{}
483 , m_bPreparingToDrag(false)
484 , m_bDraggingText(false)
485 , m_bDragSelection(false)
486 , m_bWordSelection(false)
487 , m_bLineSelection(false)
488 , m_bRectangularSelection(false)
489 , m_bColumnSelection(false)
491 , m_bOverrideCaret(false)
492 , m_nLastFindWhatLen(0)
494 , m_nPrintLineHeight(0)
495 , m_bPrintFooter(false)
496 , m_bPrintHeader(false)
501 , m_bSmoothScroll(false)
502 , m_bVertScrollBarLocked(false)
503 , m_bHorzScrollBarLocked(false)
504 , m_bShowInactiveSelection(false)
505 , m_bDisableDragAndDrop(false)
506 , m_bIncrementalSearchForward(false)
507 , m_bIncrementalSearchBackward(false)
508 , m_bIncrementalFound(false)
510 , m_nRenderingMode(s_nRenderingModeDefault)
511 , m_pCrystalRendererSaved(nullptr)
512 , m_nColumnResizing(-1)
515 if (m_nRenderingMode == RENDERING_MODE::GDI)
516 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
518 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(static_cast<int>(m_nRenderingMode)));
520 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
523 m_panSubLines->SetSize( 0, 4096 );
524 m_panSubLineIndexCache->SetSize( 0, 4096 );
527 CCrystalTextView::ResetView ();
528 SetTextType (CrystalLineParser::SRC_PLAIN);
531 CCrystalTextView::~CCrystalTextView ()
533 ASSERT (m_hAccel == nullptr);
534 ASSERT (m_pCacheBitmap == nullptr);
535 ASSERT (m_pTextBuffer == nullptr); // Must be correctly detached
537 delete m_pFindTextDlg;
539 free (m_pszLastFindWhat);
540 m_pszLastFindWhat=nullptr;
545 free(m_pszMatched); // Allocated by _tcsdup()
546 m_pszMatched = nullptr;
549 delete m_panSubLines;
550 m_panSubLines = nullptr;
552 delete m_panSubLineIndexCache;
553 m_panSubLineIndexCache = nullptr;
555 delete m_pstrIncrementalSearchString;
556 m_pstrIncrementalSearchString = nullptr;
558 delete m_pstrIncrementalSearchStringOld;
559 m_pstrIncrementalSearchStringOld = nullptr;
562 ASSERT(m_ParseCookies != nullptr);
563 delete m_ParseCookies;
564 m_ParseCookies = nullptr;
565 ASSERT(m_pnActualLineLength != nullptr);
566 delete m_pnActualLineLength;
567 m_pnActualLineLength = nullptr;
568 if (m_pMarkers != nullptr)
569 m_pMarkers->DeleteView(this);
572 BOOL CCrystalTextView::
573 PreCreateWindow (CREATESTRUCT & cs)
575 CWnd *pParentWnd = CWnd::FromHandlePermanent (cs.hwndParent);
576 if (pParentWnd == nullptr || !pParentWnd->IsKindOf (RUNTIME_CLASS (CSplitterWnd)))
578 // View must always create its own scrollbars,
579 // if only it's not used within splitter
581 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
582 // we do not need a horizontal scroll bar, if we wrap the lines
583 cs.style|= WS_VSCROLL;
585 cs.style |= (WS_HSCROLL | WS_VSCROLL);
587 cs.style |= (WS_HSCROLL | WS_VSCROLL);
591 cs.lpszClass = AfxRegisterWndClass (CS_DBLCLKS);
592 return CView::PreCreateWindow (cs);
596 /////////////////////////////////////////////////////////////////////////////
597 // CCrystalTextView drawing
599 std::pair<CPoint, CPoint> CCrystalTextView::
603 return { m_ptDrawSelStart, m_ptDrawSelEnd };
606 bool CCrystalTextView::
607 GetColumnSelection (int nLineIndex, int & nSelBegin, int & nSelEnd)
609 int nSelTop, nSelBottom;
610 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
612 nSelTop = m_ptDrawSelStart.y;
613 nSelBottom = m_ptDrawSelEnd.y;
617 nSelTop = m_ptDrawSelEnd.y;
618 nSelBottom = m_ptDrawSelStart.y;
621 if (nSelTop > nLineIndex || nLineIndex > nSelBottom)
629 int nStartCharPos = CalculateActualOffset (m_ptDrawSelStart.y, m_ptDrawSelStart.x, true);
630 int nEndCharPos = CalculateActualOffset (m_ptDrawSelEnd.y, m_ptDrawSelEnd.x, true);
631 int nLeftCharPos, nRightCharPos;
632 if (nStartCharPos > nEndCharPos)
634 nLeftCharPos = nEndCharPos;
635 nRightCharPos = nStartCharPos;
639 nLeftCharPos = nStartCharPos;
640 nRightCharPos = nEndCharPos;
642 if (nRightCharPos < m_nIdealCharPos)
643 nRightCharPos = m_nIdealCharPos;
644 nSelBegin = ApproxActualOffset (nLineIndex, nLeftCharPos);
645 nSelEnd = ApproxActualOffset (nLineIndex, nRightCharPos);
650 void CCrystalTextView::
651 GetFullySelectedLines(int & firstLine, int & lastLine)
653 auto [ptStart, ptEnd] = GetSelection ();
656 firstLine = ptStart.y;
658 firstLine = ptStart.y + 1;
659 if (ptEnd.x == GetLineLength(ptEnd.y))
662 lastLine = ptEnd.y-1;
665 CCrystalTextBuffer *CCrystalTextView::
672 * @brief : Get the line length, for cursor movement
674 * @note : there are at least 4 line lengths :
675 * - number of characters (memory, no EOL)
676 * - number of characters (memory, with EOL)
677 * - number of characters for cursor position (tabs are expanded, no EOL)
678 * - number of displayed characters (tabs are expanded, with EOL)
679 * Corresponding functions :
681 * - GetFullLineLength
682 * - GetLineActualLength
683 * - ExpandChars (returns the line to be displayed as a CString)
685 int CCrystalTextView::
686 GetLineActualLength (int nLineIndex)
688 const int nLineCount = GetLineCount ();
689 ASSERT (nLineCount > 0);
690 ASSERT (nLineIndex >= 0 && nLineIndex < nLineCount);
691 if (!m_pnActualLineLength->size())
693 m_pnActualLineLength->assign(nLineCount, -1);
696 if ((*m_pnActualLineLength)[nLineIndex] != - 1)
697 return (*m_pnActualLineLength)[nLineIndex];
699 // Actual line length is not determined yet, let's calculate a little
700 int nActualLength = 0;
701 int nLength = GetLineLength (nLineIndex);
704 LPCTSTR pszChars = GetLineChars (nLineIndex);
705 const int nTabSize = GetTabSize ();
706 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
707 switch ( GetTextLayoutMode ())
709 case TEXTLAYOUT_TABLE_NOWORDWRAP:
710 case TEXTLAYOUT_TABLE_WORDWRAP:
712 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
713 int nColumnTotalWidth = 0;
714 bool bInQuote = false;
715 const int sep = m_pTextBuffer->GetFieldDelimiter ();
716 const int quote = m_pTextBuffer->GetFieldEnclosure ();
717 for (int i = 0, nColumn = 0; i < nLength; i = pIterChar->next())
719 TCHAR c = pszChars[i];
720 if (!bInQuote && c == sep)
722 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
723 nActualLength = nColumnTotalWidth;
728 bInQuote = !bInQuote;
732 nActualLength += GetCharCellCountFromChar (pszChars + i);
733 if (nColumn < nColumnCount && nActualLength > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
734 nActualLength = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
741 for (int i = 0; i < nLength; i = pIterChar->next())
743 TCHAR c = pszChars[i];
745 nActualLength += (nTabSize - nActualLength % nTabSize);
747 nActualLength += GetCharCellCountFromChar(pszChars + i);
753 (*m_pnActualLineLength)[nLineIndex] = nActualLength;
754 return nActualLength;
757 void CCrystalTextView::
758 ScrollToChar (int nNewOffsetChar, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
761 // no horizontal scrolling, when word wrapping is enabled
762 if (GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
766 // For now, ignoring bNoSmoothScroll and m_bSmoothScroll
767 if (m_nOffsetChar != nNewOffsetChar)
769 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
770 m_nOffsetChar = nNewOffsetChar;
772 GetClientRect (&rcScroll);
773 rcScroll.left += GetMarginWidth ();
774 ScrollWindow (nScrollChars * GetCharWidth (), 0, &rcScroll, &rcScroll);
777 RecalcHorzScrollBar (true);
782 * @brief Scroll view to given line.
783 * Scrolls view so that given line is first line in the view. We limit
784 * scrolling so that there is only one empty line visible after the last
785 * line at max. So we don't allow user to scroll last line being at top or
786 * even at middle of the screen. This is how many editors behave and I
787 * (Kimmo) think it is good for us too.
788 * @param [in] nNewTopSubLine New top line for view.
789 * @param [in] bNoSmoothScroll if true don't use smooth scrolling.
790 * @param [in] bTrackScrollBar if true scrollbar is updated after scroll.
792 void CCrystalTextView::ScrollToSubLine( int nNewTopSubLine,
793 bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
795 if (m_nTopSubLine != nNewTopSubLine)
798 GetClientRect (&rcScroll);
799 rcScroll.top += GetTopMarginHeight ();
801 if (bNoSmoothScroll || ! m_bSmoothScroll)
803 // Limit scrolling so that we show one empty line at end of file
804 const int nScreenLines = GetScreenLines();
805 const int nLineCount = GetSubLineCount();
806 if (nNewTopSubLine > (nLineCount - nScreenLines))
808 nNewTopSubLine = nLineCount - nScreenLines;
809 if (nNewTopSubLine < 0)
813 const int nScrollLines = m_nTopSubLine - nNewTopSubLine;
814 m_nTopSubLine = nNewTopSubLine;
815 // OnDraw() uses m_nTopLine to determine topline
817 GetLineBySubLine(m_nTopSubLine, m_nTopLine, dummy);
818 ScrollWindow(0, nScrollLines * GetLineHeight(), nullptr, rcScroll);
822 RecalcVertScrollBar(true);
823 RecalcHorzScrollBar();
828 // Do smooth scrolling
829 int nLineHeight = GetLineHeight();
830 if (m_nTopSubLine > nNewTopSubLine)
832 int nIncrement = (m_nTopSubLine - nNewTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
833 while (m_nTopSubLine != nNewTopSubLine)
835 int nTopSubLine = m_nTopSubLine - nIncrement;
836 if (nTopSubLine < nNewTopSubLine)
837 nTopSubLine = nNewTopSubLine;
838 const int nScrollLines = nTopSubLine - m_nTopSubLine;
839 m_nTopSubLine = nTopSubLine;
840 ScrollWindow(0, - nLineHeight * nScrollLines, nullptr, rcScroll);
844 RecalcVertScrollBar(true);
845 RecalcHorzScrollBar();
851 int nIncrement = (nNewTopSubLine - m_nTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
852 while (m_nTopSubLine != nNewTopSubLine)
854 int nTopSubLine = m_nTopSubLine + nIncrement;
855 if (nTopSubLine > nNewTopSubLine)
856 nTopSubLine = nNewTopSubLine;
857 const int nScrollLines = nTopSubLine - m_nTopSubLine;
858 m_nTopSubLine = nTopSubLine;
859 ScrollWindow(0, - nLineHeight * nScrollLines, rcScroll, rcScroll);
863 RecalcVertScrollBar(true);
864 RecalcHorzScrollBar();
870 GetLineBySubLine( m_nTopSubLine, m_nTopLine, nDummy );
874 void CCrystalTextView::
875 ScrollToLine (int nNewTopLine, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
877 if( m_nTopLine != nNewTopLine )
878 ScrollToSubLine( GetSubLineIndex( nNewTopLine ), bNoSmoothScroll, bTrackScrollBar );
881 /** Append szadd to string str, and advance position curpos */
882 static void AppendStringAdv(CString & str, int & curpos, LPCTSTR szadd)
885 curpos += (int) _tcslen(szadd);
888 /** Append escaped control char to string str, and advance position curpos */
889 static void AppendEscapeAdv(CString & str, int & curpos, TCHAR c)
891 int curlen = str.GetLength();
892 LPTSTR szadd = str.GetBufferSetLength(curlen + 3) + curlen;
893 curpos += wsprintf(szadd, _T("\t%02X"), static_cast<int>(c));
896 static COLORREF GetIntermediateColor (COLORREF a, COLORREF b)
898 float ratio = 0.333f;
899 const int R = static_cast<int>((GetRValue(a) - GetRValue(b)) * ratio) + GetRValue(b);
900 const int G = static_cast<int>((GetGValue(a) - GetGValue(b)) * ratio) + GetGValue(b);
901 const int B = static_cast<int>((GetBValue(a) - GetBValue(b)) * ratio) + GetBValue(b);
905 int CCrystalTextView::
906 ExpandChars (int nLineIndex, int nOffset, int nCount, CString & line, int nActualOffset)
909 // Request whitespace characters for codepage ACP
910 // because that is the codepage used by ExtTextOut
911 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE::GDI);
918 const int nTabSize = GetTabSize ();
920 LPCTSTR pszChars = GetLineChars(nLineIndex);
922 int nLength = nCount;
924 for (int i = 0; i < nLength; i++)
926 TCHAR c = pszChars[i];
928 nCount += nTabSize - 1;
929 else if (c >= _T('\x00') && c <= _T('\x1F') && c != _T('\r') && c != _T('\n'))
933 // Preallocate line buffer, to avoid reallocations as we add characters
934 line.GetBuffer(nCount + 1); // at least this many characters
935 line.ReleaseBuffer(0);
937 const bool bTableEditing = m_pTextBuffer->GetTableEditing ();
939 if (nCount > nLength || m_bViewTabs || m_bViewEols)
941 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
942 for (int i = 0, next = 0; i < nLength; i = next)
944 next = pIterChar->next();
945 if (pszChars[i] == _T('\t'))
947 int nSpaces = bTableEditing ? 1 : (nTabSize - (nActualOffset + nCurPos) % nTabSize);
950 AppendStringAdv(line, nCurPos, lpspc->c_tab);
960 else if (pszChars[i] == ' ' && m_bViewTabs)
961 AppendStringAdv(line, nCurPos, lpspc->c_space);
962 else if (pszChars[i] == '\r' || pszChars[i] == '\n')
966 if (pszChars[i] == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
968 // Ignore \n after \r
972 if (pszChars[i] == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
974 AppendStringAdv(line, nCurPos, lpspc->c_eol);
977 else if (pszChars[i] == '\r' && m_bDistinguishEols)
978 AppendStringAdv(line, nCurPos, lpspc->c_cr);
979 else if (pszChars[i] == '\n' && m_bDistinguishEols)
980 AppendStringAdv(line, nCurPos, lpspc->c_lf);
983 AppendStringAdv(line, nCurPos, lpspc->c_eol);
988 else if (pszChars[i] >= _T('\x00') && pszChars[i] <= _T('\x1F'))
990 AppendEscapeAdv(line, nCurPos, pszChars[i]);
994 nCurPos += GetCharCellCountFromChar(pszChars + i);
995 for (; i < next; ++i)
1002 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
1003 for (int i1=0, next=0; i1<nLength; i1 = next)
1005 next = pIterChar->next();
1006 nCurPos += GetCharCellCountFromChar(pszChars + i1);
1007 for (; i1 < next; ++i1)
1008 line += pszChars[i1];
1014 int CCrystalTextView::
1015 ExpandCharsTableEditingNoWrap(int nLineIndex, int nOffset, int nCount, CString& line, int nActualOffset)
1017 if (m_pTextBuffer == nullptr || nCount <= 0)
1020 LPCTSTR pszChars = GetLineChars(nLineIndex);
1022 // Request whitespace characters for codepage ACP
1023 // because that is the codepage used by ExtTextOut
1024 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE::GDI);
1026 int nLength = nCount;
1028 int nColumnCount = 0;
1029 int nCurColumn = -1;
1030 int nColumnTotalWidth = 0;
1031 int nColumnBegin = 0;
1032 int nColumnTotalWidthBegin = 0;
1033 const int nLineLength = GetFullLineLength (nLineIndex);
1034 const int nTabSize = GetTabSize ();
1035 const int sep = m_pTextBuffer->GetFieldDelimiter ();
1036 const int quote = m_pTextBuffer->GetFieldEnclosure ();
1037 const int eollen = nLineLength - GetLineLength (nLineIndex);
1038 bool bInQuote = false;
1039 bool bInQuoteBegin = false;
1041 for (int i = 0; i < nLineLength; i++)
1043 TCHAR c = pszChars[i];
1046 nColumnBegin = nColumn;
1047 nColumnTotalWidthBegin = nColumnTotalWidth;
1048 bInQuoteBegin = bInQuote;
1050 if (nLineIndex == m_ptCursorPos.y && i == m_ptCursorPos.x)
1051 nCurColumn = nColumn;
1052 if (!bInQuote && c == sep)
1054 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
1055 nCount = nColumnTotalWidth;
1059 else if (c == quote)
1061 bInQuote = !bInQuote;
1063 else if (c >= '\x00' && c <= '\x1F' && c != '\r' && c != '\n')
1066 nColumnCount = nColumn + 1;
1068 // Preallocate line buffer, to avoid reallocations as we add characters
1069 line.GetBuffer (nCount + 1); // at least this many characters
1070 line.ReleaseBuffer (0);
1071 int nCurPos = nActualOffset;
1073 pszChars += nOffset;
1075 int curColumnTextCellWidth = 0;
1076 bool beforeCursorPos = (nLineIndex == m_ptCursorPos.y && nOffset < m_ptCursorPos.x);
1077 CString curColumnText;
1078 std::vector<std::pair<int, int>> curColumnByteLenCellWidth;
1079 nColumn = nColumnBegin;
1080 nColumnTotalWidth = nColumnTotalWidthBegin;
1081 bInQuote = bInQuoteBegin;
1082 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator (pszChars, nLength);
1083 auto nextColumnDistance = [&](int nCurPos)
1085 return (nColumn == nColumnCount - 1) ? INT_MAX : nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
1087 auto appendChars = [&](int i, int next, int pos, CString& text, int& textwidth)
1089 TCHAR c = pszChars[i];
1090 if ((c == '\r' || c == '\n') && i >= nLineLength - nOffset - eollen)
1094 if (c == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
1096 // Ignore \n after \r
1100 int prevtextwidth = textwidth;
1101 if (c == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
1102 AppendStringAdv (text, textwidth, lpspc->c_eol);
1103 else if (c == '\r' && m_bDistinguishEols)
1104 AppendStringAdv (text, textwidth, lpspc->c_cr);
1105 else if (c == '\n' && m_bDistinguishEols)
1107 if (i == 0 || pszChars[i - 1] != '\r')
1108 AppendStringAdv (text, textwidth, lpspc->c_lf);
1111 AppendStringAdv (text, textwidth, lpspc->c_eol);
1112 if (textwidth - prevtextwidth > 0)
1113 curColumnByteLenCellWidth.push_back ({ textwidth - prevtextwidth, textwidth - prevtextwidth});
1120 if (sep != '\t' || bInQuote)
1123 if (nSpaces > nextColumnDistance (pos))
1124 nSpaces = nextColumnDistance (pos);
1126 if (nSpaces >= 1 && m_bViewTabs)
1128 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1129 AppendStringAdv (text, textwidth, lpspc->c_tab);
1134 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1140 else if (c == ' ' && m_bViewTabs)
1142 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1143 AppendStringAdv (text, textwidth, lpspc->c_space);
1145 else if (c >= '\x00' && c <= '\x1F')
1147 curColumnByteLenCellWidth.push_back ({ 3, 3 });
1148 AppendEscapeAdv (text, textwidth, c);
1149 if (c == '\r' && pszChars[i + 1] == '\n')
1150 AppendEscapeAdv (text, textwidth, pszChars[i + 1]);
1154 int nLen = GetCharCellCountFromChar (pszChars + i);
1155 curColumnByteLenCellWidth.push_back ({ nLen, next - i });
1157 for (; i < next; ++i)
1158 text += pszChars[i];
1160 if (!bInQuote && c == sep)
1162 int nSpaces = nextColumnDistance (pos + 1);
1169 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1173 for (int i = 0, next = 0; i < nLength; i = next)
1175 next = pIterChar->next ();
1176 const TCHAR c = pszChars[i];
1178 bInQuote = !bInQuote;
1179 int nLen = GetCharCellCountFromChar (pszChars + i);
1180 if (nColumn == nCurColumn && beforeCursorPos)
1182 appendChars (i, next, nCurPos + curColumnTextCellWidth, curColumnText, curColumnTextCellWidth);
1183 if (next + nOffset == m_ptCursorPos.x || next >= nLength)
1185 beforeCursorPos = false;
1186 if (curColumnTextCellWidth > nextColumnDistance (nCurPos))
1188 for (size_t k = 0; k < curColumnByteLenCellWidth.size () && curColumnTextCellWidth > nextColumnDistance (nCurPos); ++k)
1190 curColumnTextCellWidth -= curColumnByteLenCellWidth[k].first;
1191 curColumnText = curColumnText.Mid (curColumnByteLenCellWidth[k].second);
1193 int nSpaces = nextColumnDistance (nCurPos) - curColumnTextCellWidth;
1196 CString spaces (' ', nSpaces);
1197 curColumnText.Insert (0, spaces);
1198 curColumnTextCellWidth = m_pTextBuffer->GetColumnWidth (nColumn);
1201 line += curColumnText;
1202 nCurPos += curColumnTextCellWidth;
1207 if (nLen <= nextColumnDistance (nCurPos))
1209 appendChars (i, next, nCurPos, line, nCurPos);
1210 curColumnByteLenCellWidth.clear ();
1214 int nSpaces = nextColumnDistance (nCurPos);
1221 if (!bInQuote && c == sep)
1223 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1229 return nCurPos - nActualOffset;
1234 * @brief Draw a chunk of text (one color, one line, full or part of line)
1236 * @note In ANSI build, this routine is buggy for multibytes or double-width characters
1238 void CCrystalTextView::
1239 DrawLineHelperImpl (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex,
1240 int nBgColorIndex, COLORREF crText, COLORREF crBkgnd, int nLineIndex, int nOffset, int nCount, int &nActualOffset)
1242 ASSERT (nCount >= 0);
1246 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_NOWORDWRAP)
1247 nActualOffset += ExpandCharsTableEditingNoWrap (nLineIndex, nOffset, nCount, line, nActualOffset);
1249 nActualOffset += ExpandChars (nLineIndex, nOffset, nCount, line, nActualOffset);
1250 const int lineLen = line.GetLength();
1251 const int nCharWidth = GetCharWidth();
1252 const int nCharWidthNarrowed = nCharWidth / 2;
1253 const int nCharWidthWidened = nCharWidth * 2 - nCharWidthNarrowed;
1254 const int nLineHeight = GetLineHeight();
1255 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator((LPCTSTR)line, lineLen);
1257 // i the character index, from 0 to lineLen-1
1260 // Pass if the text begins after the right end of the clipping region
1261 if (ptOrigin.x < rcClip.right)
1263 // Because ExtTextOut is buggy when ptOrigin.x < - 4095 * charWidth
1264 // or when nCount >= 4095
1265 // and because this is not well documented,
1266 // we decide to do the left & right clipping here
1268 // Update the position after the left clipped characters
1269 // stop for i = first visible character, at least partly
1270 const int clipLeft = rcClip.left - nCharWidth * 2;
1271 for ( ; i < lineLen; i = pIterChar->next())
1273 int pnWidthsCurrent = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1274 ptOrigin.x += pnWidthsCurrent;
1275 if (ptOrigin.x >= clipLeft)
1277 ptOrigin.x -= pnWidthsCurrent;
1284 //CSize sz = pdc->GetTextExtent(line, nCount);
1285 //ASSERT(sz.cx == m_nCharWidth * nCount);
1290 // We have to draw some characters
1294 int nWidth = rcClip.right - ptOrigin.x;
1296 // Table of charwidths as CCrystalEditor thinks they are
1297 // Seems that CrystalEditor's and ExtTextOut()'s charwidths aren't
1298 // same with some fonts and text is drawn only partially
1299 // if this table is not used.
1300 vector<int> nWidths(nWidth / nCharWidth * 2 + 2);
1301 bool bdisphex = false;
1302 for (int next = i; i < lineLen && nSumWidth < nWidth ; i = next)
1304 if (line[i] == '\t') // Escape sequence leadin?
1307 // Substitute a space narrowed to half the width of a character cell.
1309 size_t idx = i - ibegin;
1310 if (idx >= nWidths.size())
1311 nWidths.resize(nWidths.size() * 2);
1312 nSumWidth += nWidths[idx] = nCharWidthNarrowed;
1313 // 1st hex digit has normal width.
1314 idx = pIterChar->next() - ibegin;
1315 if (idx >= nWidths.size())
1316 nWidths.resize(nWidths.size() * 2);
1317 nSumWidth += nWidths[idx] = nCharWidth;
1318 // 2nd hex digit is padded by half the width of a character cell.
1319 idx = pIterChar->next() - ibegin;
1320 if (idx >= nWidths.size())
1321 nWidths.resize(nWidths.size() * 2);
1322 nSumWidth += nWidths[idx] = nCharWidthWidened;
1326 size_t idx = i - ibegin;
1327 if (idx >= nWidths.size())
1328 nWidths.resize(nWidths.size() * 2);
1329 nSumWidth += nWidths[idx] = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1331 next = pIterChar->next();
1333 int nCount1 = i - ibegin;
1335 if (ptOrigin.x + nSumWidth > rcClip.left)
1337 COLORREF crText2 = crText;
1338 COLORREF crBkgnd2 = crBkgnd;
1339 if (crText == CLR_NONE || nColorIndex & COLORINDEX_APPLYFORCE)
1340 crText2 = GetColor(nColorIndex);
1341 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1342 crBkgnd2 = GetColor(nBgColorIndex);
1343 if (nColorIndex & COLORINDEX_INTERMEDIATECOLOR)
1344 crText2 = GetIntermediateColor(crText2, crBkgnd2);
1345 m_pCrystalRenderer->SetTextColor(crText2);
1346 m_pCrystalRenderer->SetBkColor(crBkgnd2);
1348 m_pCrystalRenderer->SwitchFont(GetItalic(nColorIndex), GetBold(nColorIndex));
1349 // we are sure to have less than 4095 characters because all the chars are visible
1351 RECT rcTextBlock = {ptOrigin.x, ptOrigin.y, ptOrigin.x + nSumWidth + 2, ptOrigin.y + nLineHeight};
1352 IntersectRect(&rcIntersect, &rcClip, &rcTextBlock);
1353 m_pCrystalRenderer->DrawText(ptOrigin.x, ptOrigin.y, rcIntersect, LPCTSTR(line) + ibegin, nCount1, &nWidths[0]);
1356 // Draw rounded rectangles around control characters
1357 m_pCrystalRenderer->PushAxisAlignedClip(rcClip);
1359 for (int j = 0 ; j < nCount1 ; ++j)
1361 // Assume narrowed space is converted escape sequence leadin.
1362 if (line[ibegin + j] == ' ' && nWidths[j] < nCharWidth)
1364 m_pCrystalRenderer->DrawRoundRectangle(x + 2, ptOrigin.y + 1,
1365 x + 3 * nCharWidth - 2, ptOrigin.y + nLineHeight - 1,
1366 nCharWidth / 2, nLineHeight / 2);
1370 m_pCrystalRenderer->PopAxisAlignedClip();
1374 // Update the final position after the visible characters
1375 ptOrigin.x += nSumWidth;
1379 // Update the final position after the right clipped characters
1380 for ( ; i < lineLen; i = pIterChar->next())
1382 ptOrigin.x += GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1387 void CCrystalTextView::
1388 DrawLineHelper (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex, int nBgColorIndex,
1389 COLORREF crText, COLORREF crBkgnd, int nLineIndex, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos)
1393 if (m_bFocused || m_bShowInactiveSelection)
1395 int nSelBegin = 0, nSelEnd = 0;
1396 if ( !m_bRectangularSelection )
1398 if (m_ptDrawSelStart.y > ptTextPos.y)
1402 else if (m_ptDrawSelStart.y == ptTextPos.y)
1404 nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
1407 if (nSelBegin > nCount)
1410 if (m_ptDrawSelEnd.y > ptTextPos.y)
1414 else if (m_ptDrawSelEnd.y == ptTextPos.y)
1416 nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
1419 if (nSelEnd > nCount)
1425 int nSelLeft, nSelRight;
1426 GetColumnSelection (ptTextPos.y, nSelLeft, nSelRight);
1427 nSelBegin = nSelLeft - ptTextPos.x;
1428 nSelEnd = nSelRight - ptTextPos.x;
1429 if (nSelBegin < 0) nSelBegin = 0;
1430 if (nSelBegin > nCount) nSelBegin = nCount;
1431 if (nSelEnd < 0) nSelEnd = 0;
1432 if (nSelEnd > nCount) nSelEnd = nCount;
1435 ASSERT (nSelBegin >= 0 && nSelBegin <= nCount);
1436 ASSERT (nSelEnd >= 0 && nSelEnd <= nCount);
1437 ASSERT (nSelBegin <= nSelEnd);
1439 // Draw part of the text before selection
1442 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nSelBegin, nActualOffset);
1444 if (nSelBegin < nSelEnd)
1446 DrawLineHelperImpl (ptOrigin, rcClip,
1447 nColorIndex & ~COLORINDEX_MASK,
1448 nBgColorIndex & ~COLORINDEX_MASK,
1449 GetColor (COLORINDEX_SELTEXT),
1450 GetColor (COLORINDEX_SELBKGND),
1452 nOffset + nSelBegin, nSelEnd - nSelBegin, nActualOffset);
1454 if (nSelEnd < nCount)
1456 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset + nSelEnd, nCount - nSelEnd, nActualOffset);
1461 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset);
1466 void CCrystalTextView::
1467 GetLineColors (int nLineIndex, COLORREF & crBkgnd,
1468 COLORREF & crText, bool & bDrawWhitespace)
1470 DWORD dwLineFlags = GetLineFlags (nLineIndex);
1471 bDrawWhitespace = true;
1472 crText = RGB (255, 255, 255);
1473 if (dwLineFlags & LF_EXECUTION)
1475 crBkgnd = RGB (0, 128, 0);
1478 if (dwLineFlags & LF_BREAKPOINT)
1480 crBkgnd = RGB (255, 0, 0);
1483 if (dwLineFlags & LF_INVALID_BREAKPOINT)
1485 crBkgnd = RGB (128, 128, 0);
1490 bDrawWhitespace = false;
1493 DWORD CCrystalTextView::
1494 GetParseCookie (int nLineIndex)
1496 const int nLineCount = GetLineCount ();
1497 if (m_ParseCookies->size() == 0)
1499 // must be initialized to invalid value (DWORD) -1
1500 m_ParseCookies->assign(nLineCount, static_cast<DWORD>(-1));
1505 if ((*m_ParseCookies)[nLineIndex] != - 1)
1506 return (*m_ParseCookies)[nLineIndex];
1509 while (L >= 0 && (*m_ParseCookies)[L] == - 1)
1514 while (L <= nLineIndex)
1516 unsigned dwCookie = 0;
1518 dwCookie = (*m_ParseCookies)[L - 1];
1519 ASSERT (dwCookie != - 1);
1520 (*m_ParseCookies)[L] = ParseLine (dwCookie, GetLineChars(L), GetLineLength(L), nullptr, nBlocks);
1521 ASSERT ((*m_ParseCookies)[L] != - 1);
1525 return (*m_ParseCookies)[nLineIndex];
1528 std::vector<TEXTBLOCK> CCrystalTextView::
1529 GetAdditionalTextBlocks (int nLineIndex)
1535 void CCrystalTextView::WrapLine( int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1537 // There must be a parser attached to this view
1538 if( m_pParser == nullptr )
1541 m_pParser->WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1545 void CCrystalTextView::WrapLineCached(
1546 int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1548 if( !GetLineVisible (nLineIndex) )
1554 // If the word wrap is not active, there is no breaks in the line
1561 // word wrap is active
1562 if( nLineIndex < m_panSubLines->GetSize() && !anBreaks && (*m_panSubLines)[nLineIndex] > -1 )
1563 // return cached data
1564 nBreaks = (*m_panSubLines)[nLineIndex] - 1;
1567 // recompute line wrap
1569 WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1572 ASSERT( nBreaks > -1 );
1573 m_panSubLines->SetAtGrow( nLineIndex, nBreaks + 1 );
1575 // RecalcVertScrollBar();
1580 void CCrystalTextView::InvalidateLineCache( int nLineIndex1, int nLineIndex2 /*= -1*/ )
1582 // invalidate cached sub line index
1583 InvalidateSubLineIndexCache( nLineIndex1 );
1585 // invalidate cached sub line count
1587 if( nLineIndex2 == -1 && nLineIndex1 < m_panSubLines->GetSize() )
1588 for( int i = nLineIndex1; i < m_panSubLines->GetSize(); i++ )
1589 (*m_panSubLines)[i] = -1;
1592 if( nLineIndex1 > nLineIndex2 )
1594 int nStorage = nLineIndex1;
1595 nLineIndex1 = nLineIndex2;
1596 nLineIndex2 = nStorage;
1599 if( nLineIndex1 >= m_panSubLines->GetSize() )
1602 if( nLineIndex2 >= m_panSubLines->GetSize() )
1603 nLineIndex2 = (int) m_panSubLines->GetUpperBound();
1605 for( int i = nLineIndex1; i <= nLineIndex2; i++ )
1606 if( i >= 0 && i < m_panSubLines->GetSize() )
1607 (*m_panSubLines)[i] = -1;
1612 * @brief Invalidate sub line index cache from the specified index to the end of file.
1613 * @param [in] nLineIndex Index of the first line to invalidate
1615 void CCrystalTextView::InvalidateSubLineIndexCache( int nLineIndex )
1617 if (m_nLastLineIndexCalculatedSubLineIndex > nLineIndex)
1618 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex - 1;
1622 * @brief Invalidate items related screen size.
1624 void CCrystalTextView::InvalidateScreenRect(bool bInvalidateView)
1626 if (m_pCacheBitmap != nullptr)
1628 delete m_pCacheBitmap;
1629 m_pCacheBitmap = nullptr;
1631 m_nScreenChars = -1;
1632 m_nScreenLines = -1;
1633 InvalidateLineCache(0, -1);
1634 if (bInvalidateView)
1637 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
1638 RecalcVertScrollBar ();
1639 RecalcHorzScrollBar ();
1644 void CCrystalTextView::DrawScreenLine( CPoint &ptOrigin, const CRect &rcClip,
1645 const std::vector<TEXTBLOCK>& blocks, int &nActualItem,
1646 COLORREF crText, COLORREF crBkgnd, bool bDrawWhitespace,
1647 int nLineIndex, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos )
1649 CPoint originalOrigin = ptOrigin;
1650 CPoint ptOriginZeroWidthBlock;
1651 CRect frect = rcClip;
1652 const int nLineLength = GetViewableLineLength( ptTextPos.y );
1653 const int nLineHeight = GetLineHeight();
1654 int nBgColorIndexZeorWidthBlock = COLORINDEX_NONE;
1655 bool bPrevZeroWidthBlock = false;
1656 static const int ZEROWIDTHBLOCK_WIDTH = 2;
1658 frect.top = ptOrigin.y;
1659 frect.bottom = frect.top + nLineHeight;
1661 int nBlockSize = static_cast<int>(blocks.size());
1662 ASSERT( nActualItem < nBlockSize );
1664 if( nBlockSize > 0 && nActualItem < nBlockSize - 1 &&
1665 blocks[nActualItem + 1].m_nCharPos >= nOffset &&
1666 blocks[nActualItem + 1].m_nCharPos <= nOffset + nCount )
1668 ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1669 blocks[nActualItem].m_nCharPos <= nLineLength);
1672 for (I = nActualItem; I < blocks.size() - 1 &&
1673 blocks[I + 1].m_nCharPos <= nOffset + nCount; I ++)
1675 const TEXTBLOCK& blk = blocks[I];
1676 ASSERT(blk.m_nCharPos >= 0 && blk.m_nCharPos <= nLineLength);
1678 int nOffsetToUse = (nOffset > blk.m_nCharPos) ?
1679 nOffset : blk.m_nCharPos;
1680 if (blocks[I + 1].m_nCharPos - nOffsetToUse > 0)
1682 int nOldActualOffset = nActualOffset;
1683 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex, crText, crBkgnd, nLineIndex,
1684 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1685 blocks[I + 1].m_nCharPos - nOffsetToUse,
1686 nActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1687 if (bPrevZeroWidthBlock)
1689 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1690 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock, crText, crBkgnd, nLineIndex,
1691 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1692 blocks[I + 1].m_nCharPos - nOffsetToUse,
1693 nOldActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1694 bPrevZeroWidthBlock = false;
1699 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1701 int nBgColorIndex = blk.m_nBgColorIndex;
1702 COLORREF clrBkColor;
1703 if (IsInsideSelBlock (CPoint{nOffsetToUse, ptTextPos.y}))
1704 clrBkColor = GetColor(COLORINDEX_SELBKGND);
1705 else if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1706 clrBkColor = GetColor(nBgColorIndex);
1708 clrBkColor = crBkgnd;
1709 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1710 m_pCrystalRenderer->SetBkColor(clrBkColor);
1711 m_pCrystalRenderer->FillRectangle(rc);
1712 ptOriginZeroWidthBlock = ptOrigin;
1713 nBgColorIndexZeorWidthBlock = blk.m_nBgColorIndex;
1714 bPrevZeroWidthBlock = true;
1717 if (ptOrigin.x > rcClip.right)
1719 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_WORDWRAP)
1721 while (I < blocks.size () - 1 && blocks[I + 1].m_nCharPos <= nOffset + nCount)
1728 nActualItem = static_cast<int>(I);
1730 const TEXTBLOCK& blk = blocks[nActualItem];
1731 ASSERT(blk.m_nCharPos >= 0 &&
1732 blk.m_nCharPos <= nLineLength);
1734 if (nOffset + nCount - blk.m_nCharPos > 0)
1736 int nOldActualOffset = nActualOffset;
1737 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex,
1738 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1739 nOffset + nCount - blk.m_nCharPos,
1740 nActualOffset, CPoint(blk.m_nCharPos, ptTextPos.y));
1741 if (bPrevZeroWidthBlock)
1743 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1744 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock,
1745 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1746 nOffset + nCount - blk.m_nCharPos,
1747 nOldActualOffset, CPoint(blk.m_nCharPos, ptTextPos.y));
1748 bPrevZeroWidthBlock = false;
1753 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1755 int nBgColorIndex = blk.m_nBgColorIndex;
1756 COLORREF clrBkColor;
1757 if (IsInsideSelBlock (CPoint{blk.m_nCharPos, ptTextPos.y}))
1758 clrBkColor = GetColor(COLORINDEX_SELBKGND);
1759 else if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1760 clrBkColor = GetColor(nBgColorIndex);
1762 clrBkColor = crBkgnd;
1763 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1764 m_pCrystalRenderer->SetBkColor(clrBkColor);
1765 m_pCrystalRenderer->FillRectangle(rc);
1766 bPrevZeroWidthBlock = true;
1773 ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1774 crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset, ptTextPos);
1777 // Draw space on the right of the text
1779 frect.left = ptOrigin.x + (bPrevZeroWidthBlock ? ZEROWIDTHBLOCK_WIDTH : 0);
1781 if ((m_bFocused || m_bShowInactiveSelection)
1782 && !m_bRectangularSelection
1783 && IsInsideSelBlock(CPoint(nLineLength, ptTextPos.y))
1784 && (nOffset + nCount) == nLineLength )
1786 if (frect.left >= rcClip.left)
1788 const int nCharWidth = GetCharWidth();
1789 CRect rc(frect.left, frect.top, frect.left + nCharWidth, frect.bottom);
1790 m_pCrystalRenderer->SetBkColor(GetColor(COLORINDEX_SELBKGND));
1791 m_pCrystalRenderer->FillRectangle(rc);
1792 frect.left += nCharWidth;
1795 if (frect.left < rcClip.left)
1796 frect.left = rcClip.left;
1798 if (frect.right > frect.left)
1800 m_pCrystalRenderer->SetBkColor(bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
1801 m_pCrystalRenderer->FillRectangle(frect);
1804 // set origin to beginning of next screen line
1805 ptOrigin.x = originalOrigin.x;
1806 ptOrigin.y+= nLineHeight;
1810 class IntArray : public CArray<int, int>
1813 explicit IntArray(int len) { SetSize(len); }
1816 std::vector<TEXTBLOCK> CCrystalTextView::
1817 MergeTextBlocks (const std::vector<TEXTBLOCK>& blocks1, const std::vector<TEXTBLOCK>& blocks2) const
1821 std::vector<TEXTBLOCK> mergedBlocks(blocks1.size() + blocks2.size());
1823 for (i = 0, j = 0, k = 0; ; k++)
1825 if (i >= blocks1.size() && j >= blocks2.size())
1829 else if ((i < blocks1.size()&& j < blocks2.size()) &&
1830 (blocks1[i].m_nCharPos == blocks2[j].m_nCharPos))
1832 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1833 if ((blocks2[j].m_nColorIndex & ~COLORINDEX_MASK) == COLORINDEX_NONE)
1834 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex | (blocks2[j].m_nColorIndex & COLORINDEX_MASK);
1836 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1837 if (blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1838 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1840 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1844 else if (j >= blocks2.size() || (i < blocks1.size() &&
1845 blocks1[i].m_nCharPos < blocks2[j].m_nCharPos))
1847 mergedBlocks[k].m_nCharPos = blocks1[i].m_nCharPos;
1848 if (blocks2.size() == 0 || (blocks2[j - 1].m_nColorIndex & ~COLORINDEX_MASK) == COLORINDEX_NONE)
1849 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex |
1850 (blocks2.size() == 0 ? 0 : (blocks2[j - 1].m_nColorIndex & COLORINDEX_MASK));
1852 mergedBlocks[k].m_nColorIndex = blocks2[j - 1].m_nColorIndex;
1853 if (blocks2.size() == 0 || blocks2[j - 1].m_nBgColorIndex == COLORINDEX_NONE)
1854 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1856 mergedBlocks[k].m_nBgColorIndex = blocks2[j - 1].m_nBgColorIndex;
1859 else if (i >= blocks1.size() || (j < blocks2.size() && blocks1[i].m_nCharPos > blocks2[j].m_nCharPos))
1861 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1862 if (i > 0 && (blocks2[j].m_nColorIndex & ~COLORINDEX_MASK) == COLORINDEX_NONE)
1863 mergedBlocks[k].m_nColorIndex = blocks1[i - 1].m_nColorIndex | (blocks2[j].m_nColorIndex & COLORINDEX_MASK);
1865 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1866 if (i > 0 && blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1867 mergedBlocks[k].m_nBgColorIndex = blocks1[i - 1].m_nBgColorIndex;
1869 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1875 for (i = 0; i < k; ++i)
1878 (mergedBlocks[i - 1].m_nColorIndex != mergedBlocks[i].m_nColorIndex ||
1879 mergedBlocks[i - 1].m_nBgColorIndex != mergedBlocks[i].m_nBgColorIndex))
1881 mergedBlocks[j] = mergedBlocks[i];
1886 mergedBlocks.resize(j);
1887 return mergedBlocks;
1890 std::vector<TEXTBLOCK>
1891 CCrystalTextView::GetWhitespaceTextBlocks(int nLineIndex) const
1893 const TCHAR *pszChars = GetLineChars(nLineIndex);
1894 int nLineLength = GetLineLength(nLineIndex);
1895 std::vector<TEXTBLOCK> blocks((nLineLength + 1) * 3);
1896 blocks[0].m_nCharPos = 0;
1897 blocks[0].m_nColorIndex = COLORINDEX_NONE;
1898 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1900 if (pszChars != nullptr)
1902 for (int i = 0; i < nLineLength; ++i)
1904 if (pszChars[i] == ' ' || pszChars[i] == '\t')
1906 blocks[nBlocks].m_nCharPos = i;
1907 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE | COLORINDEX_INTERMEDIATECOLOR;
1908 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1910 while ((pszChars[i] == ' ' || pszChars[i] == '\t') && i < nLineLength)
1912 if (i < nLineLength)
1914 blocks[nBlocks].m_nCharPos = i;
1915 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1916 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1922 if (nBlocks == 0 || blocks[nBlocks].m_nColorIndex == COLORINDEX_NONE)
1924 blocks[nBlocks].m_nCharPos = nLineLength;
1925 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE | COLORINDEX_INTERMEDIATECOLOR;
1926 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1929 blocks.resize(nBlocks);
1933 std::vector<TEXTBLOCK>
1934 CCrystalTextView::GetMarkerTextBlocks(int nLineIndex) const
1936 std::vector<TEXTBLOCK> allblocks;
1937 if (!m_pMarkers->GetEnabled())
1940 int nLength = GetViewableLineLength (nLineIndex);
1942 for (const auto& marker : m_pMarkers->GetMarkers())
1944 if (!marker.second.bVisible)
1947 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1948 blocks[0].m_nCharPos = 0;
1949 blocks[0].m_nColorIndex = COLORINDEX_NONE;
1950 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1952 const TCHAR *pszChars = GetLineChars(nLineIndex);
1953 int nLineLength = GetLineLength(nLineIndex);
1954 if (pszChars != nullptr)
1956 RxNode *node = nullptr;
1957 for (const TCHAR *p = pszChars; p < pszChars + nLineLength; )
1961 size_t nPos = ::FindStringHelper(pszChars, nLineLength, p, marker.second.sFindWhat, marker.second.dwFlags | FIND_NO_WRAP, nMatchLen, node, &matches);
1964 if (nLineLength < static_cast<int>((p - pszChars) + nPos) + nMatchLen)
1965 nMatchLen = static_cast<int>(nLineLength - (p - pszChars));
1966 ASSERT(((p - pszChars) + nPos) < INT_MAX);
1967 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos);
1968 blocks[nBlocks].m_nBgColorIndex = marker.second.nBgColorIndex | COLORINDEX_APPLYFORCE;
1969 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1971 ASSERT(((p - pszChars) + nPos + nMatchLen) < INT_MAX);
1972 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos + nMatchLen);
1973 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1974 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1976 p += nPos + (nMatchLen == 0 ? 1 : nMatchLen);
1979 blocks.resize(nBlocks);
1980 allblocks = MergeTextBlocks(allblocks, blocks);
1987 std::vector<TEXTBLOCK>
1988 CCrystalTextView::GetTextBlocks(int nLineIndex)
1990 int nLength = GetViewableLineLength (nLineIndex);
1993 unsigned dwCookie = GetParseCookie(nLineIndex - 1);
1994 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1996 // insert at least one textblock of normal color at the beginning
1997 blocks[0].m_nCharPos = 0;
1998 blocks[0].m_nColorIndex = COLORINDEX_NORMALTEXT;
1999 blocks[0].m_nBgColorIndex = COLORINDEX_BKGND;
2001 (*m_ParseCookies)[nLineIndex] = ParseLine(dwCookie, GetLineChars(nLineIndex), GetLineLength(nLineIndex), blocks.data(), nBlocks);
2002 ASSERT((*m_ParseCookies)[nLineIndex] != -1);
2003 blocks.resize(nBlocks);
2005 std::vector<TEXTBLOCK> additionalBlocks = GetAdditionalTextBlocks(nLineIndex);
2006 std::vector<TEXTBLOCK> mergedBlocks;
2007 if (m_pMarkers && m_pMarkers->GetEnabled() && m_pMarkers->GetMarkers().size() > 0)
2008 mergedBlocks = MergeTextBlocks(additionalBlocks, GetMarkerTextBlocks(nLineIndex));
2010 mergedBlocks = std::move(additionalBlocks);
2011 std::vector<TEXTBLOCK> mergedBlocks2 = MergeTextBlocks(blocks, mergedBlocks);
2012 if (m_bViewTabs || m_bViewEols)
2013 return MergeTextBlocks(mergedBlocks2, GetWhitespaceTextBlocks(nLineIndex));
2014 return mergedBlocks2;
2017 void CCrystalTextView::
2018 DrawSingleLine (const CRect & rc, int nLineIndex)
2020 const int nCharWidth = GetCharWidth();
2021 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2023 if (nLineIndex == -1)
2025 // Draw line beyond the text
2026 m_pCrystalRenderer->FillSolidRectangle (rc, GetColor (COLORINDEX_WHITESPACE));
2030 // Acquire the background color for the current line
2031 bool bDrawWhitespace = false;
2032 COLORREF crBkgnd, crText;
2033 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2035 int nLength = GetViewableLineLength (nLineIndex);
2037 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2039 int nActualItem = 0;
2040 int nActualOffset = 0;
2042 std::vector<int> anBreaks(nLength);
2045 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
2047 // Draw the line text
2048 CPoint origin (rc.left - m_nOffsetChar * nCharWidth, rc.top);
2049 if (crBkgnd != CLR_NONE)
2050 m_pCrystalRenderer->SetBkColor (crBkgnd);
2051 if (crText != CLR_NONE)
2052 m_pCrystalRenderer->SetTextColor (crText);
2054 const TextLayoutMode layoutMode = GetTextLayoutMode ();
2055 if (layoutMode == TEXTLAYOUT_TABLE_WORDWRAP)
2057 anBreaks.push_back (-nLength);
2058 CPoint originOrg = origin;
2061 blocks, nActualItem,
2062 crText, crBkgnd, bDrawWhitespace,
2063 nLineIndex, 0, abs(anBreaks[0]),
2064 nActualOffset, CPoint( 0, nLineIndex ) );
2066 for( int i = 0, j = 0; i < static_cast<int> (anBreaks.size ()) - 1; i++, j++ )
2068 if (anBreaks[i] < 0)
2072 CRect frect( origin.x, originOrg.y + (j + 1) * GetLineHeight (),
2073 origin.x + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth, rc.bottom );
2074 if (frect.left < rc.left)
2075 frect.left = rc.left;
2076 if (frect.right > rc.left)
2077 m_pCrystalRenderer->FillSolidRectangle (frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2079 origin.y = originOrg.y;
2080 origin.x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth;
2085 blocks, nActualItem,
2086 crText, crBkgnd, bDrawWhitespace,
2087 nLineIndex, abs(anBreaks[i]), abs(anBreaks[i + 1]) - abs(anBreaks[i]),
2088 nActualOffset, CPoint( abs(anBreaks[i]), nLineIndex ) );
2091 else if (layoutMode == TEXTLAYOUT_WORDWRAP && nBreaks > 0)
2093 // Draw all the screen lines of the wrapped line
2094 ASSERT( anBreaks[0] < nLength );
2096 // draw start of line to first break
2099 blocks, nActualItem,
2100 crText, crBkgnd, bDrawWhitespace,
2101 nLineIndex, 0, anBreaks[0], nActualOffset, CPoint( 0, nLineIndex ) );
2103 // draw from first break to last break
2105 for( i = 0; i < nBreaks - 1; i++ )
2107 ASSERT( anBreaks[i] >= 0 && anBreaks[i] < nLength );
2110 blocks, nActualItem,
2111 crText, crBkgnd, bDrawWhitespace,
2112 nLineIndex, anBreaks[i], anBreaks[i + 1] - anBreaks[i],
2113 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
2116 // draw from last break till end of line
2119 blocks, nActualItem,
2120 crText, crBkgnd, bDrawWhitespace,
2121 nLineIndex, anBreaks[i], nLength - anBreaks[i],
2122 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
2127 blocks, nActualItem,
2128 crText, crBkgnd, bDrawWhitespace,
2129 nLineIndex, 0, nLength, nActualOffset, CPoint(0, nLineIndex));
2131 // Draw empty sublines
2132 int nEmptySubLines = GetEmptySubLines(nLineIndex);
2133 if (nEmptySubLines > 0)
2136 frect.top = frect.bottom - nEmptySubLines * GetLineHeight();
2137 m_pCrystalRenderer->FillSolidRectangle(frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2142 * @brief Escape special characters
2143 * @param [in] strText The text to escape
2144 * @param [in, out] bLastCharSpace Whether last char processed was white space
2145 * @param [in, out] nNonbreakChars The number of non-break characters in the text
2146 * @param [in] nScreenChars The maximum number of characters to display per line
2147 * @return The escaped text
2150 EscapeHTML (const CString & strText, bool & bLastCharSpace, int & nNonbreakChars, int nScreenChars)
2153 int len = strText.GetLength ();
2154 for (int i = 0; i < len; ++i)
2156 TCHAR ch = strText[i];
2160 strHTML += _T("&");
2161 bLastCharSpace = false;
2165 strHTML += _T("<");
2166 bLastCharSpace = false;
2170 strHTML += _T(">");
2171 bLastCharSpace = false;
2177 strHTML += _T("<wbr>");
2178 bLastCharSpace = false;
2182 if (i == 0 || bLastCharSpace)
2184 strHTML += _T(" ");
2185 bLastCharSpace = false;
2190 bLastCharSpace = true;
2196 bLastCharSpace = false;
2200 if (IsDBCSLeadByte (ch))
2201 strHTML += strText[++i];
2203 if ((nNonbreakChars % nScreenChars) == nScreenChars - 1)
2205 strHTML += _T("<wbr>");
2213 // Make a CString from printf-style args (single call version of CString::Format)
2214 static CString Fmt(LPCTSTR fmt, ...)
2218 va_start(args, fmt);
2219 str.FormatV(fmt, args);
2225 * @brief Return all the styles necessary to render this view as HTML code
2226 * @return The HTML styles
2228 CString CCrystalTextView::
2231 int arColorIndices[] = {
2232 COLORINDEX_NORMALTEXT,
2235 COLORINDEX_FUNCNAME,
2238 COLORINDEX_OPERATOR,
2240 COLORINDEX_PREPROCESSOR,
2241 COLORINDEX_HIGHLIGHTTEXT1,
2242 COLORINDEX_HIGHLIGHTTEXT2,
2246 int arBgColorIndices[] = {
2248 COLORINDEX_SELBKGND,
2249 COLORINDEX_HIGHLIGHTBKGND1,
2250 COLORINDEX_HIGHLIGHTBKGND2,
2251 COLORINDEX_HIGHLIGHTBKGND3,
2252 COLORINDEX_HIGHLIGHTBKGND4,
2256 for (int i = 0; i < 2; i++)
2258 for (int f = 0; f < sizeof(arColorIndices) / sizeof(int); f++)
2260 int nColorIndex = arColorIndices[f];
2261 for (int b = 0; b < sizeof(arBgColorIndices) / sizeof(int); b++)
2263 int nBgColorIndex = arBgColorIndices[b];
2266 strStyles += Fmt(_T(".sf%db%d%s {"), nColorIndex, nBgColorIndex, i == 0 ? _T("") : _T("i"));
2267 clr = GetColor(nColorIndex);
2269 clr = GetIntermediateColor(clr, GetColor(nBgColorIndex));
2270 strStyles += Fmt(_T("color: #%02x%02x%02x; "), GetRValue(clr), GetGValue(clr), GetBValue(clr));
2271 clr = GetColor(nBgColorIndex);
2272 strStyles += Fmt(_T("background-color: #%02x%02x%02x; "), GetRValue(clr), GetGValue(clr), GetBValue(clr));
2273 if (GetBold(nColorIndex))
2274 strStyles += _T("font-weight: bold; ");
2275 if (GetItalic(nColorIndex))
2276 strStyles += _T("font-style: italic; ");
2277 strStyles += _T("}\n");
2281 COLORREF clrSelMargin = GetColor(COLORINDEX_SELMARGIN);
2282 COLORREF clrNormalText = GetColor(COLORINDEX_NORMALTEXT);
2283 strStyles += Fmt(_T(".ln {text-align: right; word-break: normal; color: #%02x%02x%02x; background-color: #%02x%02x%02x;}\n"),
2284 GetRValue(clrNormalText), GetGValue(clrNormalText), GetBValue(clrNormalText),
2285 GetRValue(clrSelMargin), GetGValue(clrSelMargin), GetBValue(clrSelMargin));
2290 * @brief Return the HTML attribute associated with the specified colors
2291 * @param [in] nColorIndex Index of text color
2292 * @param [in] nBgColorIndex Index of background color
2293 * @param [in] crText Base text color
2294 * @param [in] crBkgnd Base background color
2295 * @return The HTML attribute
2297 CString CCrystalTextView::
2298 GetHTMLAttribute (int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd)
2301 COLORREF clr, clrBk;
2303 if ((crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE)) &&
2304 (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE)))
2305 return Fmt(_T("class=\"sf%db%d%s\""), nColorIndex & ~COLORINDEX_MASK, nBgColorIndex & ~COLORINDEX_MASK,
2306 (nColorIndex & COLORINDEX_INTERMEDIATECOLOR) ? _T("i") : _T(""));
2308 if (crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE))
2309 clr = GetColor (nColorIndex);
2312 if (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE))
2313 clrBk = GetColor (nBgColorIndex);
2316 if (nColorIndex & COLORINDEX_INTERMEDIATECOLOR)
2317 clr = GetIntermediateColor(clr, clrBk);
2318 strAttr += Fmt (_T("style=\"color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2319 strAttr += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clrBk), GetGValue (clrBk), GetBValue (clrBk));
2321 if (GetBold (nColorIndex))
2322 strAttr += _T("font-weight: bold; ");
2323 if (GetItalic (nColorIndex))
2324 strAttr += _T("font-style: italic; ");
2326 strAttr += _T("\"");
2332 * @brief Retrieve the html version of the line
2333 * @param [in] nLineIndex Index of line in view
2334 * @param [in] pszTag The HTML tag to enclose the line
2335 * @return The html version of the line
2337 CString CCrystalTextView::
2338 GetHTMLLine (int nLineIndex, LPCTSTR pszTag)
2340 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2342 int nLength = GetViewableLineLength (nLineIndex);
2344 // Acquire the background color for the current line
2345 bool bDrawWhitespace = false;
2346 COLORREF crBkgnd, crText;
2347 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2349 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2352 CString strExpanded;
2354 int nNonbreakChars = 0;
2355 bool bLastCharSpace = false;
2356 const int nScreenChars = 40; // GetScreenChars();
2361 strHTML += GetHTMLAttribute (COLORINDEX_NORMALTEXT, COLORINDEX_BKGND, crText, crBkgnd);
2362 strHTML += _T("><code>");
2364 auto MakeSpan = [&](const TEXTBLOCK& block, const CString& strExpanded) {
2366 strHTML += _T("<span ");
2367 strHTML += GetHTMLAttribute (block.m_nColorIndex, block.m_nBgColorIndex, crText, crBkgnd);
2369 strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2370 strHTML += _T("</span>");
2374 for (i = 0; i < blocks.size() - 1; i++)
2376 ExpandChars (nLineIndex, blocks[i].m_nCharPos, blocks[i + 1].m_nCharPos - blocks[i].m_nCharPos, strExpanded, 0);
2377 if (!strExpanded.IsEmpty())
2378 strHTML += MakeSpan(blocks[i], strExpanded);
2380 if (blocks.size() > 0)
2382 ExpandChars (nLineIndex, blocks[i].m_nCharPos, nLength - blocks[i].m_nCharPos, strExpanded, 0);
2383 if (!strExpanded.IsEmpty())
2384 strHTML += MakeSpan(blocks[i], strExpanded);
2385 if (strExpanded.Compare (CString (' ', strExpanded.GetLength())) == 0)
2386 strHTML += _T(" ");
2388 strHTML += _T("</code></");
2395 COLORREF CCrystalTextView::
2396 GetColor (int nColorIndex) const
2398 if (m_pColors != nullptr)
2400 nColorIndex &= ~COLORINDEX_MASK;
2401 return m_pColors->GetColor(nColorIndex);
2404 return RGB(0, 0, 0);
2407 DWORD CCrystalTextView::
2408 GetLineFlags (int nLineIndex) const
2410 if (m_pTextBuffer == nullptr)
2412 return m_pTextBuffer->GetLineFlags (nLineIndex);
2415 void CCrystalTextView::
2416 DrawTopMargin (const CRect& rect)
2421 auto getColumnName = [](int nColumn) -> CString
2424 for (int i = 0; ; ++i)
2426 TCHAR c = 'A' + (nColumn % 26) - (i == 0 ? 0 : 1);
2427 columnName.Insert (0, c);
2435 m_pCrystalRenderer->SetBkColor (GetColor (COLORINDEX_SELMARGIN));
2436 m_pCrystalRenderer->FillRectangle (rect);
2437 m_pCrystalRenderer->SetTextColor (GetColor (COLORINDEX_NORMALTEXT));
2438 if (m_pTextBuffer->GetTableEditing ())
2440 const int nCharWidth = GetCharWidth ();
2441 const int nMarginWidth = GetMarginWidth ();
2442 CString columnNames;
2443 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth; x < rect.Width (); ++nColumn)
2445 int nColumnWidth = m_pTextBuffer->GetColumnWidth (nColumn);
2446 CString columnName = getColumnName (nColumn);
2447 int columnNameLen = columnName.GetLength ();
2448 if (nColumnWidth < columnNameLen)
2449 columnNames += columnName.Right (nColumnWidth);
2452 int leftspaces = (nColumnWidth - columnNameLen) / 2;
2453 columnNames += CString (' ', leftspaces) + columnName + CString (' ', nColumnWidth - leftspaces - columnNameLen);
2455 x += nColumnWidth * nCharWidth;
2457 columnNames = columnNames.Mid (m_nOffsetChar).Left(rect.Width () / nCharWidth + 1);
2459 std::vector<int> nWidths (columnNames.GetLength (), nCharWidth);
2460 m_pCrystalRenderer->SwitchFont (false, false);
2461 m_pCrystalRenderer->DrawText (nMarginWidth, 0, rect, columnNames, columnNames.GetLength (), nWidths.data ());
2464 m_pCrystalRenderer->DrawRuler (GetMarginWidth (), 0, rect.Width (), rect.Height (), GetCharWidth (), m_nOffsetChar);
2468 * @brief Draw selection margin.
2469 * @param [in] pdc Pointer to draw context.
2470 * @param [in] rect The rectangle to draw.
2471 * @param [in] nLineIndex Index of line in view.
2472 * @param [in] nLineNumber Line number to display. if -1, it's not displayed.
2474 void CCrystalTextView::
2475 DrawMargin (const CRect & rect, int nLineIndex, int nLineNumber)
2477 if (!m_bSelMargin && !m_bViewLineNumbers)
2478 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_BKGND));
2480 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_SELMARGIN));
2481 m_pCrystalRenderer->FillRectangle(rect);
2483 if (m_bViewLineNumbers && nLineNumber > 0)
2485 m_pCrystalRenderer->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
2486 m_pCrystalRenderer->DrawMarginLineNumber(rect.right, rect.top, nLineNumber);
2489 // Draw line revision mark (or background) whenever we have valid lineindex
2490 COLORREF clrRevisionMark = GetColor(COLORINDEX_WHITESPACE);
2491 if (nLineIndex >= 0 && m_pTextBuffer != nullptr)
2493 // get line revision marks color
2494 DWORD dwRevisionNumber = m_pTextBuffer->GetLineRevisionNumber(nLineIndex);
2495 if (dwRevisionNumber > 0)
2497 if (m_pTextBuffer->m_dwRevisionNumberOnSave < dwRevisionNumber)
2498 clrRevisionMark = UNSAVED_REVMARK_CLR;
2500 clrRevisionMark = SAVED_REVMARK_CLR;
2504 // draw line revision marks
2505 CRect rc(rect.right - MARGIN_REV_WIDTH, rect.top, rect.right, rect.bottom);
2506 m_pCrystalRenderer->FillSolidRectangle (rc, clrRevisionMark);
2511 int nImageIndex = -1;
2512 if (nLineIndex >= 0)
2514 DWORD dwLineFlags = GetLineFlags (nLineIndex);
2515 static const DWORD adwFlags[] =
2519 LF_COMPILATION_ERROR,
2531 LF_INVALID_BREAKPOINT
2533 for (int I = 0; I < sizeof (adwFlags) / sizeof (adwFlags[0]); I++)
2535 if ((dwLineFlags & adwFlags[I]) != 0)
2542 if (nImageIndex >= 0)
2544 const int iconsize = GetMarginIconSize();
2545 m_pCrystalRenderer->DrawMarginIcon(
2546 rect.left + 2, rect.top + (GetLineHeight() - iconsize) / 2, nImageIndex, iconsize);
2549 // draw wrapped-line-icon
2550 if (nLineNumber > 0)
2552 const int iconsize = GetMarginIconSize();
2554 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2555 for (int i = 0; i < nBreaks; i++)
2557 m_pCrystalRenderer->DrawMarginIcon(
2558 rect.right - iconsize, rect.top + (GetLineHeight()
2559 - iconsize) / 2 + (i+1) * GetLineHeight(), ICON_INDEX_WRAPLINE, iconsize);
2564 bool CCrystalTextView::
2565 IsInsideSelBlock (CPoint ptTextPos)
2568 ASSERT_VALIDTEXTPOS (ptTextPos);
2569 if (ptTextPos.y < m_ptDrawSelStart.y)
2571 if (ptTextPos.y > m_ptDrawSelEnd.y)
2573 if (m_bRectangularSelection)
2574 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2575 if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
2577 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
2579 if (ptTextPos.y == m_ptDrawSelEnd.y)
2580 return ptTextPos.x < m_ptDrawSelEnd.x;
2581 ASSERT (ptTextPos.y == m_ptDrawSelStart.y);
2582 return ptTextPos.x >= m_ptDrawSelStart.x;
2584 ASSERT (m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
2585 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2588 bool CCrystalTextView::
2589 IsInsideSelection (const CPoint & ptTextPos)
2591 PrepareSelBounds ();
2592 return IsInsideSelBlock (ptTextPos);
2596 * @brief : class the selection extremities in ascending order
2598 * @note : Updates m_ptDrawSelStart and m_ptDrawSelEnd
2599 * This function must be called before reading these values
2601 void CCrystalTextView::
2604 if (m_ptSelStart.y < m_ptSelEnd.y ||
2605 (m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
2607 m_ptDrawSelStart = m_ptSelStart;
2608 m_ptDrawSelEnd = m_ptSelEnd;
2612 m_ptDrawSelStart = m_ptSelEnd;
2613 m_ptDrawSelEnd = m_ptSelStart;
2617 void CCrystalTextView::
2620 // We use the same GDI objects for both drawing and printing.
2621 // So when printing is in progress, we do nothing in this function.
2626 GetClientRect (rcClient);
2628 if (m_pTextBuffer == nullptr)
2630 m_pCrystalRenderer->BindDC(*pdc, rcClient);
2631 m_pCrystalRenderer->BeginDraw();
2632 m_pCrystalRenderer->SetBkColor(GetSysColor(COLOR_WINDOW));
2633 m_pCrystalRenderer->FillRectangle(rcClient);
2634 m_pCrystalRenderer->EndDraw();
2638 const int nLineCount = GetLineCount ();
2639 const int nLineHeight = GetLineHeight ();
2640 PrepareSelBounds ();
2642 // if the private arrays (m_ParseCookies and m_pnActualLineLength)
2643 // are defined, check they are in phase with the text buffer
2644 if (m_ParseCookies->size())
2645 ASSERT(m_ParseCookies->size() == static_cast<size_t>(nLineCount));
2646 if (m_pnActualLineLength->size())
2647 ASSERT(m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
2650 VERIFY (cacheDC.CreateCompatibleDC (pdc));
2651 if (m_pCacheBitmap == nullptr)
2653 m_pCacheBitmap = new CBitmap;
2654 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height()));
2656 CBitmap *pOldBitmap = cacheDC.SelectObject (m_pCacheBitmap);
2659 int nSubLineOffset = GetSubLineIndex( m_nTopLine ) - m_nTopSubLine;
2660 int nCursorY = TextToClient (m_ptCursorPos).y;
2663 CRect rcTopMargin(rcClient.left, rcClient.top, rcClient.right, rcClient.top + GetTopMarginHeight());
2665 rcLine.top = rcTopMargin.bottom + nSubLineOffset * nLineHeight;
2666 CRect rcMargin (rcLine.left, rcLine.top, rcLine.left + GetMarginWidth (), rcLine.top + nLineHeight);
2667 rcLine.left = rcMargin.right;
2669 m_pCrystalRenderer->BindDC(cacheDC, rcClient);
2670 m_pCrystalRenderer->BeginDraw();
2672 int nLastLineBottom = 0;
2673 int nCurrentLine = m_nTopLine;
2674 while (rcLine.top < rcClient.bottom)
2677 if( nCurrentLine < nLineCount /*&& GetLineLength( nCurrentLine ) > nMaxLineChars*/ )
2678 nSubLines = GetSubLines(nCurrentLine);
2680 rcLine.bottom = rcLine.top + nSubLines * nLineHeight;
2681 rcMargin.bottom = rcLine.bottom;
2683 CRect rcMarginAndLine(rcClient.left, rcLine.top, rcClient.right, rcLine.bottom);
2684 if (pdc->RectVisible(rcMarginAndLine))
2686 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
2688 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
2689 DrawSingleLine (rcLine, nCurrentLine);
2690 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
2691 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.bottom-1);
2692 if (m_pTextBuffer->GetTableEditing ())
2693 m_pCrystalRenderer->DrawGridLine (rcMargin.left, rcMargin.bottom-1, rcLine.right, rcMargin.bottom-1);
2694 if (nCurrentLine == m_ptCursorPos.y)
2695 m_pCrystalRenderer->DrawLineCursor (rcMargin.left, rcLine.right,
2696 nCursorY + nLineHeight - 1, 1);
2697 nLastLineBottom = rcMargin.bottom;
2701 DrawMargin (rcMargin, -1, -1);
2702 DrawSingleLine (rcLine, -1);
2707 rcLine.top = rcLine.bottom;
2708 rcMargin.top = rcLine.top;
2711 if (pdc->RectVisible (rcTopMargin))
2712 DrawTopMargin (rcTopMargin);
2714 if (m_pTextBuffer->GetTableEditing ())
2716 int nCharWidth = GetCharWidth ();
2717 int nMarginWidth = GetMarginWidth ();
2718 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth;
2719 x < rcClient.Width();
2720 x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
2722 if (x >= nMarginWidth && nColumn > 0)
2723 m_pCrystalRenderer->DrawGridLine (x, rcClient.top, x, nLastLineBottom);
2727 m_pCrystalRenderer->EndDraw();
2729 VERIFY (pdc->BitBlt (rcClient.left, rcClient.top, rcClient.Width (),
2730 rcClient.Height (), &cacheDC, 0, 0, SRCCOPY));
2732 cacheDC.SelectObject (pOldBitmap);
2733 cacheDC.DeleteDC ();
2736 void CCrystalTextView::
2739 // m_bWordWrap = false;
2745 m_nScreenLines = -1;
2746 m_nScreenChars = -1;
2747 m_nIdealCharPos = -1;
2750 InvalidateLineCache( 0, -1 );
2751 m_ParseCookies->clear();
2752 m_pnActualLineLength->clear();
2753 m_ptCursorPos.x = 0;
2754 m_ptCursorPos.y = 0;
2755 m_ptSelStart = m_ptSelEnd = m_ptCursorPos;
2756 if (m_bDragSelection)
2759 KillTimer (m_nDragSelTimer);
2761 m_bDragSelection = false;
2762 m_bVertScrollBarLocked = false;
2763 m_bHorzScrollBarLocked = false;
2764 if (::IsWindow (m_hWnd))
2766 m_bShowInactiveSelection = true; // FP: reverted because I like it
2767 m_bPrintHeader = false;
2768 m_bPrintFooter = true;
2770 m_bMultipleSearch = false; // More search
2774 void CCrystalTextView::
2777 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
2778 if (m_bFocused && !m_bCursorHidden &&
2779 CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x) >= m_nOffsetChar)
2781 int nCaretHeight = GetLineVisible(m_ptCursorPos.y) ? GetLineHeight () : 0;
2782 if (m_bOverrideCaret) //UPDATE
2783 CreateSolidCaret(GetCharWidth(), nCaretHeight);
2785 CreateSolidCaret (2, nCaretHeight);
2787 SetCaretPos (TextToClient (m_ptCursorPos));
2789 UpdateCompositionWindowPos(); /* IME */
2798 void CCrystalTextView::
2803 CRLFSTYLE CCrystalTextView::
2806 if (m_pTextBuffer != nullptr)
2808 return m_pTextBuffer->GetCRLFMode ();
2810 return CRLFSTYLE::AUTOMATIC;
2813 void CCrystalTextView::
2814 SetCRLFMode (CRLFSTYLE nCRLFMode)
2816 if (m_pTextBuffer != nullptr)
2818 m_pTextBuffer->SetCRLFMode (nCRLFMode);
2822 int CCrystalTextView::
2825 if (m_pTextBuffer == nullptr)
2828 return m_pTextBuffer->GetTabSize();
2832 void CCrystalTextView::
2833 SetTabSize (int nTabSize)
2835 ASSERT (nTabSize >= 0 && nTabSize <= 64);
2836 if (m_pTextBuffer == nullptr)
2839 if (m_pTextBuffer->GetTabSize() != nTabSize)
2841 m_pTextBuffer->SetTabSize( nTabSize );
2843 m_pnActualLineLength->clear();
2844 RecalcHorzScrollBar ();
2850 void CCrystalTextView::
2853 CSize szCharExt = m_pCrystalRenderer->GetCharWidthHeight();
2854 m_nLineHeight = szCharExt.cy;
2855 if (m_nLineHeight < 1)
2857 m_nCharWidth = szCharExt.cx;
2860 int CCrystalTextView::
2863 if (m_nLineHeight == -1)
2865 return m_nLineHeight;
2868 int CCrystalTextView::GetSubLines( int nLineIndex )
2870 // get a number of lines this wrapped lines contains
2872 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2874 return GetEmptySubLines(nLineIndex) + nBreaks + 1;
2877 bool CCrystalTextView::IsEmptySubLineIndex( int nSubLineIndex )
2881 GetLineBySubLine(nSubLineIndex, nLineIndex, dummy);
2882 int nSubLineIndexNextLine = GetSubLineIndex(nLineIndex) + GetSubLines(nLineIndex);
2883 if (nSubLineIndexNextLine - GetEmptySubLines(nLineIndex) <= nSubLineIndex && nSubLineIndex < nSubLineIndexNextLine)
2889 int CCrystalTextView::CharPosToPoint( int nLineIndex, int nCharPos, CPoint &charPoint, int* pnColumn )
2891 // if we do not wrap lines, y is allways 0 and x is equl to nCharPos
2894 charPoint.x = nCharPos;
2899 vector<int> anBreaks(GetLineLength (nLineIndex) + 1);
2902 WrapLineCached (nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
2904 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_WORDWRAP)
2909 for (; i < anBreaks.size () && abs (anBreaks[i]) <= nCharPos ; ++i)
2911 if (anBreaks[i] < 0)
2919 charPoint.x = (i > 0) ? nCharPos - abs (anBreaks[i - 1]) : nCharPos;
2922 *pnColumn = nColumn;
2924 return (i > 0)? abs (anBreaks[i - 1]) : 0;
2928 int i = (nBreaks <= 0) ? -1 : nBreaks - 1;
2929 for (; i >= 0 && nCharPos < anBreaks[i]; i--)
2932 charPoint.x = (i >= 0)? nCharPos - anBreaks[i] : nCharPos;
2933 charPoint.y = i + 1;
2935 int nReturnVal = (i >= 0)? anBreaks[i] : 0;
2941 /** Does character introduce a multicharacter character? */
2942 static inline bool IsLeadByte(TCHAR ch)
2947 return _getmbcp() && IsDBCSLeadByte(ch);
2951 int CCrystalTextView::CursorPointToCharPos( int nLineIndex, const CPoint &curPoint )
2953 // calculate char pos out of point
2954 const int nLength = GetLineLength( nLineIndex );
2955 const int nScreenChars = GetScreenChars();
2956 LPCTSTR szLine = GetLineChars( nLineIndex );
2959 vector<int> anBreaks(nLength + 1);
2962 WrapLineCached( nLineIndex, nScreenChars, &anBreaks, nBreaks );
2964 // find char pos that matches cursor position
2968 const int nTabSize = GetTabSize();
2970 int nIndex=0, nPrevIndex = 0;
2971 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(szLine, nLength);
2972 switch (GetTextLayoutMode ())
2974 case TEXTLAYOUT_TABLE_NOWORDWRAP:
2976 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
2977 int nColumnTotalWidth = 0;
2979 bool bInQuote = false;
2980 const int sep = m_pTextBuffer->GetFieldDelimiter ();
2981 const int quote = m_pTextBuffer->GetFieldEnclosure ();
2982 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
2985 if (!bInQuote && szLine[nIndex] == sep)
2987 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
2988 nOffset = nColumnTotalWidth - nXPos;
2992 if (szLine[nIndex] == quote)
2993 bInQuote = !bInQuote;
2994 if (szLine[nIndex] == '\t')
2997 nOffset = GetCharCellCountFromChar (szLine + nIndex);
2998 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
2999 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nXPos;
3004 if( nXPos > curPoint.x && nYPos == curPoint.y )
3006 else if( nYPos > curPoint.y )
3008 nIndex = nPrevIndex;
3012 nPrevIndex = nIndex;
3016 case TEXTLAYOUT_TABLE_WORDWRAP:
3020 int nColumnSumWidth = 0;
3021 int nColumnCurPoint = INT_MAX;
3022 if (curPoint.x < m_pTextBuffer->GetColumnWidth (0))
3023 nColumnCurPoint = 0;
3024 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next ())
3026 if (i < static_cast<int>(anBreaks.size ()) && nIndex == abs (anBreaks[i]))
3028 if (anBreaks[i++] < 0)
3031 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
3032 nXPos = nColumnSumWidth;
3033 if (nColumnSumWidth <= curPoint.x && curPoint.x < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
3034 nColumnCurPoint = nColumn;
3038 nXPos = nColumnSumWidth;
3044 if (szLine[nIndex] == '\t')
3047 nOffset = GetCharCellCountFromChar (szLine + nIndex);
3051 if( nXPos > curPoint.x && nYPos == curPoint.y )
3053 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
3055 nIndex = nPrevIndex;
3058 else if ( nYPos == curPoint.y)
3059 nPrevIndex = nIndex;
3061 if (nIndex == nLength && nYPos != curPoint.y)
3062 nIndex = nPrevIndex;
3067 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
3069 if( nBreaks > 0 && nYPos < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[nYPos] )
3076 if (szLine[nIndex] == _T('\t'))
3077 nOffset = nTabSize - nCurPos % nTabSize;
3079 nOffset = GetCharCellCountFromChar(szLine + nIndex);
3083 if( nXPos > curPoint.x && nYPos == curPoint.y )
3085 else if( nYPos > curPoint.y )
3087 nIndex = nPrevIndex;
3091 nPrevIndex = nIndex;
3098 void CCrystalTextView::SubLineCursorPosToTextPos( const CPoint &subLineCurPos, CPoint &textPos )
3101 int nSubLineOffset, nLine;
3103 GetLineBySubLine( subLineCurPos.y, nLine, nSubLineOffset );
3105 // compute cursor-position
3106 textPos.x = CursorPointToCharPos( nLine, CPoint( subLineCurPos.x, nSubLineOffset ) );
3111 * @brief Calculate last character position in (sub)line.
3112 * @param [in] nLineIndex Linenumber to check.
3113 * @param [in] nSublineOffset Subline index in wrapped line.
3114 * @return Position of the last character.
3116 int CCrystalTextView::SubLineEndToCharPos(int nLineIndex, int nSubLineOffset)
3118 const int nLength = GetLineLength(nLineIndex);
3120 // if word wrapping is disabled, the end is equal to the length of the line -1
3125 vector<int> anBreaks(nLength + 1);
3128 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3130 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3132 int nBreakLast = -1;
3133 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3135 if (anBreaks[i] < 0)
3137 if (j == nSubLineOffset)
3140 if (nBreakLast < static_cast<int>(anBreaks.size ()) - 1)
3141 return abs (anBreaks[nBreakLast + 1]) - 1;
3145 // if there is no break inside the line or the given subline is the last
3146 // one in this line...
3147 if (nBreaks <= 0 || nSubLineOffset == nBreaks)
3152 // compute character position for end of subline
3153 ASSERT(nSubLineOffset >= 0 && nSubLineOffset <= nBreaks);
3155 int nReturnVal = anBreaks[nSubLineOffset] - 1;
3161 * @brief Calculate first character position in (sub)line.
3162 * @param [in] nLineIndex Linenumber to check.
3163 * @param [in] nSublineOffset Subline index in wrapped line.
3164 * @return Position of the first character.
3166 int CCrystalTextView::SubLineHomeToCharPos(int nLineIndex, int nSubLineOffset)
3168 // if word wrapping is disabled, the start is 0
3169 if (!m_bWordWrap || nSubLineOffset == 0)
3173 int nLength = GetLineLength(nLineIndex);
3174 vector<int> anBreaks(nLength + 1);
3177 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3179 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3181 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3183 if (anBreaks[i] < 0)
3185 if (j == nSubLineOffset)
3186 return abs (anBreaks[i]);
3191 // if there is no break inside the line...
3197 // compute character position for end of subline
3198 ASSERT(nSubLineOffset > 0 && nSubLineOffset <= nBreaks);
3200 int nReturnVal = anBreaks[nSubLineOffset - 1];
3206 int CCrystalTextView::
3209 if (m_nCharWidth == -1)
3211 return m_nCharWidth;
3214 int CCrystalTextView::
3215 GetMaxLineLength (int nTopLine, int nLines)
3217 int nMaxLineLength = 0;
3218 const int nLineCount = (std::min)(nTopLine + nLines, GetLineCount ());
3219 for (int I = nTopLine; I < nLineCount; I++)
3221 int nActualLength = GetLineActualLength (I);
3222 if (nMaxLineLength < nActualLength)
3223 nMaxLineLength = nActualLength;
3225 return nMaxLineLength;
3228 CCrystalTextView *CCrystalTextView::
3229 GetSiblingView (int nRow, int nCol)
3231 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3232 if (pSplitter == nullptr)
3234 CWnd *pWnd = CWnd::FromHandlePermanent (
3235 ::GetDlgItem (pSplitter->m_hWnd, pSplitter->IdFromRowCol (nRow, nCol)));
3236 if (pWnd == nullptr || !pWnd->IsKindOf (RUNTIME_CLASS (CCrystalTextView)))
3238 return static_cast<CCrystalTextView *>(pWnd);
3241 void CCrystalTextView::
3242 GoToLine (int nLine, bool bRelative)
3244 int nLines = m_pTextBuffer->GetLineCount () - 1;
3245 CPoint ptCursorPos = GetCursorPos ();
3248 nLine += ptCursorPos.y;
3260 int nChars = m_pTextBuffer->GetLineLength (nLine);
3265 if (ptCursorPos.x > nChars)
3267 ptCursorPos.x = nChars;
3269 if (ptCursorPos.x >= 0)
3271 ptCursorPos.y = nLine;
3272 ASSERT_VALIDTEXTPOS (ptCursorPos);
3273 SetAnchor (ptCursorPos);
3274 SetSelection (ptCursorPos, ptCursorPos);
3275 SetCursorPos (ptCursorPos);
3276 EnsureVisible (ptCursorPos);
3281 void CCrystalTextView::
3284 CView::OnInitialUpdate ();
3285 CString sDoc = GetDocument ()->GetPathName (), sExt = GetExt (sDoc);
3286 if (!sExt.IsEmpty())
3288 AttachToBuffer (nullptr);
3290 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3291 if (pSplitter != nullptr)
3293 // See CSplitterWnd::IdFromRowCol() implementation
3294 int nRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3295 int nCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3296 ASSERT (nRow >= 0 && nRow < pSplitter->GetRowCount ());
3297 ASSERT (nCol >= 0 && nCol < pSplitter->GetColumnCount ());
3301 CCrystalTextView *pSiblingView = GetSiblingView (0, nCol);
3302 if (pSiblingView != nullptr && pSiblingView != this)
3304 m_nOffsetChar = pSiblingView->m_nOffsetChar;
3305 ASSERT (m_nOffsetChar >= 0 && m_nOffsetChar <= GetMaxLineLength (m_nTopLine, GetScreenLines()));
3311 CCrystalTextView *pSiblingView = GetSiblingView (nRow, 0);
3312 if (pSiblingView != nullptr && pSiblingView != this)
3314 m_nTopLine = pSiblingView->m_nTopLine;
3315 ASSERT (m_nTopLine >= 0 && m_nTopLine < GetLineCount ());
3319 SetFont (m_LogFont);
3320 if (m_bRememberLastPos && !sDoc.IsEmpty ())
3323 CString sKey = REG_EDITPAD;
3324 sKey += _T ("\\Remembered");
3326 if (reg.Open (HKEY_CURRENT_USER, sKey, KEY_READ) &&
3327 reg.LoadBinary (sDoc, (LPBYTE) dwLastPos, sizeof (dwLastPos)))
3330 ptCursorPos.x = dwLastPos[1];
3331 ptCursorPos.y = dwLastPos[2];
3332 if (IsValidTextPosY (ptCursorPos))
3334 if (!IsValidTextPosX (ptCursorPos))
3336 ASSERT_VALIDTEXTPOS (ptCursorPos);
3337 SetCursorPos (ptCursorPos);
3338 SetSelection (ptCursorPos, ptCursorPos);
3339 SetAnchor (ptCursorPos);
3340 EnsureVisible (ptCursorPos);
3346 /////////////////////////////////////////////////////////////////////////////
3347 // CCrystalTextView printing
3349 void CCrystalTextView::
3350 OnPrepareDC (CDC * pDC, CPrintInfo * pInfo /*= nullptr*/)
3352 CView::OnPrepareDC (pDC, pInfo);
3354 if (pInfo != nullptr)
3356 pInfo->m_bContinuePrinting = true;
3357 if (m_nPrintPages != 0 && (int) pInfo->m_nCurPage > m_nPrintPages)
3358 pInfo->m_bContinuePrinting = false;
3362 BOOL CCrystalTextView::
3363 OnPreparePrinting (CPrintInfo * pInfo)
3365 return DoPreparePrinting (pInfo);
3368 void CCrystalTextView::
3369 GetPrintHeaderText (int nPageNum, CString & text)
3371 ASSERT (m_bPrintHeader);
3375 void CCrystalTextView::
3376 GetPrintFooterText (int nPageNum, CString & text)
3378 ASSERT (m_bPrintFooter);
3379 text.Format (_T ("Page %d/%d"), nPageNum, m_nPrintPages);
3382 void CCrystalTextView::
3383 PrintHeader (CDC * pdc, int nPageNum)
3385 CRect rcHeader = m_rcPrintArea;
3386 rcHeader.bottom = rcHeader.top;
3387 rcHeader.top -= (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3390 GetPrintHeaderText (nPageNum, text);
3391 if (!text.IsEmpty ())
3392 pdc->DrawText (text, &rcHeader, DT_CENTER | DT_NOPREFIX | DT_TOP | DT_SINGLELINE);
3395 void CCrystalTextView::
3396 PrintFooter (CDC * pdc, int nPageNum)
3398 CRect rcFooter = m_rcPrintArea;
3399 rcFooter.top = rcFooter.bottom;
3400 rcFooter.bottom += (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3403 GetPrintFooterText (nPageNum, text);
3404 if (!text.IsEmpty ())
3405 pdc->DrawText (text, &rcFooter, DT_CENTER | DT_NOPREFIX | DT_BOTTOM | DT_SINGLELINE);
3409 * @brief Retrieves the print margins
3410 * @param nLeft [out] Left margin
3411 * @param nTop [out] Top margin
3412 * @param nRight [out] right margin
3413 * @param nBottom [out] Bottom margin
3415 void CCrystalTextView::
3416 GetPrintMargins (long & nLeft, long & nTop, long & nRight, long & nBottom)
3418 nLeft = DEFAULT_PRINT_MARGIN;
3419 nTop = DEFAULT_PRINT_MARGIN;
3420 nRight = DEFAULT_PRINT_MARGIN;
3421 nBottom = DEFAULT_PRINT_MARGIN;
3423 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
3426 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
3428 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
3430 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
3432 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
3437 void CCrystalTextView::
3438 RecalcPageLayouts (CDC * pdc, CPrintInfo * pInfo)
3440 m_ptPageArea = pInfo->m_rectDraw;
3441 m_ptPageArea.NormalizeRect ();
3443 m_nPrintLineHeight = pdc->GetTextExtent (_T ("X")).cy;
3445 m_rcPrintArea = m_ptPageArea;
3446 CSize szTopLeft, szBottomRight;
3447 CWinApp *pApp = AfxGetApp ();
3448 ASSERT (pApp != nullptr);
3449 GetPrintMargins (szTopLeft.cx, szTopLeft.cy, szBottomRight.cx, szBottomRight.cy);
3450 pdc->HIMETRICtoLP (&szTopLeft);
3451 pdc->HIMETRICtoLP (&szBottomRight);
3452 m_rcPrintArea.left += szTopLeft.cx;
3453 m_rcPrintArea.right -= szBottomRight.cx;
3454 m_rcPrintArea.top += szTopLeft.cy;
3455 m_rcPrintArea.bottom -= szBottomRight.cy;
3457 m_rcPrintArea.top += m_nPrintLineHeight + m_nPrintLineHeight / 2;
3459 m_rcPrintArea.bottom -= m_nPrintLineHeight + m_nPrintLineHeight / 2;
3461 InvalidateLineCache (0, -1);
3463 m_nScreenChars = (m_rcPrintArea.Width () - GetMarginWidth (pdc)) / GetCharWidth ();
3464 m_nScreenLines = m_rcPrintArea.Height () / GetLineHeight ();
3467 void CCrystalTextView::
3468 OnBeginPrinting (CDC * pdc, CPrintInfo * pInfo)
3470 ASSERT (m_pPrintFont == nullptr);
3471 LOGFONT lf = m_lfBaseFont;
3472 CDC *pDisplayDC = GetDC ();
3473 lf.lfHeight = MulDiv (lf.lfHeight, pdc->GetDeviceCaps (LOGPIXELSY), pDisplayDC->GetDeviceCaps (LOGPIXELSY));
3474 lf.lfWidth = MulDiv (lf.lfWidth, pdc->GetDeviceCaps (LOGPIXELSX), pDisplayDC->GetDeviceCaps (LOGPIXELSX));
3475 ReleaseDC (pDisplayDC);
3477 m_pCrystalRendererSaved = m_pCrystalRenderer.release();
3478 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
3480 m_pPrintFont = new CFont;
3481 if (!m_pPrintFont->CreateFontIndirect (&lf))
3483 delete m_pPrintFont;
3484 m_pPrintFont = nullptr;
3488 GetFont (m_lfSavedBaseFont);
3489 m_pPrintFont->GetLogFont (&lf);
3496 void CCrystalTextView::
3497 OnEndPrinting (CDC * pdc, CPrintInfo * pInfo)
3499 if (m_pCrystalRendererSaved)
3501 m_pCrystalRenderer.reset(m_pCrystalRendererSaved);
3502 m_pCrystalRendererSaved = nullptr;
3504 if (m_pPrintFont != nullptr)
3506 delete m_pPrintFont;
3507 m_pPrintFont = nullptr;
3508 SetFont(m_lfSavedBaseFont);
3511 m_nPrintLineHeight = 0;
3512 m_bPrinting = false;
3515 void CCrystalTextView::
3516 OnPrint (CDC * pdc, CPrintInfo * pInfo)
3518 pdc->SelectObject (m_pPrintFont);
3520 const COLORREF defaultLineColor = RGB(0,0,0);
3521 const COLORREF defaultBgColor = RGB(255,255,255);
3523 RecalcPageLayouts (pdc, pInfo);
3525 m_nPrintPages = (GetSubLineCount () + GetScreenLines () - 1) / GetScreenLines ();
3527 ASSERT (pInfo->m_nCurPage >= 1 && (int) pInfo->m_nCurPage <= m_nPrintPages);
3529 int nTopSubLine = (pInfo->m_nCurPage - 1) * GetScreenLines ();
3530 int nEndSubLine = nTopSubLine + GetScreenLines () - 1;
3531 if (nEndSubLine >= GetSubLineCount ())
3532 nEndSubLine = GetSubLineCount () - 1;
3534 int nTopLine, nEndLine;
3535 GetLineBySubLine (nTopSubLine, nTopLine, nSubLines);
3536 GetLineBySubLine (nEndSubLine, nEndLine, nSubLines);
3538 TRACE (_T ("Printing page %d of %d, lines %d - %d\n"),
3539 pInfo->m_nCurPage, m_nPrintPages, nTopLine, nEndLine);
3541 m_pCrystalRenderer->BindDC(*pdc, m_rcPrintArea);
3542 m_pCrystalRenderer->BeginDraw();
3544 m_pCrystalRenderer->SetTextColor(defaultLineColor);
3545 m_pCrystalRenderer->SetBkColor(defaultBgColor);
3549 PrintHeader (pdc, pInfo->m_nCurPage);
3554 PrintFooter (pdc, pInfo->m_nCurPage);
3557 // set clipping region
3558 // see http://support.microsoft.com/kb/128334
3559 CRect rectClip = m_rcPrintArea;
3560 rectClip.right = rectClip.left + GetMarginWidth (pdc) + GetScreenChars () * GetCharWidth ();
3561 rectClip.bottom = rectClip.top + GetScreenLines () * GetLineHeight ();
3562 if (!!pdc->IsKindOf (RUNTIME_CLASS (CPreviewDC)))
3564 CPreviewDC *pPrevDC = (CPreviewDC *)pdc;
3566 pPrevDC->PrinterDPtoScreenDP (&rectClip.TopLeft ());
3567 pPrevDC->PrinterDPtoScreenDP (&rectClip.BottomRight ());
3570 ::GetViewportOrgEx (pdc->m_hDC,&ptOrg);
3574 rgn.CreateRectRgnIndirect (&rectClip);
3575 pdc->SelectClipRgn (&rgn);
3579 CRect rcLine = m_rcPrintArea;
3580 int nLineHeight = GetLineHeight ();
3581 rcLine.bottom = rcLine.top + nLineHeight;
3583 rcMargin.right = rcMargin.left + GetMarginWidth (pdc);
3584 rcLine.left = rcMargin.right;
3586 int nSubLineOffset = GetSubLineIndex (nTopLine) - nTopSubLine;
3587 if( nSubLineOffset < 0 )
3589 rcLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
3592 int nLineCount = GetLineCount();
3594 for (nCurrentLine = nTopLine; nCurrentLine <= nEndLine; nCurrentLine++)
3596 rcLine.bottom = rcLine.top + GetSubLines (nCurrentLine) * nLineHeight;
3597 rcMargin.bottom = rcLine.bottom;
3599 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
3601 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
3602 DrawSingleLine (rcLine, nCurrentLine);
3603 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
3604 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.bottom-1);
3607 rcLine.top = rcLine.bottom;
3608 rcMargin.top = rcLine.bottom;
3611 m_pCrystalRenderer->EndDraw();
3613 pdc->SelectClipRgn (nullptr);
3617 /////////////////////////////////////////////////////////////////////////////
3618 // CCrystalTextView message handlers
3620 int CCrystalTextView::
3623 if (m_pTextBuffer == nullptr)
3624 return 1; // Single empty line
3626 int nLineCount = m_pTextBuffer->GetLineCount ();
3627 ASSERT (nLineCount > 0);
3632 int CCrystalTextView::GetSubLineCount()
3634 const int nLineCount = GetLineCount();
3636 // if we do not wrap words, number of sub lines is
3637 // equal to number of lines
3638 if( !m_bWordWrap && !m_bHideLines )
3641 // calculate number of sub lines
3642 if (nLineCount <= 0)
3644 return CCrystalTextView::GetSubLineIndex( nLineCount - 1 ) + GetSubLines( nLineCount - 1 );
3647 int CCrystalTextView::GetSubLineIndex( int nLineIndex )
3649 // if we do not wrap words, subline index of this line is equal to its index
3650 if( !m_bWordWrap && !m_bHideLines )
3653 // calculate subline index of the line
3654 int nSubLineCount = 0;
3655 int nLineCount = GetLineCount();
3657 if( nLineIndex >= nLineCount )
3658 nLineIndex = nLineCount - 1;
3660 // return cached subline index of the line if it is already cached.
3661 if (nLineIndex <= m_nLastLineIndexCalculatedSubLineIndex)
3662 return (*m_panSubLineIndexCache)[nLineIndex];
3664 // calculate subline index of the line and cache it.
3665 if (m_nLastLineIndexCalculatedSubLineIndex >= 0)
3666 nSubLineCount = (*m_panSubLineIndexCache)[m_nLastLineIndexCalculatedSubLineIndex];
3669 m_nLastLineIndexCalculatedSubLineIndex = 0;
3670 m_panSubLineIndexCache->SetAtGrow( 0, 0 );
3673 // TODO: Rethink this, it is very time consuming
3674 for( int i = m_nLastLineIndexCalculatedSubLineIndex; i < nLineIndex; i++ )
3676 m_panSubLineIndexCache->SetAtGrow( i, nSubLineCount);
3677 nSubLineCount+= GetSubLines( i );
3679 m_panSubLineIndexCache->SetAtGrow( nLineIndex, nSubLineCount);
3680 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex;
3682 return nSubLineCount;
3685 // See comment in the header file
3686 void CCrystalTextView::GetLineBySubLine(int nSubLineIndex, int &nLine, int &nSubLine)
3688 if (GetSubLineCount() == 0)
3695 ASSERT( nSubLineIndex < GetSubLineCount() );
3697 // if we do not wrap words, nLine is equal to nSubLineIndex and nSubLine is allways 0
3698 if ( !m_bWordWrap && !m_bHideLines )
3700 nLine = nSubLineIndex;
3706 const int nLineCount = GetLineCount();
3709 int base = 0, i = 0, nSubLineIndex2 = 0;
3710 for (int lim = nLineCount; lim != 0; lim >>= 1)
3712 i = base + (lim >> 1);
3713 nSubLineIndex2 = GetSubLineIndex(i);
3714 if (nSubLineIndex >= nSubLineIndex2 && nSubLineIndex < nSubLineIndex2 + GetSubLines(i))
3716 else if (nSubLineIndex2 <= nSubLineIndex) /* key > p: move right */
3720 } /* else move left */
3723 ASSERT(i < nLineCount);
3725 nSubLine = nSubLineIndex - nSubLineIndex2;
3728 int CCrystalTextView::
3729 GetLineLength (int nLineIndex) const
3731 if (m_pTextBuffer == nullptr)
3733 return m_pTextBuffer->GetLineLength (nLineIndex);
3736 int CCrystalTextView::
3737 GetFullLineLength (int nLineIndex) const
3739 if (m_pTextBuffer == nullptr)
3741 return m_pTextBuffer->GetFullLineLength (nLineIndex);
3744 // How many bytes of line are displayed on-screen?
3745 int CCrystalTextView::
3746 GetViewableLineLength (int nLineIndex) const
3749 return GetFullLineLength(nLineIndex);
3751 return GetLineLength(nLineIndex);
3754 LPCTSTR CCrystalTextView::
3755 GetLineChars (int nLineIndex) const
3757 if (m_pTextBuffer == nullptr)
3759 return m_pTextBuffer->GetLineChars (nLineIndex);
3763 * @brief Reattach buffer after deleting/inserting ghost lines :
3765 * @note no need to reinitialize the horizontal scrollbar
3766 * no need to reset the editor options (m_bOvrMode, m_bLastReplace)
3768 void CCrystalTextView::
3769 ReAttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3771 if (m_pTextBuffer != nullptr)
3772 m_pTextBuffer->RemoveView (this);
3773 if (pBuf == nullptr)
3775 pBuf = LocateTextBuffer ();
3778 m_pTextBuffer = pBuf;
3779 if (m_pTextBuffer != nullptr)
3780 m_pTextBuffer->AddView (this);
3781 // don't reset CCrystalEditView options
3782 CCrystalTextView::ResetView ();
3784 // Init scrollbars arrows
3785 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3786 if (pVertScrollBarCtrl != nullptr)
3787 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3788 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3789 // Update vertical scrollbar only
3790 RecalcVertScrollBar ();
3794 * @brief Attach buffer (maybe for the first time)
3795 * initialize the view and initialize both scrollbars
3797 void CCrystalTextView::
3798 AttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3800 if (m_pTextBuffer != nullptr)
3801 m_pTextBuffer->RemoveView (this);
3802 if (pBuf == nullptr)
3804 pBuf = LocateTextBuffer ();
3807 m_pTextBuffer = pBuf;
3808 if (m_pTextBuffer != nullptr)
3809 m_pTextBuffer->AddView (this);
3813 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3814 if (pVertScrollBarCtrl != nullptr)
3815 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3816 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3817 CScrollBar *pHorzScrollBarCtrl = GetScrollBarCtrl (SB_HORZ);
3818 if (pHorzScrollBarCtrl != nullptr)
3819 pHorzScrollBarCtrl->EnableScrollBar (GetScreenChars () >= GetMaxLineLength (m_nTopLine, GetScreenLines())?
3820 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3822 // Update scrollbars
3823 RecalcVertScrollBar ();
3824 RecalcHorzScrollBar ();
3827 void CCrystalTextView::
3830 if (m_pTextBuffer != nullptr)
3832 m_pTextBuffer->RemoveView (this);
3833 m_pTextBuffer = nullptr;
3834 // don't reset CCrystalEditView options
3835 CCrystalTextView::ResetView ();
3839 int CCrystalTextView::
3842 if (m_nScreenLines == -1)
3845 GetClientRect (&rect);
3846 m_nScreenLines = (rect.Height () - GetTopMarginHeight ()) / GetLineHeight ();
3848 return m_nScreenLines;
3851 bool CCrystalTextView::
3852 GetItalic (int nColorIndex)
3854 // WINMERGE - since italic text has problems,
3855 // lets disable it. E.g. "_" chars disappear and last
3856 // char may be cropped.
3859 // return nColorIndex == COLORINDEX_COMMENT;
3862 bool CCrystalTextView::
3863 GetBold (int nColorIndex)
3865 if (m_pColors != nullptr)
3867 nColorIndex &= ~COLORINDEX_MASK;
3868 return m_pColors->GetBold(nColorIndex);
3874 int CCrystalTextView::
3877 if (m_nScreenChars == -1)
3880 GetClientRect (&rect);
3881 m_nScreenChars = (rect.Width () - GetMarginWidth ()) / GetCharWidth ();
3883 return m_nScreenChars;
3886 void CCrystalTextView::
3889 DetachFromBuffer ();
3892 CView::OnDestroy ();
3894 if (m_pCacheBitmap != nullptr)
3896 delete m_pCacheBitmap;
3897 m_pCacheBitmap = nullptr;
3901 BOOL CCrystalTextView::
3902 OnEraseBkgnd (CDC * pdc)
3904 UNREFERENCED_PARAMETER(pdc);
3908 void CCrystalTextView::
3909 OnSize (UINT nType, int cx, int cy)
3911 CView::OnSize (nType, cx, cy);
3914 // get char position of top left visible character with old cached word wrap
3916 SubLineCursorPosToTextPos( CPoint( 0, m_nTopSubLine ), topPos );
3920 // we have to recompute the line wrapping
3921 InvalidateScreenRect(false);
3923 // compute new top sub line
3925 CharPosToPoint( topPos.y, topPos.x, topSubLine );
3926 m_nTopSubLine = GetSubLineIndex(topPos.y) + topSubLine.y;
3928 ScrollToSubLine( m_nTopSubLine );
3930 // set caret to right position
3934 RecalcVertScrollBar (false, false);
3935 RecalcHorzScrollBar (false, false);
3938 void CCrystalTextView::
3939 UpdateSiblingScrollPos (bool bHorz)
3941 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
3942 if (pSplitterWnd != nullptr)
3944 // See CSplitterWnd::IdFromRowCol() implementation for details
3945 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3946 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3947 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
3948 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
3952 int nCols = pSplitterWnd->GetColumnCount ();
3953 for (int nCol = 0; nCol < nCols; nCol++)
3955 if (nCol != nCurrentCol) // We don't need to update ourselves
3957 CCrystalTextView *pSiblingView = GetSiblingView (nCurrentRow, nCol);
3958 if (pSiblingView != nullptr)
3959 pSiblingView->OnUpdateSibling (this, false);
3965 int nRows = pSplitterWnd->GetRowCount ();
3966 for (int nRow = 0; nRow < nRows; nRow++)
3968 if (nRow != nCurrentRow) // We don't need to update ourselves
3970 CCrystalTextView *pSiblingView = GetSiblingView (nRow, nCurrentCol);
3971 if (pSiblingView != nullptr)
3972 pSiblingView->OnUpdateSibling (this, false);
3979 void CCrystalTextView::
3980 OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
3982 if (pUpdateSource != this)
3984 ASSERT (pUpdateSource != nullptr);
3985 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
3988 ASSERT (pUpdateSource->m_nTopLine >= 0);
3989 ASSERT (pUpdateSource->m_nTopLine < GetLineCount ());
3990 if (pUpdateSource->m_nTopLine != m_nTopLine)
3992 ScrollToLine (pUpdateSource->m_nTopLine, true, false);
3998 ASSERT (pUpdateSource->m_nOffsetChar >= 0);
3999 ASSERT (pUpdateSource->m_nOffsetChar < GetMaxLineLength (m_nTopLine, GetScreenLines()));
4000 if (pUpdateSource->m_nOffsetChar != m_nOffsetChar)
4002 ScrollToChar (pUpdateSource->m_nOffsetChar, true, false);
4009 void CCrystalTextView::
4010 RecalcVertScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
4012 SCROLLINFO si = {0};
4013 si.cbSize = sizeof (si);
4017 si.nPos = m_nTopSubLine;
4021 const int nScreenLines = GetScreenLines();
4022 if( m_nTopSubLine > 0 && nScreenLines >= GetSubLineCount() )
4028 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4030 si.nMax = GetSubLineCount() - 1;
4031 si.nPage = nScreenLines;
4032 si.nPos = m_nTopSubLine;
4034 VERIFY (SetScrollInfo (SB_VERT, &si, bRedraw));
4037 void CCrystalTextView::
4038 OnVScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
4040 CView::OnVScroll (nSBCode, nPos, pScrollBar);
4042 // Note we cannot use nPos because of its 16-bit nature
4043 SCROLLINFO si = {0};
4044 si.cbSize = sizeof (si);
4045 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
4046 VERIFY (GetScrollInfo (SB_VERT, &si));
4048 // Get the minimum and maximum scroll-bar positions.
4049 int nMinPos = si.nMin;
4050 int nMaxPos = si.nMax;
4052 // Get the current position of scroll box.
4053 int nCurPos = si.nPos;
4055 bool bDisableSmooth = true;
4058 case SB_TOP: // Scroll to top.
4060 bDisableSmooth = false;
4063 case SB_BOTTOM: // Scroll to bottom.
4065 bDisableSmooth = false;
4068 case SB_LINEUP: // Scroll one line up.
4069 if (nCurPos > nMinPos)
4073 case SB_LINEDOWN: // Scroll one line down.
4074 if (nCurPos < nMaxPos)
4078 case SB_PAGEUP: // Scroll one page up.
4079 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4080 bDisableSmooth = false;
4083 case SB_PAGEDOWN: // Scroll one page down.
4084 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4085 bDisableSmooth = false;
4088 case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position
4089 nCurPos = si.nTrackPos; // of the scroll box at the end of the drag operation.
4092 case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the
4093 nCurPos = si.nTrackPos; // position that the scroll box has been dragged to.
4096 ScrollToSubLine(nCurPos, bDisableSmooth);
4099 void CCrystalTextView::
4100 RecalcHorzScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
4102 SCROLLINFO si = {0};
4103 si.cbSize = sizeof (si);
4105 const int nScreenChars = GetScreenChars();
4107 if (GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
4109 if (m_nOffsetChar > nScreenChars)
4115 // Disable horizontal scroll bar
4116 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4117 SetScrollInfo (SB_HORZ, &si);
4121 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
4126 si.nPos = m_nOffsetChar;
4130 if (nScreenChars >= nMaxLineLen && m_nOffsetChar > 0)
4136 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4139 // Horiz scroll limit to longest line + one screenwidth
4140 si.nMax = nMaxLineLen + nScreenChars;
4141 si.nPage = nScreenChars;
4142 si.nPos = m_nOffsetChar;
4144 VERIFY (SetScrollInfo (SB_HORZ, &si, bRedraw));
4147 void CCrystalTextView::
4148 OnHScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
4150 // Default handler not needed
4151 //CView::OnHScroll (nSBCode, nPos, pScrollBar);
4153 // Again, we cannot use nPos because it's 16-bit
4154 SCROLLINFO si = {0};
4155 si.cbSize = sizeof (si);
4156 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
4157 VERIFY (GetScrollInfo (SB_HORZ, &si));
4159 // Get the minimum and maximum scroll-bar positions.
4160 int nMinPos = si.nMin;
4161 int nMaxPos = si.nMax;
4163 // Get the current position of scroll box.
4164 int nCurPos = si.nPos;
4168 case SB_LEFT: // Scroll to far left.
4172 case SB_RIGHT: // Scroll to far right.
4176 case SB_ENDSCROLL: // End scroll.
4179 case SB_LINELEFT: // Scroll left.
4180 if (nCurPos > nMinPos)
4184 case SB_LINERIGHT: // Scroll right.
4185 if (nCurPos < nMaxPos)
4189 case SB_PAGELEFT: // Scroll one page left.
4190 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4193 case SB_PAGERIGHT: // Scroll one page right.
4194 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4197 case SB_THUMBPOSITION: // Scroll to absolute position. The current position is
4198 nCurPos = si.nTrackPos; // specified by the nPos parameter.
4201 case SB_THUMBTRACK: // Drag scroll box to specified position. The current
4202 nCurPos = si.nTrackPos; // position is specified by the nPos parameter
4203 // The SB_THUMBTRACK scroll-bar code typically is used by applications that give
4204 // some feedback while the scroll box is being dragged.
4207 ScrollToChar (nCurPos, true);
4208 // This is needed, but why ? OnVScroll don't need to call UpdateCaret
4212 BOOL CCrystalTextView::
4213 OnSetCursor (CWnd * pWnd, UINT nHitTest, UINT message)
4215 if (nHitTest == HTCLIENT)
4218 ::GetCursorPos (&pt);
4219 ScreenToClient (&pt);
4220 if (pt.y < GetTopMarginHeight ())
4222 const int nColumnResizing = ClientToColumnResizing (pt.x);
4223 ::SetCursor (::LoadCursor (nullptr, nColumnResizing >= 0 ? IDC_SIZEWE : IDC_ARROW));
4225 else if (pt.x < GetMarginWidth ())
4227 ::SetCursor (::LoadCursor (AfxGetInstanceHandle (), MAKEINTRESOURCE (IDR_MARGIN_CURSOR)));
4231 CPoint ptText = ClientToText (pt);
4232 PrepareSelBounds ();
4233 if (IsInsideSelBlock (ptText))
4235 // [JRT]: Support For Disabling Drag and Drop...
4236 if (!m_bDisableDragAndDrop) // If Drag And Drop Not Disabled
4238 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW)); // Set To Arrow Cursor
4242 ::SetCursor (::LoadCursor (nullptr, IDC_IBEAM));
4246 return CView::OnSetCursor (pWnd, nHitTest, message);
4249 int CCrystalTextView::
4250 ClientToIdealTextPos (int x)
4253 if (x > GetMarginWidth ())
4254 nPos = m_nOffsetChar + (x - GetMarginWidth ()) / GetCharWidth ();
4261 * @brief Converts client area point to text position.
4262 * @param [in] point Client area point.
4263 * @return Text position (line index, char index in line).
4264 * @note For gray selection area char index is 0.
4266 CPoint CCrystalTextView::
4267 ClientToText (const CPoint & point)
4270 const int nSubLineCount = GetSubLineCount();
4271 const int nLineCount = GetLineCount();
4274 pt.y = m_nTopSubLine + (point.y - GetTopMarginHeight ()) / GetLineHeight();
4275 if (pt.y >= nSubLineCount)
4276 pt.y = nSubLineCount - 1;
4283 GetLineBySubLine( pt.y, nLine, nSubLineOffset );
4286 LPCTSTR pszLine = nullptr;
4288 vector<int> anBreaks(1);
4291 if (pt.y >= 0 && pt.y < nLineCount)
4293 nLength = GetLineLength( pt.y );
4294 anBreaks.resize(nLength + 1);
4295 pszLine = GetLineChars(pt.y);
4296 WrapLineCached( pt.y, GetScreenChars(), &anBreaks, nBreaks );
4298 if (nBreaks > nSubLineOffset && GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
4299 nLength = anBreaks[nSubLineOffset] - 1;
4302 // Char index for margin area is 0
4303 int nPos = ClientToIdealTextPos (point.x);
4308 const int nTabSize = GetTabSize();
4310 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, nLength);
4311 switch (GetTextLayoutMode ())
4313 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4315 int nColumnCount = m_pTextBuffer->GetColumnCount (nLine);
4316 int nColumnTotalWidth = 0;
4318 bool bInQuote = false;
4319 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4320 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4321 while (nIndex < nLength)
4324 if (!bInQuote && pszLine[nIndex] == sep)
4326 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4327 nOffset = nColumnTotalWidth - nCurPos;
4331 if (pszLine[nIndex] == quote)
4332 bInQuote = !bInQuote;
4333 if (pszLine[nIndex] == '\t')
4336 nOffset = GetCharCellCountFromChar (pszLine + nIndex);
4337 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4338 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
4343 if (n > nPos && i == nSubLineOffset)
4346 nIndex = pIterChar->next ();
4350 case TEXTLAYOUT_TABLE_WORDWRAP:
4354 int nColumnSumWidth = 0;
4355 int nColumnCurPoint = INT_MAX;
4357 if (nPos < m_pTextBuffer->GetColumnWidth (0))
4358 nColumnCurPoint = 0;
4359 while (nIndex < nLength)
4361 if (i < static_cast<int>(anBreaks.size()) && nIndex == abs(anBreaks[i]))
4363 if (anBreaks[i++] < 0)
4366 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4367 n = nColumnSumWidth;
4368 if (nColumnSumWidth <= nPos && nPos < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4369 nColumnCurPoint = nColumn;
4373 n = nColumnSumWidth;
4379 if (pszLine[nIndex] == '\t')
4382 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4386 if (n > nPos && j == nSubLineOffset)
4388 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
4390 nIndex = nPrevIndex;
4393 else if ( j == nSubLineOffset)
4394 nPrevIndex = nIndex;
4396 nIndex = pIterChar->next();
4398 if (nIndex == nLength && j != nSubLineOffset)
4399 nIndex = nPrevIndex;
4404 while (nIndex < nLength)
4406 if (nBreaks && i < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[i])
4413 if (pszLine[nIndex] == '\t')
4414 nOffset = nTabSize - nCurPos % nTabSize;
4416 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4420 if (n > nPos && i == nSubLineOffset)
4423 nIndex = pIterChar->next();
4429 ASSERT(nIndex >= 0 && nIndex <= nLength);
4434 int CCrystalTextView::
4435 ClientToColumn (int x)
4438 GetClientRect (&rcClient);
4439 int nCharWidth = GetCharWidth ();
4440 int nMarginWidth = GetMarginWidth ();
4441 for (int nColumn = 0, columnleft = nMarginWidth - m_nOffsetChar * nCharWidth;
4442 columnleft < rcClient.Width ();
4443 columnleft += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
4445 if (columnleft <= x && x < columnleft + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth)
4451 int CCrystalTextView::
4452 ClientToColumnResizing (int x)
4454 const int nColumn = ClientToColumn (x);
4455 const int nColumnL = ClientToColumn (x - 4);
4456 const int nColumnR = ClientToColumn (x + 4);
4457 if (nColumn != nColumnL || nColumn != nColumnR)
4459 return (nColumn != nColumnL) ? nColumnL : nColumn;
4464 void CCrystalTextView::
4465 AssertValidTextPos (const CPoint & point)
4467 if (GetLineCount () > 0)
4469 ASSERT (m_nTopLine >= 0 && m_nOffsetChar >= 0);
4470 ASSERT (point.y >= 0 && point.y < GetLineCount ());
4471 ASSERT (point.x >= 0 && point.x <= GetViewableLineLength (point.y));
4476 bool CCrystalTextView::
4477 IsValidTextPos (const CPoint &point)
4479 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4480 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4483 bool CCrystalTextView::
4484 IsValidTextPosX (const CPoint &point)
4486 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4487 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4490 bool CCrystalTextView::
4491 IsValidTextPosY (const CPoint &point)
4493 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4494 point.y >= 0 && point.y < GetLineCount ();
4497 CPoint CCrystalTextView::
4498 TextToClient (const CPoint & point)
4500 ASSERT_VALIDTEXTPOS (point);
4501 LPCTSTR pszLine = GetLineChars (point.y);
4503 int nColumnIndex = 0;
4507 int nSubLineStart = CharPosToPoint( point.y, point.x, charPoint, &nColumnIndex );
4508 charPoint.y+= GetSubLineIndex( point.y );
4510 // compute y-position
4511 pt.y = (charPoint.y - m_nTopSubLine) * GetLineHeight() + GetTopMarginHeight ();
4513 // if pt.x is null, we know the result
4514 if( charPoint.x == 0 && nColumnIndex == 0)
4516 pt.x = GetMarginWidth();
4520 // we have to calculate x-position
4523 pt.y = (point.y - m_nTopLine) * GetLineHeight();
4527 int nTabSize = GetTabSize ();
4528 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, point.x);
4529 switch (GetTextLayoutMode ())
4531 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4533 int nColumnCount = m_pTextBuffer->GetColumnCount (point.y);
4534 int nColumnTotalWidth = 0;
4536 bool bInQuote = false;
4537 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4538 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4539 for (int nIndex = 0, nTabs = 0; nIndex < point.x; nIndex = pIterChar->next())
4541 if (!bInQuote && pszLine[nIndex] == sep)
4543 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4544 pt.x = nColumnTotalWidth;
4548 if (pszLine[nIndex] == quote)
4549 bInQuote = !bInQuote;
4550 if (pszLine[nIndex] == _T ('\t'))
4553 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4554 if (nColumn < nColumnCount && pt.x > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4555 pt.x = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4558 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4562 case TEXTLAYOUT_TABLE_WORDWRAP:
4565 for (int i = 0; i < nColumnIndex; ++i)
4566 pt.x += m_pTextBuffer->GetColumnWidth (i);
4567 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4569 if( nIndex >= nSubLineStart )
4571 if (pszLine[nIndex] == '\t')
4574 pt.x += GetCharCellCountFromChar (pszLine + nIndex);
4577 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4583 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4586 if( nIndex == nSubLineStart )
4589 if (pszLine[nIndex] == _T ('\t'))
4590 pt.x += (nTabSize - pt.x % nTabSize);
4592 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4598 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4604 int CCrystalTextView::
4605 ColumnToClient (int nColumn)
4608 GetClientRect (&rcClient);
4609 int nCharWidth = GetCharWidth ();
4610 int columnleft = GetMarginWidth () - m_nOffsetChar * nCharWidth;
4611 for (int nColumn2 = 0; nColumn2 != nColumn && columnleft < rcClient.Width ();
4612 columnleft += m_pTextBuffer->GetColumnWidth (nColumn2++) * nCharWidth)
4617 void CCrystalTextView::
4618 InvalidateLines (int nLine1, int nLine2, bool bInvalidateMargin /*= false*/ )
4620 bInvalidateMargin = true;
4621 const int nTopMarginHeight = GetTopMarginHeight ();
4622 const int nLineHeight = GetLineHeight();
4626 GetClientRect (&rcInvalid);
4627 if (!bInvalidateMargin)
4628 rcInvalid.left += GetMarginWidth ();
4630 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4632 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4635 InvalidateRect (&rcInvalid, false);
4639 if (nLine2 < nLine1)
4646 GetClientRect (&rcInvalid);
4647 if (!bInvalidateMargin)
4648 rcInvalid.left += GetMarginWidth ();
4650 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4651 rcInvalid.bottom = (GetSubLineIndex( nLine2 ) - m_nTopSubLine + GetSubLines( nLine2 )) * nLineHeight + nTopMarginHeight;
4653 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4654 rcInvalid.bottom = (nLine2 - m_nTopLine + 1) * GetLineHeight();
4657 InvalidateRect (&rcInvalid, false);
4661 void CCrystalTextView::
4662 SetSelection (const CPoint & ptStart, const CPoint & ptEnd, bool bUpdateView /* = true */)
4664 ASSERT_VALIDTEXTPOS (ptStart);
4665 ASSERT_VALIDTEXTPOS (ptEnd);
4666 if (m_ptSelStart == ptStart && !m_bRectangularSelection)
4668 if (m_ptSelEnd != ptEnd)
4669 InvalidateLines (ptEnd.y, m_ptSelEnd.y);
4673 InvalidateLines (ptStart.y, ptEnd.y);
4674 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4676 m_ptSelStart = ptStart;
4680 void CCrystalTextView::
4681 AdjustTextPoint (CPoint & point)
4683 point.x += GetCharWidth () / 2; //todo
4687 void CCrystalTextView::
4688 OnSetFocus (CWnd * pOldWnd)
4690 CView::OnSetFocus (pOldWnd);
4693 if (m_ptSelStart != m_ptSelEnd)
4694 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4698 unsigned CCrystalTextView::
4699 ParseLine (unsigned dwCookie, const TCHAR *pszChars, int nLength, TEXTBLOCK * pBuf, int &nActualItems)
4701 return m_CurSourceDef->ParseLineX (dwCookie, pszChars, nLength, pBuf, nActualItems);
4704 int CCrystalTextView::
4705 CalculateActualOffset (int nLineIndex, int nCharIndex, bool bAccumulate)
4707 const int nLength = GetLineLength (nLineIndex);
4708 ASSERT (nCharIndex >= 0 && nCharIndex <= nLength);
4709 LPCTSTR pszChars = GetLineChars (nLineIndex);
4711 const int nTabSize = GetTabSize ();
4712 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nCharIndex);
4714 switch (GetTextLayoutMode ())
4716 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4718 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4719 int nColumnTotalWidth = 0;
4721 bool bInQuote = false;
4722 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4723 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4724 for (I = 0; I < nCharIndex; I = pIterChar->next())
4726 if (!bInQuote && pszChars[I] == sep)
4728 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4729 nOffset = nColumnTotalWidth;
4733 if (pszChars[I] == quote)
4734 bInQuote = !bInQuote;
4735 else if (pszChars[I] == '\t')
4738 nOffset += GetCharCellCountFromChar (pszChars + I);
4739 if (nColumn < nColumnCount && nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4740 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4746 case TEXTLAYOUT_TABLE_WORDWRAP:
4748 int nColumnIndex = 0;
4750 int nSubLineStart = CharPosToPoint( nLineIndex, nCharIndex, charPoint, &nColumnIndex );
4751 for (int i = 0; i < nColumnIndex; ++i)
4752 nOffset += m_pTextBuffer->GetColumnWidth (i);
4753 for (int nIndex = 0; nIndex < nCharIndex; nIndex = pIterChar->next())
4755 if( nIndex >= nSubLineStart )
4757 if (pszChars[nIndex] == '\t')
4760 nOffset += GetCharCellCountFromChar (pszChars + nIndex);
4769 vector<int> anBreaks(nLength + 1);
4772 /*if( nLength > GetScreenChars() )*/
4773 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
4781 for( J = nBreaks - 1; J >= 0 && nCharIndex < anBreaks[J]; J-- );
4782 nPreBreak = (J >= 0) ? anBreaks[J] : 0;
4785 for (I = 0; I < nCharIndex; I = pIterChar->next())
4788 if( nPreBreak == I && nBreaks )
4789 nPreOffset = nOffset;
4791 if (pszChars[I] == _T ('\t'))
4792 nOffset += (nTabSize - nOffset % nTabSize);
4794 nOffset += GetCharCellCountFromChar(pszChars + I);
4799 if( nPreBreak == I && nBreaks > 0)
4802 return nOffset - nPreOffset;
4810 int CCrystalTextView::
4811 ApproxActualOffset (int nLineIndex, int nOffset)
4816 int nLength = GetLineLength (nLineIndex);
4817 LPCTSTR pszChars = GetLineChars (nLineIndex);
4818 int nCurrentOffset = 0;
4819 int nTabSize = GetTabSize ();
4820 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
4821 switch (GetTextLayoutMode ())
4823 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4824 case TEXTLAYOUT_TABLE_WORDWRAP:
4826 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4827 int nColumnTotalWidth = 0;
4828 bool bInQuote = false;
4829 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4830 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4831 for (int I = 0, nColumn = 0; I < nLength; I = pIterChar->next())
4833 if (!bInQuote && pszChars[I] ==sep)
4835 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4836 nCurrentOffset = nColumnTotalWidth;
4840 if (pszChars[I] == quote)
4841 bInQuote = !bInQuote;
4842 if (pszChars[I] == '\t')
4845 nCurrentOffset += GetCharCellCountFromChar (pszChars + I);
4846 if (nColumn < nColumnCount && nCurrentOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4847 nCurrentOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4849 if (nCurrentOffset >= nOffset)
4851 if (nOffset <= nCurrentOffset - nTabSize / 2)
4853 return pIterChar->next ();
4860 for (int I = 0; I < nLength; I = pIterChar->next())
4862 if (pszChars[I] == _T ('\t'))
4863 nCurrentOffset += (nTabSize - nCurrentOffset % nTabSize);
4866 nCurrentOffset += GetCharCellCountFromChar(pszChars + I);
4868 if (nCurrentOffset >= nOffset)
4870 if (nOffset <= nCurrentOffset - nTabSize / 2)
4872 return pIterChar->next();
4880 void CCrystalTextView::
4881 EnsureVisible (CPoint pt)
4883 EnsureVisible(pt, pt);
4886 void CCrystalTextView::
4887 OnKillFocus (CWnd * pNewWnd)
4889 CView::OnKillFocus (pNewWnd);
4893 if (m_ptSelStart != m_ptSelEnd)
4894 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4895 if (m_bDragSelection)
4898 KillTimer (m_nDragSelTimer);
4899 m_bDragSelection = false;
4903 void CCrystalTextView::
4906 CView::OnSysColorChange ();
4910 void CCrystalTextView::
4911 GetText (const CPoint & ptStart, const CPoint & ptEnd, CString & text, bool bExcludeInvisibleLines /*= true*/)
4913 if (m_pTextBuffer != nullptr)
4914 m_pTextBuffer->GetText (ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, text);
4919 void CCrystalTextView::
4920 GetTextInColumnSelection (CString & text, bool bExcludeInvisibleLines /*= true*/)
4922 if (m_pTextBuffer == nullptr)
4928 PrepareSelBounds ();
4930 CString sEol = m_pTextBuffer->GetStringEol (CRLFSTYLE::DOS);
4933 for (int L = m_ptDrawSelStart.y; L <= m_ptDrawSelEnd.y; L++)
4934 nBufSize += GetLineLength (L) + sEol.GetLength ();
4935 LPTSTR pszBuf = text.GetBuffer (nBufSize);
4937 for (int I = m_ptDrawSelStart.y; I <= m_ptDrawSelEnd.y; I++)
4939 if (bExcludeInvisibleLines && (GetLineFlags (I) & LF_INVISIBLE))
4941 int nSelLeft, nSelRight;
4942 GetColumnSelection (I, nSelLeft, nSelRight);
4943 memcpy (pszBuf, GetLineChars (I) + nSelLeft, sizeof (TCHAR) * (nSelRight - nSelLeft));
4944 pszBuf += (nSelRight - nSelLeft);
4945 memcpy (pszBuf, sEol, sizeof (TCHAR) * sEol.GetLength ());
4946 pszBuf += sEol.GetLength ();
4949 text.ReleaseBuffer ();
4953 void CCrystalTextView::
4954 UpdateView (CCrystalTextView * pSource, CUpdateContext * pContext,
4955 DWORD dwFlags, int nLineIndex /*= -1*/ )
4957 // SetTextType (GetExt (GetDocument ()->GetPathName ()));
4958 if (dwFlags & UPDATE_RESET)
4961 RecalcVertScrollBar ();
4962 RecalcHorzScrollBar ();
4966 int nLineCount = GetLineCount ();
4967 ASSERT (nLineCount > 0);
4968 ASSERT (nLineIndex >= -1 && nLineIndex < nLineCount);
4969 if ((dwFlags & UPDATE_SINGLELINE) != 0)
4971 ASSERT (nLineIndex != -1);
4972 // All text below this line should be reparsed
4973 const int cookiesSize = (int) m_ParseCookies->size();
4974 if (cookiesSize > 0)
4976 ASSERT (cookiesSize == nLineCount);
4977 // must be reinitialized to invalid value (DWORD) - 1
4978 for (int i = nLineIndex; i < cookiesSize; ++i)
4979 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4981 // This line'th actual length must be recalculated
4982 if (m_pnActualLineLength->size())
4984 ASSERT (m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
4985 // must be initialized to invalid code -1
4986 (*m_pnActualLineLength)[nLineIndex] = -1;
4988 InvalidateLineCache( nLineIndex, nLineIndex );
4991 // Repaint the lines
4992 InvalidateLines (nLineIndex, -1, true);
4996 if (m_bViewLineNumbers)
4997 // if enabling linenumber, we must invalidate all line-cache in visible area because selection margin width changes dynamically.
4998 nLineIndex = m_nTopLine < nLineIndex ? m_nTopLine : nLineIndex;
5000 if (nLineIndex == -1)
5001 nLineIndex = 0; // Refresh all text
5003 // All text below this line should be reparsed
5004 if (m_ParseCookies->size())
5006 size_t arrSize = m_ParseCookies->size();
5007 if (arrSize != static_cast<size_t>(nLineCount))
5009 size_t oldsize = arrSize;
5010 m_ParseCookies->resize(nLineCount);
5011 arrSize = nLineCount;
5012 // must be initialized to invalid value (DWORD) - 1
5013 for (size_t i = oldsize; i < arrSize; ++i)
5014 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
5016 for (size_t i = nLineIndex; i < arrSize; ++i)
5017 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
5020 // Recalculate actual length for all lines below this
5021 if (m_pnActualLineLength->size())
5023 size_t arrsize = m_pnActualLineLength->size();
5024 if (arrsize != static_cast<size_t>(nLineCount))
5026 // Reallocate actual length array
5027 size_t oldsize = arrsize;
5028 m_pnActualLineLength->resize(nLineCount);
5029 arrsize = nLineCount;
5030 // must be initialized to invalid code -1
5031 for (size_t i = oldsize; i < arrsize; ++i)
5032 (*m_pnActualLineLength)[i] = -1;
5034 for (size_t i = nLineIndex; i < arrsize; ++i)
5035 (*m_pnActualLineLength)[i] = -1;
5038 InvalidateLineCache( nLineIndex, -1 );
5040 // Repaint the lines
5041 InvalidateLines (nLineIndex, -1, true);
5044 // All those points must be recalculated and validated
5045 if (pContext != nullptr)
5047 pContext->RecalcPoint (m_ptCursorPos);
5048 pContext->RecalcPoint (m_ptSelStart);
5049 pContext->RecalcPoint (m_ptSelEnd);
5050 pContext->RecalcPoint (m_ptAnchor);
5051 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
5052 ASSERT_VALIDTEXTPOS (m_ptSelStart);
5053 ASSERT_VALIDTEXTPOS (m_ptSelEnd);
5054 ASSERT_VALIDTEXTPOS (m_ptAnchor);
5055 if (m_bDraggingText)
5057 pContext->RecalcPoint (m_ptDraggedTextBegin);
5058 pContext->RecalcPoint (m_ptDraggedTextEnd);
5059 ASSERT_VALIDTEXTPOS (m_ptDraggedTextBegin);
5060 ASSERT_VALIDTEXTPOS (m_ptDraggedTextEnd);
5062 CPoint ptTopLine (0, m_nTopLine);
5063 pContext->RecalcPoint (ptTopLine);
5064 ASSERT_VALIDTEXTPOS (ptTopLine);
5065 m_nTopLine = ptTopLine.y;
5069 // Recalculate vertical scrollbar, if needed
5070 if ((dwFlags & UPDATE_VERTRANGE) != 0)
5072 if (!m_bVertScrollBarLocked)
5073 RecalcVertScrollBar ();
5076 // Recalculate horizontal scrollbar, if needed
5077 if ((dwFlags & UPDATE_HORZRANGE) != 0)
5079 if (!m_bHorzScrollBarLocked)
5080 RecalcHorzScrollBar ();
5084 HINSTANCE CCrystalTextView::
5085 GetResourceHandle ()
5087 #ifdef CRYSEDIT_RES_HANDLE
5088 return CRYSEDIT_RES_HANDLE;
5090 if (s_hResourceInst != nullptr)
5091 return s_hResourceInst;
5092 return AfxGetResourceHandle ();
5096 int CCrystalTextView::
5097 OnCreate (LPCREATESTRUCT lpCreateStruct)
5100 _tcscpy_s (m_lfBaseFont.lfFaceName, _T ("FixedSys"));
5101 m_lfBaseFont.lfHeight = 0;
5102 m_lfBaseFont.lfWeight = FW_NORMAL;
5103 m_lfBaseFont.lfItalic = false;
5104 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
5105 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
5106 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5107 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
5108 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
5110 if (CView::OnCreate (lpCreateStruct) == -1)
5113 ASSERT (m_hAccel == nullptr);
5114 // vvv GetResourceHandle () ???
5115 HINSTANCE hInst = AfxFindResourceHandle (MAKEINTRESOURCE(IDR_DEFAULT_ACCEL), RT_ACCELERATOR);
5116 ASSERT (hInst != nullptr);
5117 m_hAccel =::LoadAccelerators (hInst, MAKEINTRESOURCE (IDR_DEFAULT_ACCEL));
5118 ASSERT (m_hAccel != nullptr);
5122 void CCrystalTextView::
5123 SetAnchor (const CPoint & ptNewAnchor)
5125 ASSERT_VALIDTEXTPOS (ptNewAnchor);
5126 m_ptAnchor = ptNewAnchor;
5129 void CCrystalTextView::
5130 OnEditOperation (int nAction, LPCTSTR pszText, size_t cchText)
5134 BOOL CCrystalTextView::
5135 PreTranslateMessage (MSG * pMsg)
5137 if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
5139 if (m_hAccel != nullptr)
5141 if (::TranslateAccelerator (m_hWnd, m_hAccel, pMsg))
5145 else if (pMsg->message == WM_LBUTTONDBLCLK)
5146 m_dwLastDblClickTime = GetTickCount();
5147 else if (pMsg->message == WM_LBUTTONDOWN && (GetTickCount() - GetDoubleClickTime()) < m_dwLastDblClickTime)
5149 m_dwLastDblClickTime = 0;
5150 OnLButtonTrippleClk(static_cast<UINT>(pMsg->wParam), { GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam) });
5153 return CView::PreTranslateMessage (pMsg);
5156 void CCrystalTextView::
5157 SetCursorPos (const CPoint & ptCursorPos)
5159 ASSERT_VALIDTEXTPOS (ptCursorPos);
5160 m_ptCursorPos = ptCursorPos;
5161 m_nIdealCharPos = CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x);
5165 void CCrystalTextView::
5166 UpdateCompositionWindowPos() /* IME */
5168 HIMC hIMC = ImmGetContext(m_hWnd);
5169 COMPOSITIONFORM compform;
5171 compform.dwStyle = CFS_FORCE_POSITION;
5172 compform.ptCurrentPos = GetCaretPos();
5173 ImmSetCompositionWindow(hIMC, &compform);
5175 ImmReleaseContext(m_hWnd, hIMC);
5178 void CCrystalTextView::
5179 UpdateCompositionWindowFont() /* IME */
5181 HIMC hIMC = ImmGetContext(m_hWnd);
5183 ImmSetCompositionFont(hIMC, &m_lfBaseFont);
5185 ImmReleaseContext(m_hWnd, hIMC);
5188 void CCrystalTextView::
5189 SetTopMargin (bool bTopMargin)
5191 if (m_bTopMargin != bTopMargin)
5193 m_bTopMargin = bTopMargin;
5194 if (::IsWindow (m_hWnd))
5197 m_nScreenLines = -1;
5198 RecalcVertScrollBar ();
5204 void CCrystalTextView::
5205 SetSelectionMargin (bool bSelMargin)
5207 if (m_bSelMargin != bSelMargin)
5209 m_bSelMargin = bSelMargin;
5210 if (::IsWindow (m_hWnd))
5212 InvalidateScreenRect ();
5213 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5214 RecalcHorzScrollBar ();
5220 void CCrystalTextView::
5221 SetViewLineNumbers (bool bViewLineNumbers)
5223 if (m_bViewLineNumbers != bViewLineNumbers)
5225 m_bViewLineNumbers = bViewLineNumbers;
5226 if (::IsWindow (m_hWnd))
5228 InvalidateScreenRect ();
5229 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5230 RecalcHorzScrollBar ();
5236 void CCrystalTextView::
5237 SetFont (const LOGFONT & lf)
5242 m_pCrystalRenderer->SetFont(lf);
5243 if (::IsWindow (m_hWnd))
5245 InvalidateScreenRect();
5246 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5247 RecalcVertScrollBar ();
5248 RecalcHorzScrollBar ();
5256 void CCrystalTextView::
5257 OnUpdateIndicatorPosition (CCmdUI * pCmdUI)
5259 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
5261 // VVV m_ptCursorPos.x + 1 ???
5262 stat.Format (_T ("Ln %d, Col %d"), m_ptCursorPos.y + 1, m_nIdealCharPos + 1);
5263 pCmdUI->SetText (stat);
5265 if( pCmdUI->m_pOther != nullptr && !!pCmdUI->m_pOther->IsKindOf( RUNTIME_CLASS(CStatusBar) ) )
5266 OnUpdateStatusMessage( (CStatusBar*)pCmdUI->m_pOther );
5270 void CCrystalTextView::
5271 OnUpdateIndicatorCRLF (CCmdUI * pCmdUI)
5273 if (m_pTextBuffer != nullptr)
5275 std::basic_string<TCHAR> eol;
5276 CRLFSTYLE crlfMode = m_pTextBuffer->GetCRLFMode ();
5279 case CRLFSTYLE::DOS:
5280 eol = LoadResString (IDS_EOL_DOS);
5281 pCmdUI->SetText (eol.c_str());
5282 pCmdUI->Enable (true);
5284 case CRLFSTYLE::UNIX:
5285 eol = LoadResString (IDS_EOL_UNIX);
5286 pCmdUI->SetText (eol.c_str());
5287 pCmdUI->Enable (true);
5289 case CRLFSTYLE::MAC:
5290 eol = LoadResString (IDS_EOL_MAC);
5291 pCmdUI->SetText (eol.c_str());
5292 pCmdUI->Enable (true);
5294 case CRLFSTYLE::MIXED:
5295 eol = LoadResString (IDS_EOL_MIXED);
5296 pCmdUI->SetText (eol.c_str());
5297 pCmdUI->Enable (true);
5300 pCmdUI->SetText (nullptr);
5301 pCmdUI->Enable (false);
5306 pCmdUI->SetText (nullptr);
5307 pCmdUI->Enable (false);
5311 void CCrystalTextView::
5312 OnToggleBookmark (UINT nCmdID)
5314 int nBookmarkID = nCmdID - ID_EDIT_TOGGLE_BOOKMARK0;
5315 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5316 if (m_pTextBuffer != nullptr)
5318 DWORD dwFlags = GetLineFlags (m_ptCursorPos.y);
5319 DWORD dwMask = LF_BOOKMARK (nBookmarkID);
5320 m_pTextBuffer->SetLineFlag (m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0);
5324 void CCrystalTextView::
5325 OnGoBookmark (UINT nCmdID)
5327 int nBookmarkID = nCmdID - ID_EDIT_GO_BOOKMARK0;
5328 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5329 if (m_pTextBuffer != nullptr)
5331 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5334 CPoint pt (0, nLine);
5335 ASSERT_VALIDTEXTPOS (pt);
5337 SetSelection (pt, pt);
5344 void CCrystalTextView::
5347 if (m_pTextBuffer != nullptr)
5349 for (int nBookmarkID = 0; nBookmarkID <= 9; nBookmarkID++)
5351 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5354 m_pTextBuffer->SetLineFlag (nLine, LF_BOOKMARK (nBookmarkID), false);
5361 void CCrystalTextView::
5364 m_bCursorHidden = false;
5368 void CCrystalTextView::
5371 m_bCursorHidden = true;
5375 void CCrystalTextView::
5376 OnDropSource (DROPEFFECT de)
5378 ASSERT (de == DROPEFFECT_COPY);
5381 HGLOBAL CCrystalTextView::
5384 PrepareSelBounds ();
5385 if (m_ptDrawSelStart == m_ptDrawSelEnd)
5389 GetText (m_ptDrawSelStart, m_ptDrawSelEnd, text);
5390 int cchText = text.GetLength();
5391 SIZE_T cbData = (cchText + 1) * sizeof(TCHAR);
5392 HGLOBAL hData =::GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, cbData);
5393 if (hData == nullptr)
5396 LPTSTR pszData = (LPTSTR)::GlobalLock (hData);
5397 if (pszData == nullptr)
5399 ::GlobalFree(hData);
5402 memcpy (pszData, text, cbData);
5403 ::GlobalUnlock (hData);
5405 m_ptDraggedTextBegin = m_ptDrawSelStart;
5406 m_ptDraggedTextEnd = m_ptDrawSelEnd;
5410 static const TCHAR *memstr(const TCHAR *str1, size_t str1len, const TCHAR *str2, size_t str2len)
5412 ASSERT(str1 && str2 && str2len > 0);
5413 for (const TCHAR *p = str1; p < str1 + str1len; ++p)
5417 if (memcmp(p, str2, str2len * sizeof(TCHAR)) == 0)
5424 static const TCHAR *memistr(const TCHAR *str1, size_t str1len, const TCHAR *str2, size_t str2len)
5426 ASSERT(str1 && str2 && str2len > 0);
5427 for (const TCHAR *p = str1; p < str1 + str1len; ++p)
5429 if (toupper(*p) == toupper(*str2))
5432 for (i = 0; i < str2len; ++i)
5434 if (toupper(p[i]) != toupper(str2[i]))
5445 FindStringHelper (LPCTSTR pszLineBegin, size_t nLineLength, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch)
5447 if (dwFlags & FIND_REGEXP)
5454 if (pszFindWhat[0] == '^' && pszLineBegin != pszFindWhere)
5456 rxnode = RxCompile (pszFindWhat, (dwFlags & FIND_MATCH_CASE) != 0 ? RX_CASE : 0);
5457 if (rxnode && RxExec (rxnode, pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhere, rxmatch))
5459 pos = rxmatch->Open[0];
5460 ASSERT((rxmatch->Close[0] - rxmatch->Open[0]) < INT_MAX);
5461 nLen = static_cast<int>(rxmatch->Close[0] - rxmatch->Open[0]);
5467 ASSERT (pszFindWhere != nullptr);
5468 ASSERT (pszFindWhat != nullptr);
5470 int nLength = (int) _tcslen (pszFindWhat);
5471 LPCTSTR pszFindWhereOrig = pszFindWhere;
5476 if (dwFlags & FIND_MATCH_CASE)
5477 pszPos = memstr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5479 pszPos = memistr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5480 if (pszPos == nullptr)
5482 if ((dwFlags & FIND_WHOLE_WORD) == 0)
5483 return nCur + (int) (pszPos - pszFindWhere);
5484 if (pszPos > pszFindWhereOrig && xisalnum (pszPos[-1]))
5486 nCur += (int) (pszPos - pszFindWhere + 1);
5487 pszFindWhere = pszPos + 1;
5490 if (xisalnum (pszPos[nLength]))
5492 nCur += (int) (pszPos - pszFindWhere + 1);
5493 pszFindWhere = pszPos + 1;
5496 return nCur + (int) (pszPos - pszFindWhere);
5499 //~ ASSERT (false); // Unreachable
5503 * @brief Select text in editor.
5504 * @param [in] ptStartPos Star position for highlight.
5505 * @param [in] nLength Count of characters to highlight.
5506 * @param [in] bCursorToLeft If true cursor is positioned to Left-end of text
5507 * selection, if false cursor is positioned to right-end.
5509 bool CCrystalTextView::
5510 HighlightText (const CPoint & ptStartPos, int nLength,
5511 bool bCursorToLeft /*= false*/)
5513 ASSERT_VALIDTEXTPOS (ptStartPos);
5514 CPoint ptEndPos = ptStartPos;
5515 int nCount = GetLineLength (ptEndPos.y) - ptEndPos.x;
5516 if (nLength <= nCount)
5518 ptEndPos.x += nLength;
5522 while (nLength > nCount)
5524 nLength -= nCount + 1;
5525 nCount = GetLineLength (++ptEndPos.y);
5527 ptEndPos.x = nLength;
5529 ASSERT_VALIDTEXTPOS (m_ptCursorPos); // Probably 'nLength' is bigger than expected...
5531 m_ptCursorPos = bCursorToLeft ? ptStartPos : ptEndPos;
5532 m_ptAnchor = bCursorToLeft ? ptEndPos : ptStartPos;
5533 SetSelection (ptStartPos, ptEndPos);
5536 // Scrolls found text to middle of screen if out-of-screen
5537 int nScreenLines = GetScreenLines();
5538 if (ptStartPos.y < m_nTopLine || ptEndPos.y > m_nTopLine + nScreenLines)
5540 if (ptStartPos.y > nScreenLines / 2)
5541 ScrollToLine(ptStartPos.y - nScreenLines / 2);
5543 ScrollToLine(ptStartPos.y);
5544 UpdateSiblingScrollPos (false);
5546 EnsureVisible (ptStartPos, ptEndPos);
5550 bool CCrystalTextView::
5551 FindText (LPCTSTR pszText, const CPoint & ptStartPos, DWORD dwFlags,
5552 bool bWrapSearch, CPoint * pptFoundPos)
5554 if (m_pMarkers != nullptr)
5556 m_pMarkers->SetMarker(_T("EDITOR_MARKER"), pszText, dwFlags, COLORINDEX_MARKERBKGND0, false);
5557 if (m_pMarkers->GetEnabled())
5558 m_pMarkers->UpdateViews();
5560 int nLineCount = GetLineCount ();
5561 return FindTextInBlock (pszText, ptStartPos, CPoint (0, 0),
5562 CPoint (GetLineLength (nLineCount - 1), nLineCount - 1),
5563 dwFlags, bWrapSearch, pptFoundPos);
5566 int HowManyStr (LPCTSTR s, LPCTSTR m)
5570 const int l = (int) _tcslen (m);
5571 while ((p = _tcsstr (p, m)) != nullptr)
5579 int HowManyStr (LPCTSTR s, TCHAR c)
5583 while ((p = _tcschr (p, c)) != nullptr)
5591 bool CCrystalTextView::
5592 FindTextInBlock (LPCTSTR pszText, const CPoint & ptStartPosition,
5593 const CPoint & ptBlockBegin, const CPoint & ptBlockEnd,
5594 DWORD dwFlags, bool bWrapSearch, CPoint * pptFoundPos)
5596 CPoint ptCurrentPos = ptStartPosition;
5598 ASSERT (pszText != nullptr && _tcslen (pszText) > 0);
5599 ASSERT_VALIDTEXTPOS (ptCurrentPos);
5600 ASSERT_VALIDTEXTPOS (ptBlockBegin);
5601 ASSERT_VALIDTEXTPOS (ptBlockEnd);
5602 ASSERT (ptBlockBegin.y < ptBlockEnd.y || ptBlockBegin.y == ptBlockEnd.y &&
5603 ptBlockBegin.x <= ptBlockEnd.x);
5604 if (ptBlockBegin == ptBlockEnd)
5606 CWaitCursor waitCursor;
5607 if (ptCurrentPos.y < ptBlockBegin.y || ptCurrentPos.y == ptBlockBegin.y &&
5608 ptCurrentPos.x < ptBlockBegin.x)
5609 ptCurrentPos = ptBlockBegin;
5611 CString what = pszText;
5613 if (dwFlags & FIND_REGEXP)
5615 nEolns = HowManyStr (what, _T("\\n"));
5621 if (dwFlags & FIND_DIRECTION_UP)
5623 // Let's check if we deal with whole text.
5624 // At this point, we cannot search *up* in selection
5625 ASSERT (ptBlockBegin.x == 0 && ptBlockBegin.y == 0);
5626 ASSERT (ptBlockEnd.x == GetLineLength (GetLineCount () - 1) &&
5627 ptBlockEnd.y == GetLineCount () - 1);
5629 // Proceed as if we have whole text search.
5632 while (ptCurrentPos.y >= 0)
5636 if (dwFlags & FIND_REGEXP)
5638 for (int i = 0; i <= nEolns && ptCurrentPos.y >= i; i++)
5641 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y - i);
5644 nLineLength = GetLineLength (ptCurrentPos.y - i);
5646 line = _T ('\n') + line;
5650 nLineLength = ptCurrentPos.x != -1 ? ptCurrentPos.x : GetLineLength (ptCurrentPos.y - i);
5652 if (nLineLength > 0)
5654 item.SetString(pszChars, nLineLength);
5658 nLineLength = line.GetLength ();
5659 if (ptCurrentPos.x == -1)
5664 nLineLength = GetLineLength(ptCurrentPos.y);
5665 if (ptCurrentPos.x == -1)
5667 ptCurrentPos.x = nLineLength;
5669 else if( ptCurrentPos.x > nLineLength )
5670 ptCurrentPos.x = nLineLength;
5671 if (ptCurrentPos.x == -1)
5674 line.SetString (GetLineChars (ptCurrentPos.y), ptCurrentPos.x);
5677 ptrdiff_t nFoundPos = -1;
5678 int nMatchLen = what.GetLength();
5679 int nLineLen = line.GetLength();
5683 size_t nPosRel = ::FindStringHelper(line, nLineLen, static_cast<LPCTSTR>(line) + nPos, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5686 nFoundPos = nPos + nPosRel;
5687 nMatchLen = m_nLastFindWhatLen;
5688 nPos += nMatchLen == 0 ? 1 : nMatchLen;
5691 if( nFoundPos != -1 ) // Found text!
5693 ptCurrentPos.x = static_cast<int>(nFoundPos);
5694 *pptFoundPos = ptCurrentPos;
5699 if( ptCurrentPos.y >= 0 )
5700 ptCurrentPos.x = GetLineLength( ptCurrentPos.y );
5703 // Beginning of text reached
5707 // Start again from the end of text
5708 bWrapSearch = false;
5709 ptCurrentPos = CPoint (GetLineLength (GetLineCount () - 1), GetLineCount () - 1);
5716 while (ptCurrentPos.y <= ptBlockEnd.y)
5720 if (dwFlags & FIND_REGEXP)
5722 int nLines = m_pTextBuffer->GetLineCount ();
5723 for (int i = 0; i <= nEolns && ptCurrentPos.y + i < nLines; i++)
5725 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y + i);
5726 nLineLength = GetLineLength (ptCurrentPos.y + i);
5731 if (nLineLength > 0)
5733 int nLineLengthOld = line.GetLength();
5734 memcpy(line.GetBufferSetLength(nLineLengthOld + nLineLength) + nLineLengthOld, pszChars, nLineLength * sizeof(TCHAR));
5737 nLineLength = line.GetLength ();
5741 nLineLength = GetLineLength (ptCurrentPos.y) - ptCurrentPos.x;
5742 if (nLineLength <= 0)
5749 line.SetString(GetLineChars(ptCurrentPos.y), GetLineLength(ptCurrentPos.y));
5752 // Perform search in the line
5753 size_t nPos = ::FindStringHelper (line, line.GetLength (), static_cast<LPCTSTR>(line) + ptCurrentPos.x, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5756 if (m_pszMatched != nullptr)
5758 m_pszMatched = _tcsdup (line);
5761 CString item = line.Left (static_cast<LONG>(nPos));
5762 LPCTSTR current = _tcsrchr (item, _T('\n'));
5767 nEolns = HowManyStr (item, _T('\n'));
5770 ptCurrentPos.y += nEolns;
5771 ptCurrentPos.x = static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5775 ptCurrentPos.x += static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5777 if (ptCurrentPos.x < 0)
5782 ptCurrentPos.x += static_cast<LONG>(nPos);
5784 // Check of the text found is outside the block.
5785 if (ptCurrentPos.y == ptBlockEnd.y && ptCurrentPos.x >= ptBlockEnd.x)
5788 *pptFoundPos = ptCurrentPos;
5793 if (m_pszMatched != nullptr)
5795 m_pszMatched = nullptr;
5798 // Go further, text was not found
5803 // End of text reached
5807 // Start from the beginning
5808 bWrapSearch = false;
5809 ptCurrentPos = ptBlockBegin;
5813 //~ ASSERT (false); // Unreachable
5816 static DWORD ConvertSearchInfosToSearchFlags(const LastSearchInfos *lastSearch)
5818 DWORD dwSearchFlags = 0;
5819 if (lastSearch->m_bMatchCase)
5820 dwSearchFlags |= FIND_MATCH_CASE;
5821 if (lastSearch->m_bWholeWord)
5822 dwSearchFlags |= FIND_WHOLE_WORD;
5823 if (lastSearch->m_bRegExp)
5824 dwSearchFlags |= FIND_REGEXP;
5825 if (lastSearch->m_nDirection == 0)
5826 dwSearchFlags |= FIND_DIRECTION_UP;
5827 if (lastSearch->m_bNoWrap)
5828 dwSearchFlags |= FIND_NO_WRAP;
5829 if (lastSearch->m_bNoClose)
5830 dwSearchFlags |= FIND_NO_CLOSE;
5831 return dwSearchFlags;
5834 static void ConvertSearchFlagsToLastSearchInfos(LastSearchInfos *lastSearch, DWORD dwFlags)
5836 lastSearch->m_bMatchCase = (dwFlags & FIND_MATCH_CASE) != 0;
5837 lastSearch->m_bWholeWord = (dwFlags & FIND_WHOLE_WORD) != 0;
5838 lastSearch->m_bRegExp = (dwFlags & FIND_REGEXP) != 0;
5839 lastSearch->m_nDirection = (dwFlags & FIND_DIRECTION_UP) == 0;
5840 lastSearch->m_bNoWrap = (dwFlags & FIND_NO_WRAP) != 0;
5841 lastSearch->m_bNoClose = (dwFlags & FIND_NO_CLOSE) != 0;
5844 CPoint CCrystalTextView::
5845 GetSearchPos(DWORD dwSearchFlags)
5850 auto [ptStart, ptEnd] = GetSelection ();
5851 if( dwSearchFlags & FIND_DIRECTION_UP)
5852 ptSearchPos = ptStart;
5854 ptSearchPos = ptEnd;
5857 ptSearchPos = m_ptCursorPos;
5861 bool CCrystalTextView::
5862 FindText (const LastSearchInfos * lastSearch)
5865 DWORD dwSearchFlags = ConvertSearchInfosToSearchFlags(lastSearch);
5866 if (!FindText (lastSearch->m_sText, GetSearchPos(dwSearchFlags), dwSearchFlags, !lastSearch->m_bNoWrap,
5872 bool bCursorToLeft = (lastSearch->m_nDirection == 0);
5873 HighlightText (ptTextPos, m_nLastFindWhatLen, bCursorToLeft);
5875 // Save search parameters for 'F3' command
5876 m_bLastSearch = true;
5877 if (m_pszLastFindWhat != nullptr)
5878 free (m_pszLastFindWhat);
5879 m_pszLastFindWhat = _tcsdup (lastSearch->m_sText);
5880 m_dwLastSearchFlags = dwSearchFlags;
5882 // Save search parameters to registry
5883 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), m_dwLastSearchFlags));
5888 void CCrystalTextView::
5891 CWinApp *pApp = AfxGetApp ();
5892 ASSERT (pApp != nullptr);
5894 if (m_pFindTextDlg == nullptr)
5895 m_pFindTextDlg = new CFindTextDlg (this);
5897 LastSearchInfos * lastSearch = m_pFindTextDlg->GetLastSearchInfos();
5901 // Get the latest search parameters
5902 ConvertSearchFlagsToLastSearchInfos(lastSearch, m_dwLastSearchFlags);
5903 if (m_pszLastFindWhat != nullptr)
5904 lastSearch->m_sText = m_pszLastFindWhat;
5909 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), &dwFlags))
5911 ConvertSearchFlagsToLastSearchInfos(lastSearch, dwFlags);
5913 m_pFindTextDlg->UseLastSearch ();
5915 // Take the current selection, if any
5918 auto [ptSelStart, ptSelEnd] = GetSelection ();
5919 if (ptSelStart.y == ptSelEnd.y)
5920 GetText (ptSelStart, ptSelEnd, m_pFindTextDlg->m_sText);
5924 CPoint ptCursorPos = GetCursorPos ();
5925 CPoint ptStart = WordToLeft (ptCursorPos);
5926 CPoint ptEnd = WordToRight (ptCursorPos);
5927 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5928 GetText (ptStart, ptEnd, m_pFindTextDlg->m_sText);
5931 // Execute Find dialog
5933 // m_bShowInactiveSelection = true; // FP: removed because I like it
5934 m_pFindTextDlg->UpdateData(FALSE);
5935 m_pFindTextDlg->ShowWindow(SW_SHOW);
5936 // m_bShowInactiveSelection = false; // FP: removed because I like it
5940 void CCrystalTextView::
5943 bool bEnable = m_bLastSearch;
5944 // Show dialog if no last find text
5945 if (m_pszLastFindWhat == nullptr || _tcslen(m_pszLastFindWhat) == 0)
5949 sText = m_pszLastFindWhat;
5952 // If last find-text exists, cut it to first line
5953 bEnable = !sText.IsEmpty ();
5956 int pos = sText.FindOneOf (_T("\r\n"));
5958 sText = sText.Left (pos);
5962 // CTRL-F3 will find selected text..
5963 bool bControlKey = (::GetAsyncKeyState(VK_CONTROL)& 0x8000) != 0;
5964 // CTRL-SHIFT-F3 will find selected text, but opposite direction
5965 bool bShiftKey = (::GetAsyncKeyState(VK_SHIFT)& 0x8000) != 0;
5970 auto [ptSelStart, ptSelEnd] = GetSelection ();
5971 GetText (ptSelStart, ptSelEnd, sText);
5975 CPoint ptCursorPos = GetCursorPos ();
5976 CPoint ptStart = WordToLeft (ptCursorPos);
5977 CPoint ptEnd = WordToRight (ptCursorPos);
5978 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5979 GetText (ptStart, ptEnd, sText);
5981 if (!sText.IsEmpty())
5984 free(m_pszLastFindWhat);
5985 m_pszLastFindWhat = _tcsdup (sText);
5986 m_bLastSearch = true;
5990 m_dwLastSearchFlags |= FIND_DIRECTION_UP;
5992 m_dwLastSearchFlags &= ~FIND_DIRECTION_UP;
5997 // for correct backward search we need some changes:
5998 if (! FindText(sText, GetSearchPos(m_dwLastSearchFlags), m_dwLastSearchFlags,
5999 (m_dwLastSearchFlags & FIND_NO_WRAP) == 0, &ptFoundPos))
6002 prompt.Format (LoadResString(IDS_EDIT_TEXT_NOT_FOUND).c_str(), (LPCTSTR)sText);
6003 AfxMessageBox (prompt, MB_ICONINFORMATION);
6006 HighlightText (ptFoundPos, m_nLastFindWhatLen, (m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0);
6007 m_bMultipleSearch = true; // More search
6010 OnEditFind(); // No previous find, open Find-dialog
6013 void CCrystalTextView::
6014 OnUpdateEditRepeat (CCmdUI * pCmdUI)
6016 pCmdUI->Enable (true);
6019 void CCrystalTextView::
6024 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), &dwFlags))
6027 // Take the current selection, if any
6030 auto[ptSelStart, ptSelEnd] = GetSelection ();
6031 if (ptSelStart.y == ptSelEnd.y)
6032 GetText (ptSelStart, ptSelEnd, sText);
6036 CPoint ptCursorPos = GetCursorPos ();
6037 CPoint ptStart = WordToLeft (ptCursorPos);
6038 CPoint ptEnd = WordToRight (ptCursorPos);
6039 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
6040 GetText (ptStart, ptEnd, sText);
6043 CTextMarkerDlg markerDlg(*m_pMarkers, sText, dwFlags);
6045 if (markerDlg.DoModal() == IDOK)
6047 // Save search parameters to registry
6048 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), markerDlg.GetLastSearchFlags()));
6049 m_pMarkers->SaveToRegistry();
6053 void CCrystalTextView::
6056 CWinApp *pApp = AfxGetApp ();
6057 ASSERT (pApp != nullptr);
6059 CPageSetupDialog dlg;
6061 if (!pApp->GetPrinterDeviceDefaults (&pd))
6064 dlg.m_psd.hDevMode = pd.hDevMode;
6065 dlg.m_psd.hDevNames = pd.hDevNames;
6066 dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS|PSD_MARGINS;
6067 dlg.m_psd.rtMargin.left = DEFAULT_PRINT_MARGIN;
6068 dlg.m_psd.rtMargin.right = DEFAULT_PRINT_MARGIN;
6069 dlg.m_psd.rtMargin.top = DEFAULT_PRINT_MARGIN;
6070 dlg.m_psd.rtMargin.bottom = DEFAULT_PRINT_MARGIN;
6072 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
6075 if (reg.LoadNumber (_T ("PageWidth"), &dwTemp))
6076 dlg.m_psd.ptPaperSize.x = dwTemp;
6077 if (reg.LoadNumber (_T ("PageHeight"), &dwTemp))
6078 dlg.m_psd.ptPaperSize.y = dwTemp;
6079 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
6080 dlg.m_psd.rtMargin.left = dwTemp;
6081 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
6082 dlg.m_psd.rtMargin.right = dwTemp;
6083 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
6084 dlg.m_psd.rtMargin.top = dwTemp;
6085 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
6086 dlg.m_psd.rtMargin.bottom = dwTemp;
6088 if (dlg.DoModal () == IDOK)
6091 if (reg1.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
6093 VERIFY (reg1.SaveNumber (_T ("PageWidth"), dlg.m_psd.ptPaperSize.x));
6094 VERIFY (reg1.SaveNumber (_T ("PageHeight"), dlg.m_psd.ptPaperSize.y));
6095 VERIFY (reg1.SaveNumber (_T ("PageLeft"), dlg.m_psd.rtMargin.left));
6096 VERIFY (reg1.SaveNumber (_T ("PageRight"), dlg.m_psd.rtMargin.right));
6097 VERIFY (reg1.SaveNumber (_T ("PageTop"), dlg.m_psd.rtMargin.top));
6098 VERIFY (reg1.SaveNumber (_T ("PageBottom"), dlg.m_psd.rtMargin.bottom));
6100 pApp->SelectPrinter (dlg.m_psd.hDevNames, dlg.m_psd.hDevMode, false);
6105 * @brief Adds/removes bookmark on given line.
6106 * This functions adds bookmark or removes bookmark on given line.
6107 * @param [in] Index (0-based) of line to add/remove bookmark.
6109 void CCrystalTextView::ToggleBookmark(int nLine)
6111 ASSERT(nLine >= 0 && nLine < GetLineCount());
6112 if (m_pTextBuffer != nullptr)
6114 DWORD dwFlags = GetLineFlags (nLine);
6115 DWORD dwMask = LF_BOOKMARKS;
6116 m_pTextBuffer->SetLineFlag (nLine, dwMask, (dwFlags & dwMask) == 0, false);
6117 const int nBookmarkLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARKS);
6118 if (nBookmarkLine >= 0)
6119 m_bBookmarkExist = true;
6121 m_bBookmarkExist = false;
6125 * @brief Called when Toggle Bookmark is selected from the GUI.
6127 void CCrystalTextView::
6130 ToggleBookmark(m_ptCursorPos.y);
6133 void CCrystalTextView::
6136 if (m_pTextBuffer != nullptr)
6138 int nLine = m_pTextBuffer->FindNextBookmarkLine (m_ptCursorPos.y);
6141 CPoint pt (0, nLine);
6142 ASSERT_VALIDTEXTPOS (pt);
6144 SetSelection (pt, pt);
6151 void CCrystalTextView::
6154 if (m_pTextBuffer != nullptr)
6156 int nLine = m_pTextBuffer->FindPrevBookmarkLine (m_ptCursorPos.y);
6159 CPoint pt (0, nLine);
6160 ASSERT_VALIDTEXTPOS (pt);
6162 SetSelection (pt, pt);
6169 void CCrystalTextView::
6170 OnClearAllBookmarks ()
6172 if (m_pTextBuffer != nullptr)
6174 int nLineCount = GetLineCount ();
6175 for (int I = 0; I < nLineCount; I++)
6177 if (m_pTextBuffer->GetLineFlags (I) & LF_BOOKMARKS)
6178 m_pTextBuffer->SetLineFlag (I, LF_BOOKMARKS, false);
6180 m_bBookmarkExist = false;
6184 void CCrystalTextView::
6185 OnUpdateNextBookmark (CCmdUI * pCmdUI)
6187 pCmdUI->Enable (m_bBookmarkExist);
6190 void CCrystalTextView::
6191 OnUpdatePrevBookmark (CCmdUI * pCmdUI)
6193 pCmdUI->Enable (m_bBookmarkExist);
6196 void CCrystalTextView::
6197 OnUpdateClearAllBookmarks (CCmdUI * pCmdUI)
6199 pCmdUI->Enable (m_bBookmarkExist);
6202 void CCrystalTextView::
6203 SetViewTabs (bool bViewTabs)
6205 if (bViewTabs != m_bViewTabs)
6207 m_bViewTabs = bViewTabs;
6208 if (::IsWindow (m_hWnd))
6213 void CCrystalTextView::
6214 SetViewEols (bool bViewEols, bool bDistinguishEols)
6216 if (bViewEols != m_bViewEols || bDistinguishEols != m_bDistinguishEols)
6218 m_bViewEols = bViewEols;
6219 m_bDistinguishEols = bDistinguishEols;
6220 if (::IsWindow (m_hWnd))
6225 void CCrystalTextView::
6226 SetFlags (DWORD dwFlags)
6228 if (m_dwFlags != dwFlags)
6230 m_dwFlags = dwFlags;
6231 if (::IsWindow (m_hWnd))
6236 int CCrystalTextView::
6237 GetTopMarginHeight()
6241 return GetLineHeight();
6245 * @brief Calculate margin area width.
6246 * This function calculates needed margin width. Needed width is (approx.)
6247 * one char-width for bookmark etc markers and rest to linenumbers (if
6248 * visible). If we have linenumbers visible we need to adjust width so that
6249 * biggest number fits.
6250 * @return Margin area width in pixels.
6252 int CCrystalTextView::
6253 GetMarginWidth (CDC *pdc /*= nullptr*/)
6255 int nMarginWidth = 0;
6257 if (m_bViewLineNumbers)
6259 const int nLines = GetLineCount();
6262 for (n = 1; n <= nLines; n *= 10)
6264 nMarginWidth += GetCharWidth () * nNumbers;
6266 nMarginWidth += 2; // Small gap when symbol part disabled
6271 if (pdc == nullptr || !pdc->IsPrinting ())
6272 nMarginWidth += GetMarginIconSize () + 7; // Width for icon markers and some margin
6276 if (pdc == nullptr || !pdc->IsPrinting ())
6277 nMarginWidth += MARGIN_REV_WIDTH; // Space for revision marks
6280 return nMarginWidth;
6283 void CCrystalTextView::CopyProperties (CCrystalTextView *pSource)
6285 m_nTopLine = pSource->m_nTopLine;
6286 m_nTopSubLine = pSource->m_nTopSubLine;
6287 m_bViewTabs = pSource->m_bViewTabs;
6288 m_bViewEols = pSource->m_bViewEols;
6289 m_bDistinguishEols = pSource->m_bDistinguishEols;
6290 m_bTopMargin = pSource->m_bTopMargin;
6291 m_bSelMargin = pSource->m_bSelMargin;
6292 m_bViewLineNumbers = pSource->m_bViewLineNumbers;
6293 m_bSmoothScroll = pSource->m_bSmoothScroll;
6294 m_bWordWrap = pSource->m_bWordWrap;
6295 m_pColors = pSource->m_pColors;
6296 m_pMarkers = pSource->m_pMarkers;
6297 m_bDisableDragAndDrop = pSource->m_bDisableDragAndDrop;
6298 SetTextType(pSource->m_CurSourceDef);
6299 SetFont (pSource->m_lfBaseFont);
6303 // Mouse wheel event. zDelta is in multiples of 120.
6304 // Divide by 40 so each click is 3 lines. I know some
6305 // drivers let you set the ammount of scroll, but I
6306 // don't know how to retrieve this or if they just
6307 // adjust the zDelta you get here.
6308 BOOL CCrystalTextView::
6309 OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
6311 SCROLLINFO si = {0};
6312 si.cbSize = sizeof (si);
6313 si.fMask = SIF_PAGE | SIF_RANGE;
6314 VERIFY (GetScrollInfo (SB_VERT, &si));
6316 int nNewTopSubLine= m_nTopSubLine - zDelta / 40;
6318 if (nNewTopSubLine < 0)
6320 if (nNewTopSubLine > (si.nMax - (signed int)si.nPage + 1))
6321 nNewTopSubLine = si.nMax - si.nPage + 1;
6323 ScrollToSubLine(nNewTopSubLine, true);
6324 UpdateSiblingScrollPos(false);
6326 return CView::OnMouseWheel (nFlags, zDelta, pt);
6329 void CCrystalTextView::
6330 OnMouseHWheel (UINT nFlags, short zDelta, CPoint pt)
6332 SCROLLINFO si = { sizeof(si) };
6333 si.fMask = SIF_POS | SIF_RANGE;
6334 VERIFY (GetScrollInfo (SB_HORZ, &si));
6336 int nCurPos = si.nPos + zDelta / 40;
6337 if (nCurPos < si.nMin)
6339 else if (nCurPos > si.nMax)
6342 ScrollToChar (nCurPos, true);
6344 UpdateSiblingScrollPos (true);
6346 CView::OnMouseHWheel (nFlags, zDelta, pt);
6349 void CCrystalTextView::
6350 OnSourceType (UINT nId)
6352 SetTextType ((CrystalLineParser::TextType) (nId - ID_SOURCE_PLAIN));
6356 void CCrystalTextView::
6357 OnUpdateSourceType (CCmdUI * pCmdUI)
6359 pCmdUI->SetRadio (CrystalLineParser::m_SourceDefs + (pCmdUI->m_nID - ID_SOURCE_PLAIN) == m_CurSourceDef);
6365 static LPCTSTR braces = _T("{}()[]<>");
6366 LPCTSTR pos = _tcschr (braces, c);
6367 return pos != nullptr ? (int) (pos - braces) + 1 : 0;
6371 bracetype (LPCTSTR s)
6375 return bracetype (*s);
6378 void CCrystalTextView::
6381 CPoint ptCursorPos = GetCursorPos ();
6382 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6383 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y), pszEnd = pszText + ptCursorPos.x;
6384 bool bAfter = false;
6386 if (ptCursorPos.x < nLength)
6388 nType = bracetype (*pszEnd);
6393 else if (ptCursorPos.x > 0)
6395 nType = bracetype (pszEnd[-1]);
6399 else if (ptCursorPos.x > 0)
6401 nType = bracetype (pszEnd[-1]);
6406 int nOther, nCount = 0, nComment = 0;
6409 nOther = ((nType - 1) ^ 1) + 1;
6415 nOther = ((nType - 1) ^ 1) + 1;
6419 LPCTSTR pszOpenComment = m_CurSourceDef->opencomment,
6420 pszCloseComment = m_CurSourceDef->closecomment,
6421 pszCommentLine = m_CurSourceDef->commentline, pszTest;
6422 int nOpenComment = (int) _tcslen (pszOpenComment),
6423 nCloseComment = (int) _tcslen (pszCloseComment),
6424 nCommentLine = (int) _tcslen (pszCommentLine);
6429 while (--pszEnd >= pszText)
6431 pszTest = pszEnd - nOpenComment + 1;
6432 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszOpenComment, nOpenComment))
6436 if (--pszEnd < pszText)
6441 pszTest = pszEnd - nCloseComment + 1;
6442 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCloseComment, nCloseComment))
6446 if (--pszEnd < pszText)
6453 pszTest = pszEnd - nCommentLine + 1;
6454 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCommentLine, nCommentLine))
6458 if (bracetype (*pszEnd) == nType)
6462 else if (bracetype (*pszEnd) == nOther)
6466 ptCursorPos.x = (LONG) (pszEnd - pszText);
6469 SetCursorPos (ptCursorPos);
6470 SetSelection (ptCursorPos, ptCursorPos);
6471 SetAnchor (ptCursorPos);
6472 EnsureVisible (ptCursorPos);
6480 ptCursorPos.x = m_pTextBuffer->GetLineLength (--ptCursorPos.y);
6481 pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6482 pszEnd = pszText + ptCursorPos.x;
6490 LPCTSTR pszBegin = pszText;
6492 pszEnd = pszBegin + nLength;
6493 int nLines = m_pTextBuffer->GetLineCount ();
6496 while (pszText < pszEnd)
6498 pszTest = pszText + nCloseComment;
6499 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCloseComment, nCloseComment))
6503 if (pszText > pszEnd)
6508 pszTest = pszText + nOpenComment;
6509 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszOpenComment, nOpenComment))
6513 if (pszText > pszEnd)
6520 pszTest = pszText + nCommentLine;
6521 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCommentLine, nCommentLine))
6525 if (bracetype (*pszText) == nType)
6529 else if (bracetype (*pszText) == nOther)
6533 ptCursorPos.x = (LONG) (pszText - pszBegin);
6536 SetCursorPos (ptCursorPos);
6537 SetSelection (ptCursorPos, ptCursorPos);
6538 SetAnchor (ptCursorPos);
6539 EnsureVisible (ptCursorPos);
6546 if (ptCursorPos.y < nLines)
6549 nLength = m_pTextBuffer->GetLineLength (++ptCursorPos.y);
6550 pszBegin = pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6551 pszEnd = pszBegin + nLength;
6560 void CCrystalTextView::
6561 OnUpdateMatchBrace (CCmdUI * pCmdUI)
6563 CPoint ptCursorPos = GetCursorPos ();
6564 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6565 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y) + ptCursorPos.x;
6566 pCmdUI->Enable (ptCursorPos.x < nLength && (bracetype (*pszText) || ptCursorPos.x > 0 && bracetype (pszText[-1])) || ptCursorPos.x > 0 && bracetype (pszText[-1]));
6569 void CCrystalTextView::
6572 CGotoDlg dlg (this);
6576 void CCrystalTextView::
6577 OnUpdateToggleSourceHeader (CCmdUI * pCmdUI)
6579 pCmdUI->Enable (m_CurSourceDef->type == CrystalLineParser::SRC_C);
6582 void CCrystalTextView::
6583 OnToggleSourceHeader ()
6585 if (m_CurSourceDef->type == CrystalLineParser::SRC_C)
6587 CDocument *pDoc = GetDocument ();
6588 ASSERT (pDoc != nullptr);
6589 CString sFilePath = pDoc->GetPathName (), sOriginalPath = sFilePath;
6590 if (!_tcsicmp (sFilePath.Right (2), _T (".c")))
6592 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ('h');
6594 else if (!_tcsicmp (sFilePath.Right (4), _T (".cpp")))
6596 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('h');
6598 else if (!_tcsicmp (sFilePath.Right (4), _T (".inl")))
6600 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6601 if (!FileExist(sFilePath))
6603 sFilePath = sFilePath + _T ("pp");
6606 else if (!_tcsicmp (sFilePath.Right (4), _T (".hpp")))
6608 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6609 if (!FileExist(sFilePath))
6611 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6612 if (!FileExist(sFilePath))
6614 sFilePath = sFilePath + _T ("pp");
6618 else if (!_tcsicmp (sFilePath.Right (2), _T (".h")))
6620 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ("hpp");
6621 if (!FileExist(sFilePath))
6623 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6624 if (!FileExist(sFilePath))
6626 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6627 if (!FileExist(sFilePath))
6629 sFilePath = sFilePath + _T ("pp");
6634 if (FileExist(sFilePath))
6636 if (!m_bSingle || !pDoc->IsModified () || pDoc->DoSave (sOriginalPath))
6638 AfxGetApp ()->OpenDocumentFile (sFilePath);
6641 m_ptCursorLast.x = m_ptCursorLast.y = 0;
6642 ASSERT_VALIDTEXTPOS (m_ptCursorLast);
6643 CPoint ptCursorPos = m_ptCursorLast;
6644 SetCursorPos (ptCursorPos);
6645 SetSelection (ptCursorPos, ptCursorPos);
6646 SetAnchor (ptCursorPos);
6647 EnsureVisible (ptCursorPos);
6655 void CCrystalTextView::
6656 OnUpdateTopMargin (CCmdUI * pCmdUI)
6658 pCmdUI->SetCheck (m_bTopMargin);
6661 void CCrystalTextView::
6664 ASSERT (m_CurSourceDef != nullptr);
6666 m_CurSourceDef->flags &= ~SRCOPT_TOPMARGIN;
6668 m_CurSourceDef->flags |= SRCOPT_TOPMARGIN;
6669 SetTopMargin (!m_bTopMargin);
6672 void CCrystalTextView::
6673 OnUpdateSelMargin (CCmdUI * pCmdUI)
6675 pCmdUI->SetCheck (m_bSelMargin);
6678 void CCrystalTextView::
6681 ASSERT (m_CurSourceDef != nullptr);
6683 m_CurSourceDef->flags &= ~SRCOPT_SELMARGIN;
6685 m_CurSourceDef->flags |= SRCOPT_SELMARGIN;
6686 SetSelectionMargin (!m_bSelMargin);
6689 void CCrystalTextView::
6690 OnUpdateWordWrap (CCmdUI * pCmdUI)
6692 pCmdUI->SetCheck (m_bWordWrap);
6695 void CCrystalTextView::
6698 ASSERT (m_CurSourceDef != nullptr);
6701 m_CurSourceDef->flags &= ~SRCOPT_WORDWRAP;
6702 SetWordWrapping (false);
6706 m_CurSourceDef->flags |= SRCOPT_WORDWRAP;
6707 SetWordWrapping (true);
6711 void CCrystalTextView::
6715 RedrawWindow (nullptr, nullptr, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ERASENOW);
6718 void CCrystalTextView::
6719 OnToggleColumnSelection ()
6721 m_bRectangularSelection = !m_bRectangularSelection;
6725 void CCrystalTextView::SetRenderingMode(RENDERING_MODE nRenderingMode)
6728 if (nRenderingMode == RENDERING_MODE::GDI)
6729 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
6731 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(static_cast<int>(nRenderingMode)));
6732 m_pCrystalRenderer->SetFont(m_lfBaseFont);
6734 m_nRenderingMode = nRenderingMode;
6738 bool CCrystalTextView::GetWordWrapping() const
6743 void CCrystalTextView::SetWordWrapping( bool bWordWrap )
6745 m_bWordWrap = bWordWrap;
6747 if( IsWindow( m_hWnd ) )
6750 InvalidateScreenRect();
6754 CCrystalParser *CCrystalTextView::SetParser( CCrystalParser *pParser )
6756 CCrystalParser *pOldParser = m_pParser;
6758 m_pParser = pParser;
6760 if( pParser != nullptr )
6761 pParser->m_pTextView = this;
6768 * @brief Return whether a line is visible.
6770 bool CCrystalTextView::GetLineVisible (int nLineIndex) const
6772 return !m_bHideLines || !(GetLineFlags (nLineIndex) & LF_INVISIBLE);
6776 // incremental search imlementation
6777 BOOL CCrystalTextView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO *pHandlerInfo )
6779 // just look for commands
6780 if( nCode != CN_COMMAND || pExtra != nullptr )
6781 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6784 // each command that is not related to incremental search
6785 // ends the incremental search
6786 if( nID == ID_EDIT_FIND_INCREMENTAL_FORWARD ||
6787 nID == ID_EDIT_FIND_INCREMENTAL_BACKWARD ||
6788 nID == ID_EDIT_DELETE_BACK )
6789 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6791 if( nID >= ID_EDIT_FIRST && nID <= ID_EDIT_LAST )
6792 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6794 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6797 void CCrystalTextView::OnChar( wchar_t nChar, UINT nRepCnt, UINT nFlags )
6799 CView::OnChar( nChar, nRepCnt, nFlags );
6801 // we only have to handle character-input, if we are in incremental search mode
6802 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6805 // exit incremental search, when Escape is pressed
6806 if( nChar == VK_ESCAPE )
6808 // if not end incremental search
6809 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6810 SetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6811 SetCursorPos( m_cursorPosBeforeIncrementalSearch );
6812 EnsureVisible( m_cursorPosBeforeIncrementalSearch );
6816 // exit incremental search without destroying selection
6817 if( nChar == VK_RETURN )
6819 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6823 // is the character valid for incremental search?
6824 if( !_istgraph( nChar ) && !(nChar == _T(' ')) && !(nChar == _T('\t')) )
6826 // if not end incremental search
6827 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6831 // if last search was not successfull do not add a new character
6832 if( !m_bIncrementalFound )
6834 MessageBeep( MB_OK );
6838 // add character to incremental search string and search
6839 *m_pstrIncrementalSearchString += (TCHAR) nChar;
6840 OnEditFindIncremental();
6843 LRESULT CCrystalTextView::OnImeStartComposition(WPARAM wParam, LPARAM lParam) /* IME */
6845 UpdateCompositionWindowFont();
6846 UpdateCompositionWindowPos();
6848 return DefWindowProc(WM_IME_STARTCOMPOSITION, wParam, lParam);
6851 void CCrystalTextView::OnEditDeleteBack()
6853 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6856 // remove last character from search string
6857 if( m_pstrIncrementalSearchString->IsEmpty() )
6860 *m_pstrIncrementalSearchString = m_pstrIncrementalSearchString->Left( m_pstrIncrementalSearchString->GetLength() - 1 );
6861 OnEditFindIncremental();
6865 void CCrystalTextView::OnEditFindIncremental( bool bFindNextOccurence /*= false*/ )
6867 // when string is empty, then goto position where the search starts
6868 if( m_pstrIncrementalSearchString->IsEmpty() )
6870 SetSelection( m_incrementalSearchStartPos, m_incrementalSearchStartPos );
6871 SetCursorPos( m_incrementalSearchStartPos );
6872 EnsureVisible( m_incrementalSearchStartPos );
6876 // otherwise search next occurence of search string,
6877 // starting at current cursor position
6878 CPoint matchStart, matchEnd;
6880 // calculate start point for search
6881 if( bFindNextOccurence )
6883 auto[selStart, selEnd] = GetSelection ();
6884 m_incrementalSearchStartPos = (m_bIncrementalSearchBackward)? selStart : selEnd;
6887 m_bIncrementalFound = FindText(
6888 *m_pstrIncrementalSearchString,
6889 m_incrementalSearchStartPos,
6890 m_bIncrementalSearchBackward? FIND_DIRECTION_UP : 0,
6894 if( !m_bIncrementalFound )
6896 MessageBeep( MB_OK );
6900 // select found text and set cursor to end of match
6901 matchEnd = matchStart;
6902 matchEnd.x+= m_pstrIncrementalSearchString->GetLength();
6903 SetSelection( matchStart, matchEnd );
6904 SetCursorPos( matchEnd );
6905 EnsureVisible( matchEnd );
6910 void CCrystalTextView::OnEditFindIncrementalForward()
6912 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6915 if( !m_pstrIncrementalSearchString->IsEmpty() )
6916 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6917 m_pstrIncrementalSearchString->Empty();
6918 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6919 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6921 else if( m_bIncrementalSearchForward )
6923 if( m_pstrIncrementalSearchString->IsEmpty() )
6925 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6926 m_pstrIncrementalSearchStringOld->Empty();
6927 OnEditFindIncremental();
6930 OnEditFindIncremental( true );
6935 m_bIncrementalSearchForward = true;
6936 m_bIncrementalSearchBackward = false;
6937 m_bIncrementalFound = true;
6938 OnEditFindIncremental();
6941 void CCrystalTextView::OnEditFindIncrementalBackward()
6943 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6946 if( !m_pstrIncrementalSearchString->IsEmpty() )
6947 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6948 m_pstrIncrementalSearchString->Empty();
6949 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6950 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6952 else if( m_bIncrementalSearchBackward )
6954 if( m_pstrIncrementalSearchString->IsEmpty() )
6956 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6957 m_pstrIncrementalSearchStringOld->Empty();
6958 OnEditFindIncremental();
6961 OnEditFindIncremental( true );
6966 m_bIncrementalSearchForward = false;
6967 m_bIncrementalSearchBackward = true;
6968 m_bIncrementalFound = true;
6969 OnEditFindIncremental();
6972 void CCrystalTextView::OnUpdateEditFindIncrementalForward(CCmdUI* pCmdUI)
6974 if (m_pTextBuffer != nullptr)
6976 int nLines = m_pTextBuffer->GetLineCount ();
6977 int nChars = m_pTextBuffer->GetLineLength (m_ptCursorPos.y);
6978 pCmdUI->Enable(m_ptCursorPos.y < nLines - 1 || m_ptCursorPos.x < nChars);
6981 pCmdUI->Enable(false);
6984 void CCrystalTextView::OnUpdateEditFindIncrementalBackward(CCmdUI* pCmdUI)
6986 if (m_pTextBuffer != nullptr)
6988 pCmdUI->Enable(m_ptCursorPos.y > 0 || m_ptCursorPos.x > 0);
6991 pCmdUI->Enable(false);
6994 void CCrystalTextView::OnUpdateStatusMessage( CStatusBar *pStatusBar )
6996 static bool bUpdatedAtLastCall = false;
6998 ASSERT( pStatusBar != nullptr && IsWindow( pStatusBar->m_hWnd ) );
6999 if( pStatusBar == nullptr || !IsWindow( pStatusBar->m_hWnd ) )
7002 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
7004 if( bUpdatedAtLastCall )
7005 pStatusBar->SetPaneText( 0, LoadResString(AFX_IDS_IDLEMESSAGE).c_str() );
7007 bUpdatedAtLastCall = false;
7014 if( !m_bIncrementalFound )
7015 formatid = IDS_FIND_INCREMENTAL_FAILED;
7016 else if( m_bIncrementalSearchForward )
7017 formatid = IDS_FIND_INCREMENTAL_FORWARD;
7018 else if( m_bIncrementalSearchBackward )
7019 formatid = IDS_FIND_INCREMENTAL_BACKWARD;
7022 strFormat.Format( LoadResString(formatid).c_str(), (LPCTSTR)*m_pstrIncrementalSearchString );
7024 pStatusBar->SetPaneText( 0, strFormat );
7025 bUpdatedAtLastCall = false;
7029 bool CCrystalTextView::IsTextBufferInitialized () const
7031 return m_pTextBuffer && m_pTextBuffer->IsTextBufferInitialized();
7034 CString CCrystalTextView::GetTextBufferEol(int nLine) const
7036 return m_pTextBuffer->GetLineEol(nLine);
7039 void CCrystalTextView::SetMarkersContext(CCrystalTextMarkers * pMarkers)
7041 pMarkers->AddView(this);
7042 m_pMarkers = pMarkers;
7046 int CCrystalTextView::GetCharCellCountUnicodeChar(const wchar_t *pch)
7049 if (!m_bChWidthsCalculated[ch/256])
7051 if (U16_IS_SURROGATE(ch) && U16_IS_SURROGATE_LEAD(ch))
7053 return wcwidth(U16_GET_SUPPLEMENTARY(ch, pch[1]));
7057 int nWidthArray[256];
7058 wchar_t nStart = ch/256*256;
7059 wchar_t nEnd = nStart + 255;
7060 m_pCrystalRenderer->GetCharWidth(nStart, nEnd, nWidthArray);
7061 int nCharWidth = GetCharWidth();
7062 for (int i = 0; i < 256; i++)
7064 if (nCharWidth * 15 < nWidthArray[i] * 10)
7065 m_iChDoubleWidthFlags[(nStart+i)/32] |= 1 << (i % 32);
7068 wchar_t ch2 = static_cast<wchar_t>(nStart + i);
7069 if (wcwidth(ch2) > 1)
7070 m_iChDoubleWidthFlags[(nStart + i) / 32] |= 1 << (i % 32);
7073 m_bChWidthsCalculated[ch / 256] = true;
7076 if (m_iChDoubleWidthFlags[ch / 32] & (1 << (ch % 32)))
7083 /** @brief Reset computed unicode character widths. */
7084 void CCrystalTextView::ResetCharWidths ()
7087 ZeroMemory(m_bChWidthsCalculated, sizeof(m_bChWidthsCalculated));
7088 ZeroMemory(m_iChDoubleWidthFlags, sizeof(m_iChDoubleWidthFlags));
7092 // This function assumes selection is in one line
7093 void CCrystalTextView::EnsureVisible (CPoint ptStart, CPoint ptEnd)
7095 // Scroll vertically
7097 int nSubLineCount = GetSubLineCount();
7098 int nNewTopSubLine = m_nTopSubLine;
7101 CharPosToPoint( ptStart.y, ptStart.x, subLinePos );
7102 subLinePos.y += GetSubLineIndex( ptStart.y );
7104 if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
7105 nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
7106 if( subLinePos.y < nNewTopSubLine )
7107 nNewTopSubLine = subLinePos.y;
7109 if( nNewTopSubLine < 0 )
7111 if( nNewTopSubLine >= nSubLineCount )
7112 nNewTopSubLine = nSubLineCount - 1;
7114 if ( !m_bWordWrap && !m_bHideLines )
7116 // WINMERGE: This line fixes (cursor) slowdown after merges!
7117 // I don't know exactly why, but propably we are setting
7118 // m_nTopLine to zero in ResetView() and are not setting to
7119 // valid value again. Maybe this is a good place to set it?
7120 m_nTopLine = nNewTopSubLine;
7125 GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
7128 if( nNewTopSubLine != m_nTopSubLine )
7130 ScrollToSubLine( nNewTopSubLine );
7132 UpdateSiblingScrollPos( false );
7135 // Scroll horizontally
7137 // we do not need horizontally scrolling, if we wrap the words
7138 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
7141 int nActualPos = CalculateActualOffset (ptStart.y, ptStart.x);
7142 int nNewOffset = m_nOffsetChar;
7143 const int nScreenChars = GetScreenChars ();
7145 if (ptStart == ptEnd)
7147 // Keep 5 chars visible right to cursor
7148 if (nActualPos > nNewOffset + nScreenChars - 5)
7150 // Add 10 chars width space after line
7151 nNewOffset = nActualPos - nScreenChars + 10;
7153 // Keep 5 chars visible left to cursor
7154 if (nActualPos < nNewOffset + 5)
7156 // Jump by 10 char steps, so user sees previous letters too
7157 nNewOffset = nActualPos - 10;
7162 int nActualEndPos = CalculateActualOffset (ptEnd.y, ptEnd.x);
7163 const int nBeginOffset = nActualPos - m_nOffsetChar;
7164 const int nEndOffset = nActualEndPos - m_nOffsetChar;
7165 const int nSelLen = nActualEndPos - nActualPos;
7167 // Selection fits to screen, scroll whole selection visible
7168 if (nSelLen < nScreenChars)
7170 // Begin of selection not visible
7171 if (nBeginOffset > nScreenChars)
7173 // Scroll so that there is max 5 chars margin at end
7174 if (nScreenChars - nSelLen > 5)
7175 nNewOffset = nActualPos + 5 - nScreenChars + nSelLen;
7177 nNewOffset = nActualPos - 5;
7179 else if (nBeginOffset < 0)
7181 // Scroll so that there is max 5 chars margin at begin
7182 if (nScreenChars - nSelLen >= 5)
7183 nNewOffset = nActualPos - 5;
7185 nNewOffset = nActualPos - 5 - nScreenChars + nSelLen;
7187 // End of selection not visible
7188 else if (nEndOffset > nScreenChars ||
7191 nNewOffset = nActualPos - 5;
7194 else // Selection does not fit screen so scroll to begin of selection
7196 nNewOffset = nActualPos - 5;
7200 // Horiz scroll limit to longest line + one screenwidth
7201 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
7202 if (nNewOffset >= nMaxLineLen + nScreenChars)
7203 nNewOffset = nMaxLineLen + nScreenChars - 1;
7207 if (m_nOffsetChar != nNewOffset)
7209 ScrollToChar (nNewOffset);
7211 UpdateSiblingScrollPos (true);
7215 // Analyze the first line of file to detect its type
7216 // Mainly it works for xml files
7217 bool CCrystalTextView::
7218 SetTextTypeByContent (LPCTSTR pszContent)
7220 RxNode *rxnode = nullptr;
7223 if (::FindStringHelper(pszContent, _tcslen(pszContent), pszContent, _T("^\\s*\\<\\?xml\\s+.+?\\?\\>\\s*$"),
7224 FIND_REGEXP, nLen, rxnode, &rxmatch) == 0)
7228 return SetTextType(CrystalLineParser::SRC_XML);
7235 void CCrystalTextView::
7236 AutoFitColumn (int nColumn)
7238 int nLastColumn = 0;
7239 int nLastColumnWidth = 0;
7240 const int nTabSize = GetTabSize ();
7241 std::vector<int> aColumnWidths;
7242 const int nScreenChars = GetScreenChars ();
7243 const int nMaxColumnWidth = nScreenChars < 1 ? 1 : nScreenChars - 1;
7244 for (auto& pbuf : m_pTextBuffer->GetTextBufferList ())
7246 const TCHAR sep = pbuf->GetFieldDelimiter ();
7247 const int quote = pbuf->GetFieldEnclosure ();
7248 const int nLineCount = pbuf->GetLineCount ();
7249 for (int i = 0; i < nLineCount; ++i)
7251 bool bInQuote = false;
7253 int nColumnWidth = 0;
7254 const TCHAR* pszChars = pbuf->GetLineChars (i);
7255 const size_t nLineLength = pbuf->GetFullLineLength (i);
7256 for (size_t j = 0; j < nLineLength; j += U16_IS_SURROGATE (pszChars[j]) ? 2 : 1)
7258 bool bDelimiterOrNewLine = false;
7259 TCHAR c = pszChars[j];
7261 bInQuote = !bInQuote;
7262 if (!bInQuote && c == sep)
7264 bDelimiterOrNewLine = true;
7267 else if (c == '\r' || c == '\n')
7273 if (j == nLineLength - 1 || pszChars[j + 1] != '\n')
7276 bDelimiterOrNewLine = true;
7281 if (j > 0 && pszChars[j - 1] == '\r')
7285 bDelimiterOrNewLine = true;
7289 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7294 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7296 if (bDelimiterOrNewLine)
7298 if (nColumnWidth > nMaxColumnWidth)
7299 nColumnWidth = nMaxColumnWidth;
7300 if (static_cast<int>(aColumnWidths.size ()) < nColumn2 + 1)
7301 aColumnWidths.resize (nColumn2 + 1, nTabSize);
7302 if (aColumnWidths[nColumn2] < nColumnWidth)
7303 aColumnWidths[nColumn2] = nColumnWidth;
7309 if (nLastColumn < nColumn2)
7311 nLastColumn = nColumn2;
7312 nLastColumnWidth = 0;
7314 if (nLastColumnWidth < nColumnWidth)
7315 nLastColumnWidth = nColumnWidth;
7319 aColumnWidths.resize (nLastColumn + 1, nTabSize);
7320 if (aColumnWidths[nLastColumn] < nLastColumnWidth)
7321 aColumnWidths[nLastColumn] = nLastColumnWidth;
7323 for (size_t nColumn2 = 0; nColumn2 < aColumnWidths.size (); ++nColumn2)
7325 if (nColumn == -1 || nColumn == static_cast<int>(nColumn2))
7326 m_pTextBuffer->SetColumnWidth (static_cast<int>(nColumn2), aColumnWidths[nColumn2]);
7328 m_pTextBuffer->InvalidateColumns ();
7331 CCrystalTextView::TextLayoutMode CCrystalTextView::GetTextLayoutMode () const
7333 if (m_pTextBuffer && m_pTextBuffer->GetTableEditing ())
7334 return m_bWordWrap ? TEXTLAYOUT_TABLE_WORDWRAP : TEXTLAYOUT_TABLE_NOWORDWRAP;
7335 return m_bWordWrap ? TEXTLAYOUT_WORDWRAP : TEXTLAYOUT_NOWORDWRAP;
7338 ////////////////////////////////////////////////////////////////////////////