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
89 #include "ccrystaltextview.h"
92 #include "ccrystaltextbuffer.h"
93 #include "ccrystaltextmarkers.h"
94 #include "ViewableWhitespace.h"
95 #include "SyntaxColors.h"
96 #include "renderers/ccrystalrendererdirectwrite.h"
97 #include "renderers/ccrystalrenderergdi.h"
98 #include "dialogs/cfindtextdlg.h"
99 #include "dialogs/ctextmarkerdlg.h"
100 #include "dialogs/gotodlg.h"
101 #include "utils/fpattern.h"
102 #include "utils/filesup.h"
103 #include "utils/registry.h"
104 #include "utils/string_util.h"
105 #include "utils/wcwidth.h"
106 #include "utils/icu.hpp"
111 #include <imm.h> /* IME */
114 using CrystalLineParser::TEXTBLOCK;
116 // Escaped character constants in range 0x80-0xFF are interpreted in current codepage
117 // Using C locale gets us direct mapping to Unicode codepoints
118 #pragma setlocale("C")
120 #ifndef __AFXPRIV_H__
121 #pragma message("Include <afxpriv.h> in your stdafx.h to avoid this message")
126 #define new DEBUG_NEW
130 // The vcruntime.h version of _countf() gives syntax errors starting with VS 15.7.2,
131 // but only with `CCrystalTextView::m_SourceDefs` (which is local to this .cpp file),
132 // and only for X64 compilations (Win32 is ok, probably because no alignment issues
133 // are involved). I think that this could be related to C++17 compliance issues.
134 // This patch reverts to a 'traditional' definition of _countf(), a pre-existing
135 // part of the CCrystalTextView package.
138 #define _countof(array) (sizeof(array)/sizeof(array[0]))
141 #define DEFAULT_PRINT_MARGIN 1000 // 10 millimeters
143 #ifndef WM_MOUSEHWHEEL
144 # define WM_MOUSEHWHEEL 0x20e
147 /** @brief Width of revision marks. */
148 const UINT MARGIN_REV_WIDTH = 3;
150 /** @brief Color of unsaved line revision mark (dark yellow). */
151 const COLORREF UNSAVED_REVMARK_CLR = RGB(0xD7, 0xD7, 0x00);
152 /** @brief Color of saved line revision mark (green). */
153 const COLORREF SAVED_REVMARK_CLR = RGB(0x00, 0xFF, 0x00);
155 #define SMOOTH_SCROLL_FACTOR 6
157 #define ICON_INDEX_WRAPLINE 15
159 ////////////////////////////////////////////////////////////////////////////
162 LOGFONT CCrystalTextView::m_LogFont;
164 IMPLEMENT_DYNCREATE (CCrystalTextView, CView)
166 HINSTANCE CCrystalTextView::s_hResourceInst = nullptr;
167 CCrystalTextView::RENDERING_MODE CCrystalTextView::s_nRenderingModeDefault = RENDERING_MODE::GDI;
169 static ptrdiff_t FindStringHelper(const tchar_t* pszLineBegin, size_t nLineLength, const tchar_t* pszFindWhere, const tchar_t* pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch);
171 BEGIN_MESSAGE_MAP (CCrystalTextView, CView)
172 //{{AFX_MSG_MAP(CCrystalTextView)
185 ON_WM_LBUTTONDBLCLK ()
186 ON_COMMAND (ID_EDIT_COPY, OnEditCopy)
187 ON_UPDATE_COMMAND_UI (ID_EDIT_COPY, OnUpdateEditCopy)
188 ON_COMMAND (ID_EDIT_SELECT_ALL, OnEditSelectAll)
190 ON_WM_SYSCOLORCHANGE ()
192 ON_COMMAND (ID_EDIT_FIND, OnEditFind)
193 ON_COMMAND (ID_EDIT_REPEAT, OnEditRepeat)
194 ON_UPDATE_COMMAND_UI (ID_EDIT_REPEAT, OnUpdateEditRepeat)
195 ON_COMMAND (ID_EDIT_MARK, OnEditMark)
198 ON_MESSAGE (WM_IME_STARTCOMPOSITION, OnImeStartComposition) /* IME */
200 ON_COMMAND (ID_EDIT_CHAR_LEFT, OnCharLeft)
201 ON_COMMAND (ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
202 ON_COMMAND (ID_EDIT_CHAR_RIGHT, OnCharRight)
203 ON_COMMAND (ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
204 ON_COMMAND (ID_EDIT_WORD_LEFT, OnWordLeft)
205 ON_COMMAND (ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
206 ON_COMMAND (ID_EDIT_WORD_RIGHT, OnWordRight)
207 ON_COMMAND (ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
208 ON_COMMAND (ID_EDIT_LINE_UP, OnLineUp)
209 ON_COMMAND (ID_EDIT_EXT_LINE_UP, OnExtLineUp)
210 ON_COMMAND (ID_EDIT_LINE_DOWN, OnLineDown)
211 ON_COMMAND (ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
212 ON_COMMAND (ID_EDIT_SCROLL_UP, ScrollUp)
213 ON_COMMAND (ID_EDIT_SCROLL_DOWN, ScrollDown)
214 ON_COMMAND (ID_EDIT_PAGE_UP, OnPageUp)
215 ON_COMMAND (ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
216 ON_COMMAND (ID_EDIT_PAGE_DOWN, OnPageDown)
217 ON_COMMAND (ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
218 ON_COMMAND (ID_EDIT_LINE_END, OnLineEnd)
219 ON_COMMAND (ID_EDIT_EXT_LINE_END, OnExtLineEnd)
220 ON_COMMAND (ID_EDIT_HOME, OnHome)
221 ON_COMMAND (ID_EDIT_EXT_HOME, OnExtHome)
222 ON_COMMAND (ID_EDIT_TEXT_BEGIN, OnTextBegin)
223 ON_COMMAND (ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
224 ON_COMMAND (ID_EDIT_TEXT_END, OnTextEnd)
225 ON_COMMAND (ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
226 // Standard printing commands
227 ON_COMMAND (ID_FILE_PAGE_SETUP, OnFilePageSetup)
228 ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint)
229 ON_COMMAND (ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
230 ON_COMMAND (ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
232 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
233 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
235 ON_COMMAND_RANGE (ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
236 ON_COMMAND_RANGE (ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
237 ON_COMMAND (ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
239 ON_COMMAND (ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
240 ON_COMMAND (ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
241 ON_COMMAND (ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
242 ON_COMMAND (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
243 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
244 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
245 ON_UPDATE_COMMAND_UI (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
246 // Ferdi's source type chnages
247 ON_COMMAND_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnSourceType)
248 ON_UPDATE_COMMAND_UI_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnUpdateSourceType)
249 ON_COMMAND (ID_EDIT_MATCHBRACE, OnMatchBrace)
250 ON_UPDATE_COMMAND_UI (ID_EDIT_MATCHBRACE, OnUpdateMatchBrace)
251 ON_COMMAND (ID_EDIT_GOTO, OnEditGoTo)
252 ON_UPDATE_COMMAND_UI (ID_VIEW_TOGGLE_SRC_HDR, OnUpdateToggleSourceHeader)
253 ON_COMMAND (ID_VIEW_TOGGLE_SRC_HDR, OnToggleSourceHeader)
254 ON_UPDATE_COMMAND_UI (ID_VIEW_TOPMARGIN, OnUpdateTopMargin)
255 ON_COMMAND (ID_VIEW_TOPMARGIN, OnTopMargin)
256 ON_UPDATE_COMMAND_UI (ID_VIEW_SELMARGIN, OnUpdateSelMargin)
257 ON_COMMAND (ID_VIEW_SELMARGIN, OnSelMargin)
258 ON_UPDATE_COMMAND_UI (ID_VIEW_WORDWRAP, OnUpdateWordWrap)
259 ON_COMMAND (ID_VIEW_WORDWRAP, OnWordWrap)
260 ON_COMMAND (ID_FORCE_REDRAW, OnForceRedraw)
262 // incremental search
263 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnEditFindIncrementalForward)
264 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnEditFindIncrementalBackward)
265 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnUpdateEditFindIncrementalForward)
266 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnUpdateEditFindIncrementalBackward)
268 ON_COMMAND (ID_EDIT_TOGGLE_COLUMNSELECTION, OnToggleColumnSelection)
271 #define EXPAND_PRIMITIVE(impl, func) \
272 void CCrystalTextView::On##func() { m_bRectangularSelection = false; impl(false); } \
273 void CCrystalTextView::OnExt##func() { impl(true); }
274 EXPAND_PRIMITIVE (MoveLeft, CharLeft)
275 EXPAND_PRIMITIVE (MoveRight, CharRight)
276 EXPAND_PRIMITIVE (MoveWordLeft, WordLeft)
277 EXPAND_PRIMITIVE (MoveWordRight, WordRight)
278 EXPAND_PRIMITIVE (MoveUp, LineUp)
279 EXPAND_PRIMITIVE (MoveDown, LineDown)
280 EXPAND_PRIMITIVE (MovePgUp, PageUp)
281 EXPAND_PRIMITIVE (MovePgDn, PageDown)
282 EXPAND_PRIMITIVE (MoveHome, Home)
283 EXPAND_PRIMITIVE (MoveEnd, LineEnd)
284 EXPAND_PRIMITIVE (MoveCtrlHome, TextBegin)
285 EXPAND_PRIMITIVE (MoveCtrlEnd, TextEnd)
286 #undef EXPAND_PRIMITIVE
288 /////////////////////////////////////////////////////////////////////////////
289 // CCrystalTextView construction/destruction
291 bool CCrystalTextView::
292 DoSetTextType (CrystalLineParser::TextDefinition *def)
294 m_CurSourceDef = def;
295 SetFlags (def->flags);
298 // EOL is determined from file, tabsize and viewtabs are
299 // global WinMerge settings, selection margin is not needed
300 // and wordwrapping must be false always
302 SetWordWrapping ((def->flags & SRCOPT_WORDWRAP) != false);
303 SetSelectionMargin ((def->flags & SRCOPT_SELMARGIN) != false);
304 SetTabSize (def->tabsize);
305 SetViewTabs ((def->flags & SRCOPT_SHOWTABS) != false);
307 if (def->flags & SRCOPT_EOLNDOS)
311 else if (def->flags & SRCOPT_EOLNUNIX)
315 else if (def->flags & SRCOPT_EOLNMAC)
328 bool CCrystalTextView::
329 SetTextType (const tchar_t* pszExt)
331 m_CurSourceDef = CrystalLineParser::m_SourceDefs;
333 CrystalLineParser::TextDefinition *def = CrystalLineParser::GetTextType (pszExt);
335 return SetTextType (def);
338 bool CCrystalTextView::
339 SetTextType (CrystalLineParser::TextType enuType)
341 CrystalLineParser::TextDefinition *def;
343 m_CurSourceDef = def = CrystalLineParser::m_SourceDefs;
344 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
346 if (def->type == enuType)
348 return SetTextType (def);
354 bool CCrystalTextView::
355 SetTextType (CrystalLineParser::TextDefinition *def)
358 if (m_CurSourceDef != def)
359 return DoSetTextType (def);
365 void CCrystalTextView::
368 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
371 CString key = AfxGetApp ()->m_pszRegistryKey;
372 key += _T("\\") EDITPAD_SECTION;
373 if (reg.Open (HKEY_CURRENT_USER, key, KEY_READ))
375 reg.LoadNumber (_T ("DefaultEncoding"), (DWORD*) &CCrystalTextBuffer::m_nDefaultEncoding);
376 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
379 if (reg1.Open (reg.hKey, def->name, KEY_READ))
381 reg1.LoadString (_T ("Extensions"), def->exts, _countof (def->exts));
382 reg1.LoadNumber (_T ("Flags"), reinterpret_cast<DWORD *>(&def->flags));
383 // reg1.LoadNumber (_T ("TabSize"), &def->tabsize);
384 reg1.LoadString (_T ("OpenComment"), def->opencomment, _countof (def->opencomment));
385 reg1.LoadString (_T ("CloseComment"), def->closecomment, _countof (def->closecomment));
386 reg1.LoadString (_T ("CommentLine"), def->commentline, _countof (def->commentline));
387 reg1.LoadNumber (_T ("DefaultEncoding"), reinterpret_cast<DWORD *>(&def->encoding));
390 bFontLoaded = reg.LoadBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont));
396 CWindowDC dc (CWnd::GetDesktopWindow ());
397 NONCLIENTMETRICS info{ sizeof(info) };
398 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
399 memcpy (&m_LogFont, &info.lfMessageFont, sizeof (LOGFONT));
400 m_LogFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
401 m_LogFont.lfWeight = FW_NORMAL;
402 m_LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
403 m_LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
404 m_LogFont.lfQuality = DEFAULT_QUALITY;
405 m_LogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
406 tc::tcslcpy (m_LogFont.lfFaceName, _T ("Courier New"));
410 void CCrystalTextView::
413 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
415 CString key = AfxGetApp ()->m_pszRegistryKey;
416 key += _T("\\") EDITPAD_SECTION;
417 if (reg.Create (HKEY_CURRENT_USER, key, KEY_WRITE))
419 VERIFY (reg.SaveNumber (_T ("DefaultEncoding"), (DWORD) CCrystalTextBuffer::m_nDefaultEncoding));
420 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
423 if (reg1.Create (reg.hKey, def->name, KEY_WRITE))
425 VERIFY (reg1.SaveString (_T ("Extensions"), def->exts));
426 VERIFY (reg1.SaveNumber (_T ("Flags"), def->flags));
427 // VERIFY (reg1.SaveNumber (_T ("TabSize"), def->tabsize));
428 VERIFY (reg1.SaveString (_T ("OpenComment"), def->opencomment));
429 VERIFY (reg1.SaveString (_T ("CloseComment"), def->closecomment));
430 VERIFY (reg1.SaveString (_T ("CommentLine"), def->commentline));
431 VERIFY (reg1.SaveNumber (_T ("DefaultEncoding"), def->encoding));
434 VERIFY (reg.SaveBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont)));
438 CCrystalTextView::CCrystalTextView ()
440 , m_pFindTextDlg(nullptr)
441 , m_CurSourceDef(nullptr)
442 , m_dwLastDblClickTime(0)
444 , m_pszMatched(nullptr)
445 , m_bTopMargin(false)
447 , m_bViewLineNumbers(false)
449 , m_bHideLines(false)
450 , m_bLastSearch(false)
451 , m_bBookmarkExist(false)
452 , m_bSingle(false) // needed to be set in descendat classes
453 , m_bRememberLastPos(false)
455 , m_nLastLineIndexCalculatedSubLineIndex(-1)
457 , m_pTextBuffer(nullptr)
458 , m_pCacheBitmap(nullptr)
459 , m_pszLastFindWhat(nullptr)
460 , m_dwLastSearchFlags(0)
461 , m_bMultipleSearch(false)
462 , m_bCursorHidden(false)
467 , m_bDistinguishEols(false)
470 , m_pMarkers(nullptr)
471 , m_panSubLines(new std::vector<int>())
472 , m_panSubLineIndexCache(new std::vector<int>())
473 , m_pstrIncrementalSearchString(new CString)
474 , m_pstrIncrementalSearchStringOld(new CString)
475 , m_ParseCookies(new vector<uint32_t>)
476 , m_pnActualLineLength(new vector<int>)
480 , m_lfSavedBaseFont{}
482 , m_pPrintFont(nullptr)
484 , m_bChWidthsCalculated{}
485 , m_iChDoubleWidthFlags{}
487 , m_bPreparingToDrag(false)
488 , m_bDraggingText(false)
489 , m_bDragSelection(false)
490 , m_bWordSelection(false)
491 , m_bLineSelection(false)
492 , m_bRectangularSelection(false)
493 , m_bColumnSelection(false)
496 , m_nLastFindWhatLen(0)
498 , m_nPrintLineHeight(0)
499 , m_bPrintFooter(false)
500 , m_bPrintHeader(false)
505 , m_bSmoothScroll(false)
506 , m_bVertScrollBarLocked(false)
507 , m_bHorzScrollBarLocked(false)
508 , m_bShowInactiveSelection(false)
509 , m_bDisableDragAndDrop(false)
510 , m_bIncrementalSearchForward(false)
511 , m_bIncrementalSearchBackward(false)
512 , m_bIncrementalFound(false)
514 , m_nRenderingMode(s_nRenderingModeDefault)
515 , m_pCrystalRendererSaved(nullptr)
516 , m_nColumnResizing(-1)
517 , m_nLineNumberUsedAsHeaders(-1)
520 if (m_nRenderingMode == RENDERING_MODE::GDI)
521 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
523 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(static_cast<int>(m_nRenderingMode)));
525 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
528 m_panSubLines->reserve(4096);
529 m_panSubLines->resize(0);
530 m_panSubLineIndexCache->reserve (4096);
531 m_panSubLineIndexCache->resize ( 0);
534 CCrystalTextView::ResetView ();
535 SetTextType (CrystalLineParser::SRC_PLAIN);
538 CCrystalTextView::~CCrystalTextView ()
540 ASSERT (m_hAccel == nullptr);
541 ASSERT (m_pCacheBitmap == nullptr);
542 ASSERT (m_pTextBuffer == nullptr); // Must be correctly detached
544 delete m_pFindTextDlg;
546 free (m_pszLastFindWhat);
547 m_pszLastFindWhat=nullptr;
552 free(m_pszMatched); // Allocated by tc::tcsdup()
553 m_pszMatched = nullptr;
556 delete m_panSubLines;
557 m_panSubLines = nullptr;
559 delete m_panSubLineIndexCache;
560 m_panSubLineIndexCache = nullptr;
562 delete m_pstrIncrementalSearchString;
563 m_pstrIncrementalSearchString = nullptr;
565 delete m_pstrIncrementalSearchStringOld;
566 m_pstrIncrementalSearchStringOld = nullptr;
569 ASSERT(m_ParseCookies != nullptr);
570 delete m_ParseCookies;
571 m_ParseCookies = nullptr;
572 ASSERT(m_pnActualLineLength != nullptr);
573 delete m_pnActualLineLength;
574 m_pnActualLineLength = nullptr;
575 if (m_pMarkers != nullptr)
576 m_pMarkers->DeleteView(this);
579 BOOL CCrystalTextView::
580 PreCreateWindow (CREATESTRUCT & cs)
582 CWnd *pParentWnd = CWnd::FromHandlePermanent (cs.hwndParent);
583 if (pParentWnd == nullptr || !pParentWnd->IsKindOf (RUNTIME_CLASS (CSplitterWnd)))
585 // View must always create its own scrollbars,
586 // if only it's not used within splitter
588 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
589 // we do not need a horizontal scroll bar, if we wrap the lines
590 cs.style|= WS_VSCROLL;
592 cs.style |= (WS_HSCROLL | WS_VSCROLL);
594 cs.style |= (WS_HSCROLL | WS_VSCROLL);
598 cs.lpszClass = AfxRegisterWndClass (CS_DBLCLKS);
599 return CView::PreCreateWindow (cs);
603 /////////////////////////////////////////////////////////////////////////////
604 // CCrystalTextView drawing
606 std::pair<CEPoint, CEPoint> CCrystalTextView::
610 return { m_ptDrawSelStart, m_ptDrawSelEnd };
613 bool CCrystalTextView::
614 GetColumnSelection (int nLineIndex, int & nSelBegin, int & nSelEnd)
616 int nSelTop, nSelBottom;
617 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
619 nSelTop = m_ptDrawSelStart.y;
620 nSelBottom = m_ptDrawSelEnd.y;
624 nSelTop = m_ptDrawSelEnd.y;
625 nSelBottom = m_ptDrawSelStart.y;
628 if (nSelTop > nLineIndex || nLineIndex > nSelBottom)
636 int nStartCharPos = CalculateActualOffset (m_ptDrawSelStart.y, m_ptDrawSelStart.x, true);
637 int nEndCharPos = CalculateActualOffset (m_ptDrawSelEnd.y, m_ptDrawSelEnd.x, true);
638 int nLeftCharPos, nRightCharPos;
639 if (nStartCharPos > nEndCharPos)
641 nLeftCharPos = nEndCharPos;
642 nRightCharPos = nStartCharPos;
646 nLeftCharPos = nStartCharPos;
647 nRightCharPos = nEndCharPos;
649 if (nRightCharPos < m_nIdealCharPos)
650 nRightCharPos = m_nIdealCharPos;
651 nSelBegin = ApproxActualOffset (nLineIndex, nLeftCharPos);
652 nSelEnd = ApproxActualOffset (nLineIndex, nRightCharPos);
657 void CCrystalTextView::
658 GetFullySelectedLines(int & firstLine, int & lastLine)
660 auto [ptStart, ptEnd] = GetSelection ();
663 firstLine = ptStart.y;
665 firstLine = ptStart.y + 1;
666 if (ptEnd.x == GetLineLength(ptEnd.y))
669 lastLine = ptEnd.y-1;
672 CCrystalTextBuffer *CCrystalTextView::
679 * @brief : Get the line length, for cursor movement
681 * @note : there are at least 4 line lengths :
682 * - number of characters (memory, no EOL)
683 * - number of characters (memory, with EOL)
684 * - number of characters for cursor position (tabs are expanded, no EOL)
685 * - number of displayed characters (tabs are expanded, with EOL)
686 * Corresponding functions :
688 * - GetFullLineLength
689 * - GetLineActualLength
690 * - ExpandChars (returns the line to be displayed as a CString)
692 int CCrystalTextView::
693 GetLineActualLength (int nLineIndex)
695 const int nLineCount = GetLineCount ();
696 ASSERT (nLineCount > 0);
697 ASSERT (nLineIndex >= 0 && nLineIndex < nLineCount);
698 if (!m_pnActualLineLength->size())
700 m_pnActualLineLength->assign(nLineCount, -1);
703 if ((*m_pnActualLineLength)[nLineIndex] != - 1)
704 return (*m_pnActualLineLength)[nLineIndex];
706 // Actual line length is not determined yet, let's calculate a little
707 int nActualLength = 0;
708 int nLength = GetLineLength (nLineIndex);
711 const tchar_t* pszChars = GetLineChars (nLineIndex);
712 const int nTabSize = GetTabSize ();
713 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
714 switch ( GetTextLayoutMode ())
716 case TEXTLAYOUT_TABLE_NOWORDWRAP:
717 case TEXTLAYOUT_TABLE_WORDWRAP:
719 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
720 int nColumnTotalWidth = 0;
721 bool bInQuote = false;
722 const int sep = m_pTextBuffer->GetFieldDelimiter ();
723 const int quote = m_pTextBuffer->GetFieldEnclosure ();
724 for (int i = 0, nColumn = 0; i < nLength; i = pIterChar->next())
726 tchar_t c = pszChars[i];
727 if (!bInQuote && c == sep)
729 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
730 nActualLength = nColumnTotalWidth;
735 bInQuote = !bInQuote;
739 nActualLength += GetCharCellCountFromChar (pszChars + i);
740 if (nColumn < nColumnCount && nActualLength > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
741 nActualLength = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
748 for (int i = 0; i < nLength; i = pIterChar->next())
750 tchar_t c = pszChars[i];
752 nActualLength += (nTabSize - nActualLength % nTabSize);
754 nActualLength += GetCharCellCountFromChar(pszChars + i);
760 (*m_pnActualLineLength)[nLineIndex] = nActualLength;
761 return nActualLength;
764 void CCrystalTextView::
765 ScrollToChar (int nNewOffsetChar, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
768 // no horizontal scrolling, when word wrapping is enabled
769 if (GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
773 // For now, ignoring bNoSmoothScroll and m_bSmoothScroll
774 if (m_nOffsetChar != nNewOffsetChar)
776 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
777 m_nOffsetChar = nNewOffsetChar;
779 GetClientRect (&rcScroll);
780 rcScroll.left += GetMarginWidth ();
781 CRect rcTopMargin(rcScroll.left, rcScroll.top, rcScroll.right, GetTopMarginHeight());
782 InvalidateRect (&rcTopMargin); // Make sure the ruler is drawn correctly when scrolling horizontally
783 ScrollWindow (nScrollChars * GetCharWidth (), 0, &rcScroll, &rcScroll);
786 RecalcHorzScrollBar (true);
791 * @brief Scroll view to given line.
792 * Scrolls view so that given line is first line in the view. We limit
793 * scrolling so that there is only one empty line visible after the last
794 * line at max. So we don't allow user to scroll last line being at top or
795 * even at middle of the screen. This is how many editors behave and I
796 * (Kimmo) think it is good for us too.
797 * @param [in] nNewTopSubLine New top line for view.
798 * @param [in] bNoSmoothScroll if true don't use smooth scrolling.
799 * @param [in] bTrackScrollBar if true scrollbar is updated after scroll.
801 void CCrystalTextView::ScrollToSubLine( int nNewTopSubLine,
802 bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
804 if (m_nTopSubLine != nNewTopSubLine)
807 GetClientRect (&rcScroll);
809 if (m_pTextBuffer->GetTableEditing () &&
810 ((m_nTopSubLine > 0 && nNewTopSubLine == 0) || (m_nTopSubLine == 0 && nNewTopSubLine > 0)))
812 CRect rcTopMargin(rcScroll.left, rcScroll.top, rcScroll.right, GetTopMarginHeight ());
813 InvalidateRect (&rcTopMargin);
816 rcScroll.top += GetTopMarginHeight ();
818 if (bNoSmoothScroll || ! m_bSmoothScroll)
820 // Limit scrolling so that we show one empty line at end of file
821 const int nScreenLines = GetScreenLines();
822 const int nLineCount = GetSubLineCount();
823 if (nNewTopSubLine > (nLineCount - nScreenLines))
825 nNewTopSubLine = nLineCount - nScreenLines;
826 if (nNewTopSubLine < 0)
830 const int nScrollLines = m_nTopSubLine - nNewTopSubLine;
831 m_nTopSubLine = nNewTopSubLine;
832 // OnDraw() uses m_nTopLine to determine topline
834 GetLineBySubLine(m_nTopSubLine, m_nTopLine, dummy);
835 ScrollWindow(0, nScrollLines * GetLineHeight(), rcScroll, rcScroll);
839 RecalcVertScrollBar(true);
840 InvalidateHorzScrollBar ();
845 // Do smooth scrolling
846 int nLineHeight = GetLineHeight();
847 if (m_nTopSubLine > nNewTopSubLine)
849 int nIncrement = (m_nTopSubLine - nNewTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
850 while (m_nTopSubLine != nNewTopSubLine)
852 int nTopSubLine = m_nTopSubLine - nIncrement;
853 if (nTopSubLine < nNewTopSubLine)
854 nTopSubLine = nNewTopSubLine;
855 const int nScrollLines = nTopSubLine - m_nTopSubLine;
856 m_nTopSubLine = nTopSubLine;
857 ScrollWindow(0, - nLineHeight * nScrollLines, rcScroll, rcScroll);
861 RecalcVertScrollBar(true);
862 InvalidateHorzScrollBar ();
868 int nIncrement = (nNewTopSubLine - m_nTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
869 while (m_nTopSubLine != nNewTopSubLine)
871 int nTopSubLine = m_nTopSubLine + nIncrement;
872 if (nTopSubLine > nNewTopSubLine)
873 nTopSubLine = nNewTopSubLine;
874 const int nScrollLines = nTopSubLine - m_nTopSubLine;
875 m_nTopSubLine = nTopSubLine;
876 ScrollWindow(0, - nLineHeight * nScrollLines, rcScroll, rcScroll);
880 RecalcVertScrollBar(true);
881 InvalidateHorzScrollBar ();
887 GetLineBySubLine( m_nTopSubLine, m_nTopLine, nDummy );
891 void CCrystalTextView::
892 ScrollToLine (int nNewTopLine, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
894 if( m_nTopLine != nNewTopLine )
895 ScrollToSubLine( GetSubLineIndex( nNewTopLine ), bNoSmoothScroll, bTrackScrollBar );
898 /** Append szadd to string str, and advance position curpos */
899 static void AppendStringAdv(CString & str, int & curpos, const tchar_t* szadd)
902 curpos += (int) tc::tcslen(szadd);
905 /** Append escaped control char to string str, and advance position curpos */
906 static void AppendEscapeAdv(CString & str, int & curpos, tchar_t c)
908 int curlen = str.GetLength();
909 tchar_t* szadd = str.GetBufferSetLength(curlen + 3) + curlen;
910 curpos += wsprintf(szadd, _T("\t%02X"), static_cast<int>(c));
913 static COLORREF GetIntermediateColor (COLORREF a, COLORREF b)
915 float ratio = 0.333f;
916 const int R = static_cast<int>((GetRValue(a) - GetRValue(b)) * ratio) + GetRValue(b);
917 const int G = static_cast<int>((GetGValue(a) - GetGValue(b)) * ratio) + GetGValue(b);
918 const int B = static_cast<int>((GetBValue(a) - GetBValue(b)) * ratio) + GetBValue(b);
922 int CCrystalTextView::
923 ExpandChars (int nLineIndex, int nOffset, int nCount, CString & line, int nActualOffset)
926 // Request whitespace characters for codepage ACP
927 // because that is the codepage used by ExtTextOut
928 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE::GDI);
935 const int nTabSize = GetTabSize ();
937 const tchar_t* pszChars = GetLineChars(nLineIndex);
939 int nLength = nCount;
941 for (int i = 0; i < nLength; i++)
943 tchar_t c = pszChars[i];
945 nCount += nTabSize - 1;
946 else if (c >= _T('\x00') && c <= _T('\x1F') && c != _T('\r') && c != _T('\n'))
950 // Preallocate line buffer, to avoid reallocations as we add characters
951 line.GetBuffer(nCount + 1); // at least this many characters
952 line.ReleaseBuffer(0);
954 const bool bTableEditing = m_pTextBuffer->GetTableEditing ();
956 if (nCount > nLength || m_bViewTabs || m_bViewEols)
958 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
959 for (int i = 0, next = 0; i < nLength; i = next)
961 next = pIterChar->next();
962 if (pszChars[i] == _T('\t'))
964 int nSpaces = bTableEditing ? 1 : (nTabSize - (nActualOffset + nCurPos) % nTabSize);
967 AppendStringAdv(line, nCurPos, lpspc->c_tab);
977 else if (pszChars[i] == ' ' && m_bViewTabs)
978 AppendStringAdv(line, nCurPos, lpspc->c_space);
979 else if (pszChars[i] == '\r' || pszChars[i] == '\n')
983 if (pszChars[i] == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
985 // Ignore \n after \r
989 if (i < nLength - 1 && pszChars[i] == '\r' && pszChars[i+1] == '\n' && m_bDistinguishEols)
991 AppendStringAdv(line, nCurPos, lpspc->c_eol);
994 else if (pszChars[i] == '\r' && m_bDistinguishEols)
995 AppendStringAdv(line, nCurPos, lpspc->c_cr);
996 else if (pszChars[i] == '\n' && m_bDistinguishEols)
997 AppendStringAdv(line, nCurPos, lpspc->c_lf);
1000 AppendStringAdv(line, nCurPos, lpspc->c_eol);
1005 else if (pszChars[i] >= _T('\x00') && pszChars[i] <= _T('\x1F'))
1007 AppendEscapeAdv(line, nCurPos, pszChars[i]);
1011 nCurPos += GetCharCellCountFromChar(pszChars + i);
1012 for (; i < next; ++i)
1013 line += pszChars[i];
1019 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
1020 for (int i1=0, next=0; i1<nLength; i1 = next)
1022 next = pIterChar->next();
1023 nCurPos += GetCharCellCountFromChar(pszChars + i1);
1024 for (; i1 < next; ++i1)
1025 line += pszChars[i1];
1031 int CCrystalTextView::
1032 ExpandCharsTableEditingNoWrap(int nLineIndex, int nOffset, int nCount, CString& line, int nActualOffset)
1034 if (m_pTextBuffer == nullptr || nCount <= 0)
1037 const tchar_t* pszChars = GetLineChars(nLineIndex);
1039 // Request whitespace characters for codepage ACP
1040 // because that is the codepage used by ExtTextOut
1041 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE::GDI);
1043 int nLength = nCount;
1045 int nColumnCount = 0;
1046 int nCurColumn = -1;
1047 int nColumnTotalWidth = 0;
1048 int nColumnBegin = 0;
1049 int nColumnTotalWidthBegin = 0;
1050 const int nLineLength = GetFullLineLength (nLineIndex);
1051 const int nTabSize = GetTabSize ();
1052 const int sep = m_pTextBuffer->GetFieldDelimiter ();
1053 const int quote = m_pTextBuffer->GetFieldEnclosure ();
1054 const int eollen = nLineLength - GetLineLength (nLineIndex);
1055 bool bInQuote = false;
1056 bool bInQuoteBegin = false;
1058 for (int i = 0; i < nLineLength; i++)
1060 tchar_t c = pszChars[i];
1063 nColumnBegin = nColumn;
1064 nColumnTotalWidthBegin = nColumnTotalWidth;
1065 bInQuoteBegin = bInQuote;
1067 if (nLineIndex == m_ptCursorPos.y && i == m_ptCursorPos.x)
1068 nCurColumn = nColumn;
1069 if (!bInQuote && c == sep)
1071 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
1072 nCount = nColumnTotalWidth;
1076 else if (c == quote)
1078 bInQuote = !bInQuote;
1080 else if (c >= '\x00' && c <= '\x1F' && c != '\r' && c != '\n')
1083 nColumnCount = nColumn + 1;
1085 // Preallocate line buffer, to avoid reallocations as we add characters
1086 line.GetBuffer (nCount + 1); // at least this many characters
1087 line.ReleaseBuffer (0);
1088 int nCurPos = nActualOffset;
1090 pszChars += nOffset;
1092 int curColumnTextCellWidth = 0;
1093 bool beforeCursorPos = (nLineIndex == m_ptCursorPos.y && nOffset < m_ptCursorPos.x);
1094 CString curColumnText;
1095 std::vector<std::pair<int, int>> curColumnByteLenCellWidth;
1096 nColumn = nColumnBegin;
1097 nColumnTotalWidth = nColumnTotalWidthBegin;
1098 bInQuote = bInQuoteBegin;
1099 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator (pszChars, nLineLength - nOffset);
1100 auto nextColumnDistance = [&](int nCurPos)
1102 return (nColumn == nColumnCount - 1) ? INT_MAX : nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
1104 auto appendChars = [&](int i, int next, int pos, CString& text, int& textwidth)
1106 tchar_t c = pszChars[i];
1107 if ((c == '\r' || c == '\n') && i >= nLineLength - nOffset - eollen)
1111 if (c == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
1113 // Ignore \n after \r
1117 int prevtextwidth = textwidth;
1118 if (c == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
1119 AppendStringAdv (text, textwidth, lpspc->c_eol);
1120 else if (c == '\r' && m_bDistinguishEols)
1121 AppendStringAdv (text, textwidth, lpspc->c_cr);
1122 else if (c == '\n' && m_bDistinguishEols)
1124 if (i == 0 || pszChars[i - 1] != '\r')
1125 AppendStringAdv (text, textwidth, lpspc->c_lf);
1128 AppendStringAdv (text, textwidth, lpspc->c_eol);
1129 if (textwidth - prevtextwidth > 0)
1130 curColumnByteLenCellWidth.push_back ({ textwidth - prevtextwidth, textwidth - prevtextwidth});
1137 if (sep != '\t' || bInQuote)
1140 if (nSpaces > nextColumnDistance (pos))
1141 nSpaces = nextColumnDistance (pos);
1143 if (nSpaces >= 1 && m_bViewTabs)
1145 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1146 AppendStringAdv (text, textwidth, lpspc->c_tab);
1151 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1157 else if (c == ' ' && m_bViewTabs)
1159 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1160 AppendStringAdv (text, textwidth, lpspc->c_space);
1162 else if (c >= '\x00' && c <= '\x1F')
1164 curColumnByteLenCellWidth.push_back ({ 3, 3 });
1165 AppendEscapeAdv (text, textwidth, c);
1166 if (c == '\r' && pszChars[i + 1] == '\n')
1167 AppendEscapeAdv (text, textwidth, pszChars[i + 1]);
1171 int nLen = GetCharCellCountFromChar (pszChars + i);
1172 curColumnByteLenCellWidth.push_back ({ nLen, next - i });
1174 for (; i < next; ++i)
1175 text += pszChars[i];
1177 if (!bInQuote && c == sep)
1179 int nSpaces = nextColumnDistance (pos + 1);
1186 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1191 for (i = 0, next = 0; i < nLength; i = next)
1193 next = pIterChar->next ();
1194 tchar_t c = pszChars[i];
1196 bInQuote = !bInQuote;
1197 int nLen = GetCharCellCountFromChar (pszChars + i);
1198 if (nColumn == nCurColumn && beforeCursorPos)
1200 appendChars (i, next, nCurPos + curColumnTextCellWidth, curColumnText, curColumnTextCellWidth);
1201 if (next + nOffset == m_ptCursorPos.x || next >= nLength)
1203 int curColumnTextLenAppended = 0;
1204 int curColumnTextCellWidthAppended = 0;
1205 if (next + nOffset == m_ptCursorPos.x)
1206 beforeCursorPos = false;
1209 int curColumnTextLenSaved = curColumnText.GetLength();
1210 int curColumnTextCellWidthSaved = curColumnTextCellWidth;
1212 for (; i < nLineLength - nOffset && nColumn == nCurColumn && next + nOffset < m_ptCursorPos.x; i = next)
1214 next = pIterChar->next ();
1217 bInQuote = !bInQuote;
1218 nLen = GetCharCellCountFromChar (pszChars + i);
1219 appendChars (i, next, nCurPos + curColumnTextCellWidth, curColumnText, curColumnTextCellWidth);
1221 curColumnTextLenAppended = curColumnText.GetLength() - curColumnTextLenSaved;
1222 curColumnTextCellWidthAppended = curColumnTextCellWidth - curColumnTextCellWidthSaved;
1224 if (curColumnTextCellWidth > nextColumnDistance (nCurPos))
1226 for (size_t k = 0; k < curColumnByteLenCellWidth.size () && curColumnTextCellWidth > nextColumnDistance (nCurPos); ++k)
1228 curColumnTextCellWidth -= curColumnByteLenCellWidth[k].first;
1229 curColumnText = curColumnText.Mid (curColumnByteLenCellWidth[k].second);
1231 int nSpaces = nextColumnDistance (nCurPos) - curColumnTextCellWidth;
1234 CString spaces (' ', nSpaces);
1235 curColumnText.Insert (0, spaces);
1236 curColumnTextCellWidth = m_pTextBuffer->GetColumnWidth (nColumn);
1239 if (curColumnTextLenAppended > 0)
1241 if (curColumnTextLenAppended < curColumnText.GetLength())
1243 line += curColumnText.Left(curColumnText.GetLength() - curColumnTextLenAppended);
1244 nCurPos += curColumnTextCellWidth - curColumnTextCellWidthAppended;
1249 line += curColumnText;
1250 nCurPos += curColumnTextCellWidth;
1256 if (nLen <= nextColumnDistance (nCurPos))
1258 appendChars (i, next, nCurPos, line, nCurPos);
1259 curColumnByteLenCellWidth.clear ();
1263 int nSpaces = nextColumnDistance (nCurPos);
1270 if (!bInQuote && c == sep)
1272 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1278 return nCurPos - nActualOffset;
1283 * @brief Draw a chunk of text (one color, one line, full or part of line)
1285 * @note In ANSI build, this routine is buggy for multibytes or double-width characters
1287 void CCrystalTextView::
1288 DrawLineHelperImpl (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex,
1289 int nBgColorIndex, COLORREF crText, COLORREF crBkgnd, int nLineIndex, int nOffset, int nCount, int &nActualOffset)
1291 ASSERT (nCount >= 0);
1295 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_NOWORDWRAP)
1296 nActualOffset += ExpandCharsTableEditingNoWrap (nLineIndex, nOffset, nCount, line, nActualOffset);
1298 nActualOffset += ExpandChars (nLineIndex, nOffset, nCount, line, nActualOffset);
1299 const int lineLen = line.GetLength();
1300 const int nCharWidth = GetCharWidth();
1301 const int nCharWidthNarrowed = nCharWidth / 2;
1302 const int nCharWidthWidened = nCharWidth * 2 - nCharWidthNarrowed;
1303 const int nLineHeight = GetLineHeight();
1304 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator((const tchar_t*)line, lineLen);
1306 // i the character index, from 0 to lineLen-1
1309 // Pass if the text begins after the right end of the clipping region
1310 if (ptOrigin.x < rcClip.right && ptOrigin.y < rcClip.bottom)
1312 // Because ExtTextOut is buggy when ptOrigin.x < - 4095 * charWidth
1313 // or when nCount >= 4095
1314 // and because this is not well documented,
1315 // we decide to do the left & right clipping here
1317 // Update the position after the left clipped characters
1318 // stop for i = first visible character, at least partly
1319 const int clipLeft = rcClip.left - nCharWidth * 2;
1320 for ( ; i < lineLen; i = pIterChar->next())
1322 int pnWidthsCurrent = GetCharCellCountFromChar(static_cast<const tchar_t *>(line) + i) * nCharWidth;
1323 ptOrigin.x += pnWidthsCurrent;
1324 if (ptOrigin.x >= clipLeft)
1326 ptOrigin.x -= pnWidthsCurrent;
1333 //CSize sz = pdc->GetTextExtent(line, nCount);
1334 //ASSERT(sz.cx == m_nCharWidth * nCount);
1339 // We have to draw some characters
1343 int nWidth = rcClip.right - ptOrigin.x;
1345 // Table of charwidths as CCrystalEditor thinks they are
1346 // Seems that CrystalEditor's and ExtTextOut()'s charwidths aren't
1347 // same with some fonts and text is drawn only partially
1348 // if this table is not used.
1349 vector<int> nWidths(nWidth / nCharWidth * 2 + 2);
1350 bool bdisphex = false;
1351 for (int next = i; i < lineLen && nSumWidth < nWidth ; i = next)
1353 if (line[i] == '\t') // Escape sequence leadin?
1356 // Substitute a space narrowed to half the width of a character cell.
1358 size_t idx = i - ibegin;
1359 if (idx >= nWidths.size())
1360 nWidths.resize(nWidths.size() * 2);
1361 nSumWidth += nWidths[idx] = nCharWidthNarrowed;
1362 // 1st hex digit has normal width.
1363 idx = pIterChar->next() - ibegin;
1364 if (idx >= nWidths.size())
1365 nWidths.resize(nWidths.size() * 2);
1366 nSumWidth += nWidths[idx] = nCharWidth;
1367 // 2nd hex digit is padded by half the width of a character cell.
1368 idx = pIterChar->next() - ibegin;
1369 if (idx >= nWidths.size())
1370 nWidths.resize(nWidths.size() * 2);
1371 nSumWidth += nWidths[idx] = nCharWidthWidened;
1375 size_t idx = i - ibegin;
1376 if (idx >= nWidths.size())
1377 nWidths.resize(nWidths.size() * 2);
1378 nSumWidth += nWidths[idx] = GetCharCellCountFromChar(static_cast<const tchar_t *>(line) + i) * nCharWidth;
1380 next = pIterChar->next();
1382 int nCount1 = i - ibegin;
1384 if (ptOrigin.x + nSumWidth > rcClip.left)
1386 COLORREF crText2 = crText;
1387 COLORREF crBkgnd2 = crBkgnd;
1388 if (crText == CLR_NONE || nColorIndex & COLORINDEX_APPLYFORCE)
1389 crText2 = GetColor(nColorIndex);
1390 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1391 crBkgnd2 = GetColor(nBgColorIndex);
1392 if (nColorIndex & COLORINDEX_INTERMEDIATECOLOR)
1393 crText2 = GetIntermediateColor(crText2, crBkgnd2);
1394 m_pCrystalRenderer->SetTextColor(crText2);
1395 m_pCrystalRenderer->SetBkColor(crBkgnd2);
1397 m_pCrystalRenderer->SwitchFont(GetItalic(nColorIndex), GetBold(nColorIndex));
1398 // we are sure to have less than 4095 characters because all the chars are visible
1400 RECT rcTextBlock = {ptOrigin.x, ptOrigin.y, ptOrigin.x + nSumWidth + 2, ptOrigin.y + nLineHeight};
1401 IntersectRect(&rcIntersect, &rcClip, &rcTextBlock);
1402 m_pCrystalRenderer->DrawText(ptOrigin.x, ptOrigin.y, rcIntersect, (const tchar_t*)(line) + ibegin, nCount1, &nWidths[0]);
1405 // Draw rounded rectangles around control characters
1406 m_pCrystalRenderer->PushAxisAlignedClip(rcClip);
1408 for (int j = 0 ; j < nCount1 ; ++j)
1410 // Assume narrowed space is converted escape sequence leadin.
1411 if (line[ibegin + j] == ' ' && nWidths[j] < nCharWidth)
1413 m_pCrystalRenderer->DrawRoundRectangle(x + 2, ptOrigin.y + 1,
1414 x + 3 * nCharWidth - 2, ptOrigin.y + nLineHeight - 1,
1415 nCharWidth / 2, nLineHeight / 2);
1419 m_pCrystalRenderer->PopAxisAlignedClip();
1423 // Update the final position after the visible characters
1424 ptOrigin.x += nSumWidth;
1428 // Update the final position after the right clipped characters
1429 for ( ; i < lineLen; i = pIterChar->next())
1431 ptOrigin.x += GetCharCellCountFromChar(static_cast<const tchar_t *>(line) + i) * nCharWidth;
1436 bool CCrystalTextView::
1437 GetSelectionLeftRight(int nLineIndex, int& nSelLeft, int& nSelRight)
1439 int nLineLength = GetLineLength (nLineIndex);
1442 if ( !m_bRectangularSelection )
1444 if (m_ptDrawSelStart.y > nLineIndex)
1445 nSelLeft = nLineLength;
1446 else if (m_ptDrawSelStart.y == nLineIndex)
1447 nSelLeft = m_ptDrawSelStart.x;
1448 if (m_ptDrawSelEnd.y > nLineIndex)
1449 nSelRight = nLineLength;
1450 else if (m_ptDrawSelEnd.y == nLineIndex)
1451 nSelRight = m_ptDrawSelEnd.x;
1452 return (m_ptDrawSelStart.y <= nLineIndex && nLineIndex <= m_ptDrawSelEnd.y);
1455 return GetColumnSelection (nLineIndex, nSelLeft, nSelRight);
1458 void CCrystalTextView::
1459 DrawLineHelper (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex, int nBgColorIndex,
1460 COLORREF crText, COLORREF crBkgnd,
1461 int nLineIndex, int nOffset, int nCount, int &nActualOffset, CEPoint ptTextPos,
1462 int nSelLeft, int nSelRight)
1466 if (m_bFocused || m_bShowInactiveSelection)
1468 int nSelBegin = std::clamp<int>(nSelLeft - ptTextPos.x, 0, nCount);
1469 int nSelEnd = std::clamp<int>(nSelRight - ptTextPos.x, 0, nCount);
1471 ASSERT (nSelBegin >= 0 && nSelBegin <= nCount);
1472 ASSERT (nSelEnd >= 0 && nSelEnd <= nCount);
1473 ASSERT (nSelBegin <= nSelEnd);
1475 // Draw part of the text before selection
1478 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nSelBegin, nActualOffset);
1480 if (nSelBegin < nSelEnd)
1482 DrawLineHelperImpl (ptOrigin, rcClip,
1483 nColorIndex & ~COLORINDEX_MASK,
1484 nBgColorIndex & ~COLORINDEX_MASK,
1485 GetColor (COLORINDEX_SELTEXT),
1486 GetColor (COLORINDEX_SELBKGND),
1488 nOffset + nSelBegin, nSelEnd - nSelBegin, nActualOffset);
1490 if (nSelEnd < nCount)
1492 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset + nSelEnd, nCount - nSelEnd, nActualOffset);
1497 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset);
1502 void CCrystalTextView::
1503 GetLineColors (int nLineIndex, COLORREF & crBkgnd,
1504 COLORREF & crText, bool & bDrawWhitespace)
1506 lineflags_t dwLineFlags = GetLineFlags (nLineIndex);
1507 bDrawWhitespace = true;
1508 crText = RGB (255, 255, 255);
1509 if (dwLineFlags & LF_EXECUTION)
1511 crBkgnd = RGB (0, 128, 0);
1514 if (dwLineFlags & LF_BREAKPOINT)
1516 crBkgnd = RGB (255, 0, 0);
1519 if (dwLineFlags & LF_INVALID_BREAKPOINT)
1521 crBkgnd = RGB (128, 128, 0);
1526 bDrawWhitespace = false;
1529 DWORD CCrystalTextView::
1530 GetParseCookie (int nLineIndex)
1532 const int nLineCount = GetLineCount ();
1533 if (m_ParseCookies->size() == 0)
1535 // must be initialized to invalid value (DWORD) -1
1536 m_ParseCookies->assign(nLineCount, static_cast<uint32_t>(-1));
1541 if ((*m_ParseCookies)[nLineIndex] != - 1)
1542 return (*m_ParseCookies)[nLineIndex];
1545 while (L >= 0 && (*m_ParseCookies)[L] == - 1)
1550 while (L <= nLineIndex)
1552 unsigned dwCookie = 0;
1554 dwCookie = (*m_ParseCookies)[L - 1];
1555 ASSERT (dwCookie != - 1);
1556 (*m_ParseCookies)[L] = ParseLine (dwCookie, GetLineChars(L), GetLineLength(L), nullptr, nBlocks);
1557 ASSERT ((*m_ParseCookies)[L] != - 1);
1561 return (*m_ParseCookies)[nLineIndex];
1564 std::vector<TEXTBLOCK> CCrystalTextView::
1565 GetAdditionalTextBlocks (int nLineIndex)
1571 void CCrystalTextView::WrapLine( int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1573 // There must be a parser attached to this view
1574 if( m_pParser == nullptr )
1577 m_pParser->WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1581 void CCrystalTextView::WrapLineCached(
1582 int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1584 if( !GetLineVisible (nLineIndex) )
1590 // If the word wrap is not active, there is no breaks in the line
1597 // word wrap is active
1598 if( nLineIndex < m_panSubLines->size () && !anBreaks && (*m_panSubLines)[nLineIndex] > -1 )
1599 // return cached data
1600 nBreaks = (*m_panSubLines)[nLineIndex] - 1;
1603 // recompute line wrap
1605 WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1608 ASSERT( nBreaks > -1 );
1609 if (nLineIndex >= m_panSubLines->size())
1610 m_panSubLines->resize(nLineIndex + 1);
1611 (*m_panSubLines)[nLineIndex] = nBreaks + 1;
1613 // RecalcVertScrollBar();
1618 void CCrystalTextView::InvalidateLineCache( int nLineIndex1, int nLineIndex2 /*= -1*/ )
1620 // invalidate cached sub line index
1621 InvalidateSubLineIndexCache( nLineIndex1 );
1623 // invalidate cached sub line count
1625 if( nLineIndex2 == -1 && nLineIndex1 < m_panSubLines->size () )
1626 for( int i = nLineIndex1; i < m_panSubLines->size (); i++ )
1627 (*m_panSubLines)[i] = -1;
1630 if( nLineIndex1 > nLineIndex2 )
1632 int nStorage = nLineIndex1;
1633 nLineIndex1 = nLineIndex2;
1634 nLineIndex2 = nStorage;
1637 if( nLineIndex1 >= m_panSubLines->size () )
1640 if( nLineIndex2 >= m_panSubLines->size () )
1641 nLineIndex2 = (int) m_panSubLines->size () - 1;
1643 for( int i = nLineIndex1; i <= nLineIndex2; i++ )
1644 if( i >= 0 && i < m_panSubLines->size () )
1645 (*m_panSubLines)[i] = -1;
1650 * @brief Invalidate sub line index cache from the specified index to the end of file.
1651 * @param [in] nLineIndex Index of the first line to invalidate
1653 void CCrystalTextView::InvalidateSubLineIndexCache( int nLineIndex )
1655 if (m_nLastLineIndexCalculatedSubLineIndex > nLineIndex)
1656 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex - 1;
1660 * @brief Invalidate items related screen size.
1662 void CCrystalTextView::InvalidateScreenRect(bool bInvalidateView)
1664 if (m_pCacheBitmap != nullptr)
1666 delete m_pCacheBitmap;
1667 m_pCacheBitmap = nullptr;
1669 m_nScreenChars = -1;
1670 m_nScreenLines = -1;
1671 InvalidateLineCache(0, -1);
1672 if (bInvalidateView)
1675 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
1676 InvalidateVertScrollBar ();
1677 InvalidateHorzScrollBar ();
1682 void CCrystalTextView::DrawScreenLine( CPoint &ptOrigin, const CRect &rcClip,
1683 const std::vector<TEXTBLOCK>& blocks, int &nActualItem,
1684 COLORREF crText, COLORREF crBkgnd, bool bDrawWhitespace,
1685 int nLineIndex, int nOffset, int nCount, int &nActualOffset, CEPoint ptTextPos )
1687 CPoint originalOrigin = ptOrigin;
1688 CPoint ptOriginZeroWidthBlock;
1689 CRect frect = rcClip;
1690 const int nLineLength = GetViewableLineLength( ptTextPos.y );
1691 const int nLineHeight = GetLineHeight();
1692 int nBgColorIndexZeorWidthBlock = COLORINDEX_NONE;
1693 bool bPrevZeroWidthBlock = false;
1694 static const int ZEROWIDTHBLOCK_WIDTH = 2;
1696 frect.top = ptOrigin.y;
1697 frect.bottom = frect.top + nLineHeight;
1699 int nBlockSize = static_cast<int>(blocks.size());
1700 ASSERT( nActualItem < nBlockSize );
1702 int nSelLeft = 0, nSelRight = 0;
1703 GetSelectionLeftRight(ptTextPos.y, nSelLeft, nSelRight);
1705 if( nBlockSize > 0 && nActualItem < nBlockSize - 1 &&
1706 blocks[nActualItem + 1].m_nCharPos >= nOffset &&
1707 blocks[nActualItem + 1].m_nCharPos <= nOffset + nCount )
1709 ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1710 blocks[nActualItem].m_nCharPos <= nLineLength);
1713 for (I = nActualItem; I < blocks.size() - 1 &&
1714 blocks[I + 1].m_nCharPos <= nOffset + nCount; I ++)
1716 const TEXTBLOCK& blk = blocks[I];
1717 ASSERT(blk.m_nCharPos >= 0 && blk.m_nCharPos <= nLineLength);
1719 int nOffsetToUse = (nOffset > blk.m_nCharPos) ?
1720 nOffset : blk.m_nCharPos;
1721 if (blocks[I + 1].m_nCharPos - nOffsetToUse > 0)
1723 int nOldActualOffset = nActualOffset;
1724 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex, crText, crBkgnd, nLineIndex,
1725 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1726 blocks[I + 1].m_nCharPos - nOffsetToUse,
1727 nActualOffset, CEPoint( nOffsetToUse, ptTextPos.y ), nSelLeft, nSelRight);
1728 if (bPrevZeroWidthBlock)
1730 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1731 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock, crText, crBkgnd, nLineIndex,
1732 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1733 blocks[I + 1].m_nCharPos - nOffsetToUse,
1734 nOldActualOffset, CEPoint( nOffsetToUse, ptTextPos.y ), nSelLeft, nSelRight);
1735 bPrevZeroWidthBlock = false;
1740 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1742 int nBgColorIndex = blk.m_nBgColorIndex;
1743 COLORREF clrBkColor;
1744 if (IsInsideSelBlock (CEPoint{nOffsetToUse, ptTextPos.y}))
1745 clrBkColor = GetColor(COLORINDEX_SELBKGND);
1746 else if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1747 clrBkColor = GetColor(nBgColorIndex);
1749 clrBkColor = crBkgnd;
1750 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1751 m_pCrystalRenderer->SetBkColor(clrBkColor);
1752 m_pCrystalRenderer->FillRectangle(rc);
1753 ptOriginZeroWidthBlock = ptOrigin;
1754 nBgColorIndexZeorWidthBlock = blk.m_nBgColorIndex;
1755 bPrevZeroWidthBlock = true;
1758 if (ptOrigin.x > rcClip.right)
1760 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_WORDWRAP)
1762 while (I < blocks.size () - 1 && blocks[I + 1].m_nCharPos <= nOffset + nCount)
1769 nActualItem = static_cast<int>(I);
1771 const TEXTBLOCK& blk = blocks[nActualItem];
1772 ASSERT(blk.m_nCharPos >= 0 &&
1773 blk.m_nCharPos <= nLineLength);
1775 if (nOffset + nCount - blk.m_nCharPos > 0)
1777 int nOldActualOffset = nActualOffset;
1778 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex,
1779 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1780 nOffset + nCount - blk.m_nCharPos,
1781 nActualOffset, CEPoint(blk.m_nCharPos, ptTextPos.y), nSelLeft, nSelRight);
1782 if (bPrevZeroWidthBlock)
1784 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1785 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock,
1786 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1787 nOffset + nCount - blk.m_nCharPos,
1788 nOldActualOffset, CEPoint(blk.m_nCharPos, ptTextPos.y), nSelLeft, nSelRight);
1789 bPrevZeroWidthBlock = false;
1794 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1796 int nBgColorIndex = blk.m_nBgColorIndex;
1797 COLORREF clrBkColor;
1798 if (IsInsideSelBlock (CEPoint{blk.m_nCharPos, ptTextPos.y}))
1799 clrBkColor = GetColor(COLORINDEX_SELBKGND);
1800 else if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1801 clrBkColor = GetColor(nBgColorIndex);
1803 clrBkColor = crBkgnd;
1804 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1805 m_pCrystalRenderer->SetBkColor(clrBkColor);
1806 m_pCrystalRenderer->FillRectangle(rc);
1807 bPrevZeroWidthBlock = true;
1814 ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1815 crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset, ptTextPos, nSelLeft, nSelRight);
1818 // Draw space on the right of the text
1820 frect.left = ptOrigin.x + (bPrevZeroWidthBlock ? ZEROWIDTHBLOCK_WIDTH : 0);
1822 if ((m_bFocused || m_bShowInactiveSelection)
1823 && !m_bRectangularSelection
1824 && IsInsideSelBlock(CEPoint(nLineLength, ptTextPos.y))
1825 && (nOffset + nCount) == nLineLength )
1827 if (frect.left >= rcClip.left)
1829 const int nCharWidth = GetCharWidth();
1830 CRect rc(frect.left, frect.top, frect.left + nCharWidth, frect.bottom);
1831 m_pCrystalRenderer->SetBkColor(GetColor(COLORINDEX_SELBKGND));
1832 m_pCrystalRenderer->FillRectangle(rc);
1833 frect.left += nCharWidth;
1836 if (frect.left < rcClip.left)
1837 frect.left = rcClip.left;
1839 if (frect.right > frect.left)
1841 m_pCrystalRenderer->SetBkColor(bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
1842 m_pCrystalRenderer->FillRectangle(frect);
1845 // set origin to beginning of next screen line
1846 ptOrigin.x = originalOrigin.x;
1847 ptOrigin.y+= nLineHeight;
1851 std::vector<TEXTBLOCK> CCrystalTextView::
1852 MergeTextBlocks (const std::vector<TEXTBLOCK>& blocks1, const std::vector<TEXTBLOCK>& blocks2) const
1856 std::vector<TEXTBLOCK> mergedBlocks(blocks1.size() + blocks2.size());
1858 for (i = 0, j = 0, k = 0; ; k++)
1860 if (i >= blocks1.size() && j >= blocks2.size())
1864 else if ((i < blocks1.size()&& j < blocks2.size()) &&
1865 (blocks1[i].m_nCharPos == blocks2[j].m_nCharPos))
1867 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1868 if ((blocks2[j].m_nColorIndex & ~COLORINDEX_MASK) == COLORINDEX_NONE)
1869 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex | (blocks2[j].m_nColorIndex & COLORINDEX_MASK);
1871 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1872 if (blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1873 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1875 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1879 else if (j >= blocks2.size() || (i < blocks1.size() &&
1880 blocks1[i].m_nCharPos < blocks2[j].m_nCharPos))
1882 mergedBlocks[k].m_nCharPos = blocks1[i].m_nCharPos;
1883 if (blocks2.size() == 0 || (blocks2[j - 1].m_nColorIndex & ~COLORINDEX_MASK) == COLORINDEX_NONE)
1884 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex |
1885 (blocks2.size() == 0 ? 0 : (blocks2[j - 1].m_nColorIndex & COLORINDEX_MASK));
1887 mergedBlocks[k].m_nColorIndex = blocks2[j - 1].m_nColorIndex;
1888 if (blocks2.size() == 0 || blocks2[j - 1].m_nBgColorIndex == COLORINDEX_NONE)
1889 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1891 mergedBlocks[k].m_nBgColorIndex = blocks2[j - 1].m_nBgColorIndex;
1894 else if (i >= blocks1.size() || (j < blocks2.size() && blocks1[i].m_nCharPos > blocks2[j].m_nCharPos))
1896 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1897 if (i > 0 && (blocks2[j].m_nColorIndex & ~COLORINDEX_MASK) == COLORINDEX_NONE)
1898 mergedBlocks[k].m_nColorIndex = blocks1[i - 1].m_nColorIndex | (blocks2[j].m_nColorIndex & COLORINDEX_MASK);
1900 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1901 if (i > 0 && blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1902 mergedBlocks[k].m_nBgColorIndex = blocks1[i - 1].m_nBgColorIndex;
1904 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1910 for (i = 0; i < k; ++i)
1913 (mergedBlocks[i - 1].m_nColorIndex != mergedBlocks[i].m_nColorIndex ||
1914 mergedBlocks[i - 1].m_nBgColorIndex != mergedBlocks[i].m_nBgColorIndex))
1916 mergedBlocks[j] = mergedBlocks[i];
1921 mergedBlocks.resize(j);
1922 return mergedBlocks;
1925 std::vector<TEXTBLOCK>
1926 CCrystalTextView::GetWhitespaceTextBlocks(int nLineIndex) const
1928 const tchar_t *pszChars = GetLineChars(nLineIndex);
1929 int nLineLength = GetLineLength(nLineIndex);
1930 std::vector<TEXTBLOCK> blocks((nLineLength + 1) * 3);
1931 blocks[0].m_nCharPos = 0;
1932 blocks[0].m_nColorIndex = COLORINDEX_NONE;
1933 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1935 if (pszChars != nullptr)
1937 for (int i = 0; i < nLineLength; ++i)
1939 if (pszChars[i] == ' ' || pszChars[i] == '\t')
1941 blocks[nBlocks].m_nCharPos = i;
1942 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE | COLORINDEX_INTERMEDIATECOLOR;
1943 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1945 while (i < nLineLength && (pszChars[i] == ' ' || pszChars[i] == '\t'))
1947 if (i < nLineLength)
1949 blocks[nBlocks].m_nCharPos = i;
1950 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1951 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1957 if (nBlocks == 0 || blocks[nBlocks].m_nColorIndex == COLORINDEX_NONE)
1959 blocks[nBlocks].m_nCharPos = nLineLength;
1960 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE | COLORINDEX_INTERMEDIATECOLOR;
1961 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1964 blocks.resize(nBlocks);
1968 std::vector<TEXTBLOCK>
1969 CCrystalTextView::GetMarkerTextBlocks(int nLineIndex) const
1971 std::vector<TEXTBLOCK> allblocks;
1972 if (!m_pMarkers->GetEnabled())
1975 int nLength = GetViewableLineLength (nLineIndex);
1977 for (const auto& marker : m_pMarkers->GetMarkers())
1979 if (!marker.second.bVisible)
1982 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1983 blocks[0].m_nCharPos = 0;
1984 blocks[0].m_nColorIndex = COLORINDEX_NONE;
1985 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1987 const tchar_t *pszChars = GetLineChars(nLineIndex);
1988 int nLineLength = GetLineLength(nLineIndex);
1989 if (pszChars != nullptr)
1991 RxNode *node = nullptr;
1992 for (const tchar_t *p = pszChars; p < pszChars + nLineLength; )
1996 size_t nPos = ::FindStringHelper(pszChars, nLineLength, p, marker.second.sFindWhat, marker.second.dwFlags | FIND_NO_WRAP, nMatchLen, node, &matches);
1999 if (nLineLength < static_cast<int>(nPos) + nMatchLen)
2000 nMatchLen = static_cast<int>(nLineLength - nPos);
2001 ASSERT(nPos < INT_MAX);
2002 blocks[nBlocks].m_nCharPos = static_cast<int>(nPos);
2003 blocks[nBlocks].m_nBgColorIndex = marker.second.nBgColorIndex | COLORINDEX_APPLYFORCE;
2004 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
2006 ASSERT((nPos + nMatchLen) < INT_MAX);
2007 blocks[nBlocks].m_nCharPos = static_cast<int>(nPos + nMatchLen);
2008 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
2009 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
2011 p = pszChars + nPos + (nMatchLen == 0 ? 1 : nMatchLen);
2014 blocks.resize(nBlocks);
2015 allblocks = MergeTextBlocks(allblocks, blocks);
2022 std::vector<TEXTBLOCK>
2023 CCrystalTextView::GetTextBlocks(int nLineIndex)
2025 int nLength = GetViewableLineLength (nLineIndex);
2028 unsigned dwCookie = GetParseCookie(nLineIndex - 1);
2029 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
2031 // insert at least one textblock of normal color at the beginning
2032 blocks[0].m_nCharPos = 0;
2033 blocks[0].m_nColorIndex = COLORINDEX_NORMALTEXT;
2034 blocks[0].m_nBgColorIndex = COLORINDEX_BKGND;
2036 (*m_ParseCookies)[nLineIndex] = ParseLine(dwCookie, GetLineChars(nLineIndex), GetLineLength(nLineIndex), blocks.data(), nBlocks);
2037 ASSERT((*m_ParseCookies)[nLineIndex] != -1);
2038 blocks.resize(nBlocks);
2040 std::vector<TEXTBLOCK> additionalBlocks = GetAdditionalTextBlocks(nLineIndex);
2041 std::vector<TEXTBLOCK> mergedBlocks;
2042 if (m_pMarkers && m_pMarkers->GetEnabled() && m_pMarkers->GetMarkers().size() > 0)
2043 mergedBlocks = MergeTextBlocks(additionalBlocks, GetMarkerTextBlocks(nLineIndex));
2045 mergedBlocks = std::move(additionalBlocks);
2046 std::vector<TEXTBLOCK> mergedBlocks2 = MergeTextBlocks(blocks, mergedBlocks);
2047 if (m_bViewTabs || m_bViewEols)
2048 return MergeTextBlocks(mergedBlocks2, GetWhitespaceTextBlocks(nLineIndex));
2049 return mergedBlocks2;
2052 void CCrystalTextView::
2053 DrawSingleLine (const CRect & rc, int nLineIndex)
2055 const int nCharWidth = GetCharWidth();
2056 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2058 if (nLineIndex == -1)
2060 // Draw line beyond the text
2061 m_pCrystalRenderer->FillSolidRectangle (rc, GetColor (COLORINDEX_WHITESPACE));
2065 // Acquire the background color for the current line
2066 bool bDrawWhitespace = false;
2067 COLORREF crBkgnd, crText;
2068 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2070 int nLength = GetViewableLineLength (nLineIndex);
2072 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2074 int nActualItem = 0;
2075 int nActualOffset = 0;
2077 std::vector<int> anBreaks(nLength);
2080 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
2082 // Draw the line text
2083 CPoint origin (rc.left - m_nOffsetChar * nCharWidth, rc.top);
2084 if (crBkgnd != CLR_NONE)
2085 m_pCrystalRenderer->SetBkColor (crBkgnd);
2086 if (crText != CLR_NONE)
2087 m_pCrystalRenderer->SetTextColor (crText);
2089 const TextLayoutMode layoutMode = GetTextLayoutMode ();
2090 if (layoutMode == TEXTLAYOUT_TABLE_WORDWRAP)
2092 anBreaks.push_back (-nLength);
2093 CPoint originOrg = origin;
2096 blocks, nActualItem,
2097 crText, crBkgnd, bDrawWhitespace,
2098 nLineIndex, 0, abs(anBreaks[0]),
2099 nActualOffset, CEPoint( 0, nLineIndex ) );
2101 for( int i = 0, j = 0; i < static_cast<int> (anBreaks.size ()) - 1; i++, j++ )
2103 if (anBreaks[i] < 0)
2107 CRect frect( origin.x, originOrg.y + (j + 1) * GetLineHeight (),
2108 origin.x + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth, rc.bottom );
2109 if (frect.left < rc.left)
2110 frect.left = rc.left;
2111 if (frect.right > rc.left)
2112 m_pCrystalRenderer->FillSolidRectangle (frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2114 origin.y = originOrg.y;
2115 origin.x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth;
2120 blocks, nActualItem,
2121 crText, crBkgnd, bDrawWhitespace,
2122 nLineIndex, abs(anBreaks[i]), abs(anBreaks[i + 1]) - abs(anBreaks[i]),
2123 nActualOffset, CEPoint( abs(anBreaks[i]), nLineIndex ) );
2126 else if (layoutMode == TEXTLAYOUT_WORDWRAP && nBreaks > 0)
2128 // Draw all the screen lines of the wrapped line
2129 ASSERT( anBreaks[0] < nLength );
2131 // draw start of line to first break
2134 blocks, nActualItem,
2135 crText, crBkgnd, bDrawWhitespace,
2136 nLineIndex, 0, anBreaks[0], nActualOffset, CEPoint( 0, nLineIndex ) );
2138 // draw from first break to last break
2140 for( i = 0; i < nBreaks - 1; i++ )
2142 ASSERT( anBreaks[i] >= 0 && anBreaks[i] < nLength );
2145 blocks, nActualItem,
2146 crText, crBkgnd, bDrawWhitespace,
2147 nLineIndex, anBreaks[i], anBreaks[i + 1] - anBreaks[i],
2148 nActualOffset, CEPoint( anBreaks[i], nLineIndex ) );
2151 // draw from last break till end of line
2154 blocks, nActualItem,
2155 crText, crBkgnd, bDrawWhitespace,
2156 nLineIndex, anBreaks[i], nLength - anBreaks[i],
2157 nActualOffset, CEPoint( anBreaks[i], nLineIndex ) );
2162 blocks, nActualItem,
2163 crText, crBkgnd, bDrawWhitespace,
2164 nLineIndex, 0, nLength, nActualOffset, CEPoint(0, nLineIndex));
2166 // Draw empty sublines
2167 int nEmptySubLines = GetEmptySubLines(nLineIndex);
2168 if (nEmptySubLines > 0)
2171 frect.top += (nBreaks + 1) * GetLineHeight ();
2172 m_pCrystalRenderer->FillSolidRectangle(frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2177 * @brief Escape special characters
2178 * @param [in] strText The text to escape
2179 * @param [in, out] bLastCharSpace Whether last char processed was white space
2180 * @param [in, out] nNonbreakChars The number of non-break characters in the text
2181 * @param [in] nScreenChars The maximum number of characters to display per line
2182 * @return The escaped text
2185 EscapeHTML (const CString & strText, bool & bLastCharSpace, int & nNonbreakChars, int nScreenChars)
2188 int len = strText.GetLength ();
2189 for (int i = 0; i < len; ++i)
2191 tchar_t ch = strText[i];
2195 strHTML += _T("&");
2196 bLastCharSpace = false;
2200 strHTML += _T("<");
2201 bLastCharSpace = false;
2205 strHTML += _T(">");
2206 bLastCharSpace = false;
2212 strHTML += _T("<wbr>");
2213 bLastCharSpace = false;
2217 if (i == 0 || bLastCharSpace)
2219 strHTML += _T(" ");
2220 bLastCharSpace = false;
2225 bLastCharSpace = true;
2231 bLastCharSpace = false;
2235 if (IsDBCSLeadByte (ch))
2236 strHTML += strText[++i];
2238 if ((nNonbreakChars % nScreenChars) == nScreenChars - 1)
2240 strHTML += _T("<wbr>");
2248 // Make a CString from printf-style args (single call version of CString::Format)
2249 static CString Fmt(const tchar_t* fmt, ...)
2253 va_start(args, fmt);
2254 str.FormatV(fmt, args);
2260 * @brief Return all the styles necessary to render this view as HTML code
2261 * @return The HTML styles
2263 CString CCrystalTextView::
2266 int arColorIndices[] = {
2267 COLORINDEX_NORMALTEXT,
2270 COLORINDEX_FUNCNAME,
2273 COLORINDEX_OPERATOR,
2275 COLORINDEX_PREPROCESSOR,
2276 COLORINDEX_HIGHLIGHTTEXT1,
2277 COLORINDEX_HIGHLIGHTTEXT2,
2281 int arBgColorIndices[] = {
2283 COLORINDEX_SELBKGND,
2284 COLORINDEX_HIGHLIGHTBKGND1,
2285 COLORINDEX_HIGHLIGHTBKGND2,
2286 COLORINDEX_HIGHLIGHTBKGND3,
2287 COLORINDEX_HIGHLIGHTBKGND4,
2291 for (int i = 0; i < 2; i++)
2293 for (int f = 0; f < sizeof(arColorIndices) / sizeof(int); f++)
2295 int nColorIndex = arColorIndices[f];
2296 for (int b = 0; b < sizeof(arBgColorIndices) / sizeof(int); b++)
2298 int nBgColorIndex = arBgColorIndices[b];
2301 strStyles += Fmt(_T(".sf%db%d%s {"), nColorIndex, nBgColorIndex, i == 0 ? _T("") : _T("i"));
2302 clr = GetColor(nColorIndex);
2304 clr = GetIntermediateColor(clr, GetColor(nBgColorIndex));
2305 strStyles += Fmt(_T("color: #%02x%02x%02x; "), GetRValue(clr), GetGValue(clr), GetBValue(clr));
2306 clr = GetColor(nBgColorIndex);
2307 strStyles += Fmt(_T("background-color: #%02x%02x%02x; "), GetRValue(clr), GetGValue(clr), GetBValue(clr));
2308 if (GetBold(nColorIndex))
2309 strStyles += _T("font-weight: bold; ");
2310 if (GetItalic(nColorIndex))
2311 strStyles += _T("font-style: italic; ");
2312 strStyles += _T("}\n");
2316 COLORREF clrSelMargin = GetColor(COLORINDEX_SELMARGIN);
2317 COLORREF clrNormalText = GetColor(COLORINDEX_NORMALTEXT);
2318 strStyles += Fmt(_T(".ln {text-align: right; word-break: normal; color: #%02x%02x%02x; background-color: #%02x%02x%02x;}\n"),
2319 GetRValue(clrNormalText), GetGValue(clrNormalText), GetBValue(clrNormalText),
2320 GetRValue(clrSelMargin), GetGValue(clrSelMargin), GetBValue(clrSelMargin));
2325 * @brief Return the HTML attribute associated with the specified colors
2326 * @param [in] nColorIndex Index of text color
2327 * @param [in] nBgColorIndex Index of background color
2328 * @param [in] crText Base text color
2329 * @param [in] crBkgnd Base background color
2330 * @return The HTML attribute
2332 CString CCrystalTextView::
2333 GetHTMLAttribute (int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd)
2336 COLORREF clr, clrBk;
2338 if ((crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE)) &&
2339 (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE)))
2340 return Fmt(_T("class=\"sf%db%d%s\""), nColorIndex & ~COLORINDEX_MASK, nBgColorIndex & ~COLORINDEX_MASK,
2341 (nColorIndex & COLORINDEX_INTERMEDIATECOLOR) ? _T("i") : _T(""));
2343 if (crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE))
2344 clr = GetColor (nColorIndex);
2347 if (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE))
2348 clrBk = GetColor (nBgColorIndex);
2351 if (nColorIndex & COLORINDEX_INTERMEDIATECOLOR)
2352 clr = GetIntermediateColor(clr, clrBk);
2353 strAttr += Fmt (_T("style=\"color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2354 strAttr += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clrBk), GetGValue (clrBk), GetBValue (clrBk));
2356 if (GetBold (nColorIndex))
2357 strAttr += _T("font-weight: bold; ");
2358 if (GetItalic (nColorIndex))
2359 strAttr += _T("font-style: italic; ");
2361 strAttr += _T("\"");
2367 * @brief Retrieve the html version of the line
2368 * @param [in] nLineIndex Index of line in view
2369 * @param [in] pszTag The HTML tag to enclose the line
2370 * @return The html version of the line
2372 CString CCrystalTextView::
2373 GetHTMLLine (int nLineIndex, const tchar_t* pszTag)
2375 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2377 int nLength = GetViewableLineLength (nLineIndex);
2379 // Acquire the background color for the current line
2380 bool bDrawWhitespace = false;
2381 COLORREF crBkgnd, crText;
2382 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2384 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2387 CString strExpanded;
2389 int nNonbreakChars = 0;
2390 bool bLastCharSpace = false;
2391 const int nScreenChars = 40; // GetScreenChars();
2396 strHTML += GetHTMLAttribute (COLORINDEX_NORMALTEXT, COLORINDEX_BKGND, crText, crBkgnd);
2397 strHTML += _T("><code>");
2399 auto MakeSpan = [&](const TEXTBLOCK& block, const CString& strExpanded) {
2401 strHTML += _T("<span ");
2402 strHTML += GetHTMLAttribute (block.m_nColorIndex, block.m_nBgColorIndex, crText, crBkgnd);
2404 strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2405 strHTML += _T("</span>");
2409 for (i = 0; i < blocks.size() - 1; i++)
2411 ExpandChars (nLineIndex, blocks[i].m_nCharPos, blocks[i + 1].m_nCharPos - blocks[i].m_nCharPos, strExpanded, 0);
2412 if (!strExpanded.IsEmpty())
2413 strHTML += MakeSpan(blocks[i], strExpanded);
2415 if (blocks.size() > 0)
2417 ExpandChars (nLineIndex, blocks[i].m_nCharPos, nLength - blocks[i].m_nCharPos, strExpanded, 0);
2418 if (!strExpanded.IsEmpty())
2419 strHTML += MakeSpan(blocks[i], strExpanded);
2420 if (strExpanded.Compare (CString (' ', strExpanded.GetLength())) == 0)
2421 strHTML += _T(" ");
2423 strHTML += _T("</code></");
2430 COLORREF CCrystalTextView::
2431 GetColor (int nColorIndex) const
2433 if (m_pColors != nullptr)
2435 nColorIndex &= ~COLORINDEX_MASK;
2436 return m_pColors->GetColor(nColorIndex);
2439 return RGB(0, 0, 0);
2442 lineflags_t CCrystalTextView::
2443 GetLineFlags (int nLineIndex) const
2445 if (m_pTextBuffer == nullptr)
2447 return m_pTextBuffer->GetLineFlags (nLineIndex);
2450 void CCrystalTextView::
2451 GetTopMarginText (const CRect& rect, CString& text, std::vector<int>& nWidths)
2453 auto getColumnName = [](int nColumn) -> CString
2456 for (int i = 0; ; ++i)
2458 tchar_t c = 'A' + (nColumn % 26) - (i == 0 ? 0 : 1);
2459 columnName.Insert (0, c);
2467 auto replaceControlChars = [](const CString& text) -> CString
2470 for (int i = 0; i < text.GetLength(); ++i)
2472 if (_istcntrl(text[i]))
2474 if (i == 0 || !_istcntrl(text[i - 1]))
2483 const int nCharWidth = GetCharWidth ();
2484 const int nMarginWidth = GetMarginWidth ();
2485 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth; x < rect.Width (); ++nColumn)
2487 int nColumnWidth = m_pTextBuffer->GetColumnWidth (nColumn);
2489 if (m_nTopSubLine > 0 && m_nLineNumberUsedAsHeaders >= 0 && m_nLineNumberUsedAsHeaders < m_pTextBuffer->GetLineCount())
2490 columnName = replaceControlChars (m_pTextBuffer->GetCellText (m_nLineNumberUsedAsHeaders, nColumn));
2491 if (columnName.IsEmpty())
2492 columnName = getColumnName (nColumn);
2493 int columnNameLen = 0;
2494 std::vector<int> nCharWidths;
2495 for (int i = 0; i < columnName.GetLength(); ++i)
2497 int cnt = GetCharCellCountFromChar (((const tchar_t*)columnName) + i);
2498 nCharWidths.push_back (cnt * nCharWidth);
2499 columnNameLen += cnt;
2501 while (nColumnWidth < columnNameLen)
2503 columnNameLen -= nCharWidths.back() / nCharWidth;
2504 columnName.Truncate(columnName.GetLength() - 1);
2505 nCharWidths.resize(columnName.GetLength());
2507 const int leftspaces = (nColumnWidth - columnNameLen) / 2;
2508 const int rightspaces = nColumnWidth - leftspaces - columnNameLen;
2509 text += CString (' ', leftspaces) + columnName + CString (' ', rightspaces);
2510 std::vector<int> preWidths(leftspaces, nCharWidth);
2511 std::vector<int> postWidths(rightspaces, nCharWidth);
2512 nCharWidths.insert (nCharWidths.begin (), preWidths.begin (), preWidths.end ());
2513 nCharWidths.insert (nCharWidths.end (), postWidths.begin (), postWidths.end ());
2514 x += nColumnWidth * nCharWidth;
2515 nWidths.insert (nWidths.end (), nCharWidths.begin (), nCharWidths.end ());
2519 void CCrystalTextView::
2520 DrawTopMargin (const CRect& rect)
2524 m_pCrystalRenderer->SetBkColor (GetColor (COLORINDEX_SELMARGIN));
2525 m_pCrystalRenderer->FillRectangle (rect);
2526 m_pCrystalRenderer->SetTextColor (GetColor (COLORINDEX_NORMALTEXT));
2527 if (m_pTextBuffer->GetTableEditing ())
2529 CString columnNames;
2530 std::vector<int> nWidths;
2531 GetTopMarginText (rect, columnNames, nWidths);
2532 m_pCrystalRenderer->SwitchFont (false, false);
2533 m_pCrystalRenderer->DrawText (rect.left + GetMarginWidth () - m_nOffsetChar * GetCharWidth (), 0, rect, columnNames, columnNames.GetLength (), nWidths.data ());
2536 m_pCrystalRenderer->DrawRuler (GetMarginWidth (), 0, rect.Width (), rect.Height (), GetCharWidth (), m_nOffsetChar);
2540 * @brief Draw selection margin.
2541 * @param [in] pdc Pointer to draw context.
2542 * @param [in] rect The rectangle to draw.
2543 * @param [in] nLineIndex Index of line in view.
2544 * @param [in] nLineNumber Line number to display. if -1, it's not displayed.
2546 void CCrystalTextView::
2547 DrawMargin (const CRect & rect, int nLineIndex, int nLineNumber)
2549 if (!m_bSelMargin && !m_bViewLineNumbers)
2550 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_BKGND));
2552 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_SELMARGIN));
2553 m_pCrystalRenderer->FillRectangle(rect);
2555 if (m_bViewLineNumbers && nLineNumber > 0)
2557 m_pCrystalRenderer->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
2558 m_pCrystalRenderer->DrawMarginLineNumber(rect.right, rect.top, nLineNumber);
2561 // Draw line revision mark (or background) whenever we have valid lineindex
2562 COLORREF clrRevisionMark = GetColor(COLORINDEX_WHITESPACE);
2563 if (nLineIndex >= 0 && m_pTextBuffer != nullptr)
2565 // get line revision marks color
2566 uint32_t dwRevisionNumber = m_pTextBuffer->GetLineRevisionNumber(nLineIndex);
2567 if (dwRevisionNumber > 0)
2569 if (m_pTextBuffer->m_dwRevisionNumberOnSave < dwRevisionNumber)
2570 clrRevisionMark = UNSAVED_REVMARK_CLR;
2572 clrRevisionMark = SAVED_REVMARK_CLR;
2576 // draw line revision marks
2577 CRect rc(rect.right - MARGIN_REV_WIDTH, rect.top, rect.right, rect.bottom);
2578 m_pCrystalRenderer->FillSolidRectangle (rc, clrRevisionMark);
2583 int nImageIndex = -1;
2584 if (nLineIndex >= 0)
2586 lineflags_t dwLineFlags = GetLineFlags (nLineIndex);
2587 static const lineflags_t adwFlags[] =
2591 LF_COMPILATION_ERROR,
2603 LF_INVALID_BREAKPOINT
2605 for (int I = 0; I < sizeof (adwFlags) / sizeof (adwFlags[0]); I++)
2607 if ((dwLineFlags & adwFlags[I]) != 0)
2614 if (nImageIndex >= 0)
2616 const int iconsize = GetMarginIconSize();
2617 m_pCrystalRenderer->DrawMarginIcon(
2618 rect.left + 2, rect.top + (GetLineHeight() - iconsize) / 2, nImageIndex, iconsize);
2621 // draw wrapped-line-icon
2622 if (nLineNumber > 0)
2624 const int iconsize = GetMarginIconSize();
2626 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2627 for (int i = 0; i < nBreaks; i++)
2629 m_pCrystalRenderer->DrawMarginIcon(
2630 rect.right - iconsize, rect.top + (GetLineHeight()
2631 - iconsize) / 2 + (i+1) * GetLineHeight(), ICON_INDEX_WRAPLINE, iconsize);
2636 bool CCrystalTextView::
2637 IsInsideSelBlock (CEPoint ptTextPos)
2640 ASSERT_VALIDTEXTPOS (ptTextPos);
2641 if (ptTextPos.y < m_ptDrawSelStart.y)
2643 if (ptTextPos.y > m_ptDrawSelEnd.y)
2645 if (m_bRectangularSelection)
2646 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2647 if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
2649 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
2651 if (ptTextPos.y == m_ptDrawSelEnd.y)
2652 return ptTextPos.x < m_ptDrawSelEnd.x;
2653 ASSERT (ptTextPos.y == m_ptDrawSelStart.y);
2654 return ptTextPos.x >= m_ptDrawSelStart.x;
2656 ASSERT (m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
2657 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2660 bool CCrystalTextView::
2661 IsInsideSelection (const CEPoint & ptTextPos)
2663 PrepareSelBounds ();
2664 return IsInsideSelBlock (ptTextPos);
2668 * @brief : class the selection extremities in ascending order
2670 * @note : Updates m_ptDrawSelStart and m_ptDrawSelEnd
2671 * This function must be called before reading these values
2673 void CCrystalTextView::
2676 if (m_ptSelStart.y < m_ptSelEnd.y ||
2677 (m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
2679 m_ptDrawSelStart = m_ptSelStart;
2680 m_ptDrawSelEnd = m_ptSelEnd;
2684 m_ptDrawSelStart = m_ptSelEnd;
2685 m_ptDrawSelEnd = m_ptSelStart;
2689 void CCrystalTextView::
2692 // We use the same GDI objects for both drawing and printing.
2693 // So when printing is in progress, we do nothing in this function.
2698 GetClientRect (rcClient);
2700 if (m_pTextBuffer == nullptr)
2702 m_pCrystalRenderer->BindDC(*pdc, rcClient);
2703 m_pCrystalRenderer->BeginDraw();
2704 m_pCrystalRenderer->SetBkColor(GetSysColor(COLOR_WINDOW));
2705 m_pCrystalRenderer->FillRectangle(rcClient);
2706 m_pCrystalRenderer->EndDraw();
2710 const int nLineCount = GetLineCount ();
2711 const int nLineHeight = GetLineHeight ();
2712 PrepareSelBounds ();
2714 // if the private arrays (m_ParseCookies and m_pnActualLineLength)
2715 // are defined, check they are in phase with the text buffer
2716 if (m_ParseCookies->size())
2717 ASSERT(m_ParseCookies->size() == static_cast<size_t>(nLineCount));
2718 if (m_pnActualLineLength->size())
2719 ASSERT(m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
2722 VERIFY (cacheDC.CreateCompatibleDC (pdc));
2723 if (m_pCacheBitmap == nullptr)
2725 m_pCacheBitmap = new CBitmap;
2726 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height()));
2728 CBitmap *pOldBitmap = cacheDC.SelectObject (m_pCacheBitmap);
2731 int nSubLineOffset = GetSubLineIndex( m_nTopLine ) - m_nTopSubLine;
2732 int nCursorY = TextToClient (m_ptCursorPos).y;
2735 CRect rcTopMargin(rcClient.left, rcClient.top, rcClient.right, rcClient.top + GetTopMarginHeight());
2737 rcLine.top = rcTopMargin.bottom + nSubLineOffset * nLineHeight;
2738 CRect rcMargin (rcLine.left, rcLine.top, rcLine.left + GetMarginWidth (), rcLine.top + nLineHeight);
2739 rcLine.left = rcMargin.right;
2741 m_pCrystalRenderer->BindDC(cacheDC, rcClient);
2742 m_pCrystalRenderer->BeginDraw();
2744 int nLastLineBottom = 0;
2745 int nCurrentLine = m_nTopLine;
2746 while (rcLine.top < rcClient.bottom)
2749 if( nCurrentLine < nLineCount /*&& GetLineLength( nCurrentLine ) > nMaxLineChars*/ )
2750 nSubLines = GetSubLines(nCurrentLine);
2752 rcLine.bottom = (std::min)(rcClient.bottom, rcLine.top + nSubLines * nLineHeight);
2753 rcMargin.bottom = rcLine.bottom;
2755 CRect rcMarginAndLine(rcClient.left, rcLine.top, rcClient.right, rcLine.bottom);
2756 if (pdc->RectVisible(rcMarginAndLine))
2758 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
2760 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
2761 DrawSingleLine (rcLine, nCurrentLine);
2762 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
2763 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.top + nSubLines * nLineHeight - 1);
2764 if (m_pTextBuffer->GetTableEditing ())
2765 m_pCrystalRenderer->DrawGridLine (rcMargin.left, rcMargin.top + nSubLines * nLineHeight - 1, rcLine.right, rcMargin.top + nSubLines * nLineHeight - 1, 24);
2766 if (nCurrentLine == m_ptCursorPos.y)
2767 m_pCrystalRenderer->DrawLineCursor (rcMargin.left, rcLine.right,
2768 nCursorY + nLineHeight - 1, 1);
2769 nLastLineBottom = rcMargin.bottom;
2773 DrawMargin (rcMargin, -1, -1);
2774 DrawSingleLine (rcLine, -1);
2779 rcLine.top += nSubLines * nLineHeight;
2780 rcMargin.top = rcLine.top;
2783 if (pdc->RectVisible (rcTopMargin))
2784 DrawTopMargin (rcTopMargin);
2786 if (m_pTextBuffer->GetTableEditing ())
2788 int nCharWidth = GetCharWidth ();
2789 int nMarginWidth = GetMarginWidth ();
2790 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth;
2791 x < rcClient.Width();
2792 x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
2794 if (x >= nMarginWidth && nColumn > 0)
2795 m_pCrystalRenderer->DrawGridLine (x, rcClient.top, x, nLastLineBottom, 24);
2799 m_pCrystalRenderer->EndDraw();
2801 VERIFY (pdc->BitBlt (rcClient.left, rcClient.top, rcClient.Width (),
2802 rcClient.Height (), &cacheDC, 0, 0, SRCCOPY));
2804 cacheDC.SelectObject (pOldBitmap);
2805 cacheDC.DeleteDC ();
2808 void CCrystalTextView::
2811 // m_bWordWrap = false;
2817 m_nScreenLines = -1;
2818 m_nScreenChars = -1;
2819 m_nIdealCharPos = -1;
2822 InvalidateLineCache( 0, -1 );
2823 m_ParseCookies->clear();
2824 m_pnActualLineLength->clear();
2825 m_ptCursorPos.x = 0;
2826 m_ptCursorPos.y = 0;
2827 m_ptSelStart = m_ptSelEnd = m_ptCursorPos;
2828 if (m_bDragSelection)
2831 KillTimer (m_nDragSelTimer);
2833 m_bDragSelection = false;
2834 m_bVertScrollBarLocked = false;
2835 m_bHorzScrollBarLocked = false;
2836 if (::IsWindow (m_hWnd))
2838 m_bShowInactiveSelection = true; // FP: reverted because I like it
2839 m_bPrintHeader = false;
2840 m_bPrintFooter = true;
2842 m_bMultipleSearch = false; // More search
2846 void CCrystalTextView::
2849 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
2850 if (m_bFocused && !m_bCursorHidden &&
2851 CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x) >= m_nOffsetChar &&
2852 m_ptCursorPos.y >= m_nTopLine)
2854 int nCaretHeight = GetLineVisible(m_ptCursorPos.y) ? GetLineHeight () : 0;
2855 if (m_bOvrMode) //UPDATE
2857 int nCaretWidth = GetCharWidth ();
2858 if (m_ptCursorPos.x < GetLineLength (m_ptCursorPos.y))
2860 const tchar_t* pszLine = GetLineChars (m_ptCursorPos.y);
2861 if (pszLine[m_ptCursorPos.x] != '\t')
2862 nCaretWidth *= GetCharCellCountFromChar (pszLine + m_ptCursorPos.x);
2864 CreateSolidCaret (nCaretWidth, nCaretHeight);
2867 CreateSolidCaret (2, nCaretHeight);
2869 SetCaretPos (TextToClient (m_ptCursorPos));
2871 UpdateCompositionWindowPos(); /* IME */
2880 void CCrystalTextView::
2885 CRLFSTYLE CCrystalTextView::
2888 if (m_pTextBuffer != nullptr)
2890 return m_pTextBuffer->GetCRLFMode ();
2892 return CRLFSTYLE::AUTOMATIC;
2895 void CCrystalTextView::
2896 SetCRLFMode (CRLFSTYLE nCRLFMode)
2898 if (m_pTextBuffer != nullptr)
2900 m_pTextBuffer->SetCRLFMode (nCRLFMode);
2904 int CCrystalTextView::
2907 if (m_pTextBuffer == nullptr)
2910 return m_pTextBuffer->GetTabSize();
2914 void CCrystalTextView::
2915 SetTabSize (int nTabSize)
2917 ASSERT (nTabSize >= 0 && nTabSize <= 64);
2918 if (m_pTextBuffer == nullptr)
2921 if (m_pTextBuffer->GetTabSize() != nTabSize)
2923 m_pTextBuffer->SetTabSize( nTabSize );
2925 m_pnActualLineLength->clear();
2926 InvalidateHorzScrollBar ();
2932 void CCrystalTextView::
2935 CSize szCharExt = m_pCrystalRenderer->GetCharWidthHeight();
2936 m_nLineHeight = szCharExt.cy;
2937 if (m_nLineHeight < 1)
2939 m_nCharWidth = szCharExt.cx;
2942 int CCrystalTextView::
2945 if (m_nLineHeight == -1)
2947 return m_nLineHeight;
2950 int CCrystalTextView::GetSubLines( int nLineIndex )
2952 // get a number of lines this wrapped lines contains
2954 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2956 return GetEmptySubLines(nLineIndex) + nBreaks + 1;
2959 bool CCrystalTextView::IsEmptySubLineIndex( int nSubLineIndex )
2963 GetLineBySubLine(nSubLineIndex, nLineIndex, dummy);
2964 int nSubLineIndexNextLine = GetSubLineIndex(nLineIndex) + GetSubLines(nLineIndex);
2965 if (nSubLineIndexNextLine - GetEmptySubLines(nLineIndex) <= nSubLineIndex && nSubLineIndex < nSubLineIndexNextLine)
2971 int CCrystalTextView::CharPosToPoint( int nLineIndex, int nCharPos, CEPoint &charPoint, int* pnColumn )
2973 // if we do not wrap lines, y is allways 0 and x is equl to nCharPos
2976 charPoint.x = nCharPos;
2981 vector<int> anBreaks(GetLineLength (nLineIndex) + 1);
2984 WrapLineCached (nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
2986 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_WORDWRAP)
2991 for (; i < anBreaks.size () && abs (anBreaks[i]) <= nCharPos ; ++i)
2993 if (anBreaks[i] < 0)
3001 charPoint.x = (i > 0) ? nCharPos - abs (anBreaks[i - 1]) : nCharPos;
3004 *pnColumn = nColumn;
3006 return (i > 0)? abs (anBreaks[i - 1]) : 0;
3010 int i = (nBreaks <= 0) ? -1 : nBreaks - 1;
3011 for (; i >= 0 && nCharPos < anBreaks[i]; i--)
3014 charPoint.x = (i >= 0)? nCharPos - anBreaks[i] : nCharPos;
3015 charPoint.y = i + 1;
3017 int nReturnVal = (i >= 0)? anBreaks[i] : 0;
3023 /** Does character introduce a multicharacter character? */
3024 static inline bool IsLeadByte(tchar_t ch)
3029 return _getmbcp() && IsDBCSLeadByte(ch);
3033 int CCrystalTextView::CursorPointToCharPos( int nLineIndex, const CEPoint &curPoint )
3035 // calculate char pos out of point
3036 const int nLength = GetLineLength( nLineIndex );
3037 const int nScreenChars = GetScreenChars();
3038 const tchar_t* szLine = GetLineChars( nLineIndex );
3041 vector<int> anBreaks(nLength + 1);
3044 WrapLineCached( nLineIndex, nScreenChars, &anBreaks, nBreaks );
3046 // find char pos that matches cursor position
3050 const int nTabSize = GetTabSize();
3052 int nIndex=0, nPrevIndex = 0;
3053 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(szLine, nLength);
3054 switch (GetTextLayoutMode ())
3056 case TEXTLAYOUT_TABLE_NOWORDWRAP:
3058 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
3059 int nColumnTotalWidth = 0;
3061 bool bInQuote = false;
3062 const int sep = m_pTextBuffer->GetFieldDelimiter ();
3063 const int quote = m_pTextBuffer->GetFieldEnclosure ();
3064 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
3067 if (!bInQuote && szLine[nIndex] == sep)
3069 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
3070 nOffset = nColumnTotalWidth - nXPos;
3074 if (szLine[nIndex] == quote)
3075 bInQuote = !bInQuote;
3076 if (szLine[nIndex] == '\t')
3079 nOffset = GetCharCellCountFromChar (szLine + nIndex);
3080 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
3081 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nXPos;
3086 if( nXPos > curPoint.x && nYPos == curPoint.y )
3088 else if( nYPos > curPoint.y )
3090 nIndex = nPrevIndex;
3094 nPrevIndex = nIndex;
3098 case TEXTLAYOUT_TABLE_WORDWRAP:
3102 int nColumnSumWidth = 0;
3103 int nColumnCurPoint = INT_MAX;
3104 if (curPoint.x < m_pTextBuffer->GetColumnWidth (0))
3105 nColumnCurPoint = 0;
3106 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next ())
3108 if (i < static_cast<int>(anBreaks.size ()) && nIndex == abs (anBreaks[i]))
3110 if (anBreaks[i++] < 0)
3113 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
3114 nXPos = nColumnSumWidth;
3115 if (nColumnSumWidth <= curPoint.x && curPoint.x < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
3116 nColumnCurPoint = nColumn;
3120 nXPos = nColumnSumWidth;
3126 if (szLine[nIndex] == '\t')
3129 nOffset = GetCharCellCountFromChar (szLine + nIndex);
3133 if( nXPos > curPoint.x && nYPos == curPoint.y )
3135 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
3137 nIndex = nPrevIndex;
3140 else if ( nYPos == curPoint.y)
3141 nPrevIndex = nIndex;
3143 if (nIndex == nLength && nYPos != curPoint.y)
3144 nIndex = nPrevIndex;
3149 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
3151 if( nBreaks > 0 && nYPos < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[nYPos] )
3158 if (szLine[nIndex] == _T('\t'))
3159 nOffset = nTabSize - nCurPos % nTabSize;
3161 nOffset = GetCharCellCountFromChar(szLine + nIndex);
3165 if( nXPos > curPoint.x && nYPos == curPoint.y )
3167 else if( nYPos > curPoint.y )
3169 nIndex = nPrevIndex;
3173 nPrevIndex = nIndex;
3180 void CCrystalTextView::SubLineCursorPosToTextPos( const CEPoint &subLineCurPos, CEPoint &textPos )
3183 int nSubLineOffset, nLine;
3185 GetLineBySubLine( subLineCurPos.y, nLine, nSubLineOffset );
3187 // compute cursor-position
3188 textPos.x = CursorPointToCharPos( nLine, CEPoint( subLineCurPos.x, nSubLineOffset ) );
3193 * @brief Calculate last character position in (sub)line.
3194 * @param [in] nLineIndex Linenumber to check.
3195 * @param [in] nSublineOffset Subline index in wrapped line.
3196 * @return Position of the last character.
3198 int CCrystalTextView::SubLineEndToCharPos(int nLineIndex, int nSubLineOffset)
3200 const int nLength = GetLineLength(nLineIndex);
3202 // if word wrapping is disabled, the end is equal to the length of the line -1
3207 vector<int> anBreaks(nLength + 1);
3210 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3212 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3214 int nBreakLast = -1;
3215 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3217 if (anBreaks[i] < 0)
3219 if (j == nSubLineOffset)
3222 if (nBreakLast < static_cast<int>(anBreaks.size ()) - 1)
3223 return abs (anBreaks[nBreakLast + 1]) - 1;
3227 // if there is no break inside the line or the given subline is the last
3228 // one in this line...
3229 if (nBreaks <= 0 || nSubLineOffset == nBreaks)
3234 // compute character position for end of subline
3235 ASSERT(nSubLineOffset >= 0 && nSubLineOffset <= nBreaks);
3237 int nReturnVal = anBreaks[nSubLineOffset] - 1;
3243 * @brief Calculate first character position in (sub)line.
3244 * @param [in] nLineIndex Linenumber to check.
3245 * @param [in] nSublineOffset Subline index in wrapped line.
3246 * @return Position of the first character.
3248 int CCrystalTextView::SubLineHomeToCharPos(int nLineIndex, int nSubLineOffset)
3250 // if word wrapping is disabled, the start is 0
3251 if (!m_bWordWrap || nSubLineOffset == 0)
3255 int nLength = GetLineLength(nLineIndex);
3256 vector<int> anBreaks(nLength + 1);
3259 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3261 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3263 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3265 if (anBreaks[i] < 0)
3267 if (j == nSubLineOffset)
3268 return abs (anBreaks[i]);
3273 // if there is no break inside the line...
3279 // compute character position for end of subline
3280 ASSERT(nSubLineOffset > 0 && nSubLineOffset <= nBreaks);
3282 int nReturnVal = anBreaks[nSubLineOffset - 1];
3288 int CCrystalTextView::
3291 if (m_nCharWidth == -1)
3293 return m_nCharWidth;
3296 int CCrystalTextView::
3297 GetMaxLineLength (int nTopLine, int nLines)
3299 int nMaxLineLength = 0;
3300 const int nLineCount = (std::min)(nTopLine + nLines, GetLineCount ());
3301 for (int I = nTopLine; I < nLineCount; I++)
3303 int nActualLength = GetLineActualLength (I);
3304 if (nMaxLineLength < nActualLength)
3305 nMaxLineLength = nActualLength;
3307 return nMaxLineLength;
3310 bool CCrystalTextView::
3311 CoverLength(int nTopLine, int nLines, int min_length)
3313 const int nLineCount = (std::min)(nTopLine + nLines, GetLineCount ());
3314 for (int I = nTopLine; I != nLineCount; I++)
3316 if (GetLineActualLength (I) >= min_length)
3322 CCrystalTextView *CCrystalTextView::
3323 GetSiblingView (int nRow, int nCol)
3325 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3326 if (pSplitter == nullptr)
3328 CWnd *pWnd = CWnd::FromHandlePermanent (
3329 ::GetDlgItem (pSplitter->m_hWnd, pSplitter->IdFromRowCol (nRow, nCol)));
3330 if (pWnd == nullptr || !pWnd->IsKindOf (RUNTIME_CLASS (CCrystalTextView)))
3332 return static_cast<CCrystalTextView *>(pWnd);
3335 void CCrystalTextView::
3336 GoToLine (int nLine, bool bRelative)
3338 int nLines = m_pTextBuffer->GetLineCount () - 1;
3339 CEPoint ptCursorPos = GetCursorPos ();
3342 nLine += ptCursorPos.y;
3354 int nChars = m_pTextBuffer->GetLineLength (nLine);
3359 if (ptCursorPos.x > nChars)
3361 ptCursorPos.x = nChars;
3363 if (ptCursorPos.x >= 0)
3365 ptCursorPos.y = nLine;
3366 ASSERT_VALIDTEXTPOS (ptCursorPos);
3367 SetAnchor (ptCursorPos);
3368 SetSelection (ptCursorPos, ptCursorPos);
3369 SetCursorPos (ptCursorPos);
3370 EnsureVisible (ptCursorPos);
3375 void CCrystalTextView::
3378 CView::OnInitialUpdate ();
3379 CString sDoc = GetDocument ()->GetPathName (), sExt = GetExt (sDoc);
3380 if (!sExt.IsEmpty())
3382 AttachToBuffer (nullptr);
3384 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3385 if (pSplitter != nullptr)
3387 // See CSplitterWnd::IdFromRowCol() implementation
3388 int nRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3389 int nCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3390 ASSERT (nRow >= 0 && nRow < pSplitter->GetRowCount ());
3391 ASSERT (nCol >= 0 && nCol < pSplitter->GetColumnCount ());
3395 CCrystalTextView *pSiblingView = GetSiblingView (0, nCol);
3396 if (pSiblingView != nullptr && pSiblingView != this)
3398 m_nOffsetChar = pSiblingView->m_nOffsetChar;
3399 ASSERT (m_nOffsetChar >= 0 && m_nOffsetChar <= GetMaxLineLength (m_nTopLine, GetScreenLines()));
3405 CCrystalTextView *pSiblingView = GetSiblingView (nRow, 0);
3406 if (pSiblingView != nullptr && pSiblingView != this)
3408 m_nTopLine = pSiblingView->m_nTopLine;
3409 ASSERT (m_nTopLine >= 0 && m_nTopLine < GetLineCount ());
3413 SetFont (m_LogFont);
3414 if (m_bRememberLastPos && !sDoc.IsEmpty ())
3417 CString sKey = AfxGetApp ()->m_pszRegistryKey;
3418 sKey += _T ("\\") EDITPAD_SECTION _T ("\\Remembered");
3420 if (reg.Open (HKEY_CURRENT_USER, sKey, KEY_READ) &&
3421 reg.LoadBinary (sDoc, (LPBYTE) dwLastPos, sizeof (dwLastPos)))
3423 CEPoint ptCursorPos;
3424 ptCursorPos.x = dwLastPos[1];
3425 ptCursorPos.y = dwLastPos[2];
3426 if (IsValidTextPosY (ptCursorPos))
3428 if (!IsValidTextPosX (ptCursorPos))
3430 ASSERT_VALIDTEXTPOS (ptCursorPos);
3431 SetCursorPos (ptCursorPos);
3432 SetSelection (ptCursorPos, ptCursorPos);
3433 SetAnchor (ptCursorPos);
3434 EnsureVisible (ptCursorPos);
3440 /////////////////////////////////////////////////////////////////////////////
3441 // CCrystalTextView printing
3443 void CCrystalTextView::
3444 OnPrepareDC (CDC * pDC, CPrintInfo * pInfo /*= nullptr*/)
3446 CView::OnPrepareDC (pDC, pInfo);
3448 if (pInfo != nullptr)
3450 pInfo->m_bContinuePrinting = true;
3451 if (m_nPrintPages != 0 && (int) pInfo->m_nCurPage > m_nPrintPages)
3452 pInfo->m_bContinuePrinting = false;
3456 BOOL CCrystalTextView::
3457 OnPreparePrinting (CPrintInfo * pInfo)
3459 return DoPreparePrinting (pInfo);
3462 void CCrystalTextView::
3463 GetPrintHeaderText (int nPageNum, CString & text)
3465 ASSERT (m_bPrintHeader);
3469 void CCrystalTextView::
3470 GetPrintFooterText (int nPageNum, CString & text)
3472 ASSERT (m_bPrintFooter);
3473 text.Format (_T ("Page %d/%d"), nPageNum, m_nPrintPages);
3476 void CCrystalTextView::
3477 PrintHeader (CDC * pdc, int nPageNum)
3479 CRect rcHeader = m_rcPrintArea;
3480 rcHeader.bottom = rcHeader.top;
3481 rcHeader.top -= (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3484 GetPrintHeaderText (nPageNum, text);
3485 if (!text.IsEmpty ())
3486 pdc->DrawText (text, &rcHeader, DT_CENTER | DT_NOPREFIX | DT_TOP | DT_SINGLELINE);
3489 void CCrystalTextView::
3490 PrintFooter (CDC * pdc, int nPageNum)
3492 CRect rcFooter = m_rcPrintArea;
3493 rcFooter.top = rcFooter.bottom;
3494 rcFooter.bottom += (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3497 GetPrintFooterText (nPageNum, text);
3498 if (!text.IsEmpty ())
3499 pdc->DrawText (text, &rcFooter, DT_CENTER | DT_NOPREFIX | DT_BOTTOM | DT_SINGLELINE);
3503 * @brief Retrieves the print margins
3504 * @param nLeft [out] Left margin
3505 * @param nTop [out] Top margin
3506 * @param nRight [out] right margin
3507 * @param nBottom [out] Bottom margin
3509 void CCrystalTextView::
3510 GetPrintMargins (long & nLeft, long & nTop, long & nRight, long & nBottom)
3512 CWinApp *pApp = AfxGetApp ();
3513 ASSERT (pApp != nullptr);
3514 nLeft = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageLeft"), DEFAULT_PRINT_MARGIN);
3515 nRight = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageRight"), DEFAULT_PRINT_MARGIN);
3516 nTop = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageTop"), DEFAULT_PRINT_MARGIN);
3517 nBottom = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageBottom"), DEFAULT_PRINT_MARGIN);
3520 void CCrystalTextView::
3521 RecalcPageLayouts (CDC * pdc, CPrintInfo * pInfo)
3523 m_ptPageArea = pInfo->m_rectDraw;
3524 m_ptPageArea.NormalizeRect ();
3526 m_nPrintLineHeight = pdc->GetTextExtent (_T ("X")).cy;
3528 m_rcPrintArea = m_ptPageArea;
3529 CSize szTopLeft, szBottomRight;
3530 GetPrintMargins (szTopLeft.cx, szTopLeft.cy, szBottomRight.cx, szBottomRight.cy);
3531 pdc->HIMETRICtoLP (&szTopLeft);
3532 pdc->HIMETRICtoLP (&szBottomRight);
3533 m_rcPrintArea.left += szTopLeft.cx;
3534 m_rcPrintArea.right -= szBottomRight.cx;
3535 m_rcPrintArea.top += szTopLeft.cy;
3536 m_rcPrintArea.bottom -= szBottomRight.cy;
3538 m_rcPrintArea.top += m_nPrintLineHeight + m_nPrintLineHeight / 2;
3540 m_rcPrintArea.bottom -= m_nPrintLineHeight + m_nPrintLineHeight / 2;
3542 InvalidateLineCache (0, -1);
3544 m_nScreenChars = (m_rcPrintArea.Width () - GetMarginWidth (pdc)) / GetCharWidth ();
3545 m_nScreenLines = m_rcPrintArea.Height () / GetLineHeight ();
3548 void CCrystalTextView::
3549 OnBeginPrinting (CDC * pdc, CPrintInfo * pInfo)
3551 ASSERT (m_pPrintFont == nullptr);
3552 LOGFONT lf = m_lfBaseFont;
3553 CDC *pDisplayDC = GetDC ();
3554 lf.lfHeight = MulDiv (lf.lfHeight, pdc->GetDeviceCaps (LOGPIXELSY), pDisplayDC->GetDeviceCaps (LOGPIXELSY));
3555 lf.lfWidth = MulDiv (lf.lfWidth, pdc->GetDeviceCaps (LOGPIXELSX), pDisplayDC->GetDeviceCaps (LOGPIXELSX));
3556 ReleaseDC (pDisplayDC);
3558 m_pCrystalRendererSaved = m_pCrystalRenderer.release();
3559 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
3561 m_pPrintFont = new CFont;
3562 if (!m_pPrintFont->CreateFontIndirect (&lf))
3564 delete m_pPrintFont;
3565 m_pPrintFont = nullptr;
3569 GetFont (m_lfSavedBaseFont);
3570 m_pPrintFont->GetLogFont (&lf);
3577 void CCrystalTextView::
3578 OnEndPrinting (CDC * pdc, CPrintInfo * pInfo)
3580 if (m_pCrystalRendererSaved)
3582 m_pCrystalRenderer.reset(m_pCrystalRendererSaved);
3583 m_pCrystalRendererSaved = nullptr;
3585 if (m_pPrintFont != nullptr)
3587 delete m_pPrintFont;
3588 m_pPrintFont = nullptr;
3589 SetFont(m_lfSavedBaseFont);
3592 m_nPrintLineHeight = 0;
3593 m_bPrinting = false;
3596 void CCrystalTextView::
3597 OnPrint (CDC * pdc, CPrintInfo * pInfo)
3599 pdc->SelectObject (m_pPrintFont);
3601 const COLORREF defaultLineColor = RGB(0,0,0);
3602 const COLORREF defaultBgColor = RGB(255,255,255);
3604 RecalcPageLayouts (pdc, pInfo);
3606 m_nPrintPages = (GetSubLineCount () + GetScreenLines () - 1) / GetScreenLines ();
3608 ASSERT (pInfo->m_nCurPage >= 1 && (int) pInfo->m_nCurPage <= m_nPrintPages);
3610 int nTopSubLine = (pInfo->m_nCurPage - 1) * GetScreenLines ();
3611 int nEndSubLine = nTopSubLine + GetScreenLines () - 1;
3612 if (nEndSubLine >= GetSubLineCount ())
3613 nEndSubLine = GetSubLineCount () - 1;
3615 int nTopLine, nEndLine;
3616 GetLineBySubLine (nTopSubLine, nTopLine, nSubLines);
3617 GetLineBySubLine (nEndSubLine, nEndLine, nSubLines);
3619 TRACE (_T ("Printing page %d of %d, lines %d - %d\n"),
3620 pInfo->m_nCurPage, m_nPrintPages, nTopLine, nEndLine);
3622 m_pCrystalRenderer->BindDC(*pdc, m_rcPrintArea);
3623 m_pCrystalRenderer->BeginDraw();
3625 m_pCrystalRenderer->SetTextColor(defaultLineColor);
3626 m_pCrystalRenderer->SetBkColor(defaultBgColor);
3630 PrintHeader (pdc, pInfo->m_nCurPage);
3635 PrintFooter (pdc, pInfo->m_nCurPage);
3638 // set clipping region
3639 // see http://support.microsoft.com/kb/128334
3640 CRect rectClip = m_rcPrintArea;
3641 rectClip.right = rectClip.left + GetMarginWidth (pdc) + GetScreenChars () * GetCharWidth ();
3642 rectClip.bottom = rectClip.top + GetScreenLines () * GetLineHeight ();
3643 if (!!pdc->IsKindOf (RUNTIME_CLASS (CPreviewDC)))
3645 CPreviewDC *pPrevDC = (CPreviewDC *)pdc;
3647 pPrevDC->PrinterDPtoScreenDP (&rectClip.TopLeft ());
3648 pPrevDC->PrinterDPtoScreenDP (&rectClip.BottomRight ());
3651 ::GetViewportOrgEx (pdc->m_hDC,&ptOrg);
3655 rgn.CreateRectRgnIndirect (&rectClip);
3656 pdc->SelectClipRgn (&rgn);
3660 CRect rcLine = m_rcPrintArea;
3661 int nLineHeight = GetLineHeight ();
3662 rcLine.bottom = rcLine.top + nLineHeight;
3664 rcMargin.right = rcMargin.left + GetMarginWidth (pdc);
3665 rcLine.left = rcMargin.right;
3667 int nSubLineOffset = GetSubLineIndex (nTopLine) - nTopSubLine;
3668 if( nSubLineOffset < 0 )
3670 rcLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
3673 int nLineCount = GetLineCount();
3675 for (nCurrentLine = nTopLine; nCurrentLine <= nEndLine; nCurrentLine++)
3677 rcLine.bottom = rcLine.top + GetSubLines (nCurrentLine) * nLineHeight;
3678 rcMargin.bottom = rcLine.bottom;
3680 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
3682 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
3683 DrawSingleLine (rcLine, nCurrentLine);
3684 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
3685 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.bottom-1);
3688 rcLine.top = rcLine.bottom;
3689 rcMargin.top = rcLine.bottom;
3692 m_pCrystalRenderer->EndDraw();
3694 pdc->SelectClipRgn (nullptr);
3698 /////////////////////////////////////////////////////////////////////////////
3699 // CCrystalTextView message handlers
3701 int CCrystalTextView::
3704 if (m_pTextBuffer == nullptr)
3705 return 1; // Single empty line
3707 int nLineCount = m_pTextBuffer->GetLineCount ();
3708 ASSERT (nLineCount > 0);
3713 int CCrystalTextView::GetSubLineCount()
3715 const int nLineCount = GetLineCount();
3717 // if we do not wrap words, number of sub lines is
3718 // equal to number of lines
3719 if( !m_bWordWrap && !m_bHideLines )
3722 // calculate number of sub lines
3723 if (nLineCount <= 0)
3725 return CCrystalTextView::GetSubLineIndex( nLineCount - 1 ) + GetSubLines( nLineCount - 1 );
3728 int CCrystalTextView::GetSubLineIndex( int nLineIndex )
3730 // if we do not wrap words, subline index of this line is equal to its index
3731 if( !m_bWordWrap && !m_bHideLines )
3734 // calculate subline index of the line
3735 int nSubLineCount = 0;
3736 int nLineCount = GetLineCount();
3738 if( nLineIndex >= nLineCount )
3739 nLineIndex = nLineCount - 1;
3741 // return cached subline index of the line if it is already cached.
3742 if (nLineIndex <= m_nLastLineIndexCalculatedSubLineIndex)
3743 return (*m_panSubLineIndexCache)[nLineIndex];
3745 // calculate subline index of the line and cache it.
3746 if (m_nLastLineIndexCalculatedSubLineIndex >= 0)
3747 nSubLineCount = (*m_panSubLineIndexCache)[m_nLastLineIndexCalculatedSubLineIndex];
3750 m_nLastLineIndexCalculatedSubLineIndex = 0;
3751 if (m_panSubLineIndexCache->size () >= 0)
3752 m_panSubLineIndexCache->resize (1);
3753 (*m_panSubLineIndexCache)[0] = 0;
3756 // TODO: Rethink this, it is very time consuming
3757 for( int i = m_nLastLineIndexCalculatedSubLineIndex; i < nLineIndex; i++ )
3759 if (m_panSubLineIndexCache->size () >= i)
3760 m_panSubLineIndexCache->resize (i + 1);
3761 (*m_panSubLineIndexCache)[i] = nSubLineCount;
3762 nSubLineCount+= GetSubLines( i );
3764 if (m_panSubLineIndexCache->size () >= nLineIndex)
3765 m_panSubLineIndexCache->resize (nLineIndex + 1);
3766 (*m_panSubLineIndexCache)[nLineIndex] = nSubLineCount;
3767 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex;
3769 return nSubLineCount;
3772 // See comment in the header file
3773 void CCrystalTextView::GetLineBySubLine(int nSubLineIndex, int &nLine, int &nSubLine)
3775 if (GetSubLineCount() == 0)
3782 ASSERT( nSubLineIndex < GetSubLineCount() );
3784 // if we do not wrap words, nLine is equal to nSubLineIndex and nSubLine is allways 0
3785 if ( !m_bWordWrap && !m_bHideLines )
3787 nLine = nSubLineIndex;
3793 const int nLineCount = GetLineCount();
3796 int base = 0, i = 0, nSubLineIndex2 = 0;
3797 for (int lim = nLineCount; lim != 0; lim >>= 1)
3799 i = base + (lim >> 1);
3800 nSubLineIndex2 = GetSubLineIndex(i);
3801 if (nSubLineIndex >= nSubLineIndex2 && nSubLineIndex < nSubLineIndex2 + GetSubLines(i))
3803 else if (nSubLineIndex2 <= nSubLineIndex) /* key > p: move right */
3807 } /* else move left */
3810 ASSERT(i < nLineCount);
3812 nSubLine = nSubLineIndex - nSubLineIndex2;
3815 int CCrystalTextView::
3816 GetLineLength (int nLineIndex) const
3818 if (m_pTextBuffer == nullptr)
3820 return m_pTextBuffer->GetLineLength (nLineIndex);
3823 int CCrystalTextView::
3824 GetFullLineLength (int nLineIndex) const
3826 if (m_pTextBuffer == nullptr)
3828 return m_pTextBuffer->GetFullLineLength (nLineIndex);
3831 // How many bytes of line are displayed on-screen?
3832 int CCrystalTextView::
3833 GetViewableLineLength (int nLineIndex) const
3836 return GetFullLineLength(nLineIndex);
3838 return GetLineLength(nLineIndex);
3841 const tchar_t* CCrystalTextView::
3842 GetLineChars (int nLineIndex) const
3844 if (m_pTextBuffer == nullptr)
3846 return m_pTextBuffer->GetLineChars (nLineIndex);
3850 * @brief Reattach buffer after deleting/inserting ghost lines :
3852 * @note no need to reinitialize the horizontal scrollbar
3853 * no need to reset the editor options (m_bOvrMode, m_bLastReplace)
3855 void CCrystalTextView::
3856 ReAttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3858 if (m_pTextBuffer != nullptr)
3859 m_pTextBuffer->RemoveView (this);
3860 if (pBuf == nullptr)
3862 pBuf = LocateTextBuffer ();
3865 m_pTextBuffer = pBuf;
3866 if (m_pTextBuffer != nullptr)
3867 m_pTextBuffer->AddView (this);
3868 // don't reset CCrystalEditView options
3869 CCrystalTextView::ResetView ();
3871 // Init scrollbars arrows
3872 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3873 if (pVertScrollBarCtrl != nullptr)
3874 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3875 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3876 // Update vertical scrollbar only
3877 InvalidateVertScrollBar ();
3881 * @brief Attach buffer (maybe for the first time)
3882 * initialize the view and initialize both scrollbars
3884 void CCrystalTextView::
3885 AttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3887 if (m_pTextBuffer != nullptr)
3888 m_pTextBuffer->RemoveView (this);
3889 if (pBuf == nullptr)
3891 pBuf = LocateTextBuffer ();
3894 m_pTextBuffer = pBuf;
3895 if (m_pTextBuffer != nullptr)
3896 m_pTextBuffer->AddView (this);
3900 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3901 if (pVertScrollBarCtrl != nullptr)
3902 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3903 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3904 CScrollBar *pHorzScrollBarCtrl = GetScrollBarCtrl (SB_HORZ);
3905 if (pHorzScrollBarCtrl != nullptr)
3906 pHorzScrollBarCtrl->EnableScrollBar(CoverLength(m_nTopLine, GetScreenLines(), GetScreenChars()) ?
3907 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3909 // Update scrollbars
3910 InvalidateVertScrollBar ();
3911 InvalidateHorzScrollBar ();
3914 void CCrystalTextView::
3917 if (m_pTextBuffer != nullptr)
3919 m_pTextBuffer->RemoveView (this);
3920 m_pTextBuffer = nullptr;
3921 // don't reset CCrystalEditView options
3922 CCrystalTextView::ResetView ();
3926 int CCrystalTextView::
3929 if (m_nScreenLines == -1)
3932 GetClientRect (&rect);
3933 m_nScreenLines = (rect.Height () - GetTopMarginHeight ()) / GetLineHeight ();
3935 return m_nScreenLines;
3938 bool CCrystalTextView::
3939 GetItalic (int nColorIndex)
3941 // WINMERGE - since italic text has problems,
3942 // lets disable it. E.g. "_" chars disappear and last
3943 // char may be cropped.
3946 // return nColorIndex == COLORINDEX_COMMENT;
3949 bool CCrystalTextView::
3950 GetBold (int nColorIndex)
3952 if (m_pColors != nullptr)
3954 nColorIndex &= ~COLORINDEX_MASK;
3955 return m_pColors->GetBold(nColorIndex);
3961 int CCrystalTextView::
3964 if (m_nScreenChars == -1)
3967 GetClientRect (&rect);
3968 m_nScreenChars = (rect.Width () - GetMarginWidth ()) / GetCharWidth ();
3970 return m_nScreenChars;
3973 void CCrystalTextView::
3976 DetachFromBuffer ();
3979 CView::OnDestroy ();
3981 if (m_pCacheBitmap != nullptr)
3983 delete m_pCacheBitmap;
3984 m_pCacheBitmap = nullptr;
3988 BOOL CCrystalTextView::
3989 OnEraseBkgnd (CDC * pdc)
3991 UNREFERENCED_PARAMETER(pdc);
3995 void CCrystalTextView::
3996 OnSize (UINT nType, int cx, int cy)
3998 CView::OnSize (nType, cx, cy);
4001 // get char position of top left visible character with old cached word wrap
4003 SubLineCursorPosToTextPos( CEPoint( 0, m_nTopSubLine ), topPos );
4007 // we have to recompute the line wrapping
4008 InvalidateScreenRect(false);
4010 // compute new top sub line
4012 CharPosToPoint( topPos.y, topPos.x, topSubLine );
4013 m_nTopSubLine = GetSubLineIndex(topPos.y) + topSubLine.y;
4015 ScrollToSubLine( m_nTopSubLine );
4017 // set caret to right position
4021 InvalidateVertScrollBar ();
4022 InvalidateHorzScrollBar ();
4025 void CCrystalTextView::
4026 UpdateSiblingScrollPos (bool bHorz)
4028 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
4029 if (pSplitterWnd != nullptr)
4031 // See CSplitterWnd::IdFromRowCol() implementation for details
4032 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
4033 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
4034 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
4035 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
4039 int nCols = pSplitterWnd->GetColumnCount ();
4040 for (int nCol = 0; nCol < nCols; nCol++)
4042 if (nCol != nCurrentCol) // We don't need to update ourselves
4044 CCrystalTextView *pSiblingView = GetSiblingView (nCurrentRow, nCol);
4045 if (pSiblingView != nullptr)
4046 pSiblingView->OnUpdateSibling (this, false);
4052 int nRows = pSplitterWnd->GetRowCount ();
4053 for (int nRow = 0; nRow < nRows; nRow++)
4055 if (nRow != nCurrentRow) // We don't need to update ourselves
4057 CCrystalTextView *pSiblingView = GetSiblingView (nRow, nCurrentCol);
4058 if (pSiblingView != nullptr)
4059 pSiblingView->OnUpdateSibling (this, false);
4066 void CCrystalTextView::
4067 OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
4069 if (pUpdateSource != this)
4071 ASSERT (pUpdateSource != nullptr);
4072 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
4075 ASSERT (pUpdateSource->m_nTopLine >= 0);
4076 ASSERT (pUpdateSource->m_nTopLine < GetLineCount ());
4077 if (pUpdateSource->m_nTopLine != m_nTopLine)
4079 ScrollToLine (pUpdateSource->m_nTopLine, true, false);
4085 ASSERT (pUpdateSource->m_nOffsetChar >= 0);
4086 ASSERT (pUpdateSource->m_nOffsetChar < GetMaxLineLength (m_nTopLine, GetScreenLines()));
4087 if (pUpdateSource->m_nOffsetChar != m_nOffsetChar)
4089 ScrollToChar (pUpdateSource->m_nOffsetChar, true, false);
4096 void CCrystalTextView::
4097 RecalcVertScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
4099 SCROLLINFO si{ sizeof (si) };
4103 si.nPos = m_nTopSubLine;
4107 const int nScreenLines = GetScreenLines();
4108 if( m_nTopSubLine > 0 && nScreenLines >= GetSubLineCount() )
4114 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4116 si.nMax = GetSubLineCount() - 1;
4117 si.nPage = nScreenLines;
4118 si.nPos = m_nTopSubLine;
4120 VERIFY (SetScrollInfo (SB_VERT, &si, bRedraw));
4123 void CCrystalTextView::
4124 OnVScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
4126 CView::OnVScroll (nSBCode, nPos, pScrollBar);
4128 // Note we cannot use nPos because of its 16-bit nature
4129 SCROLLINFO si{ sizeof(si) };
4130 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
4131 VERIFY (GetScrollInfo (SB_VERT, &si));
4133 // Get the minimum and maximum scroll-bar positions.
4134 int nMinPos = si.nMin;
4135 int nMaxPos = si.nMax;
4137 // Get the current position of scroll box.
4138 int nCurPos = si.nPos;
4140 bool bDisableSmooth = true;
4143 case SB_TOP: // Scroll to top.
4145 bDisableSmooth = false;
4148 case SB_BOTTOM: // Scroll to bottom.
4150 bDisableSmooth = false;
4153 case SB_LINEUP: // Scroll one line up.
4154 if (nCurPos > nMinPos)
4158 case SB_LINEDOWN: // Scroll one line down.
4159 if (nCurPos < nMaxPos)
4163 case SB_PAGEUP: // Scroll one page up.
4164 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4165 bDisableSmooth = false;
4168 case SB_PAGEDOWN: // Scroll one page down.
4169 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4170 bDisableSmooth = false;
4173 case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position
4174 nCurPos = si.nTrackPos; // of the scroll box at the end of the drag operation.
4177 case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the
4178 nCurPos = si.nTrackPos; // position that the scroll box has been dragged to.
4181 ScrollToSubLine(nCurPos, bDisableSmooth);
4185 void CCrystalTextView::
4186 RecalcHorzScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
4188 SCROLLINFO si{ sizeof(si) };
4190 const int nScreenChars = GetScreenChars();
4191 const TextLayoutMode layoutMode = GetTextLayoutMode ();
4193 if (layoutMode == TEXTLAYOUT_WORDWRAP)
4195 if (m_nOffsetChar > nScreenChars)
4201 // Disable horizontal scroll bar
4202 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4204 SetScrollInfo (SB_HORZ, &si);
4208 int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
4209 if (layoutMode == TEXTLAYOUT_TABLE_NOWORDWRAP || layoutMode == TEXTLAYOUT_TABLE_WORDWRAP)
4211 auto widths = m_pTextBuffer->GetColumnWidths ();
4212 nMaxLineLen = (std::max)(nMaxLineLen, std::accumulate (widths.begin (), widths.end (), 0));
4218 si.nPos = m_nOffsetChar;
4222 if (nScreenChars >= nMaxLineLen && m_nOffsetChar > 0)
4228 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4231 // Horiz scroll limit to longest line + one screenwidth
4232 si.nMax = nMaxLineLen + nScreenChars;
4233 si.nPage = nScreenChars;
4234 si.nPos = m_nOffsetChar;
4236 VERIFY (SetScrollInfo (SB_HORZ, &si, bRedraw));
4239 void CCrystalTextView::
4240 OnHScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
4242 // Default handler not needed
4243 //CView::OnHScroll (nSBCode, nPos, pScrollBar);
4245 // Again, we cannot use nPos because it's 16-bit
4246 SCROLLINFO si { sizeof(si) };
4247 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
4248 VERIFY (GetScrollInfo (SB_HORZ, &si));
4250 // Get the minimum and maximum scroll-bar positions.
4251 int nMinPos = si.nMin;
4252 int nMaxPos = si.nMax;
4254 // Get the current position of scroll box.
4255 int nCurPos = si.nPos;
4259 case SB_LEFT: // Scroll to far left.
4263 case SB_RIGHT: // Scroll to far right.
4267 case SB_ENDSCROLL: // End scroll.
4270 case SB_LINELEFT: // Scroll left.
4271 if (nCurPos > nMinPos)
4275 case SB_LINERIGHT: // Scroll right.
4276 if (nCurPos < nMaxPos)
4280 case SB_PAGELEFT: // Scroll one page left.
4281 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4284 case SB_PAGERIGHT: // Scroll one page right.
4285 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4288 case SB_THUMBPOSITION: // Scroll to absolute position. The current position is
4289 nCurPos = si.nTrackPos; // specified by the nPos parameter.
4292 case SB_THUMBTRACK: // Drag scroll box to specified position. The current
4293 nCurPos = si.nTrackPos; // position is specified by the nPos parameter
4294 // The SB_THUMBTRACK scroll-bar code typically is used by applications that give
4295 // some feedback while the scroll box is being dragged.
4298 ScrollToChar (nCurPos, true);
4299 // This is needed, but why ? OnVScroll don't need to call UpdateCaret
4303 BOOL CCrystalTextView::
4304 OnSetCursor (CWnd * pWnd, UINT nHitTest, UINT message)
4306 if (nHitTest == HTCLIENT)
4309 ::GetCursorPos (&pt);
4310 ScreenToClient (&pt);
4311 if (pt.y < GetTopMarginHeight ())
4313 if (m_pTextBuffer->GetTableEditing ())
4315 const int nColumnResizing = ClientToColumnResizing (pt.x);
4316 ::SetCursor (::LoadCursor (nullptr, nColumnResizing >= 0 ? IDC_SIZEWE : IDC_ARROW));
4320 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW));
4323 else if (pt.x < GetMarginWidth ())
4325 ::SetCursor (::LoadCursor (AfxGetInstanceHandle (), MAKEINTRESOURCE (IDR_MARGIN_CURSOR)));
4329 CEPoint ptText = ClientToText (pt);
4330 PrepareSelBounds ();
4331 if (IsInsideSelBlock (ptText))
4333 // [JRT]: Support For Disabling Drag and Drop...
4334 if (!m_bDisableDragAndDrop) // If Drag And Drop Not Disabled
4336 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW)); // Set To Arrow Cursor
4340 ::SetCursor (::LoadCursor (nullptr, IDC_IBEAM));
4344 return CView::OnSetCursor (pWnd, nHitTest, message);
4347 int CCrystalTextView::
4348 ClientToIdealTextPos (int x)
4351 if (x > GetMarginWidth ())
4352 nPos = m_nOffsetChar + (x - GetMarginWidth ()) / GetCharWidth ();
4359 * @brief Converts client area point to text position.
4360 * @param [in] point Client area point.
4361 * @return Text position (line index, char index in line).
4362 * @note For gray selection area char index is 0.
4364 CEPoint CCrystalTextView::
4365 ClientToText (const CPoint & point)
4368 const int nSubLineCount = GetSubLineCount();
4369 const int nLineCount = GetLineCount();
4372 pt.y = m_nTopSubLine + (point.y - GetTopMarginHeight ()) / GetLineHeight();
4373 if (pt.y >= nSubLineCount)
4374 pt.y = nSubLineCount - 1;
4381 GetLineBySubLine( pt.y, nLine, nSubLineOffset );
4384 const tchar_t* pszLine = nullptr;
4386 vector<int> anBreaks(1);
4389 if (pt.y >= 0 && pt.y < nLineCount)
4391 nLength = GetLineLength( pt.y );
4392 anBreaks.resize(nLength + 1);
4393 pszLine = GetLineChars(pt.y);
4394 WrapLineCached( pt.y, GetScreenChars(), &anBreaks, nBreaks );
4396 if (nBreaks > nSubLineOffset && GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
4397 nLength = anBreaks[nSubLineOffset] - 1;
4400 // Char index for margin area is 0
4401 int nPos = ClientToIdealTextPos (point.x);
4406 const int nTabSize = GetTabSize();
4408 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, nLength);
4409 switch (GetTextLayoutMode ())
4411 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4413 int nColumnCount = m_pTextBuffer->GetColumnCount (nLine);
4414 int nColumnTotalWidth = 0;
4416 bool bInQuote = false;
4417 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4418 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4419 while (nIndex < nLength)
4422 if (!bInQuote && pszLine[nIndex] == sep)
4424 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4425 nOffset = nColumnTotalWidth - nCurPos;
4429 if (pszLine[nIndex] == quote)
4430 bInQuote = !bInQuote;
4431 if (pszLine[nIndex] == '\t')
4434 nOffset = GetCharCellCountFromChar (pszLine + nIndex);
4435 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4436 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
4441 if (n > nPos && i == nSubLineOffset)
4444 nIndex = pIterChar->next ();
4448 case TEXTLAYOUT_TABLE_WORDWRAP:
4452 int nColumnSumWidth = 0;
4453 int nColumnCurPoint = INT_MAX;
4455 if (nPos < m_pTextBuffer->GetColumnWidth (0))
4456 nColumnCurPoint = 0;
4457 while (nIndex < nLength)
4459 if (i < static_cast<int>(anBreaks.size()) && nIndex == abs(anBreaks[i]))
4461 if (anBreaks[i++] < 0)
4464 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4465 n = nColumnSumWidth;
4466 if (nColumnSumWidth <= nPos && nPos < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4467 nColumnCurPoint = nColumn;
4471 n = nColumnSumWidth;
4477 if (pszLine[nIndex] == '\t')
4480 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4484 if (n > nPos && j == nSubLineOffset)
4486 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
4488 nIndex = nPrevIndex;
4491 else if ( j == nSubLineOffset)
4492 nPrevIndex = nIndex;
4494 nIndex = pIterChar->next();
4496 if (nIndex == nLength && j != nSubLineOffset)
4497 nIndex = nPrevIndex;
4502 while (nIndex < nLength)
4504 if (nBreaks && i < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[i])
4511 if (pszLine[nIndex] == '\t')
4512 nOffset = nTabSize - nCurPos % nTabSize;
4514 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4518 if (n > nPos && i == nSubLineOffset)
4521 nIndex = pIterChar->next();
4527 ASSERT(nIndex >= 0 && nIndex <= nLength);
4532 int CCrystalTextView::
4533 ClientToColumn (int x)
4536 GetClientRect (&rcClient);
4537 int nCharWidth = GetCharWidth ();
4538 int nMarginWidth = GetMarginWidth ();
4539 for (int nColumn = 0, columnleft = nMarginWidth - m_nOffsetChar * nCharWidth;
4540 columnleft < rcClient.Width ();
4541 columnleft += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
4543 if (columnleft <= x && x < columnleft + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth)
4549 int CCrystalTextView::
4550 ClientToColumnResizing (int x)
4552 const int nColumn = ClientToColumn (x);
4553 const int nColumnL = ClientToColumn (x - 4);
4554 const int nColumnR = ClientToColumn (x + 4);
4555 if (nColumn != nColumnL || nColumn != nColumnR)
4557 return (nColumn != nColumnL) ? nColumnL : nColumn;
4562 void CCrystalTextView::
4563 AssertValidTextPos (const CEPoint & point)
4565 if (GetLineCount () > 0)
4567 ASSERT (m_nTopLine >= 0 && m_nOffsetChar >= 0);
4568 ASSERT (point.y >= 0 && point.y < GetLineCount ());
4569 ASSERT (point.x >= 0 && point.x <= GetViewableLineLength (point.y));
4574 bool CCrystalTextView::
4575 IsValidTextPos (const CEPoint &point)
4577 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4578 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4581 bool CCrystalTextView::
4582 IsValidTextPosX (const CEPoint &point)
4584 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4585 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4588 bool CCrystalTextView::
4589 IsValidTextPosY (const CEPoint &point)
4591 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4592 point.y >= 0 && point.y < GetLineCount ();
4595 CPoint CCrystalTextView::
4596 TextToClient (const CEPoint & point)
4598 ASSERT_VALIDTEXTPOS (point);
4599 const tchar_t* pszLine = GetLineChars (point.y);
4601 int nColumnIndex = 0;
4605 int nSubLineStart = CharPosToPoint( point.y, point.x, charPoint, &nColumnIndex );
4606 charPoint.y+= GetSubLineIndex( point.y );
4608 // compute y-position
4609 pt.y = (charPoint.y - m_nTopSubLine) * GetLineHeight() + GetTopMarginHeight ();
4611 // if pt.x is null, we know the result
4612 if( charPoint.x == 0 && nColumnIndex == 0)
4614 pt.x = GetMarginWidth();
4618 // we have to calculate x-position
4621 pt.y = (point.y - m_nTopLine) * GetLineHeight();
4625 int nTabSize = GetTabSize ();
4626 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, point.x);
4627 switch (GetTextLayoutMode ())
4629 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4631 int nColumnCount = m_pTextBuffer->GetColumnCount (point.y);
4632 int nColumnTotalWidth = 0;
4634 bool bInQuote = false;
4635 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4636 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4637 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4639 if (!bInQuote && pszLine[nIndex] == sep)
4641 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4642 pt.x = nColumnTotalWidth;
4646 if (pszLine[nIndex] == quote)
4647 bInQuote = !bInQuote;
4648 if (pszLine[nIndex] == _T ('\t'))
4651 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4652 if (nColumn < nColumnCount && pt.x > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4653 pt.x = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4656 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4660 case TEXTLAYOUT_TABLE_WORDWRAP:
4663 for (int i = 0; i < nColumnIndex; ++i)
4664 pt.x += m_pTextBuffer->GetColumnWidth (i);
4665 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4667 if( nIndex >= nSubLineStart )
4669 if (pszLine[nIndex] == '\t')
4672 pt.x += GetCharCellCountFromChar (pszLine + nIndex);
4675 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4681 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4684 if( nIndex == nSubLineStart )
4687 if (pszLine[nIndex] == _T ('\t'))
4688 pt.x += (nTabSize - pt.x % nTabSize);
4690 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4696 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4702 int CCrystalTextView::
4703 ColumnToClient (int nColumn)
4706 GetClientRect (&rcClient);
4707 int nCharWidth = GetCharWidth ();
4708 int columnleft = GetMarginWidth () - m_nOffsetChar * nCharWidth;
4709 for (int nColumn2 = 0; nColumn2 != nColumn && columnleft < rcClient.Width ();
4710 columnleft += m_pTextBuffer->GetColumnWidth (nColumn2++) * nCharWidth)
4715 void CCrystalTextView::
4716 InvalidateLines (int nLine1, int nLine2, bool bInvalidateMargin /*= false*/ )
4718 bInvalidateMargin = true;
4719 const int nTopMarginHeight = GetTopMarginHeight ();
4720 const int nLineHeight = GetLineHeight();
4724 GetClientRect (&rcInvalid);
4725 if (!bInvalidateMargin)
4726 rcInvalid.left += GetMarginWidth ();
4728 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4730 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4733 InvalidateRect (&rcInvalid, false);
4737 if (nLine2 < nLine1)
4744 GetClientRect (&rcInvalid);
4745 if (!bInvalidateMargin)
4746 rcInvalid.left += GetMarginWidth ();
4748 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4749 rcInvalid.bottom = (GetSubLineIndex( nLine2 ) - m_nTopSubLine + GetSubLines( nLine2 )) * nLineHeight + nTopMarginHeight;
4751 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4752 rcInvalid.bottom = (nLine2 - m_nTopLine + 1) * GetLineHeight();
4755 InvalidateRect (&rcInvalid, false);
4759 void CCrystalTextView::
4760 SetSelection (const CEPoint & ptStart, const CEPoint & ptEnd, bool bUpdateView /* = true */)
4762 ASSERT_VALIDTEXTPOS (ptStart);
4763 ASSERT_VALIDTEXTPOS (ptEnd);
4764 if (m_ptSelStart == ptStart && !m_bRectangularSelection)
4766 if (m_ptSelEnd != ptEnd)
4767 InvalidateLines (ptEnd.y, m_ptSelEnd.y);
4771 InvalidateLines (ptStart.y, ptEnd.y);
4772 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4774 m_ptSelStart = ptStart;
4778 void CCrystalTextView::
4779 AdjustTextPoint (CPoint & point)
4781 point.x += GetCharWidth () / 2; //todo
4785 void CCrystalTextView::
4786 OnSetFocus (CWnd * pOldWnd)
4788 CView::OnSetFocus (pOldWnd);
4791 if (m_ptSelStart != m_ptSelEnd)
4792 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4796 unsigned CCrystalTextView::
4797 ParseLine (unsigned dwCookie, const tchar_t *pszChars, int nLength, TEXTBLOCK * pBuf, int &nActualItems)
4799 return m_CurSourceDef->ParseLineX (dwCookie, pszChars, nLength, pBuf, nActualItems);
4802 int CCrystalTextView::
4803 CalculateActualOffset (int nLineIndex, int nCharIndex, bool bAccumulate)
4805 const int nLength = GetLineLength (nLineIndex);
4806 ASSERT (nCharIndex >= 0 && nCharIndex <= nLength);
4807 const tchar_t* pszChars = GetLineChars (nLineIndex);
4809 const int nTabSize = GetTabSize ();
4810 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nCharIndex);
4812 switch (GetTextLayoutMode ())
4814 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4816 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4817 int nColumnTotalWidth = 0;
4819 bool bInQuote = false;
4820 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4821 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4822 for (I = 0; I < nCharIndex; I = pIterChar->next())
4824 if (!bInQuote && pszChars[I] == sep)
4826 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4827 nOffset = nColumnTotalWidth;
4831 if (pszChars[I] == quote)
4832 bInQuote = !bInQuote;
4833 else if (pszChars[I] == '\t')
4836 nOffset += GetCharCellCountFromChar (pszChars + I);
4837 if (nColumn < nColumnCount && nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4838 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4844 case TEXTLAYOUT_TABLE_WORDWRAP:
4846 int nColumnIndex = 0;
4848 int nSubLineStart = CharPosToPoint( nLineIndex, nCharIndex, charPoint, &nColumnIndex );
4849 for (int i = 0; i < nColumnIndex; ++i)
4850 nOffset += m_pTextBuffer->GetColumnWidth (i);
4851 for (int nIndex = 0; nIndex < nCharIndex; nIndex = pIterChar->next())
4853 if( nIndex >= nSubLineStart )
4855 if (pszChars[nIndex] == '\t')
4858 nOffset += GetCharCellCountFromChar (pszChars + nIndex);
4867 vector<int> anBreaks(nLength + 1);
4870 /*if( nLength > GetScreenChars() )*/
4871 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
4879 for( J = nBreaks - 1; J >= 0 && nCharIndex < anBreaks[J]; J-- );
4880 nPreBreak = (J >= 0) ? anBreaks[J] : 0;
4883 for (I = 0; I < nCharIndex; I = pIterChar->next())
4886 if( nPreBreak == I && nBreaks )
4887 nPreOffset = nOffset;
4889 if (pszChars[I] == _T ('\t'))
4890 nOffset += (nTabSize - nOffset % nTabSize);
4892 nOffset += GetCharCellCountFromChar(pszChars + I);
4897 if( nPreBreak == I && nBreaks > 0)
4900 return nOffset - nPreOffset;
4908 int CCrystalTextView::
4909 ApproxActualOffset (int nLineIndex, int nOffset)
4914 int nLength = GetLineLength (nLineIndex);
4915 const tchar_t* pszChars = GetLineChars (nLineIndex);
4916 int nCurrentOffset = 0;
4917 int nTabSize = GetTabSize ();
4918 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
4919 switch (GetTextLayoutMode ())
4921 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4922 case TEXTLAYOUT_TABLE_WORDWRAP:
4924 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4925 int nColumnTotalWidth = 0;
4926 bool bInQuote = false;
4927 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4928 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4929 for (int I = 0, nColumn = 0; I < nLength; I = pIterChar->next())
4931 if (!bInQuote && pszChars[I] ==sep)
4933 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4934 nCurrentOffset = nColumnTotalWidth;
4938 if (pszChars[I] == quote)
4939 bInQuote = !bInQuote;
4940 if (pszChars[I] == '\t')
4943 nCurrentOffset += GetCharCellCountFromChar (pszChars + I);
4944 if (nColumn < nColumnCount && nCurrentOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4945 nCurrentOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4947 if (nCurrentOffset >= nOffset)
4949 if (nOffset <= nCurrentOffset - nTabSize / 2)
4951 return pIterChar->next ();
4958 for (int I = 0; I < nLength; I = pIterChar->next())
4960 if (pszChars[I] == _T ('\t'))
4961 nCurrentOffset += (nTabSize - nCurrentOffset % nTabSize);
4964 nCurrentOffset += GetCharCellCountFromChar(pszChars + I);
4966 if (nCurrentOffset >= nOffset)
4968 if (nOffset <= nCurrentOffset - nTabSize / 2)
4970 return pIterChar->next();
4978 void CCrystalTextView::
4979 EnsureVisible (CEPoint pt)
4981 EnsureVisible(pt, pt);
4984 void CCrystalTextView::
4985 OnKillFocus (CWnd * pNewWnd)
4987 CView::OnKillFocus (pNewWnd);
4991 if (m_ptSelStart != m_ptSelEnd)
4992 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4993 if (m_bDragSelection)
4996 KillTimer (m_nDragSelTimer);
4997 m_bDragSelection = false;
5001 void CCrystalTextView::
5004 CView::OnSysColorChange ();
5008 void CCrystalTextView::
5009 GetText (const CEPoint & ptStart, const CEPoint & ptEnd, CString & text, bool bExcludeInvisibleLines /*= true*/)
5011 if (m_pTextBuffer != nullptr)
5012 m_pTextBuffer->GetText (ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, text);
5017 void CCrystalTextView::
5018 GetTextInColumnSelection (CString & text, bool bExcludeInvisibleLines /*= true*/)
5020 if (m_pTextBuffer == nullptr)
5026 PrepareSelBounds ();
5028 CString sEol = m_pTextBuffer->GetStringEol (CRLFSTYLE::DOS);
5031 for (int L = m_ptDrawSelStart.y; L <= m_ptDrawSelEnd.y; L++)
5032 nBufSize += GetLineLength (L) + sEol.GetLength ();
5033 tchar_t* pszBuf = text.GetBuffer (nBufSize);
5035 for (int I = m_ptDrawSelStart.y; I <= m_ptDrawSelEnd.y; I++)
5037 if (bExcludeInvisibleLines && (GetLineFlags (I) & LF_INVISIBLE))
5039 int nSelLeft, nSelRight;
5040 GetColumnSelection (I, nSelLeft, nSelRight);
5041 memcpy (pszBuf, GetLineChars (I) + nSelLeft, sizeof (tchar_t) * (nSelRight - nSelLeft));
5042 pszBuf += (nSelRight - nSelLeft);
5043 memcpy (pszBuf, sEol, sizeof (tchar_t) * sEol.GetLength ());
5044 pszBuf += sEol.GetLength ();
5047 text.ReleaseBuffer ();
5051 void CCrystalTextView::
5052 UpdateView (CCrystalTextView * pSource, CUpdateContext * pContext,
5053 DWORD dwFlags, int nLineIndex /*= -1*/ )
5055 // SetTextType (GetExt (GetDocument ()->GetPathName ()));
5056 if (dwFlags & UPDATE_RESET)
5059 InvalidateVertScrollBar ();
5060 InvalidateHorzScrollBar ();
5064 int nLineCount = GetLineCount ();
5065 ASSERT (nLineCount > 0);
5066 ASSERT (nLineIndex >= -1 && nLineIndex < nLineCount);
5067 if ((dwFlags & UPDATE_SINGLELINE) != 0)
5069 ASSERT (nLineIndex != -1);
5070 // All text below this line should be reparsed
5071 const int cookiesSize = (int) m_ParseCookies->size();
5072 if (cookiesSize > 0)
5074 ASSERT (cookiesSize == nLineCount);
5075 // must be reinitialized to invalid value (DWORD) - 1
5076 for (int i = nLineIndex; i < cookiesSize; ++i)
5077 (*m_ParseCookies)[i] = static_cast<uint32_t>(-1);
5079 // This line'th actual length must be recalculated
5080 if (m_pnActualLineLength->size())
5082 ASSERT (m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
5083 // must be initialized to invalid code -1
5084 (*m_pnActualLineLength)[nLineIndex] = -1;
5086 InvalidateLineCache( nLineIndex, nLineIndex );
5089 // Repaint the lines
5090 InvalidateLines (nLineIndex, -1, true);
5094 if (m_bViewLineNumbers)
5095 // if enabling linenumber, we must invalidate all line-cache in visible area because selection margin width changes dynamically.
5096 nLineIndex = m_nTopLine < nLineIndex ? m_nTopLine : nLineIndex;
5098 if (nLineIndex == -1)
5099 nLineIndex = 0; // Refresh all text
5101 // All text below this line should be reparsed
5102 if (m_ParseCookies->size())
5104 size_t arrSize = m_ParseCookies->size();
5105 if (arrSize != static_cast<size_t>(nLineCount))
5107 size_t oldsize = arrSize;
5108 m_ParseCookies->resize(nLineCount);
5109 arrSize = nLineCount;
5110 // must be initialized to invalid value (DWORD) - 1
5111 for (size_t i = oldsize; i < arrSize; ++i)
5112 (*m_ParseCookies)[i] = static_cast<uint32_t>(-1);
5114 for (size_t i = nLineIndex; i < arrSize; ++i)
5115 (*m_ParseCookies)[i] = static_cast<uint32_t>(-1);
5118 // Recalculate actual length for all lines below this
5119 if (m_pnActualLineLength->size())
5121 size_t arrsize = m_pnActualLineLength->size();
5122 if (arrsize != static_cast<size_t>(nLineCount))
5124 // Reallocate actual length array
5125 size_t oldsize = arrsize;
5126 m_pnActualLineLength->resize(nLineCount);
5127 arrsize = nLineCount;
5128 // must be initialized to invalid code -1
5129 for (size_t i = oldsize; i < arrsize; ++i)
5130 (*m_pnActualLineLength)[i] = -1;
5132 for (size_t i = nLineIndex; i < arrsize; ++i)
5133 (*m_pnActualLineLength)[i] = -1;
5136 InvalidateLineCache( nLineIndex, -1 );
5138 // Repaint the lines
5139 InvalidateLines (nLineIndex, -1, true);
5142 // All those points must be recalculated and validated
5143 if (pContext != nullptr)
5145 pContext->RecalcPoint (m_ptCursorPos);
5146 pContext->RecalcPoint (m_ptSelStart);
5147 pContext->RecalcPoint (m_ptSelEnd);
5148 pContext->RecalcPoint (m_ptAnchor);
5149 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
5150 ASSERT_VALIDTEXTPOS (m_ptSelStart);
5151 ASSERT_VALIDTEXTPOS (m_ptSelEnd);
5152 ASSERT_VALIDTEXTPOS (m_ptAnchor);
5153 if (m_bDraggingText)
5155 pContext->RecalcPoint (m_ptDraggedTextBegin);
5156 pContext->RecalcPoint (m_ptDraggedTextEnd);
5157 ASSERT_VALIDTEXTPOS (m_ptDraggedTextBegin);
5158 ASSERT_VALIDTEXTPOS (m_ptDraggedTextEnd);
5160 CEPoint ptTopLine (0, m_nTopLine);
5161 pContext->RecalcPoint (ptTopLine);
5162 ASSERT_VALIDTEXTPOS (ptTopLine);
5163 m_nTopLine = ptTopLine.y;
5167 // Recalculate vertical scrollbar, if needed
5168 if ((dwFlags & UPDATE_VERTRANGE) != 0)
5170 if (!m_bVertScrollBarLocked)
5171 InvalidateVertScrollBar ();
5174 // Recalculate horizontal scrollbar, if needed
5175 if ((dwFlags & UPDATE_HORZRANGE) != 0)
5177 if (!m_bHorzScrollBarLocked)
5178 InvalidateHorzScrollBar ();
5182 HINSTANCE CCrystalTextView::
5183 GetResourceHandle ()
5185 #ifdef CRYSEDIT_RES_HANDLE
5186 return CRYSEDIT_RES_HANDLE;
5188 if (s_hResourceInst != nullptr)
5189 return s_hResourceInst;
5190 return AfxGetResourceHandle ();
5194 int CCrystalTextView::
5195 OnCreate (LPCREATESTRUCT lpCreateStruct)
5198 tc::tcslcpy (m_lfBaseFont.lfFaceName, _T ("FixedSys"));
5199 m_lfBaseFont.lfHeight = 0;
5200 m_lfBaseFont.lfWeight = FW_NORMAL;
5201 m_lfBaseFont.lfItalic = false;
5202 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
5203 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
5204 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5205 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
5206 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
5208 if (CView::OnCreate (lpCreateStruct) == -1)
5211 ASSERT (m_hAccel == nullptr);
5212 // vvv GetResourceHandle () ???
5213 HINSTANCE hInst = AfxFindResourceHandle (MAKEINTRESOURCE(IDR_DEFAULT_ACCEL), RT_ACCELERATOR);
5214 ASSERT (hInst != nullptr);
5215 m_hAccel =::LoadAccelerators (hInst, MAKEINTRESOURCE (IDR_DEFAULT_ACCEL));
5216 ASSERT (m_hAccel != nullptr);
5220 void CCrystalTextView::
5221 SetAnchor (const CEPoint & ptNewAnchor)
5223 ASSERT_VALIDTEXTPOS (ptNewAnchor);
5224 m_ptAnchor = ptNewAnchor;
5227 void CCrystalTextView::
5228 OnEditOperation (int nAction, const tchar_t* pszText, size_t cchText)
5232 BOOL CCrystalTextView::
5233 PreTranslateMessage (MSG * pMsg)
5235 if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
5237 if (m_hAccel != nullptr)
5239 if (::TranslateAccelerator (m_hWnd, m_hAccel, pMsg))
5243 else if (pMsg->message == WM_LBUTTONDBLCLK)
5244 m_dwLastDblClickTime = GetTickCount();
5245 else if (pMsg->message == WM_LBUTTONDOWN && (GetTickCount() - GetDoubleClickTime()) < m_dwLastDblClickTime)
5247 m_dwLastDblClickTime = 0;
5248 OnLButtonTrippleClk(static_cast<UINT>(pMsg->wParam), { GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam) });
5251 return CView::PreTranslateMessage (pMsg);
5254 void CCrystalTextView::
5255 SetCursorPos (const CEPoint & ptCursorPos)
5257 ASSERT_VALIDTEXTPOS (ptCursorPos);
5258 m_ptCursorPos = ptCursorPos;
5259 m_nIdealCharPos = CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x);
5263 void CCrystalTextView::
5264 UpdateCompositionWindowPos() /* IME */
5266 HIMC hIMC = ImmGetContext(m_hWnd);
5267 COMPOSITIONFORM compform;
5269 compform.dwStyle = CFS_FORCE_POSITION;
5270 compform.ptCurrentPos = GetCaretPos();
5271 ImmSetCompositionWindow(hIMC, &compform);
5273 ImmReleaseContext(m_hWnd, hIMC);
5276 void CCrystalTextView::
5277 UpdateCompositionWindowFont() /* IME */
5279 HIMC hIMC = ImmGetContext(m_hWnd);
5281 ImmSetCompositionFont(hIMC, &m_lfBaseFont);
5283 ImmReleaseContext(m_hWnd, hIMC);
5286 void CCrystalTextView::
5287 SetTopMargin (bool bTopMargin)
5289 if (m_bTopMargin != bTopMargin)
5291 m_bTopMargin = bTopMargin;
5292 if (::IsWindow (m_hWnd))
5295 m_nScreenLines = -1;
5296 InvalidateVertScrollBar ();
5302 void CCrystalTextView::
5303 SetSelectionMargin (bool bSelMargin)
5305 if (m_bSelMargin != bSelMargin)
5307 m_bSelMargin = bSelMargin;
5308 if (::IsWindow (m_hWnd))
5310 InvalidateScreenRect ();
5311 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5312 RecalcHorzScrollBar ();
5318 void CCrystalTextView::
5319 SetViewLineNumbers (bool bViewLineNumbers)
5321 if (m_bViewLineNumbers != bViewLineNumbers)
5323 m_bViewLineNumbers = bViewLineNumbers;
5324 if (::IsWindow (m_hWnd))
5326 InvalidateScreenRect ();
5327 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5328 RecalcHorzScrollBar ();
5334 void CCrystalTextView::
5335 SetFont (const LOGFONT & lf)
5340 m_pCrystalRenderer->SetFont(lf);
5341 if (::IsWindow (m_hWnd))
5343 InvalidateScreenRect();
5344 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5345 InvalidateVertScrollBar ();
5346 InvalidateHorzScrollBar ();
5354 void CCrystalTextView::
5355 OnUpdateIndicatorPosition (CCmdUI * pCmdUI)
5357 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
5359 // VVV m_ptCursorPos.x + 1 ???
5360 stat.Format (_T ("Ln %d, Col %d"), m_ptCursorPos.y + 1, m_nIdealCharPos + 1);
5361 pCmdUI->SetText (stat);
5363 if( pCmdUI->m_pOther != nullptr && !!pCmdUI->m_pOther->IsKindOf( RUNTIME_CLASS(CStatusBar) ) )
5364 OnUpdateStatusMessage( (CStatusBar*)pCmdUI->m_pOther );
5368 void CCrystalTextView::
5369 OnUpdateIndicatorCRLF (CCmdUI * pCmdUI)
5371 if (m_pTextBuffer != nullptr)
5373 std::basic_string<tchar_t> eol;
5374 CRLFSTYLE crlfMode = m_pTextBuffer->GetCRLFMode ();
5377 case CRLFSTYLE::DOS:
5378 eol = LoadResString (IDS_EOL_DOS);
5379 pCmdUI->SetText (eol.c_str());
5380 pCmdUI->Enable (true);
5382 case CRLFSTYLE::UNIX:
5383 eol = LoadResString (IDS_EOL_UNIX);
5384 pCmdUI->SetText (eol.c_str());
5385 pCmdUI->Enable (true);
5387 case CRLFSTYLE::MAC:
5388 eol = LoadResString (IDS_EOL_MAC);
5389 pCmdUI->SetText (eol.c_str());
5390 pCmdUI->Enable (true);
5392 case CRLFSTYLE::MIXED:
5393 eol = LoadResString (IDS_EOL_MIXED);
5394 pCmdUI->SetText (eol.c_str());
5395 pCmdUI->Enable (true);
5398 pCmdUI->SetText (nullptr);
5399 pCmdUI->Enable (false);
5404 pCmdUI->SetText (nullptr);
5405 pCmdUI->Enable (false);
5409 void CCrystalTextView::
5410 OnToggleBookmark (UINT nCmdID)
5412 int nBookmarkID = nCmdID - ID_EDIT_TOGGLE_BOOKMARK0;
5413 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5414 if (m_pTextBuffer != nullptr)
5416 lineflags_t dwFlags = GetLineFlags (m_ptCursorPos.y);
5417 lineflags_t dwMask = LF_BOOKMARK (nBookmarkID);
5418 m_pTextBuffer->SetLineFlag (m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0);
5422 void CCrystalTextView::
5423 OnGoBookmark (UINT nCmdID)
5425 int nBookmarkID = nCmdID - ID_EDIT_GO_BOOKMARK0;
5426 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5427 if (m_pTextBuffer != nullptr)
5429 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5432 CEPoint pt (0, nLine);
5433 ASSERT_VALIDTEXTPOS (pt);
5435 SetSelection (pt, pt);
5442 void CCrystalTextView::
5445 if (m_pTextBuffer != nullptr)
5447 for (int nBookmarkID = 0; nBookmarkID <= 9; nBookmarkID++)
5449 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5452 m_pTextBuffer->SetLineFlag (nLine, LF_BOOKMARK (nBookmarkID), false);
5459 void CCrystalTextView::
5462 m_bCursorHidden = false;
5466 void CCrystalTextView::
5469 m_bCursorHidden = true;
5473 void CCrystalTextView::
5474 OnDropSource (DROPEFFECT de)
5476 ASSERT (de == DROPEFFECT_COPY);
5479 HGLOBAL CCrystalTextView::
5482 PrepareSelBounds ();
5483 if (m_ptDrawSelStart == m_ptDrawSelEnd)
5487 GetText (m_ptDrawSelStart, m_ptDrawSelEnd, text);
5488 int cchText = text.GetLength();
5489 SIZE_T cbData = (cchText + 1) * sizeof(tchar_t);
5490 HGLOBAL hData =::GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, cbData);
5491 if (hData == nullptr)
5494 tchar_t* pszData = (tchar_t*)::GlobalLock (hData);
5495 if (pszData == nullptr)
5497 ::GlobalFree(hData);
5500 memcpy (pszData, text, cbData);
5501 ::GlobalUnlock (hData);
5503 m_ptDraggedTextBegin = m_ptDrawSelStart;
5504 m_ptDraggedTextEnd = m_ptDrawSelEnd;
5508 static const tchar_t *memstr(const tchar_t *str1, size_t str1len, const tchar_t *str2, size_t str2len)
5510 ASSERT(str1 && str2 && str2len > 0);
5511 for (const tchar_t *p = str1; p < str1 + str1len; ++p)
5515 if (memcmp(p, str2, str2len * sizeof(tchar_t)) == 0)
5522 inline tchar_t mytoupper(tchar_t ch)
5524 return static_cast<tchar_t>(reinterpret_cast<uintptr_t>(CharUpper(reinterpret_cast<LPTSTR>(ch))));
5527 static const tchar_t *memistr(const tchar_t *str1, size_t str1len, const tchar_t *str2, size_t str2len)
5529 ASSERT(str1 && str2 && str2len > 0);
5530 for (const tchar_t *p = str1; p < str1 + str1len; ++p)
5532 if (mytoupper(*p) == mytoupper(*str2))
5535 for (i = 0; i < str2len; ++i)
5537 if (mytoupper(p[i]) != mytoupper(str2[i]))
5548 FindStringHelper (const tchar_t* pszLineBegin, size_t nLineLength, const tchar_t* pszFindWhere, const tchar_t* pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch)
5550 if (dwFlags & FIND_REGEXP)
5557 if (pszFindWhat[0] == '^' && pszLineBegin != pszFindWhere)
5559 rxnode = RxCompile (pszFindWhat, (dwFlags & FIND_MATCH_CASE) != 0 ? RX_CASE : 0);
5560 if (rxnode && RxExec (rxnode, pszLineBegin, nLineLength, pszFindWhere, rxmatch))
5562 pos = rxmatch->Open[0];
5563 ASSERT((rxmatch->Close[0] - rxmatch->Open[0]) < INT_MAX);
5564 nLen = static_cast<int>(rxmatch->Close[0] - rxmatch->Open[0]);
5570 ASSERT (pszFindWhere != nullptr);
5571 ASSERT (pszFindWhat != nullptr);
5572 int nCur = static_cast<int>(pszFindWhere - pszLineBegin);
5573 int nLength = (int) tc::tcslen (pszFindWhat);
5574 const tchar_t* pszFindWhereOrig = pszFindWhere;
5578 const tchar_t* pszPos;
5579 if (dwFlags & FIND_MATCH_CASE)
5580 pszPos = memstr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5582 pszPos = memistr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5583 if (pszPos == nullptr)
5585 if ((dwFlags & FIND_WHOLE_WORD) == 0)
5586 return nCur + (int) (pszPos - pszFindWhere);
5587 if (pszPos > pszFindWhereOrig && xisalnum (pszPos[-1]))
5589 nCur += (int) (pszPos - pszFindWhere + 1);
5590 pszFindWhere = pszPos + 1;
5593 if (xisalnum (pszPos[nLength]))
5595 nCur += (int) (pszPos - pszFindWhere + 1);
5596 pszFindWhere = pszPos + 1;
5599 return nCur + (int) (pszPos - pszFindWhere);
5602 //~ ASSERT (false); // Unreachable
5606 * @brief Select text in editor.
5607 * @param [in] ptStartPos Star position for highlight.
5608 * @param [in] nLength Count of characters to highlight.
5609 * @param [in] bCursorToLeft If true cursor is positioned to Left-end of text
5610 * selection, if false cursor is positioned to right-end.
5612 bool CCrystalTextView::
5613 HighlightText (const CEPoint & ptStartPos, int nLength,
5614 bool bCursorToLeft /*= false*/, bool bUpdateView /*= true*/)
5616 ASSERT_VALIDTEXTPOS (ptStartPos);
5617 CEPoint ptEndPos = ptStartPos;
5618 int nCount = GetLineLength (ptEndPos.y) - ptEndPos.x;
5619 if (nLength <= nCount)
5621 ptEndPos.x += nLength;
5625 while (nLength > nCount)
5627 nLength -= nCount + 1;
5628 nCount = GetLineLength (++ptEndPos.y);
5630 ptEndPos.x = nLength;
5632 ASSERT_VALIDTEXTPOS (m_ptCursorPos); // Probably 'nLength' is bigger than expected...
5634 m_ptCursorPos = bCursorToLeft ? ptStartPos : ptEndPos;
5635 m_ptAnchor = bCursorToLeft ? ptEndPos : ptStartPos;
5636 SetSelection (ptStartPos, ptEndPos);
5643 // Scrolls found text to middle of screen if out-of-screen
5644 int nScreenLines = GetScreenLines();
5645 if (ptStartPos.y < m_nTopLine || ptEndPos.y > m_nTopLine + nScreenLines)
5647 if (ptStartPos.y > nScreenLines / 2)
5648 ScrollToLine(ptStartPos.y - nScreenLines / 2);
5650 ScrollToLine(ptStartPos.y);
5651 UpdateSiblingScrollPos (false);
5653 EnsureVisible (ptStartPos, ptEndPos);
5657 bool CCrystalTextView::
5658 FindText (const tchar_t* pszText, const CEPoint & ptStartPos, DWORD dwFlags,
5659 bool bWrapSearch, CEPoint * pptFoundPos)
5661 if (m_pMarkers != nullptr)
5663 m_pMarkers->SetMarker(_T("EDITOR_MARKER"), pszText, dwFlags, COLORINDEX_MARKERBKGND0, false);
5664 if (m_pMarkers->GetEnabled())
5665 m_pMarkers->UpdateViews();
5667 int nLineCount = GetLineCount ();
5668 return FindTextInBlock (pszText, ptStartPos, CEPoint (0, 0),
5669 CEPoint (GetLineLength (nLineCount - 1), nLineCount - 1),
5670 dwFlags, bWrapSearch, pptFoundPos);
5673 int HowManyStr (const tchar_t* s, const tchar_t* m)
5675 const tchar_t* p = s;
5677 const int l = (int) tc::tcslen (m);
5678 while ((p = tc::tcsstr (p, m)) != nullptr)
5686 int HowManyStr (const tchar_t* s, tchar_t c)
5688 const tchar_t* p = s;
5690 while ((p = tc::tcschr (p, c)) != nullptr)
5698 bool CCrystalTextView::
5699 FindTextInBlock (const tchar_t* pszText, const CEPoint & ptStartPosition,
5700 const CEPoint & ptBlockBegin, const CEPoint & ptBlockEnd,
5701 DWORD dwFlags, bool bWrapSearch, CEPoint * pptFoundPos)
5703 CEPoint ptCurrentPos = ptStartPosition;
5705 ASSERT (pszText != nullptr && tc::tcslen (pszText) > 0);
5706 ASSERT_VALIDTEXTPOS (ptCurrentPos);
5707 ASSERT_VALIDTEXTPOS (ptBlockBegin);
5708 ASSERT_VALIDTEXTPOS (ptBlockEnd);
5709 ASSERT (ptBlockBegin.y < ptBlockEnd.y || ptBlockBegin.y == ptBlockEnd.y &&
5710 ptBlockBegin.x <= ptBlockEnd.x);
5711 if (ptBlockBegin == ptBlockEnd)
5713 CWaitCursor waitCursor;
5714 if (ptCurrentPos.y < ptBlockBegin.y || ptCurrentPos.y == ptBlockBegin.y &&
5715 ptCurrentPos.x < ptBlockBegin.x)
5716 ptCurrentPos = ptBlockBegin;
5718 CString what = pszText;
5720 if (dwFlags & FIND_REGEXP)
5722 nEolns = HowManyStr (what, _T("\\n"));
5728 if (dwFlags & FIND_DIRECTION_UP)
5730 // Let's check if we deal with whole text.
5731 // At this point, we cannot search *up* in selection
5732 ASSERT (ptBlockBegin.x == 0 && ptBlockBegin.y == 0);
5733 ASSERT (ptBlockEnd.x == GetLineLength (GetLineCount () - 1) &&
5734 ptBlockEnd.y == GetLineCount () - 1);
5736 // Proceed as if we have whole text search.
5739 while (ptCurrentPos.y >= 0)
5743 if (dwFlags & FIND_REGEXP)
5745 for (int i = 0; i <= nEolns && ptCurrentPos.y >= i; i++)
5748 const tchar_t* pszChars = GetLineChars (ptCurrentPos.y - i);
5751 nLineLength = GetLineLength (ptCurrentPos.y - i);
5753 line = _T ('\n') + line;
5757 nLineLength = ptCurrentPos.x != -1 ? ptCurrentPos.x : GetLineLength (ptCurrentPos.y - i);
5759 if (nLineLength > 0)
5761 item.SetString(pszChars, nLineLength);
5765 nLineLength = line.GetLength ();
5766 if (ptCurrentPos.x == -1)
5771 nLineLength = GetLineLength(ptCurrentPos.y);
5772 if (ptCurrentPos.x == -1)
5774 ptCurrentPos.x = nLineLength;
5776 else if( ptCurrentPos.x > nLineLength )
5777 ptCurrentPos.x = nLineLength;
5778 if (ptCurrentPos.x == -1)
5781 line.SetString (GetLineChars (ptCurrentPos.y), ptCurrentPos.x);
5784 ptrdiff_t nFoundPos = -1;
5785 int nMatchLen = what.GetLength();
5786 int nLineLen = line.GetLength();
5790 nPos = ::FindStringHelper(line, nLineLen, static_cast<const tchar_t*>(line) + nPos, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5794 nMatchLen = m_nLastFindWhatLen;
5795 nPos += nMatchLen == 0 ? 1 : nMatchLen;
5798 if( nFoundPos != -1 ) // Found text!
5800 ptCurrentPos.x = static_cast<int>(nFoundPos);
5801 *pptFoundPos = ptCurrentPos;
5806 if( ptCurrentPos.y >= 0 )
5807 ptCurrentPos.x = GetLineLength( ptCurrentPos.y );
5810 // Beginning of text reached
5814 // Start again from the end of text
5815 bWrapSearch = false;
5816 ptCurrentPos = CEPoint (GetLineLength (GetLineCount () - 1), GetLineCount () - 1);
5823 while (ptCurrentPos.y <= ptBlockEnd.y)
5827 if (dwFlags & FIND_REGEXP)
5829 int nLines = m_pTextBuffer->GetLineCount ();
5830 for (int i = 0; i <= nEolns && ptCurrentPos.y + i < nLines; i++)
5832 const tchar_t* pszChars = GetLineChars (ptCurrentPos.y + i);
5833 nLineLength = GetLineLength (ptCurrentPos.y + i);
5838 if (nLineLength > 0)
5840 int nLineLengthOld = line.GetLength();
5841 memcpy(line.GetBufferSetLength(nLineLengthOld + nLineLength) + nLineLengthOld, pszChars, nLineLength * sizeof(tchar_t));
5844 nLineLength = line.GetLength ();
5848 nLineLength = GetLineLength (ptCurrentPos.y) - ptCurrentPos.x;
5849 if (nLineLength <= 0)
5856 line.SetString(GetLineChars(ptCurrentPos.y), GetLineLength(ptCurrentPos.y));
5859 // Perform search in the line
5860 size_t nPos = ::FindStringHelper (line, line.GetLength (), static_cast<const tchar_t*>(line) + ptCurrentPos.x, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5863 if (m_pszMatched != nullptr)
5865 m_pszMatched = tc::tcsdup (line);
5868 CString item = line.Left (static_cast<LONG>(nPos));
5869 const tchar_t* current = tc::tcsrchr (item, _T('\n'));
5874 nEolns = HowManyStr (item, _T('\n'));
5877 ptCurrentPos.y += nEolns;
5878 ptCurrentPos.x = static_cast<LONG>(nPos - (current - (const tchar_t*) item));
5882 ptCurrentPos.x = static_cast<LONG>(nPos - (current - (const tchar_t*) item));
5884 if (ptCurrentPos.x < 0)
5889 ptCurrentPos.x = static_cast<LONG>(nPos);
5891 // Check of the text found is outside the block.
5892 if (ptCurrentPos.y == ptBlockEnd.y && ptCurrentPos.x >= ptBlockEnd.x)
5895 *pptFoundPos = ptCurrentPos;
5900 if (m_pszMatched != nullptr)
5902 m_pszMatched = nullptr;
5905 // Go further, text was not found
5910 // End of text reached
5914 // Start from the beginning
5915 bWrapSearch = false;
5916 ptCurrentPos = ptBlockBegin;
5920 //~ ASSERT (false); // Unreachable
5923 static DWORD ConvertSearchInfosToSearchFlags(const LastSearchInfos *lastSearch)
5925 DWORD dwSearchFlags = 0;
5926 if (lastSearch->m_bMatchCase)
5927 dwSearchFlags |= FIND_MATCH_CASE;
5928 if (lastSearch->m_bWholeWord)
5929 dwSearchFlags |= FIND_WHOLE_WORD;
5930 if (lastSearch->m_bRegExp)
5931 dwSearchFlags |= FIND_REGEXP;
5932 if (lastSearch->m_nDirection == 0)
5933 dwSearchFlags |= FIND_DIRECTION_UP;
5934 if (lastSearch->m_bNoWrap)
5935 dwSearchFlags |= FIND_NO_WRAP;
5936 if (lastSearch->m_bNoClose)
5937 dwSearchFlags |= FIND_NO_CLOSE;
5938 return dwSearchFlags;
5941 static void ConvertSearchFlagsToLastSearchInfos(LastSearchInfos *lastSearch, DWORD dwFlags)
5943 lastSearch->m_bMatchCase = (dwFlags & FIND_MATCH_CASE) != 0;
5944 lastSearch->m_bWholeWord = (dwFlags & FIND_WHOLE_WORD) != 0;
5945 lastSearch->m_bRegExp = (dwFlags & FIND_REGEXP) != 0;
5946 lastSearch->m_nDirection = (dwFlags & FIND_DIRECTION_UP) == 0;
5947 lastSearch->m_bNoWrap = (dwFlags & FIND_NO_WRAP) != 0;
5948 lastSearch->m_bNoClose = (dwFlags & FIND_NO_CLOSE) != 0;
5951 CEPoint CCrystalTextView::
5952 GetSearchPos(DWORD dwSearchFlags)
5954 CEPoint ptSearchPos;
5957 auto [ptStart, ptEnd] = GetSelection ();
5958 if( dwSearchFlags & FIND_DIRECTION_UP)
5959 ptSearchPos = ptStart;
5961 ptSearchPos = ptEnd;
5964 ptSearchPos = m_ptCursorPos;
5968 bool CCrystalTextView::
5969 FindText (const LastSearchInfos * lastSearch)
5972 DWORD dwSearchFlags = ConvertSearchInfosToSearchFlags(lastSearch);
5973 if (!FindText (lastSearch->m_sText, GetSearchPos(dwSearchFlags), dwSearchFlags, !lastSearch->m_bNoWrap,
5979 bool bCursorToLeft = (lastSearch->m_nDirection == 0);
5980 HighlightText (ptTextPos, m_nLastFindWhatLen, bCursorToLeft);
5982 // Save search parameters for 'F3' command
5983 m_bLastSearch = true;
5984 if (m_pszLastFindWhat != nullptr)
5985 free (m_pszLastFindWhat);
5986 m_pszLastFindWhat = tc::tcsdup (lastSearch->m_sText);
5987 m_dwLastSearchFlags = dwSearchFlags;
5989 // Save search parameters to registry
5990 VERIFY (AfxGetApp ()->WriteProfileInt (EDITPAD_SECTION, _T ("FindFlags"), m_dwLastSearchFlags));
5995 void CCrystalTextView::
5998 if (m_pFindTextDlg == nullptr)
5999 m_pFindTextDlg = new CFindTextDlg (this);
6001 LastSearchInfos * lastSearch = m_pFindTextDlg->GetLastSearchInfos();
6005 // Get the latest search parameters
6006 ConvertSearchFlagsToLastSearchInfos(lastSearch, m_dwLastSearchFlags);
6007 if (m_pszLastFindWhat != nullptr)
6008 lastSearch->m_sText = m_pszLastFindWhat;
6012 DWORD dwFlags = AfxGetApp ()->GetProfileInt (EDITPAD_SECTION, _T("FindFlags"), FIND_NO_CLOSE);
6013 ConvertSearchFlagsToLastSearchInfos(lastSearch, dwFlags);
6015 m_pFindTextDlg->UseLastSearch ();
6017 // Take the current selection, if any
6020 auto [ptSelStart, ptSelEnd] = GetSelection ();
6021 if (ptSelStart.y == ptSelEnd.y)
6022 GetText (ptSelStart, ptSelEnd, m_pFindTextDlg->m_sText);
6026 CEPoint ptCursorPos = GetCursorPos ();
6027 CEPoint ptStart = WordToLeft (ptCursorPos);
6028 CEPoint ptEnd = WordToRight (ptCursorPos);
6029 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
6030 GetText (ptStart, ptEnd, m_pFindTextDlg->m_sText);
6033 // Execute Find dialog
6035 // m_bShowInactiveSelection = true; // FP: removed because I like it
6036 m_pFindTextDlg->UpdateData(FALSE);
6037 m_pFindTextDlg->ShowWindow(SW_SHOW);
6038 // m_bShowInactiveSelection = false; // FP: removed because I like it
6042 void CCrystalTextView::
6045 bool bEnable = m_bLastSearch;
6046 // Show dialog if no last find text
6047 if (m_pszLastFindWhat == nullptr || tc::tcslen(m_pszLastFindWhat) == 0)
6051 sText = m_pszLastFindWhat;
6054 // If last find-text exists, cut it to first line
6055 bEnable = !sText.IsEmpty ();
6058 int pos = sText.FindOneOf (_T("\r\n"));
6060 sText = sText.Left (pos);
6064 // CTRL-F3 will find selected text..
6065 bool bControlKey = (::GetAsyncKeyState(VK_CONTROL)& 0x8000) != 0;
6066 // CTRL-SHIFT-F3 will find selected text, but opposite direction
6067 bool bShiftKey = (::GetAsyncKeyState(VK_SHIFT)& 0x8000) != 0;
6072 auto [ptSelStart, ptSelEnd] = GetSelection ();
6073 GetText (ptSelStart, ptSelEnd, sText);
6077 CEPoint ptCursorPos = GetCursorPos ();
6078 CEPoint ptStart = WordToLeft (ptCursorPos);
6079 CEPoint ptEnd = WordToRight (ptCursorPos);
6080 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
6081 GetText (ptStart, ptEnd, sText);
6083 if (!sText.IsEmpty())
6086 free(m_pszLastFindWhat);
6087 m_pszLastFindWhat = tc::tcsdup (sText);
6088 m_bLastSearch = true;
6092 m_dwLastSearchFlags |= FIND_DIRECTION_UP;
6094 m_dwLastSearchFlags &= ~FIND_DIRECTION_UP;
6099 // for correct backward search we need some changes:
6100 if (! FindText(sText, GetSearchPos(m_dwLastSearchFlags), m_dwLastSearchFlags,
6101 (m_dwLastSearchFlags & FIND_NO_WRAP) == 0, &ptFoundPos))
6104 prompt.Format (LoadResString(IDS_EDIT_TEXT_NOT_FOUND).c_str(), (const tchar_t*)sText);
6105 AfxMessageBox (prompt, MB_ICONINFORMATION);
6108 HighlightText (ptFoundPos, m_nLastFindWhatLen, (m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0);
6109 m_bMultipleSearch = true; // More search
6112 OnEditFind(); // No previous find, open Find-dialog
6115 void CCrystalTextView::
6116 OnUpdateEditRepeat (CCmdUI * pCmdUI)
6118 pCmdUI->Enable (true);
6121 void CCrystalTextView::
6125 DWORD dwFlags = AfxGetApp ()->GetProfileInt (EDITPAD_SECTION, _T("MarkerFlags"), 0);
6127 // Take the current selection, if any
6130 auto[ptSelStart, ptSelEnd] = GetSelection ();
6131 if (ptSelStart.y == ptSelEnd.y)
6132 GetText (ptSelStart, ptSelEnd, sText);
6136 CEPoint ptCursorPos = GetCursorPos ();
6137 CEPoint ptStart = WordToLeft (ptCursorPos);
6138 CEPoint ptEnd = WordToRight (ptCursorPos);
6139 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
6140 GetText (ptStart, ptEnd, sText);
6143 CTextMarkerDlg markerDlg(*m_pMarkers, sText, dwFlags);
6145 if (markerDlg.DoModal() == IDOK)
6147 // Save search parameters to registry
6148 VERIFY (AfxGetApp ()->WriteProfileInt (EDITPAD_SECTION, _T ("MarkerFlags"), markerDlg.GetLastSearchFlags()));
6149 m_pMarkers->SaveToRegistry();
6153 void CCrystalTextView::
6156 CWinApp *pApp = AfxGetApp ();
6157 ASSERT (pApp != nullptr);
6159 CPageSetupDialog dlg;
6161 if (!pApp->GetPrinterDeviceDefaults (&pd))
6164 dlg.m_psd.hDevMode = pd.hDevMode;
6165 dlg.m_psd.hDevNames = pd.hDevNames;
6166 dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS|PSD_MARGINS;
6167 dlg.m_psd.ptPaperSize.x = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageWidth"), dlg.m_psd.ptPaperSize.x);
6168 dlg.m_psd.ptPaperSize.y = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageHeight"), dlg.m_psd.ptPaperSize.y);
6169 dlg.m_psd.rtMargin.left = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageLeft"), DEFAULT_PRINT_MARGIN);
6170 dlg.m_psd.rtMargin.right = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageRight"), DEFAULT_PRINT_MARGIN);
6171 dlg.m_psd.rtMargin.top = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageTop"), DEFAULT_PRINT_MARGIN);
6172 dlg.m_psd.rtMargin.bottom = pApp->GetProfileInt(EDITPAD_SECTION, _T("PageBottom"), DEFAULT_PRINT_MARGIN);
6173 if (dlg.DoModal () == IDOK)
6175 VERIFY (pApp->WriteProfileInt (EDITPAD_SECTION, _T ("PageWidth"), dlg.m_psd.ptPaperSize.x));
6176 VERIFY (pApp->WriteProfileInt (EDITPAD_SECTION, _T ("PageHeight"), dlg.m_psd.ptPaperSize.y));
6177 VERIFY (pApp->WriteProfileInt (EDITPAD_SECTION, _T ("PageLeft"), dlg.m_psd.rtMargin.left));
6178 VERIFY (pApp->WriteProfileInt (EDITPAD_SECTION, _T ("PageRight"), dlg.m_psd.rtMargin.right));
6179 VERIFY (pApp->WriteProfileInt (EDITPAD_SECTION, _T ("PageTop"), dlg.m_psd.rtMargin.top));
6180 VERIFY (pApp->WriteProfileInt (EDITPAD_SECTION, _T ("PageBottom"), dlg.m_psd.rtMargin.bottom));
6181 pApp->SelectPrinter (dlg.m_psd.hDevNames, dlg.m_psd.hDevMode, false);
6186 * @brief Adds/removes bookmark on given line.
6187 * This functions adds bookmark or removes bookmark on given line.
6188 * @param [in] Index (0-based) of line to add/remove bookmark.
6190 void CCrystalTextView::ToggleBookmark(int nLine)
6192 ASSERT(nLine >= 0 && nLine < GetLineCount());
6193 if (m_pTextBuffer != nullptr)
6195 lineflags_t dwFlags = GetLineFlags (nLine);
6196 lineflags_t dwMask = LF_BOOKMARKS;
6197 m_pTextBuffer->SetLineFlag (nLine, dwMask, (dwFlags & dwMask) == 0, false);
6198 const int nBookmarkLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARKS);
6199 if (nBookmarkLine >= 0)
6200 m_bBookmarkExist = true;
6202 m_bBookmarkExist = false;
6206 * @brief Called when Toggle Bookmark is selected from the GUI.
6208 void CCrystalTextView::
6211 ToggleBookmark(m_ptCursorPos.y);
6214 void CCrystalTextView::
6217 if (m_pTextBuffer != nullptr)
6219 int nLine = m_pTextBuffer->FindNextBookmarkLine (m_ptCursorPos.y);
6222 CEPoint pt (0, nLine);
6223 ASSERT_VALIDTEXTPOS (pt);
6225 SetSelection (pt, pt);
6232 void CCrystalTextView::
6235 if (m_pTextBuffer != nullptr)
6237 int nLine = m_pTextBuffer->FindPrevBookmarkLine (m_ptCursorPos.y);
6240 CEPoint pt (0, nLine);
6241 ASSERT_VALIDTEXTPOS (pt);
6243 SetSelection (pt, pt);
6250 void CCrystalTextView::
6251 OnClearAllBookmarks ()
6253 if (m_pTextBuffer != nullptr)
6255 int nLineCount = GetLineCount ();
6256 for (int I = 0; I < nLineCount; I++)
6258 if (m_pTextBuffer->GetLineFlags (I) & LF_BOOKMARKS)
6259 m_pTextBuffer->SetLineFlag (I, LF_BOOKMARKS, false);
6261 m_bBookmarkExist = false;
6265 void CCrystalTextView::
6266 OnUpdateNextBookmark (CCmdUI * pCmdUI)
6268 pCmdUI->Enable (m_bBookmarkExist);
6271 void CCrystalTextView::
6272 OnUpdatePrevBookmark (CCmdUI * pCmdUI)
6274 pCmdUI->Enable (m_bBookmarkExist);
6277 void CCrystalTextView::
6278 OnUpdateClearAllBookmarks (CCmdUI * pCmdUI)
6280 pCmdUI->Enable (m_bBookmarkExist);
6283 void CCrystalTextView::
6284 SetViewTabs (bool bViewTabs)
6286 if (bViewTabs != m_bViewTabs)
6288 m_bViewTabs = bViewTabs;
6289 if (::IsWindow (m_hWnd))
6294 void CCrystalTextView::
6295 SetViewEols (bool bViewEols, bool bDistinguishEols)
6297 if (bViewEols != m_bViewEols || bDistinguishEols != m_bDistinguishEols)
6299 m_bViewEols = bViewEols;
6300 m_bDistinguishEols = bDistinguishEols;
6301 if (::IsWindow (m_hWnd))
6306 void CCrystalTextView::
6307 SetFlags (DWORD dwFlags)
6309 if (m_dwFlags != dwFlags)
6311 m_dwFlags = dwFlags;
6312 if (::IsWindow (m_hWnd))
6317 int CCrystalTextView::
6318 GetTopMarginHeight()
6322 return GetLineHeight();
6326 * @brief Calculate margin area width.
6327 * This function calculates needed margin width. Needed width is (approx.)
6328 * one char-width for bookmark etc markers and rest to linenumbers (if
6329 * visible). If we have linenumbers visible we need to adjust width so that
6330 * biggest number fits.
6331 * @return Margin area width in pixels.
6333 int CCrystalTextView::
6334 GetMarginWidth (CDC *pdc /*= nullptr*/)
6336 int nMarginWidth = 0;
6338 if (m_bViewLineNumbers)
6340 const int nLines = GetLineCount();
6343 for (n = 1; n <= nLines; n *= 10)
6345 nMarginWidth += GetCharWidth () * nNumbers;
6347 nMarginWidth += 2; // Small gap when symbol part disabled
6352 if (pdc == nullptr || !pdc->IsPrinting ())
6353 nMarginWidth += GetMarginIconSize () + 7; // Width for icon markers and some margin
6357 if (pdc == nullptr || !pdc->IsPrinting ())
6358 nMarginWidth += MARGIN_REV_WIDTH; // Space for revision marks
6361 return nMarginWidth;
6364 void CCrystalTextView::CopyProperties (CCrystalTextView *pSource)
6366 m_nTopLine = pSource->m_nTopLine;
6367 m_nTopSubLine = pSource->m_nTopSubLine;
6368 m_bViewTabs = pSource->m_bViewTabs;
6369 m_bViewEols = pSource->m_bViewEols;
6370 m_bDistinguishEols = pSource->m_bDistinguishEols;
6371 m_bTopMargin = pSource->m_bTopMargin;
6372 m_bSelMargin = pSource->m_bSelMargin;
6373 m_bViewLineNumbers = pSource->m_bViewLineNumbers;
6374 m_bSmoothScroll = pSource->m_bSmoothScroll;
6375 m_bWordWrap = pSource->m_bWordWrap;
6376 m_pColors = pSource->m_pColors;
6377 m_pMarkers = pSource->m_pMarkers;
6378 m_bDisableDragAndDrop = pSource->m_bDisableDragAndDrop;
6379 SetTextType(pSource->m_CurSourceDef);
6380 SetFont (pSource->m_lfBaseFont);
6384 // Mouse wheel event. zDelta is in multiples of 120.
6385 // Divide by 40 so each click is 3 lines. I know some
6386 // drivers let you set the ammount of scroll, but I
6387 // don't know how to retrieve this or if they just
6388 // adjust the zDelta you get here.
6389 BOOL CCrystalTextView::
6390 OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
6392 SCROLLINFO si{ sizeof(si) };
6393 si.fMask = SIF_PAGE | SIF_RANGE;
6394 VERIFY (GetScrollInfo (SB_VERT, &si));
6396 int nNewTopSubLine= m_nTopSubLine - zDelta / 40;
6398 if (nNewTopSubLine < 0)
6400 if (nNewTopSubLine > (si.nMax - (signed int)si.nPage + 1))
6401 nNewTopSubLine = si.nMax - si.nPage + 1;
6403 ScrollToSubLine(nNewTopSubLine, true);
6404 UpdateSiblingScrollPos(false);
6407 return CView::OnMouseWheel (nFlags, zDelta, pt);
6410 void CCrystalTextView::
6411 OnMouseHWheel (UINT nFlags, short zDelta, CPoint pt)
6413 SCROLLINFO si = { sizeof(si) };
6414 si.fMask = SIF_POS | SIF_RANGE;
6415 VERIFY (GetScrollInfo (SB_HORZ, &si));
6417 int nCurPos = si.nPos + zDelta / 40;
6418 if (nCurPos < si.nMin)
6420 else if (nCurPos > si.nMax)
6423 ScrollToChar (nCurPos, true);
6425 UpdateSiblingScrollPos (true);
6427 CView::OnMouseHWheel (nFlags, zDelta, pt);
6430 void CCrystalTextView::
6431 OnSourceType (UINT nId)
6433 SetTextType ((CrystalLineParser::TextType) (nId - ID_SOURCE_PLAIN));
6437 void CCrystalTextView::
6438 OnUpdateSourceType (CCmdUI * pCmdUI)
6440 pCmdUI->SetRadio (CrystalLineParser::m_SourceDefs + (pCmdUI->m_nID - ID_SOURCE_PLAIN) == m_CurSourceDef);
6444 bracetype (tchar_t c)
6446 static const tchar_t* braces = _T("{}()[]<>");
6447 const tchar_t* pos = tc::tcschr (braces, c);
6448 return pos != nullptr ? (int) (pos - braces) + 1 : 0;
6452 bracetype (const tchar_t* s)
6456 return bracetype (*s);
6459 void CCrystalTextView::
6462 CEPoint ptCursorPos = GetCursorPos ();
6463 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6464 const tchar_t* pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y), *pszEnd = pszText + ptCursorPos.x;
6465 bool bAfter = false;
6467 if (ptCursorPos.x < nLength)
6469 nType = bracetype (*pszEnd);
6474 else if (ptCursorPos.x > 0)
6476 nType = bracetype (pszEnd[-1]);
6480 else if (ptCursorPos.x > 0)
6482 nType = bracetype (pszEnd[-1]);
6487 int nOther, nCount = 0, nComment = 0;
6490 nOther = ((nType - 1) ^ 1) + 1;
6496 nOther = ((nType - 1) ^ 1) + 1;
6500 const tchar_t* pszOpenComment = m_CurSourceDef->opencomment,
6501 *pszCloseComment = m_CurSourceDef->closecomment,
6502 *pszCommentLine = m_CurSourceDef->commentline, *pszTest;
6503 int nOpenComment = (int) tc::tcslen (pszOpenComment),
6504 nCloseComment = (int) tc::tcslen (pszCloseComment),
6505 nCommentLine = (int) tc::tcslen (pszCommentLine);
6510 while (--pszEnd >= pszText)
6512 pszTest = pszEnd - nOpenComment + 1;
6513 if (pszTest >= pszText && !tc::tcsnicmp (pszTest, pszOpenComment, nOpenComment))
6517 if (--pszEnd < pszText)
6522 pszTest = pszEnd - nCloseComment + 1;
6523 if (pszTest >= pszText && !tc::tcsnicmp (pszTest, pszCloseComment, nCloseComment))
6527 if (--pszEnd < pszText)
6534 pszTest = pszEnd - nCommentLine + 1;
6535 if (pszTest >= pszText && !tc::tcsnicmp (pszTest, pszCommentLine, nCommentLine))
6539 if (bracetype (*pszEnd) == nType)
6543 else if (bracetype (*pszEnd) == nOther)
6547 ptCursorPos.x = (LONG) (pszEnd - pszText);
6550 SetCursorPos (ptCursorPos);
6551 SetSelection (ptCursorPos, ptCursorPos);
6552 SetAnchor (ptCursorPos);
6553 EnsureVisible (ptCursorPos);
6561 ptCursorPos.x = m_pTextBuffer->GetLineLength (--ptCursorPos.y);
6562 pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6563 pszEnd = pszText + ptCursorPos.x;
6571 const tchar_t* pszBegin = pszText;
6573 pszEnd = pszBegin + nLength;
6574 int nLines = m_pTextBuffer->GetLineCount ();
6577 while (pszText < pszEnd)
6579 pszTest = pszText + nCloseComment;
6580 if (pszTest <= pszEnd && !tc::tcsnicmp (pszText, pszCloseComment, nCloseComment))
6584 if (pszText > pszEnd)
6589 pszTest = pszText + nOpenComment;
6590 if (pszTest <= pszEnd && !tc::tcsnicmp (pszText, pszOpenComment, nOpenComment))
6594 if (pszText > pszEnd)
6601 pszTest = pszText + nCommentLine;
6602 if (pszTest <= pszEnd && !tc::tcsnicmp (pszText, pszCommentLine, nCommentLine))
6606 if (bracetype (*pszText) == nType)
6610 else if (bracetype (*pszText) == nOther)
6614 ptCursorPos.x = (LONG) (pszText - pszBegin);
6617 SetCursorPos (ptCursorPos);
6618 SetSelection (ptCursorPos, ptCursorPos);
6619 SetAnchor (ptCursorPos);
6620 EnsureVisible (ptCursorPos);
6627 if (ptCursorPos.y < nLines)
6630 nLength = m_pTextBuffer->GetLineLength (++ptCursorPos.y);
6631 pszBegin = pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6632 pszEnd = pszBegin + nLength;
6641 void CCrystalTextView::
6642 OnUpdateMatchBrace (CCmdUI * pCmdUI)
6644 CEPoint ptCursorPos = GetCursorPos ();
6645 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6646 const tchar_t* pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y) + ptCursorPos.x;
6647 pCmdUI->Enable (ptCursorPos.x < nLength && (bracetype (*pszText) || ptCursorPos.x > 0 && bracetype (pszText[-1])) || ptCursorPos.x > 0 && bracetype (pszText[-1]));
6650 void CCrystalTextView::
6653 CGotoDlg dlg (this);
6657 void CCrystalTextView::
6658 OnUpdateToggleSourceHeader (CCmdUI * pCmdUI)
6660 pCmdUI->Enable (m_CurSourceDef->type == CrystalLineParser::SRC_C);
6663 void CCrystalTextView::
6664 OnToggleSourceHeader ()
6666 if (m_CurSourceDef->type == CrystalLineParser::SRC_C)
6668 CDocument *pDoc = GetDocument ();
6669 ASSERT (pDoc != nullptr);
6670 CString sFilePath = pDoc->GetPathName (), sOriginalPath = sFilePath;
6671 if (!tc::tcsicmp (sFilePath.Right (2), _T (".c")))
6673 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ('h');
6675 else if (!tc::tcsicmp (sFilePath.Right (4), _T (".cpp")))
6677 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('h');
6679 else if (!tc::tcsicmp (sFilePath.Right (4), _T (".inl")))
6681 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6682 if (!FileExist(sFilePath))
6684 sFilePath = sFilePath + _T ("pp");
6687 else if (!tc::tcsicmp (sFilePath.Right (4), _T (".hpp")))
6689 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6690 if (!FileExist(sFilePath))
6692 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6693 if (!FileExist(sFilePath))
6695 sFilePath = sFilePath + _T ("pp");
6699 else if (!tc::tcsicmp (sFilePath.Right (2), _T (".h")))
6701 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ("hpp");
6702 if (!FileExist(sFilePath))
6704 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6705 if (!FileExist(sFilePath))
6707 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6708 if (!FileExist(sFilePath))
6710 sFilePath = sFilePath + _T ("pp");
6715 if (FileExist(sFilePath))
6717 if (!m_bSingle || !pDoc->IsModified () || pDoc->DoSave (sOriginalPath))
6719 AfxGetApp ()->OpenDocumentFile (sFilePath);
6722 m_ptCursorLast.x = m_ptCursorLast.y = 0;
6723 ASSERT_VALIDTEXTPOS (m_ptCursorLast);
6724 CEPoint ptCursorPos = m_ptCursorLast;
6725 SetCursorPos (ptCursorPos);
6726 SetSelection (ptCursorPos, ptCursorPos);
6727 SetAnchor (ptCursorPos);
6728 EnsureVisible (ptCursorPos);
6736 void CCrystalTextView::
6737 OnUpdateTopMargin (CCmdUI * pCmdUI)
6739 pCmdUI->SetCheck (m_bTopMargin);
6742 void CCrystalTextView::
6745 ASSERT (m_CurSourceDef != nullptr);
6747 m_CurSourceDef->flags &= ~SRCOPT_TOPMARGIN;
6749 m_CurSourceDef->flags |= SRCOPT_TOPMARGIN;
6750 SetTopMargin (!m_bTopMargin);
6753 void CCrystalTextView::
6754 OnUpdateSelMargin (CCmdUI * pCmdUI)
6756 pCmdUI->SetCheck (m_bSelMargin);
6759 void CCrystalTextView::
6762 ASSERT (m_CurSourceDef != nullptr);
6764 m_CurSourceDef->flags &= ~SRCOPT_SELMARGIN;
6766 m_CurSourceDef->flags |= SRCOPT_SELMARGIN;
6767 SetSelectionMargin (!m_bSelMargin);
6770 void CCrystalTextView::
6771 OnUpdateWordWrap (CCmdUI * pCmdUI)
6773 pCmdUI->SetCheck (m_bWordWrap);
6776 void CCrystalTextView::
6779 ASSERT (m_CurSourceDef != nullptr);
6782 m_CurSourceDef->flags &= ~SRCOPT_WORDWRAP;
6783 SetWordWrapping (false);
6787 m_CurSourceDef->flags |= SRCOPT_WORDWRAP;
6788 SetWordWrapping (true);
6792 void CCrystalTextView::
6796 RedrawWindow (nullptr, nullptr, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ERASENOW);
6799 void CCrystalTextView::
6800 OnToggleColumnSelection ()
6802 m_bRectangularSelection = !m_bRectangularSelection;
6806 void CCrystalTextView::SetRenderingMode(RENDERING_MODE nRenderingMode)
6809 if (nRenderingMode == RENDERING_MODE::GDI)
6810 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
6812 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(static_cast<int>(nRenderingMode)));
6813 m_pCrystalRenderer->SetFont(m_lfBaseFont);
6815 m_nRenderingMode = nRenderingMode;
6819 bool CCrystalTextView::GetWordWrapping() const
6824 void CCrystalTextView::SetWordWrapping( bool bWordWrap )
6826 m_bWordWrap = bWordWrap;
6828 if( IsWindow( m_hWnd ) )
6831 InvalidateScreenRect();
6835 CCrystalParser *CCrystalTextView::SetParser( CCrystalParser *pParser )
6837 CCrystalParser *pOldParser = m_pParser;
6839 m_pParser = pParser;
6841 if( pParser != nullptr )
6842 pParser->m_pTextView = this;
6849 * @brief Return whether a line is visible.
6851 bool CCrystalTextView::GetLineVisible (int nLineIndex) const
6853 return !m_bHideLines || !(GetLineFlags (nLineIndex) & LF_INVISIBLE);
6857 // incremental search imlementation
6858 BOOL CCrystalTextView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO *pHandlerInfo )
6860 // just look for commands
6861 if( nCode != CN_COMMAND || pExtra != nullptr )
6862 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6865 // each command that is not related to incremental search
6866 // ends the incremental search
6867 if( nID == ID_EDIT_FIND_INCREMENTAL_FORWARD ||
6868 nID == ID_EDIT_FIND_INCREMENTAL_BACKWARD ||
6869 nID == ID_EDIT_DELETE_BACK )
6870 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6872 if( nID >= ID_EDIT_FIRST && nID <= ID_EDIT_LAST )
6873 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6875 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6878 void CCrystalTextView::OnChar( wchar_t nChar, UINT nRepCnt, UINT nFlags )
6880 CView::OnChar( nChar, nRepCnt, nFlags );
6882 // we only have to handle character-input, if we are in incremental search mode
6883 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6886 // exit incremental search, when Escape is pressed
6887 if( nChar == VK_ESCAPE )
6889 // if not end incremental search
6890 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6891 SetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6892 SetCursorPos( m_cursorPosBeforeIncrementalSearch );
6893 EnsureVisible( m_cursorPosBeforeIncrementalSearch );
6897 // exit incremental search without destroying selection
6898 if( nChar == VK_RETURN )
6900 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6904 // is the character valid for incremental search?
6905 if( !_istgraph( nChar ) && !(nChar == _T(' ')) && !(nChar == _T('\t')) )
6907 // if not end incremental search
6908 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6912 // if last search was not successfull do not add a new character
6913 if( !m_bIncrementalFound )
6915 MessageBeep( MB_OK );
6919 // add character to incremental search string and search
6920 *m_pstrIncrementalSearchString += (tchar_t) nChar;
6921 OnEditFindIncremental();
6924 LRESULT CCrystalTextView::OnImeStartComposition(WPARAM wParam, LPARAM lParam) /* IME */
6926 UpdateCompositionWindowFont();
6927 UpdateCompositionWindowPos();
6929 return DefWindowProc(WM_IME_STARTCOMPOSITION, wParam, lParam);
6932 void CCrystalTextView::OnEditDeleteBack()
6934 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6937 // remove last character from search string
6938 if( m_pstrIncrementalSearchString->IsEmpty() )
6941 *m_pstrIncrementalSearchString = m_pstrIncrementalSearchString->Left( m_pstrIncrementalSearchString->GetLength() - 1 );
6942 OnEditFindIncremental();
6946 void CCrystalTextView::OnEditFindIncremental( bool bFindNextOccurence /*= false*/ )
6948 // when string is empty, then goto position where the search starts
6949 if( m_pstrIncrementalSearchString->IsEmpty() )
6951 SetSelection( m_incrementalSearchStartPos, m_incrementalSearchStartPos );
6952 SetCursorPos( m_incrementalSearchStartPos );
6953 EnsureVisible( m_incrementalSearchStartPos );
6957 // otherwise search next occurence of search string,
6958 // starting at current cursor position
6959 CEPoint matchStart, matchEnd;
6961 // calculate start point for search
6962 if( bFindNextOccurence )
6964 auto[selStart, selEnd] = GetSelection ();
6965 m_incrementalSearchStartPos = (m_bIncrementalSearchBackward)? selStart : selEnd;
6968 m_bIncrementalFound = FindText(
6969 *m_pstrIncrementalSearchString,
6970 m_incrementalSearchStartPos,
6971 m_bIncrementalSearchBackward? FIND_DIRECTION_UP : 0,
6975 if( !m_bIncrementalFound )
6977 MessageBeep( MB_OK );
6981 // select found text and set cursor to end of match
6982 matchEnd = matchStart;
6983 matchEnd.x+= m_pstrIncrementalSearchString->GetLength();
6984 SetSelection( matchStart, matchEnd );
6985 SetCursorPos( matchEnd );
6986 EnsureVisible( matchEnd );
6991 void CCrystalTextView::OnEditFindIncrementalForward()
6993 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6996 if( !m_pstrIncrementalSearchString->IsEmpty() )
6997 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6998 m_pstrIncrementalSearchString->Empty();
6999 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
7000 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
7002 else if( m_bIncrementalSearchForward )
7004 if( m_pstrIncrementalSearchString->IsEmpty() )
7006 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
7007 m_pstrIncrementalSearchStringOld->Empty();
7008 OnEditFindIncremental();
7011 OnEditFindIncremental( true );
7016 m_bIncrementalSearchForward = true;
7017 m_bIncrementalSearchBackward = false;
7018 m_bIncrementalFound = true;
7019 OnEditFindIncremental();
7022 void CCrystalTextView::OnEditFindIncrementalBackward()
7024 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
7027 if( !m_pstrIncrementalSearchString->IsEmpty() )
7028 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
7029 m_pstrIncrementalSearchString->Empty();
7030 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
7031 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
7033 else if( m_bIncrementalSearchBackward )
7035 if( m_pstrIncrementalSearchString->IsEmpty() )
7037 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
7038 m_pstrIncrementalSearchStringOld->Empty();
7039 OnEditFindIncremental();
7042 OnEditFindIncremental( true );
7047 m_bIncrementalSearchForward = false;
7048 m_bIncrementalSearchBackward = true;
7049 m_bIncrementalFound = true;
7050 OnEditFindIncremental();
7053 void CCrystalTextView::OnUpdateEditFindIncrementalForward(CCmdUI* pCmdUI)
7055 if (m_pTextBuffer != nullptr)
7057 int nLines = m_pTextBuffer->GetLineCount ();
7058 int nChars = m_pTextBuffer->GetLineLength (m_ptCursorPos.y);
7059 pCmdUI->Enable(m_ptCursorPos.y < nLines - 1 || m_ptCursorPos.x < nChars);
7062 pCmdUI->Enable(false);
7065 void CCrystalTextView::OnUpdateEditFindIncrementalBackward(CCmdUI* pCmdUI)
7067 if (m_pTextBuffer != nullptr)
7069 pCmdUI->Enable(m_ptCursorPos.y > 0 || m_ptCursorPos.x > 0);
7072 pCmdUI->Enable(false);
7075 void CCrystalTextView::OnUpdateStatusMessage( CStatusBar *pStatusBar )
7077 static bool bUpdatedAtLastCall = false;
7079 ASSERT( pStatusBar != nullptr && IsWindow( pStatusBar->m_hWnd ) );
7080 if( pStatusBar == nullptr || !IsWindow( pStatusBar->m_hWnd ) )
7083 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
7085 if( bUpdatedAtLastCall )
7086 pStatusBar->SetPaneText( 0, LoadResString(AFX_IDS_IDLEMESSAGE).c_str() );
7088 bUpdatedAtLastCall = false;
7095 if( !m_bIncrementalFound )
7096 formatid = IDS_FIND_INCREMENTAL_FAILED;
7097 else if( m_bIncrementalSearchForward )
7098 formatid = IDS_FIND_INCREMENTAL_FORWARD;
7099 else if( m_bIncrementalSearchBackward )
7100 formatid = IDS_FIND_INCREMENTAL_BACKWARD;
7103 strFormat.Format( LoadResString(formatid).c_str(), (const tchar_t*)*m_pstrIncrementalSearchString );
7105 pStatusBar->SetPaneText( 0, strFormat );
7106 bUpdatedAtLastCall = false;
7110 bool CCrystalTextView::IsTextBufferInitialized () const
7112 return m_pTextBuffer && m_pTextBuffer->IsTextBufferInitialized();
7115 CString CCrystalTextView::GetTextBufferEol(int nLine) const
7117 return m_pTextBuffer->GetLineEol(nLine);
7120 void CCrystalTextView::SetMarkersContext(CCrystalTextMarkers * pMarkers)
7122 pMarkers->AddView(this);
7123 m_pMarkers = pMarkers;
7127 int CCrystalTextView::GetCharCellCountUnicodeChar(const wchar_t *pch)
7130 if (!m_bChWidthsCalculated[ch/256])
7132 if (U16_IS_SURROGATE(ch) && U16_IS_SURROGATE_LEAD(ch))
7134 return wcwidth(U16_GET_SUPPLEMENTARY(ch, pch[1]));
7138 int nWidthArray[256];
7139 wchar_t nStart = ch/256*256;
7140 wchar_t nEnd = nStart + 255;
7141 m_pCrystalRenderer->GetCharWidth(nStart, nEnd, nWidthArray);
7142 int nCharWidth = GetCharWidth();
7143 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE::GDI);
7144 for (int i = 0; i < 256; i++)
7146 wchar_t ch2 = static_cast<wchar_t>(nStart + i);
7147 if (nCharWidth * 15 < nWidthArray[i] * 10)
7149 if (ch2 != lpspc->c_space[0] && ch2 != lpspc->c_tab[0])
7150 m_iChDoubleWidthFlags[ch2 / 32] |= 1 << (i % 32);
7154 if (wcwidth(ch2) > 1)
7155 m_iChDoubleWidthFlags[ch2 / 32] |= 1 << (i % 32);
7158 m_bChWidthsCalculated[ch / 256] = true;
7161 if (m_iChDoubleWidthFlags[ch / 32] & (1 << (ch % 32)))
7168 /** @brief Reset computed unicode character widths. */
7169 void CCrystalTextView::ResetCharWidths ()
7172 ZeroMemory(m_bChWidthsCalculated, sizeof(m_bChWidthsCalculated));
7173 ZeroMemory(m_iChDoubleWidthFlags, sizeof(m_iChDoubleWidthFlags));
7177 // This function assumes selection is in one line
7178 void CCrystalTextView::EnsureVisible (CEPoint ptStart, CEPoint ptEnd)
7180 // Scroll vertically
7182 int nSubLineCount = GetSubLineCount();
7183 int nNewTopSubLine = m_nTopSubLine;
7186 CharPosToPoint( ptStart.y, ptStart.x, subLinePos );
7187 subLinePos.y += GetSubLineIndex( ptStart.y );
7189 if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
7190 nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
7191 if( subLinePos.y < nNewTopSubLine )
7192 nNewTopSubLine = subLinePos.y;
7194 if( nNewTopSubLine < 0 )
7196 if( nNewTopSubLine >= nSubLineCount )
7197 nNewTopSubLine = nSubLineCount - 1;
7199 if ( !m_bWordWrap && !m_bHideLines )
7201 // WINMERGE: This line fixes (cursor) slowdown after merges!
7202 // I don't know exactly why, but propably we are setting
7203 // m_nTopLine to zero in ResetView() and are not setting to
7204 // valid value again. Maybe this is a good place to set it?
7205 m_nTopLine = nNewTopSubLine;
7210 GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
7213 if( nNewTopSubLine != m_nTopSubLine )
7215 ScrollToSubLine( nNewTopSubLine );
7217 UpdateSiblingScrollPos( false );
7220 // Scroll horizontally
7222 // we do not need horizontally scrolling, if we wrap the words
7223 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
7226 int nActualPos = CalculateActualOffset (ptStart.y, ptStart.x);
7227 int nNewOffset = m_nOffsetChar;
7228 const int nScreenChars = GetScreenChars ();
7230 if (ptStart == ptEnd)
7232 // Keep 5 chars visible right to cursor
7233 if (nActualPos > nNewOffset + nScreenChars - 5)
7235 // Add 10 chars width space after line
7236 nNewOffset = nActualPos - nScreenChars + 10;
7238 // Keep 5 chars visible left to cursor
7239 if (nActualPos < nNewOffset + 5)
7241 // Jump by 10 char steps, so user sees previous letters too
7242 nNewOffset = nActualPos - 10;
7247 int nActualEndPos = CalculateActualOffset (ptEnd.y, ptEnd.x);
7248 const int nBeginOffset = nActualPos - m_nOffsetChar;
7249 const int nEndOffset = nActualEndPos - m_nOffsetChar;
7250 const int nSelLen = nActualEndPos - nActualPos;
7252 // Selection fits to screen, scroll whole selection visible
7253 if (nSelLen < nScreenChars)
7255 // Begin of selection not visible
7256 if (nBeginOffset > nScreenChars)
7258 // Scroll so that there is max 5 chars margin at end
7259 if (nScreenChars - nSelLen > 5)
7260 nNewOffset = nActualPos + 5 - nScreenChars + nSelLen;
7262 nNewOffset = nActualPos - 5;
7264 else if (nBeginOffset < 0)
7266 // Scroll so that there is max 5 chars margin at begin
7267 if (nScreenChars - nSelLen >= 5)
7268 nNewOffset = nActualPos - 5;
7270 nNewOffset = nActualPos - 5 - nScreenChars + nSelLen;
7272 // End of selection not visible
7273 else if (nEndOffset > nScreenChars ||
7276 nNewOffset = nActualPos - 5;
7279 else // Selection does not fit screen so scroll to begin of selection
7281 nNewOffset = nActualPos - 5;
7285 // Horiz scroll limit to longest line + one screenwidth
7286 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
7287 if (nNewOffset >= nMaxLineLen + nScreenChars)
7288 nNewOffset = nMaxLineLen + nScreenChars - 1;
7292 if (m_nOffsetChar != nNewOffset)
7294 ScrollToChar (nNewOffset);
7296 UpdateSiblingScrollPos (true);
7300 // Analyze the first line of file to detect its type
7301 // Mainly it works for xml files
7302 bool CCrystalTextView::
7303 SetTextTypeByContent (const tchar_t* pszContent)
7305 RxNode *rxnode = nullptr;
7308 if (::FindStringHelper(pszContent, tc::tcslen(pszContent), pszContent, _T("^\\s*\\<\\?xml\\s+.+?\\?\\>\\s*$"),
7309 FIND_REGEXP, nLen, rxnode, &rxmatch) == 0)
7313 return SetTextType(CrystalLineParser::SRC_XML);
7320 void CCrystalTextView::
7321 AutoFitColumn (int nColumn)
7323 int nLastColumn = 0;
7324 int nLastColumnWidth = 0;
7325 const int nTabSize = GetTabSize ();
7326 std::vector<int> aColumnWidths;
7327 const int nScreenChars = GetScreenChars ();
7328 const int nMaxColumnWidth = nScreenChars < 1 ? 1 : nScreenChars - 1;
7329 for (auto& pbuf : m_pTextBuffer->GetTextBufferList ())
7331 const tchar_t sep = pbuf->GetFieldDelimiter ();
7332 const int quote = pbuf->GetFieldEnclosure ();
7333 const int nLineCount = pbuf->GetLineCount ();
7334 for (int i = 0; i < nLineCount; ++i)
7336 bool bInQuote = false;
7338 int nColumnWidth = 0;
7339 const tchar_t* pszChars = pbuf->GetLineChars (i);
7340 const size_t nLineLength = pbuf->GetFullLineLength (i);
7341 for (size_t j = 0; j < nLineLength; j += U16_IS_SURROGATE (pszChars[j]) ? 2 : 1)
7343 bool bDelimiterOrNewLine = false;
7344 tchar_t c = pszChars[j];
7346 bInQuote = !bInQuote;
7347 if (!bInQuote && c == sep)
7349 bDelimiterOrNewLine = true;
7352 else if (c == '\r' || c == '\n')
7358 if (j == nLineLength - 1 || pszChars[j + 1] != '\n')
7361 bDelimiterOrNewLine = true;
7366 if (j > 0 && pszChars[j - 1] == '\r')
7370 bDelimiterOrNewLine = true;
7374 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7379 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7381 if (bDelimiterOrNewLine)
7383 if (nColumnWidth > nMaxColumnWidth)
7384 nColumnWidth = nMaxColumnWidth;
7385 if (static_cast<int>(aColumnWidths.size ()) < nColumn2 + 1)
7386 aColumnWidths.resize (nColumn2 + 1, nTabSize);
7387 if (aColumnWidths[nColumn2] < nColumnWidth)
7388 aColumnWidths[nColumn2] = nColumnWidth;
7394 if (nLastColumn < nColumn2)
7396 nLastColumn = nColumn2;
7397 nLastColumnWidth = 0;
7399 if (nLastColumnWidth < nColumnWidth)
7400 nLastColumnWidth = nColumnWidth;
7404 aColumnWidths.resize (nLastColumn + 1, nTabSize);
7405 if (aColumnWidths[nLastColumn] < nLastColumnWidth)
7406 aColumnWidths[nLastColumn] = nLastColumnWidth;
7408 for (size_t nColumn2 = 0; nColumn2 < aColumnWidths.size (); ++nColumn2)
7410 if (nColumn == -1 || nColumn == static_cast<int>(nColumn2))
7411 m_pTextBuffer->SetColumnWidth (static_cast<int>(nColumn2), aColumnWidths[nColumn2]);
7413 m_pTextBuffer->InvalidateColumns ();
7416 CCrystalTextView::TextLayoutMode CCrystalTextView::GetTextLayoutMode () const
7418 if (m_pTextBuffer && m_pTextBuffer->GetTableEditing ())
7419 return m_bWordWrap ? TEXTLAYOUT_TABLE_WORDWRAP : TEXTLAYOUT_TABLE_NOWORDWRAP;
7420 return m_bWordWrap ? TEXTLAYOUT_WORDWRAP : TEXTLAYOUT_NOWORDWRAP;
7423 ////////////////////////////////////////////////////////////////////////////