1 ////////////////////////////////////////////////////////////////////////////
2 // File: ccrystaltextview.cpp
4 // Created: 29-Dec-1998
6 // Author: Stcherbatchenko Andrei
7 // E-mail: windfall@gmx.de
9 // Implementation of the CCrystalTextView class, a part of Crystal Edit -
10 // syntax coloring text editor.
12 // You are free to use or modify this code to the following restrictions:
13 // - Acknowledge me somewhere in your about box, simple "Parts of code by.."
14 // will be enough. If you can't (or don't want to), contact me personally.
15 // - LEAVE THIS HEADER INTACT
16 ////////////////////////////////////////////////////////////////////////////
18 ////////////////////////////////////////////////////////////////////////////
20 // FIX: missing UpdateCaret() in CCrystalTextView::SetFont
21 // FIX: missing UpdateCaret() in CCrystalTextView::RecalcVertScrollBar
22 // FIX: mistype in CCrystalTextView::RecalcPageLayouts + instead of +=
23 // FIX: removed condition 'm_nLineHeight < 20' in
24 // CCrystalTextView::CalcLineCharDim(). This caused painting defects
25 // when using very small fonts.
27 // FEATURE: Some experiments with smooth scrolling, controlled by
28 // m_bSmoothScroll member variable, by default turned off.
29 // See ScrollToLine function for implementation details.
30 ////////////////////////////////////////////////////////////////////////////
32 ////////////////////////////////////////////////////////////////////////////
34 // Paul Selormey, James R. Twine
35 // + FEATURE: description for Undo/Redo actions
36 // + FEATURE: multiple MSVC-like bookmarks
37 // + FEATURE: 'Disable backspace at beginning of line' option
38 // + FEATURE: 'Disable drag-n-drop editing' option
40 // + FIX: ResetView() now virtual
41 // + FEATURE: Added OnEditOperation() virtual: base for auto-indent,
43 ////////////////////////////////////////////////////////////////////////////
45 ////////////////////////////////////////////////////////////////////////////
48 // + FEATURE: regular expressions, go to line and things ...
49 // + FEATURE: plenty of syntax highlighting definitions
50 // + FEATURE: corrected bug in syntax highlighting C comments
51 // + FEATURE: extended registry support for saving settings
52 // + FEATURE: some other things I've forgotten ...
54 // ... it's being edited very rapidly so sorry for non-commented
55 // and maybe "ugly" code ...
56 ////////////////////////////////////////////////////////////////////////////
58 ////////////////////////////////////////////////////////////////////////////
59 // 01-Jun-99 to 31-Aug-99
60 // Sven Wiegand (search for "//BEGIN SW" to find my changes):
62 // + FEATURE: support for language switching on the fly with class
64 // + FEATURE: word wrapping
65 // + FIX: Setting m_nIdealCharPos, when choosing cursor position by mouse
66 // + FIX: Backward search
67 // + FEATURE: incremental search
68 ////////////////////////////////////////////////////////////////////////////
70 ////////////////////////////////////////////////////////////////////////////
74 // + FIX: Opening large files won't crash anymore and will go very fast
75 // (removed call to RecalcVertScrollBar() in WrapLineCached())
76 // + FIX: Problems with repainting and cursor-position by resizing window
77 // fixed by adding call to ScrollToSubLine() in OnSize().
78 // + FEATURE: Supporting [Return] to exit incremental-search-mode
80 ///////////////////////////////////////////////////////////////////////////////
83 * @file ccrystaltextview.cpp
85 * @brief Implementation of the CCrystalTextView class
92 #include <imm.h> /* IME */
95 #include "ccrystaltextview.h"
96 #include "ccrystaltextbuffer.h"
97 #include "ccrystaltextmarkers.h"
98 #include "ViewableWhitespace.h"
99 #include "SyntaxColors.h"
100 #include "renderers/ccrystalrendererdirectwrite.h"
101 #include "renderers/ccrystalrenderergdi.h"
102 #include "dialogs/cfindtextdlg.h"
103 #include "dialogs/ctextmarkerdlg.h"
104 #include "dialogs/gotodlg.h"
105 #include "utils/fpattern.h"
106 #include "utils/filesup.h"
107 #include "utils/registry.h"
108 #include "utils/string_util.h"
109 #include "utils/wcwidth.h"
110 #include "utils/icu.hpp"
113 using CrystalLineParser::TEXTBLOCK;
115 // Escaped character constants in range 0x80-0xFF are interpreted in current codepage
116 // Using C locale gets us direct mapping to Unicode codepoints
117 #pragma setlocale("C")
119 #ifndef __AFXPRIV_H__
120 #pragma message("Include <afxpriv.h> in your stdafx.h to avoid this message")
125 #define new DEBUG_NEW
129 // The vcruntime.h version of _countf() gives syntax errors starting with VS 15.7.2,
130 // but only with `CCrystalTextView::m_SourceDefs` (which is local to this .cpp file),
131 // and only for X64 compilations (Win32 is ok, probably because no alignment issues
132 // are involved). I think that this could be related to C++17 compliance issues.
133 // This patch reverts to a 'traditional' definition of _countf(), a pre-existing
134 // part of the CCrystalTextView package.
137 #define _countof(array) (sizeof(array)/sizeof(array[0]))
140 #define DEFAULT_PRINT_MARGIN 1000 // 10 millimeters
142 #ifndef WM_MOUSEHWHEEL
143 # define WM_MOUSEHWHEEL 0x20e
146 /** @brief Width of revision marks. */
147 const UINT MARGIN_REV_WIDTH = 3;
149 /** @brief Color of unsaved line revision mark (dark yellow). */
150 const COLORREF UNSAVED_REVMARK_CLR = RGB(0xD7, 0xD7, 0x00);
151 /** @brief Color of saved line revision mark (green). */
152 const COLORREF SAVED_REVMARK_CLR = RGB(0x00, 0xFF, 0x00);
154 #define SMOOTH_SCROLL_FACTOR 6
156 #define ICON_INDEX_WRAPLINE 15
158 ////////////////////////////////////////////////////////////////////////////
161 LOGFONT CCrystalTextView::m_LogFont;
163 IMPLEMENT_DYNCREATE (CCrystalTextView, CView)
165 HINSTANCE CCrystalTextView::s_hResourceInst = nullptr;
166 CCrystalTextView::RENDERING_MODE CCrystalTextView::s_nRenderingModeDefault = RENDERING_MODE_GDI;
168 static ptrdiff_t FindStringHelper(LPCTSTR pszLineBegin, size_t nLineLength, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch);
170 BEGIN_MESSAGE_MAP (CCrystalTextView, CView)
171 //{{AFX_MSG_MAP(CCrystalTextView)
184 ON_WM_LBUTTONDBLCLK ()
185 ON_COMMAND (ID_EDIT_COPY, OnEditCopy)
186 ON_UPDATE_COMMAND_UI (ID_EDIT_COPY, OnUpdateEditCopy)
187 ON_COMMAND (ID_EDIT_SELECT_ALL, OnEditSelectAll)
189 ON_WM_SYSCOLORCHANGE ()
191 ON_COMMAND (ID_EDIT_FIND, OnEditFind)
192 ON_COMMAND (ID_EDIT_REPEAT, OnEditRepeat)
193 ON_UPDATE_COMMAND_UI (ID_EDIT_REPEAT, OnUpdateEditRepeat)
194 ON_COMMAND (ID_EDIT_MARK, OnEditMark)
197 ON_MESSAGE (WM_IME_STARTCOMPOSITION, OnImeStartComposition) /* IME */
199 ON_COMMAND (ID_EDIT_CHAR_LEFT, OnCharLeft)
200 ON_COMMAND (ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
201 ON_COMMAND (ID_EDIT_CHAR_RIGHT, OnCharRight)
202 ON_COMMAND (ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
203 ON_COMMAND (ID_EDIT_WORD_LEFT, OnWordLeft)
204 ON_COMMAND (ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
205 ON_COMMAND (ID_EDIT_WORD_RIGHT, OnWordRight)
206 ON_COMMAND (ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
207 ON_COMMAND (ID_EDIT_LINE_UP, OnLineUp)
208 ON_COMMAND (ID_EDIT_EXT_LINE_UP, OnExtLineUp)
209 ON_COMMAND (ID_EDIT_LINE_DOWN, OnLineDown)
210 ON_COMMAND (ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
211 ON_COMMAND (ID_EDIT_SCROLL_UP, ScrollUp)
212 ON_COMMAND (ID_EDIT_SCROLL_DOWN, ScrollDown)
213 ON_COMMAND (ID_EDIT_PAGE_UP, OnPageUp)
214 ON_COMMAND (ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
215 ON_COMMAND (ID_EDIT_PAGE_DOWN, OnPageDown)
216 ON_COMMAND (ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
217 ON_COMMAND (ID_EDIT_LINE_END, OnLineEnd)
218 ON_COMMAND (ID_EDIT_EXT_LINE_END, OnExtLineEnd)
219 ON_COMMAND (ID_EDIT_HOME, OnHome)
220 ON_COMMAND (ID_EDIT_EXT_HOME, OnExtHome)
221 ON_COMMAND (ID_EDIT_TEXT_BEGIN, OnTextBegin)
222 ON_COMMAND (ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
223 ON_COMMAND (ID_EDIT_TEXT_END, OnTextEnd)
224 ON_COMMAND (ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
225 // Standard printing commands
226 ON_COMMAND (ID_FILE_PAGE_SETUP, OnFilePageSetup)
227 ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint)
228 ON_COMMAND (ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
229 ON_COMMAND (ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
231 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
232 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
234 ON_COMMAND_RANGE (ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
235 ON_COMMAND_RANGE (ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
236 ON_COMMAND (ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
238 ON_COMMAND (ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
239 ON_COMMAND (ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
240 ON_COMMAND (ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
241 ON_COMMAND (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
242 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
243 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
244 ON_UPDATE_COMMAND_UI (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
245 // Ferdi's source type chnages
246 ON_COMMAND_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnSourceType)
247 ON_UPDATE_COMMAND_UI_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnUpdateSourceType)
248 ON_COMMAND (ID_EDIT_MATCHBRACE, OnMatchBrace)
249 ON_UPDATE_COMMAND_UI (ID_EDIT_MATCHBRACE, OnUpdateMatchBrace)
250 ON_COMMAND (ID_EDIT_GOTO, OnEditGoTo)
251 ON_UPDATE_COMMAND_UI (ID_VIEW_TOGGLE_SRC_HDR, OnUpdateToggleSourceHeader)
252 ON_COMMAND (ID_VIEW_TOGGLE_SRC_HDR, OnToggleSourceHeader)
253 ON_UPDATE_COMMAND_UI (ID_VIEW_TOPMARGIN, OnUpdateTopMargin)
254 ON_COMMAND (ID_VIEW_TOPMARGIN, OnTopMargin)
255 ON_UPDATE_COMMAND_UI (ID_VIEW_SELMARGIN, OnUpdateSelMargin)
256 ON_COMMAND (ID_VIEW_SELMARGIN, OnSelMargin)
257 ON_UPDATE_COMMAND_UI (ID_VIEW_WORDWRAP, OnUpdateWordWrap)
258 ON_COMMAND (ID_VIEW_WORDWRAP, OnWordWrap)
259 ON_COMMAND (ID_FORCE_REDRAW, OnForceRedraw)
261 // incremental search
262 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnEditFindIncrementalForward)
263 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnEditFindIncrementalBackward)
264 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnUpdateEditFindIncrementalForward)
265 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnUpdateEditFindIncrementalBackward)
267 ON_COMMAND (ID_EDIT_TOGGLE_COLUMNSELECTION, OnToggleColumnSelection)
270 #define EXPAND_PRIMITIVE(impl, func) \
271 void CCrystalTextView::On##func() { m_bRectangularSelection = false; impl(false); } \
272 void CCrystalTextView::OnExt##func() { impl(true); }
273 EXPAND_PRIMITIVE (MoveLeft, CharLeft)
274 EXPAND_PRIMITIVE (MoveRight, CharRight)
275 EXPAND_PRIMITIVE (MoveWordLeft, WordLeft)
276 EXPAND_PRIMITIVE (MoveWordRight, WordRight)
277 EXPAND_PRIMITIVE (MoveUp, LineUp)
278 EXPAND_PRIMITIVE (MoveDown, LineDown)
279 EXPAND_PRIMITIVE (MovePgUp, PageUp)
280 EXPAND_PRIMITIVE (MovePgDn, PageDown)
281 EXPAND_PRIMITIVE (MoveHome, Home)
282 EXPAND_PRIMITIVE (MoveEnd, LineEnd)
283 EXPAND_PRIMITIVE (MoveCtrlHome, TextBegin)
284 EXPAND_PRIMITIVE (MoveCtrlEnd, TextEnd)
285 #undef EXPAND_PRIMITIVE
287 /////////////////////////////////////////////////////////////////////////////
288 // CCrystalTextView construction/destruction
290 bool CCrystalTextView::
291 DoSetTextType (CrystalLineParser::TextDefinition *def)
293 m_CurSourceDef = def;
294 SetFlags (def->flags);
297 // EOL is determined from file, tabsize and viewtabs are
298 // global WinMerge settings, selection margin is not needed
299 // and wordwrapping must be false always
301 SetWordWrapping ((def->flags & SRCOPT_WORDWRAP) != false);
302 SetSelectionMargin ((def->flags & SRCOPT_SELMARGIN) != false);
303 SetTabSize (def->tabsize);
304 SetViewTabs ((def->flags & SRCOPT_SHOWTABS) != false);
306 if (def->flags & SRCOPT_EOLNDOS)
310 else if (def->flags & SRCOPT_EOLNUNIX)
314 else if (def->flags & SRCOPT_EOLNMAC)
327 bool CCrystalTextView::
328 SetTextType (LPCTSTR pszExt)
330 m_CurSourceDef = CrystalLineParser::m_SourceDefs;
332 CrystalLineParser::TextDefinition *def = CrystalLineParser::GetTextType (pszExt);
334 return SetTextType (def);
337 bool CCrystalTextView::
338 SetTextType (CrystalLineParser::TextType enuType)
340 CrystalLineParser::TextDefinition *def;
342 m_CurSourceDef = def = CrystalLineParser::m_SourceDefs;
343 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
345 if (def->type == enuType)
347 return SetTextType (def);
353 bool CCrystalTextView::
354 SetTextType (CrystalLineParser::TextDefinition *def)
357 if (m_CurSourceDef != def)
358 return DoSetTextType (def);
364 void CCrystalTextView::
367 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
370 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
372 reg.LoadNumber (_T ("DefaultEncoding"), (DWORD*) &CCrystalTextBuffer::m_nDefaultEncoding);
373 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
376 if (reg1.Open (reg.hKey, def->name, KEY_READ))
378 reg1.LoadString (_T ("Extensions"), def->exts, _countof (def->exts));
379 reg1.LoadNumber (_T ("Flags"), &def->flags);
380 // reg1.LoadNumber (_T ("TabSize"), &def->tabsize);
381 reg1.LoadString (_T ("OpenComment"), def->opencomment, _countof (def->opencomment));
382 reg1.LoadString (_T ("CloseComment"), def->closecomment, _countof (def->closecomment));
383 reg1.LoadString (_T ("CommentLine"), def->commentline, _countof (def->commentline));
384 reg1.LoadNumber (_T ("DefaultEncoding"), &def->encoding);
387 bFontLoaded = reg.LoadBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont));
393 CWindowDC dc (CWnd::GetDesktopWindow ());
394 NONCLIENTMETRICS info;
395 info.cbSize = sizeof(info);
396 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
397 memcpy (&m_LogFont, &info.lfMessageFont, sizeof (LOGFONT));
398 m_LogFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
399 m_LogFont.lfWeight = FW_NORMAL;
400 m_LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
401 m_LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
402 m_LogFont.lfQuality = DEFAULT_QUALITY;
403 m_LogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
404 _tcscpy_s (m_LogFont.lfFaceName, _T ("Courier New"));
408 void CCrystalTextView::
411 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
413 if (reg.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
415 VERIFY (reg.SaveNumber (_T ("DefaultEncoding"), (DWORD) CCrystalTextBuffer::m_nDefaultEncoding));
416 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
419 if (reg1.Create (reg.hKey, def->name, KEY_WRITE))
421 VERIFY (reg1.SaveString (_T ("Extensions"), def->exts));
422 VERIFY (reg1.SaveNumber (_T ("Flags"), def->flags));
423 // VERIFY (reg1.SaveNumber (_T ("TabSize"), def->tabsize));
424 VERIFY (reg1.SaveString (_T ("OpenComment"), def->opencomment));
425 VERIFY (reg1.SaveString (_T ("CloseComment"), def->closecomment));
426 VERIFY (reg1.SaveString (_T ("CommentLine"), def->commentline));
427 VERIFY (reg1.SaveNumber (_T ("DefaultEncoding"), def->encoding));
430 VERIFY (reg.SaveBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont)));
434 CCrystalTextView::CCrystalTextView ()
436 , m_pFindTextDlg(nullptr)
437 , m_CurSourceDef(nullptr)
438 , m_dwLastDblClickTime(0)
440 , m_pszMatched(nullptr)
441 , m_bTopMargin(false)
443 , m_bViewLineNumbers(false)
445 , m_bHideLines(false)
446 , m_bLastSearch(false)
447 , m_bBookmarkExist(false)
448 , m_bSingle(false) // needed to be set in descendat classes
449 , m_bRememberLastPos(false)
451 , m_nLastLineIndexCalculatedSubLineIndex(-1)
453 , m_pTextBuffer(nullptr)
454 , m_pCacheBitmap(nullptr)
455 , m_pszLastFindWhat(nullptr)
456 , m_dwLastSearchFlags(0)
457 , m_bMultipleSearch(false)
458 , m_bCursorHidden(false)
463 , m_bDistinguishEols(false)
466 , m_pMarkers(nullptr)
467 , m_panSubLines(new CArray<int, int>())
468 , m_panSubLineIndexCache(new CArray<int, int>())
469 , m_pstrIncrementalSearchString(new CString)
470 , m_pstrIncrementalSearchStringOld(new CString)
471 , m_ParseCookies(new vector<DWORD>)
472 , m_pnActualLineLength(new vector<int>)
476 , m_lfSavedBaseFont{}
478 , m_pPrintFont(nullptr)
480 , m_bChWidthsCalculated{}
481 , m_iChDoubleWidthFlags{}
483 , m_bPreparingToDrag(false)
484 , m_bDraggingText(false)
485 , m_bDragSelection(false)
486 , m_bWordSelection(false)
487 , m_bLineSelection(false)
488 , m_bRectangularSelection(false)
489 , m_bColumnSelection(false)
491 , m_bOverrideCaret(false)
492 , m_nLastFindWhatLen(0)
494 , m_nPrintLineHeight(0)
495 , m_bPrintFooter(false)
496 , m_bPrintHeader(false)
501 , m_bSmoothScroll(false)
502 , m_bVertScrollBarLocked(false)
503 , m_bHorzScrollBarLocked(false)
504 , m_bShowInactiveSelection(false)
505 , m_bDisableDragAndDrop(false)
506 , m_bIncrementalSearchForward(false)
507 , m_bIncrementalSearchBackward(false)
508 , m_bIncrementalFound(false)
510 , m_nRenderingMode(s_nRenderingModeDefault)
511 , m_pCrystalRendererSaved(nullptr)
512 , m_nColumnResizing(-1)
515 if (m_nRenderingMode == RENDERING_MODE_GDI)
516 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
518 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(m_nRenderingMode));
520 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
523 m_panSubLines->SetSize( 0, 4096 );
524 m_panSubLineIndexCache->SetSize( 0, 4096 );
527 CCrystalTextView::ResetView ();
528 SetTextType (CrystalLineParser::SRC_PLAIN);
531 CCrystalTextView::~CCrystalTextView ()
533 ASSERT (m_hAccel == nullptr);
534 ASSERT (m_pCacheBitmap == nullptr);
535 ASSERT (m_pTextBuffer == nullptr); // Must be correctly detached
537 delete m_pFindTextDlg;
539 free (m_pszLastFindWhat);
540 m_pszLastFindWhat=nullptr;
545 free(m_pszMatched); // Allocated by _tcsdup()
546 m_pszMatched = nullptr;
549 delete m_panSubLines;
550 m_panSubLines = nullptr;
552 delete m_panSubLineIndexCache;
553 m_panSubLineIndexCache = nullptr;
555 delete m_pstrIncrementalSearchString;
556 m_pstrIncrementalSearchString = nullptr;
558 delete m_pstrIncrementalSearchStringOld;
559 m_pstrIncrementalSearchStringOld = nullptr;
562 ASSERT(m_ParseCookies != nullptr);
563 delete m_ParseCookies;
564 m_ParseCookies = nullptr;
565 ASSERT(m_pnActualLineLength != nullptr);
566 delete m_pnActualLineLength;
567 m_pnActualLineLength = nullptr;
568 if (m_pMarkers != nullptr)
569 m_pMarkers->DeleteView(this);
572 BOOL CCrystalTextView::
573 PreCreateWindow (CREATESTRUCT & cs)
575 CWnd *pParentWnd = CWnd::FromHandlePermanent (cs.hwndParent);
576 if (pParentWnd == nullptr || !pParentWnd->IsKindOf (RUNTIME_CLASS (CSplitterWnd)))
578 // View must always create its own scrollbars,
579 // if only it's not used within splitter
581 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
582 // we do not need a horizontal scroll bar, if we wrap the lines
583 cs.style|= WS_VSCROLL;
585 cs.style |= (WS_HSCROLL | WS_VSCROLL);
587 cs.style |= (WS_HSCROLL | WS_VSCROLL);
591 cs.lpszClass = AfxRegisterWndClass (CS_DBLCLKS);
592 return CView::PreCreateWindow (cs);
596 /////////////////////////////////////////////////////////////////////////////
597 // CCrystalTextView drawing
599 void CCrystalTextView::
600 GetSelection (CPoint & ptStart, CPoint & ptEnd)
603 ptStart = m_ptDrawSelStart;
604 ptEnd = m_ptDrawSelEnd;
607 bool CCrystalTextView::
608 GetColumnSelection (int nLineIndex, int & nSelBegin, int & nSelEnd)
610 int nSelTop, nSelBottom;
611 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
613 nSelTop = m_ptDrawSelStart.y;
614 nSelBottom = m_ptDrawSelEnd.y;
618 nSelTop = m_ptDrawSelEnd.y;
619 nSelBottom = m_ptDrawSelStart.y;
622 if (nSelTop > nLineIndex || nLineIndex > nSelBottom)
630 int nStartCharPos = CalculateActualOffset (m_ptDrawSelStart.y, m_ptDrawSelStart.x, true);
631 int nEndCharPos = CalculateActualOffset (m_ptDrawSelEnd.y, m_ptDrawSelEnd.x, true);
632 int nLeftCharPos, nRightCharPos;
633 if (nStartCharPos > nEndCharPos)
635 nLeftCharPos = nEndCharPos;
636 nRightCharPos = nStartCharPos;
640 nLeftCharPos = nStartCharPos;
641 nRightCharPos = nEndCharPos;
643 if (nRightCharPos < m_nIdealCharPos)
644 nRightCharPos = m_nIdealCharPos;
645 nSelBegin = ApproxActualOffset (nLineIndex, nLeftCharPos);
646 nSelEnd = ApproxActualOffset (nLineIndex, nRightCharPos);
651 void CCrystalTextView::
652 GetFullySelectedLines(int & firstLine, int & lastLine)
656 GetSelection(ptStart, ptEnd);
659 firstLine = ptStart.y;
661 firstLine = ptStart.y + 1;
662 if (ptEnd.x == GetLineLength(ptEnd.y))
665 lastLine = ptEnd.y-1;
668 CCrystalTextBuffer *CCrystalTextView::
675 * @brief : Get the line length, for cursor movement
677 * @note : there are at least 4 line lengths :
678 * - number of characters (memory, no EOL)
679 * - number of characters (memory, with EOL)
680 * - number of characters for cursor position (tabs are expanded, no EOL)
681 * - number of displayed characters (tabs are expanded, with EOL)
682 * Corresponding functions :
684 * - GetFullLineLength
685 * - GetLineActualLength
686 * - ExpandChars (returns the line to be displayed as a CString)
688 int CCrystalTextView::
689 GetLineActualLength (int nLineIndex)
691 const int nLineCount = GetLineCount ();
692 ASSERT (nLineCount > 0);
693 ASSERT (nLineIndex >= 0 && nLineIndex < nLineCount);
694 if (!m_pnActualLineLength->size())
696 m_pnActualLineLength->assign(nLineCount, -1);
699 if ((*m_pnActualLineLength)[nLineIndex] != - 1)
700 return (*m_pnActualLineLength)[nLineIndex];
702 // Actual line length is not determined yet, let's calculate a little
703 int nActualLength = 0;
704 int nLength = GetLineLength (nLineIndex);
707 LPCTSTR pszChars = GetLineChars (nLineIndex);
708 const int nTabSize = GetTabSize ();
709 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
710 switch ( GetTextLayoutMode ())
712 case TEXTLAYOUT_TABLE_NOWORDWRAP:
713 case TEXTLAYOUT_TABLE_WORDWRAP:
715 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
716 int nColumnTotalWidth = 0;
717 bool bInQuote = false;
718 const int sep = m_pTextBuffer->GetFieldDelimiter ();
719 const int quote = m_pTextBuffer->GetFieldEnclosure ();
720 for (int i = 0, nColumn = 0; i < nLength; i = pIterChar->next())
722 TCHAR c = pszChars[i];
723 if (!bInQuote && c == sep)
725 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
726 nActualLength = nColumnTotalWidth;
731 bInQuote = !bInQuote;
735 nActualLength += GetCharCellCountFromChar (pszChars + i);
736 if (nColumn < nColumnCount && nActualLength > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
737 nActualLength = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
744 for (int i = 0; i < nLength; i = pIterChar->next())
746 TCHAR c = pszChars[i];
748 nActualLength += (nTabSize - nActualLength % nTabSize);
750 nActualLength += GetCharCellCountFromChar(pszChars + i);
756 (*m_pnActualLineLength)[nLineIndex] = nActualLength;
757 return nActualLength;
760 void CCrystalTextView::
761 ScrollToChar (int nNewOffsetChar, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
764 // no horizontal scrolling, when word wrapping is enabled
765 if (GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
769 // For now, ignoring bNoSmoothScroll and m_bSmoothScroll
770 if (m_nOffsetChar != nNewOffsetChar)
772 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
773 m_nOffsetChar = nNewOffsetChar;
775 GetClientRect (&rcScroll);
776 rcScroll.left += GetMarginWidth ();
777 ScrollWindow (nScrollChars * GetCharWidth (), 0, &rcScroll, &rcScroll);
780 RecalcHorzScrollBar (true);
785 * @brief Scroll view to given line.
786 * Scrolls view so that given line is first line in the view. We limit
787 * scrolling so that there is only one empty line visible after the last
788 * line at max. So we don't allow user to scroll last line being at top or
789 * even at middle of the screen. This is how many editors behave and I
790 * (Kimmo) think it is good for us too.
791 * @param [in] nNewTopSubLine New top line for view.
792 * @param [in] bNoSmoothScroll if true don't use smooth scrolling.
793 * @param [in] bTrackScrollBar if true scrollbar is updated after scroll.
795 void CCrystalTextView::ScrollToSubLine( int nNewTopSubLine,
796 bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
798 if (m_nTopSubLine != nNewTopSubLine)
801 GetClientRect (&rcScroll);
802 rcScroll.top += GetTopMarginHeight ();
804 if (bNoSmoothScroll || ! m_bSmoothScroll)
806 // Limit scrolling so that we show one empty line at end of file
807 const int nScreenLines = GetScreenLines();
808 const int nLineCount = GetSubLineCount();
809 if (nNewTopSubLine > (nLineCount - nScreenLines))
811 nNewTopSubLine = nLineCount - nScreenLines;
812 if (nNewTopSubLine < 0)
816 const int nScrollLines = m_nTopSubLine - nNewTopSubLine;
817 m_nTopSubLine = nNewTopSubLine;
818 // OnDraw() uses m_nTopLine to determine topline
820 GetLineBySubLine(m_nTopSubLine, m_nTopLine, dummy);
821 ScrollWindow(0, nScrollLines * GetLineHeight(), nullptr, rcScroll);
825 RecalcVertScrollBar(true);
826 RecalcHorzScrollBar();
831 // Do smooth scrolling
832 int nLineHeight = GetLineHeight();
833 if (m_nTopSubLine > nNewTopSubLine)
835 int nIncrement = (m_nTopSubLine - nNewTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
836 while (m_nTopSubLine != nNewTopSubLine)
838 int nTopSubLine = m_nTopSubLine - nIncrement;
839 if (nTopSubLine < nNewTopSubLine)
840 nTopSubLine = nNewTopSubLine;
841 const int nScrollLines = nTopSubLine - m_nTopSubLine;
842 m_nTopSubLine = nTopSubLine;
843 ScrollWindow(0, - nLineHeight * nScrollLines, nullptr, rcScroll);
847 RecalcVertScrollBar(true);
848 RecalcHorzScrollBar();
854 int nIncrement = (nNewTopSubLine - m_nTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
855 while (m_nTopSubLine != nNewTopSubLine)
857 int nTopSubLine = m_nTopSubLine + nIncrement;
858 if (nTopSubLine > nNewTopSubLine)
859 nTopSubLine = nNewTopSubLine;
860 const int nScrollLines = nTopSubLine - m_nTopSubLine;
861 m_nTopSubLine = nTopSubLine;
862 ScrollWindow(0, - nLineHeight * nScrollLines, rcScroll, rcScroll);
866 RecalcVertScrollBar(true);
867 RecalcHorzScrollBar();
873 GetLineBySubLine( m_nTopSubLine, m_nTopLine, nDummy );
877 void CCrystalTextView::
878 ScrollToLine (int nNewTopLine, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
880 if( m_nTopLine != nNewTopLine )
881 ScrollToSubLine( GetSubLineIndex( nNewTopLine ), bNoSmoothScroll, bTrackScrollBar );
884 /** Append szadd to string str, and advance position curpos */
885 static void AppendStringAdv(CString & str, int & curpos, LPCTSTR szadd)
888 curpos += (int) _tcslen(szadd);
891 /** Append escaped control char to string str, and advance position curpos */
892 static void AppendEscapeAdv(CString & str, int & curpos, TCHAR c)
894 int curlen = str.GetLength();
895 LPTSTR szadd = str.GetBufferSetLength(curlen + 3) + curlen;
896 curpos += wsprintf(szadd, _T("\t%02X"), static_cast<int>(c));
899 int CCrystalTextView::
900 ExpandChars (int nLineIndex, int nOffset, int nCount, CString & line, int nActualOffset)
903 // Request whitespace characters for codepage ACP
904 // because that is the codepage used by ExtTextOut
905 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE_GDI);
912 const int nTabSize = GetTabSize ();
914 LPCTSTR pszChars = GetLineChars(nLineIndex);
916 int nLength = nCount;
918 for (int i = 0; i < nLength; i++)
920 TCHAR c = pszChars[i];
922 nCount += nTabSize - 1;
923 else if (c >= _T('\x00') && c <= _T('\x1F') && c != _T('\r') && c != _T('\n'))
927 // Preallocate line buffer, to avoid reallocations as we add characters
928 line.GetBuffer(nCount + 1); // at least this many characters
929 line.ReleaseBuffer(0);
931 const bool bTableEditing = m_pTextBuffer->GetTableEditing ();
933 if (nCount > nLength || m_bViewTabs || m_bViewEols)
935 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
936 for (int i = 0, next = 0; i < nLength; i = next)
938 next = pIterChar->next();
939 if (pszChars[i] == _T('\t'))
941 int nSpaces = bTableEditing ? 1 : (nTabSize - (nActualOffset + nCurPos) % nTabSize);
944 AppendStringAdv(line, nCurPos, lpspc->c_tab);
954 else if (pszChars[i] == ' ' && m_bViewTabs)
955 AppendStringAdv(line, nCurPos, lpspc->c_space);
956 else if (pszChars[i] == '\r' || pszChars[i] == '\n')
960 if (pszChars[i] == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
962 // Ignore \n after \r
966 if (pszChars[i] == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
968 AppendStringAdv(line, nCurPos, lpspc->c_eol);
971 else if (pszChars[i] == '\r' && m_bDistinguishEols)
972 AppendStringAdv(line, nCurPos, lpspc->c_cr);
973 else if (pszChars[i] == '\n' && m_bDistinguishEols)
974 AppendStringAdv(line, nCurPos, lpspc->c_lf);
977 AppendStringAdv(line, nCurPos, lpspc->c_eol);
982 else if (pszChars[i] >= _T('\x00') && pszChars[i] <= _T('\x1F'))
984 AppendEscapeAdv(line, nCurPos, pszChars[i]);
988 nCurPos += GetCharCellCountFromChar(pszChars + i);
989 for (; i < next; ++i)
996 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
997 for (int i1=0, next=0; i1<nLength; i1 = next)
999 next = pIterChar->next();
1000 nCurPos += GetCharCellCountFromChar(pszChars + i1);
1001 for (; i1 < next; ++i1)
1002 line += pszChars[i1];
1008 int CCrystalTextView::
1009 ExpandCharsTableEditingNoWrap(int nLineIndex, int nOffset, int nCount, CString& line, int nActualOffset)
1011 if (m_pTextBuffer == nullptr || nCount <= 0)
1014 LPCTSTR pszChars = GetLineChars(nLineIndex);
1016 // Request whitespace characters for codepage ACP
1017 // because that is the codepage used by ExtTextOut
1018 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE_GDI);
1020 int nLength = nCount;
1022 int nColumnCount = 0;
1023 int nCurColumn = -1;
1024 int nColumnTotalWidth = 0;
1025 int nColumnBegin = 0;
1026 int nColumnTotalWidthBegin = 0;
1027 const int nLineLength = GetFullLineLength (nLineIndex);
1028 const int nTabSize = GetTabSize ();
1029 const int sep = m_pTextBuffer->GetFieldDelimiter ();
1030 const int quote = m_pTextBuffer->GetFieldEnclosure ();
1031 const int eollen = nLineLength - GetLineLength (nLineIndex);
1032 bool bInQuote = false;
1033 bool bInQuoteBegin = false;
1035 for (int i = 0; i < nLineLength; i++)
1037 TCHAR c = pszChars[i];
1040 nColumnBegin = nColumn;
1041 nColumnTotalWidthBegin = nColumnTotalWidth;
1042 bInQuoteBegin = bInQuote;
1044 if (nLineIndex == m_ptCursorPos.y && i == m_ptCursorPos.x)
1045 nCurColumn = nColumn;
1046 if (!bInQuote && c == sep)
1048 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
1049 nCount = nColumnTotalWidth;
1053 else if (c == quote)
1055 bInQuote = !bInQuote;
1057 else if (c >= '\x00' && c <= '\x1F' && c != '\r' && c != '\n')
1060 nColumnCount = nColumn + 1;
1062 // Preallocate line buffer, to avoid reallocations as we add characters
1063 line.GetBuffer (nCount + 1); // at least this many characters
1064 line.ReleaseBuffer (0);
1065 int nCurPos = nActualOffset;
1067 pszChars += nOffset;
1069 int curColumnTextCellWidth = 0;
1070 bool beforeCursorPos = (nLineIndex == m_ptCursorPos.y && nOffset < m_ptCursorPos.x);
1071 CString curColumnText;
1072 std::vector<std::pair<int, int>> curColumnByteLenCellWidth;
1073 nColumn = nColumnBegin;
1074 nColumnTotalWidth = nColumnTotalWidthBegin;
1075 bInQuote = bInQuoteBegin;
1076 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator (pszChars, nLength);
1077 auto nextColumnDistance = [&](int nCurPos)
1079 return (nColumn == nColumnCount - 1) ? INT_MAX : nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
1081 auto appendChars = [&](int i, int next, int pos, CString& text, int& textwidth)
1083 TCHAR c = pszChars[i];
1084 if ((c == '\r' || c == '\n') && i >= nLineLength - nOffset - eollen)
1088 if (c == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
1090 // Ignore \n after \r
1094 int prevtextwidth = textwidth;
1095 if (c == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
1096 AppendStringAdv (text, textwidth, lpspc->c_eol);
1097 else if (c == '\r' && m_bDistinguishEols)
1098 AppendStringAdv (text, textwidth, lpspc->c_cr);
1099 else if (c == '\n' && m_bDistinguishEols)
1101 if (i == 0 || pszChars[i - 1] != '\r')
1102 AppendStringAdv (text, textwidth, lpspc->c_lf);
1105 AppendStringAdv (text, textwidth, lpspc->c_eol);
1106 if (textwidth - prevtextwidth > 0)
1107 curColumnByteLenCellWidth.push_back ({ textwidth - prevtextwidth, textwidth - prevtextwidth});
1114 if (sep != '\t' || bInQuote)
1117 if (nSpaces > nextColumnDistance (pos))
1118 nSpaces = nextColumnDistance (pos);
1120 if (nSpaces >= 1 && m_bViewTabs)
1122 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1123 AppendStringAdv (text, textwidth, lpspc->c_tab);
1128 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1134 else if (c == ' ' && m_bViewTabs)
1136 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1137 AppendStringAdv (text, textwidth, lpspc->c_space);
1139 else if (c >= '\x00' && c <= '\x1F')
1141 curColumnByteLenCellWidth.push_back ({ 3, 3 });
1142 AppendEscapeAdv (text, textwidth, c);
1143 if (c == '\r' && pszChars[i + 1] == '\n')
1144 AppendEscapeAdv (text, textwidth, pszChars[i + 1]);
1148 int nLen = GetCharCellCountFromChar (pszChars + i);
1149 curColumnByteLenCellWidth.push_back ({ nLen, next - i });
1151 for (; i < next; ++i)
1152 text += pszChars[i];
1154 if (!bInQuote && c == sep)
1156 int nSpaces = nextColumnDistance (pos + 1);
1163 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1167 for (int i = 0, next = 0; i < nLength; i = next)
1169 next = pIterChar->next ();
1170 const TCHAR c = pszChars[i];
1172 bInQuote = !bInQuote;
1173 int nLen = GetCharCellCountFromChar (pszChars + i);
1174 if (nColumn == nCurColumn && beforeCursorPos)
1176 appendChars (i, next, nCurPos + curColumnTextCellWidth, curColumnText, curColumnTextCellWidth);
1177 if (next + nOffset == m_ptCursorPos.x || next >= nLength)
1179 beforeCursorPos = false;
1180 if (curColumnTextCellWidth > nextColumnDistance (nCurPos))
1182 for (size_t k = 0; k < curColumnByteLenCellWidth.size () && curColumnTextCellWidth > nextColumnDistance (nCurPos); ++k)
1184 curColumnTextCellWidth -= curColumnByteLenCellWidth[k].first;
1185 curColumnText = curColumnText.Mid (curColumnByteLenCellWidth[k].second);
1187 int nSpaces = nextColumnDistance (nCurPos) - curColumnTextCellWidth;
1190 CString spaces (' ', nSpaces);
1191 curColumnText.Insert (0, spaces);
1192 curColumnTextCellWidth = m_pTextBuffer->GetColumnWidth (nColumn);
1195 line += curColumnText;
1196 nCurPos += curColumnTextCellWidth;
1201 if (nLen <= nextColumnDistance (nCurPos))
1203 appendChars (i, next, nCurPos, line, nCurPos);
1204 curColumnByteLenCellWidth.clear ();
1208 int nSpaces = nextColumnDistance (nCurPos);
1215 if (!bInQuote && c == sep)
1217 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1223 return nCurPos - nActualOffset;
1228 * @brief Draw a chunk of text (one color, one line, full or part of line)
1230 * @note In ANSI build, this routine is buggy for multibytes or double-width characters
1232 void CCrystalTextView::
1233 DrawLineHelperImpl (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex,
1234 int nBgColorIndex, COLORREF crText, COLORREF crBkgnd, int nLineIndex, int nOffset, int nCount, int &nActualOffset)
1236 ASSERT (nCount >= 0);
1240 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_NOWORDWRAP)
1241 nActualOffset += ExpandCharsTableEditingNoWrap (nLineIndex, nOffset, nCount, line, nActualOffset);
1243 nActualOffset += ExpandChars (nLineIndex, nOffset, nCount, line, nActualOffset);
1244 const int lineLen = line.GetLength();
1245 const int nCharWidth = GetCharWidth();
1246 const int nCharWidthNarrowed = nCharWidth / 2;
1247 const int nCharWidthWidened = nCharWidth * 2 - nCharWidthNarrowed;
1248 const int nLineHeight = GetLineHeight();
1249 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator((LPCTSTR)line, lineLen);
1251 // i the character index, from 0 to lineLen-1
1254 // Pass if the text begins after the right end of the clipping region
1255 if (ptOrigin.x < rcClip.right)
1257 // Because ExtTextOut is buggy when ptOrigin.x < - 4095 * charWidth
1258 // or when nCount >= 4095
1259 // and because this is not well documented,
1260 // we decide to do the left & right clipping here
1262 // Update the position after the left clipped characters
1263 // stop for i = first visible character, at least partly
1264 const int clipLeft = rcClip.left - nCharWidth * 2;
1265 for ( ; i < lineLen; i = pIterChar->next())
1267 int pnWidthsCurrent = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1268 ptOrigin.x += pnWidthsCurrent;
1269 if (ptOrigin.x >= clipLeft)
1271 ptOrigin.x -= pnWidthsCurrent;
1278 //CSize sz = pdc->GetTextExtent(line, nCount);
1279 //ASSERT(sz.cx == m_nCharWidth * nCount);
1284 // We have to draw some characters
1288 int nWidth = rcClip.right - ptOrigin.x;
1290 // Table of charwidths as CCrystalEditor thinks they are
1291 // Seems that CrystalEditor's and ExtTextOut()'s charwidths aren't
1292 // same with some fonts and text is drawn only partially
1293 // if this table is not used.
1294 vector<int> nWidths(nWidth / nCharWidth * 2 + 2);
1295 bool bdisphex = false;
1296 for (int next = i; i < lineLen && nSumWidth < nWidth ; i = next)
1298 if (line[i] == '\t') // Escape sequence leadin?
1301 // Substitute a space narrowed to half the width of a character cell.
1303 size_t idx = i - ibegin;
1304 if (idx >= nWidths.size())
1305 nWidths.resize(nWidths.size() * 2);
1306 nSumWidth += nWidths[idx] = nCharWidthNarrowed;
1307 // 1st hex digit has normal width.
1308 idx = pIterChar->next() - ibegin;
1309 if (idx >= nWidths.size())
1310 nWidths.resize(nWidths.size() * 2);
1311 nSumWidth += nWidths[idx] = nCharWidth;
1312 // 2nd hex digit is padded by half the width of a character cell.
1313 idx = pIterChar->next() - ibegin;
1314 if (idx >= nWidths.size())
1315 nWidths.resize(nWidths.size() * 2);
1316 nSumWidth += nWidths[idx] = nCharWidthWidened;
1320 size_t idx = i - ibegin;
1321 if (idx >= nWidths.size())
1322 nWidths.resize(nWidths.size() * 2);
1323 nSumWidth += nWidths[idx] = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1325 next = pIterChar->next();
1327 int nCount1 = i - ibegin;
1329 if (ptOrigin.x + nSumWidth > rcClip.left)
1331 if (crText == CLR_NONE || nColorIndex & COLORINDEX_APPLYFORCE)
1332 m_pCrystalRenderer->SetTextColor(GetColor(nColorIndex));
1334 m_pCrystalRenderer->SetTextColor(crText);
1335 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1336 m_pCrystalRenderer->SetBkColor(GetColor(nBgColorIndex));
1338 m_pCrystalRenderer->SetBkColor(crBkgnd);
1340 m_pCrystalRenderer->SwitchFont(GetItalic(nColorIndex), GetBold(nColorIndex));
1341 // we are sure to have less than 4095 characters because all the chars are visible
1343 RECT rcTextBlock = {ptOrigin.x, ptOrigin.y, ptOrigin.x + nSumWidth + 2, ptOrigin.y + nLineHeight};
1344 IntersectRect(&rcIntersect, &rcClip, &rcTextBlock);
1345 m_pCrystalRenderer->DrawText(ptOrigin.x, ptOrigin.y, rcIntersect, LPCTSTR(line) + ibegin, nCount1, &nWidths[0]);
1348 // Draw rounded rectangles around control characters
1349 m_pCrystalRenderer->PushAxisAlignedClip(rcClip);
1351 for (int j = 0 ; j < nCount1 ; ++j)
1353 // Assume narrowed space is converted escape sequence leadin.
1354 if (line[ibegin + j] == ' ' && nWidths[j] < nCharWidth)
1356 m_pCrystalRenderer->DrawRoundRectangle(x + 2, ptOrigin.y + 1,
1357 x + 3 * nCharWidth - 2, ptOrigin.y + nLineHeight - 1,
1358 nCharWidth / 2, nLineHeight / 2);
1362 m_pCrystalRenderer->PopAxisAlignedClip();
1366 // Update the final position after the visible characters
1367 ptOrigin.x += nSumWidth;
1371 // Update the final position after the right clipped characters
1372 for ( ; i < lineLen; i = pIterChar->next())
1374 ptOrigin.x += GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1379 void CCrystalTextView::
1380 DrawLineHelper (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex, int nBgColorIndex,
1381 COLORREF crText, COLORREF crBkgnd, int nLineIndex, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos)
1385 if (m_bFocused || m_bShowInactiveSelection)
1387 int nSelBegin = 0, nSelEnd = 0;
1388 if ( !m_bRectangularSelection )
1390 if (m_ptDrawSelStart.y > ptTextPos.y)
1394 else if (m_ptDrawSelStart.y == ptTextPos.y)
1396 nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
1399 if (nSelBegin > nCount)
1402 if (m_ptDrawSelEnd.y > ptTextPos.y)
1406 else if (m_ptDrawSelEnd.y == ptTextPos.y)
1408 nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
1411 if (nSelEnd > nCount)
1417 int nSelLeft, nSelRight;
1418 GetColumnSelection (ptTextPos.y, nSelLeft, nSelRight);
1419 nSelBegin = nSelLeft - ptTextPos.x;
1420 nSelEnd = nSelRight - ptTextPos.x;
1421 if (nSelBegin < 0) nSelBegin = 0;
1422 if (nSelBegin > nCount) nSelBegin = nCount;
1423 if (nSelEnd < 0) nSelEnd = 0;
1424 if (nSelEnd > nCount) nSelEnd = nCount;
1427 ASSERT (nSelBegin >= 0 && nSelBegin <= nCount);
1428 ASSERT (nSelEnd >= 0 && nSelEnd <= nCount);
1429 ASSERT (nSelBegin <= nSelEnd);
1431 // Draw part of the text before selection
1434 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nSelBegin, nActualOffset);
1436 if (nSelBegin < nSelEnd)
1438 DrawLineHelperImpl (ptOrigin, rcClip,
1439 nColorIndex & ~COLORINDEX_APPLYFORCE,
1440 nBgColorIndex & ~COLORINDEX_APPLYFORCE,
1441 GetColor (COLORINDEX_SELTEXT),
1442 GetColor (COLORINDEX_SELBKGND),
1444 nOffset + nSelBegin, nSelEnd - nSelBegin, nActualOffset);
1446 if (nSelEnd < nCount)
1448 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset + nSelEnd, nCount - nSelEnd, nActualOffset);
1453 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset);
1458 void CCrystalTextView::
1459 GetLineColors (int nLineIndex, COLORREF & crBkgnd,
1460 COLORREF & crText, bool & bDrawWhitespace)
1462 DWORD dwLineFlags = GetLineFlags (nLineIndex);
1463 bDrawWhitespace = true;
1464 crText = RGB (255, 255, 255);
1465 if (dwLineFlags & LF_EXECUTION)
1467 crBkgnd = RGB (0, 128, 0);
1470 if (dwLineFlags & LF_BREAKPOINT)
1472 crBkgnd = RGB (255, 0, 0);
1475 if (dwLineFlags & LF_INVALID_BREAKPOINT)
1477 crBkgnd = RGB (128, 128, 0);
1482 bDrawWhitespace = false;
1485 DWORD CCrystalTextView::
1486 GetParseCookie (int nLineIndex)
1488 const int nLineCount = GetLineCount ();
1489 if (m_ParseCookies->size() == 0)
1491 // must be initialized to invalid value (DWORD) -1
1492 m_ParseCookies->assign(nLineCount, static_cast<DWORD>(-1));
1497 if ((*m_ParseCookies)[nLineIndex] != - 1)
1498 return (*m_ParseCookies)[nLineIndex];
1501 while (L >= 0 && (*m_ParseCookies)[L] == - 1)
1506 while (L <= nLineIndex)
1510 dwCookie = (*m_ParseCookies)[L - 1];
1511 ASSERT (dwCookie != - 1);
1512 (*m_ParseCookies)[L] = ParseLine (dwCookie, GetLineChars(L), GetLineLength(L), nullptr, nBlocks);
1513 ASSERT ((*m_ParseCookies)[L] != - 1);
1517 return (*m_ParseCookies)[nLineIndex];
1520 std::vector<TEXTBLOCK> CCrystalTextView::
1521 GetAdditionalTextBlocks (int nLineIndex)
1527 void CCrystalTextView::WrapLine( int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1529 // There must be a parser attached to this view
1530 if( m_pParser == nullptr )
1533 m_pParser->WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1537 void CCrystalTextView::WrapLineCached(
1538 int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1540 if( !GetLineVisible (nLineIndex) )
1546 // If the word wrap is not active, there is no breaks in the line
1553 // word wrap is active
1554 if( nLineIndex < m_panSubLines->GetSize() && !anBreaks && (*m_panSubLines)[nLineIndex] > -1 )
1555 // return cached data
1556 nBreaks = (*m_panSubLines)[nLineIndex] - 1;
1559 // recompute line wrap
1561 WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1564 ASSERT( nBreaks > -1 );
1565 m_panSubLines->SetAtGrow( nLineIndex, nBreaks + 1 );
1567 // RecalcVertScrollBar();
1572 void CCrystalTextView::InvalidateLineCache( int nLineIndex1, int nLineIndex2 /*= -1*/ )
1574 // invalidate cached sub line index
1575 InvalidateSubLineIndexCache( nLineIndex1 );
1577 // invalidate cached sub line count
1579 if( nLineIndex2 == -1 && nLineIndex1 < m_panSubLines->GetSize() )
1580 for( int i = nLineIndex1; i < m_panSubLines->GetSize(); i++ )
1581 (*m_panSubLines)[i] = -1;
1584 if( nLineIndex1 > nLineIndex2 )
1586 int nStorage = nLineIndex1;
1587 nLineIndex1 = nLineIndex2;
1588 nLineIndex2 = nStorage;
1591 if( nLineIndex1 >= m_panSubLines->GetSize() )
1594 if( nLineIndex2 >= m_panSubLines->GetSize() )
1595 nLineIndex2 = (int) m_panSubLines->GetUpperBound();
1597 for( int i = nLineIndex1; i <= nLineIndex2; i++ )
1598 if( i >= 0 && i < m_panSubLines->GetSize() )
1599 (*m_panSubLines)[i] = -1;
1604 * @brief Invalidate sub line index cache from the specified index to the end of file.
1605 * @param [in] nLineIndex Index of the first line to invalidate
1607 void CCrystalTextView::InvalidateSubLineIndexCache( int nLineIndex )
1609 if (m_nLastLineIndexCalculatedSubLineIndex > nLineIndex)
1610 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex - 1;
1614 * @brief Invalidate items related screen size.
1616 void CCrystalTextView::InvalidateScreenRect(bool bInvalidateView)
1618 if (m_pCacheBitmap != nullptr)
1620 delete m_pCacheBitmap;
1621 m_pCacheBitmap = nullptr;
1623 m_nScreenChars = -1;
1624 m_nScreenLines = -1;
1625 InvalidateLineCache(0, -1);
1626 if (bInvalidateView)
1629 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
1630 RecalcVertScrollBar ();
1631 RecalcHorzScrollBar ();
1636 void CCrystalTextView::DrawScreenLine( CPoint &ptOrigin, const CRect &rcClip,
1637 const std::vector<TEXTBLOCK>& blocks, int &nActualItem,
1638 COLORREF crText, COLORREF crBkgnd, bool bDrawWhitespace,
1639 int nLineIndex, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos )
1641 CPoint originalOrigin = ptOrigin;
1642 CPoint ptOriginZeroWidthBlock;
1643 CRect frect = rcClip;
1644 const int nLineLength = GetViewableLineLength( ptTextPos.y );
1645 const int nLineHeight = GetLineHeight();
1646 int nBgColorIndexZeorWidthBlock = COLORINDEX_NONE;
1647 bool bPrevZeroWidthBlock = false;
1648 static const int ZEROWIDTHBLOCK_WIDTH = 2;
1650 frect.top = ptOrigin.y;
1651 frect.bottom = frect.top + nLineHeight;
1653 int nBlockSize = static_cast<int>(blocks.size());
1654 ASSERT( nActualItem < nBlockSize );
1656 if( nBlockSize > 0 && nActualItem < nBlockSize - 1 &&
1657 blocks[nActualItem + 1].m_nCharPos >= nOffset &&
1658 blocks[nActualItem + 1].m_nCharPos <= nOffset + nCount )
1660 ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1661 blocks[nActualItem].m_nCharPos <= nLineLength);
1664 for (I = nActualItem; I < blocks.size() - 1 &&
1665 blocks[I + 1].m_nCharPos <= nOffset + nCount; I ++)
1667 const TEXTBLOCK& blk = blocks[I];
1668 ASSERT(blk.m_nCharPos >= 0 && blk.m_nCharPos <= nLineLength);
1670 int nOffsetToUse = (nOffset > blk.m_nCharPos) ?
1671 nOffset : blk.m_nCharPos;
1672 if (blocks[I + 1].m_nCharPos - nOffsetToUse > 0)
1674 int nOldActualOffset = nActualOffset;
1675 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex, crText, crBkgnd, nLineIndex,
1676 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1677 blocks[I + 1].m_nCharPos - nOffsetToUse,
1678 nActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1679 if (bPrevZeroWidthBlock)
1681 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1682 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock, crText, crBkgnd, nLineIndex,
1683 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1684 blocks[I + 1].m_nCharPos - nOffsetToUse,
1685 nOldActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1686 bPrevZeroWidthBlock = false;
1691 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1693 int nBgColorIndex = blk.m_nBgColorIndex;
1694 COLORREF clrBkColor;
1695 if (IsInsideSelBlock (CPoint{nOffsetToUse, ptTextPos.y}))
1696 clrBkColor = GetColor(COLORINDEX_SELBKGND);
1697 else if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1698 clrBkColor = GetColor(nBgColorIndex);
1700 clrBkColor = crBkgnd;
1701 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1702 m_pCrystalRenderer->SetBkColor(clrBkColor);
1703 m_pCrystalRenderer->FillRectangle(rc);
1704 ptOriginZeroWidthBlock = ptOrigin;
1705 nBgColorIndexZeorWidthBlock = blk.m_nBgColorIndex;
1706 bPrevZeroWidthBlock = true;
1709 if (ptOrigin.x > rcClip.right)
1711 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_WORDWRAP)
1713 while (I < blocks.size () - 1 && blocks[I + 1].m_nCharPos <= nOffset + nCount)
1720 nActualItem = static_cast<int>(I);
1722 const TEXTBLOCK& blk = blocks[nActualItem];
1723 ASSERT(blk.m_nCharPos >= 0 &&
1724 blk.m_nCharPos <= nLineLength);
1726 if (nOffset + nCount - blk.m_nCharPos > 0)
1728 int nOldActualOffset = nActualOffset;
1729 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex,
1730 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1731 nOffset + nCount - blk.m_nCharPos,
1732 nActualOffset, CPoint(blk.m_nCharPos, ptTextPos.y));
1733 if (bPrevZeroWidthBlock)
1735 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1736 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock,
1737 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1738 nOffset + nCount - blk.m_nCharPos,
1739 nOldActualOffset, CPoint(blk.m_nCharPos, ptTextPos.y));
1740 bPrevZeroWidthBlock = false;
1745 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1747 int nBgColorIndex = blk.m_nBgColorIndex;
1748 COLORREF clrBkColor;
1749 if (IsInsideSelBlock (CPoint{blk.m_nCharPos, ptTextPos.y}))
1750 clrBkColor = GetColor(COLORINDEX_SELBKGND);
1751 else if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1752 clrBkColor = GetColor(nBgColorIndex);
1754 clrBkColor = crBkgnd;
1755 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1756 m_pCrystalRenderer->SetBkColor(clrBkColor);
1757 m_pCrystalRenderer->FillRectangle(rc);
1758 bPrevZeroWidthBlock = true;
1765 ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1766 crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset, ptTextPos);
1769 // Draw space on the right of the text
1771 frect.left = ptOrigin.x + (bPrevZeroWidthBlock ? ZEROWIDTHBLOCK_WIDTH : 0);
1773 if ((m_bFocused || m_bShowInactiveSelection)
1774 && !m_bRectangularSelection
1775 && IsInsideSelBlock(CPoint(nLineLength, ptTextPos.y))
1776 && (nOffset + nCount) == nLineLength )
1778 if (frect.left >= rcClip.left)
1780 const int nCharWidth = GetCharWidth();
1781 CRect rc(frect.left, frect.top, frect.left + nCharWidth, frect.bottom);
1782 m_pCrystalRenderer->SetBkColor(GetColor(COLORINDEX_SELBKGND));
1783 m_pCrystalRenderer->FillRectangle(rc);
1784 frect.left += nCharWidth;
1787 if (frect.left < rcClip.left)
1788 frect.left = rcClip.left;
1790 if (frect.right > frect.left)
1792 m_pCrystalRenderer->SetBkColor(bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
1793 m_pCrystalRenderer->FillRectangle(frect);
1796 // set origin to beginning of next screen line
1797 ptOrigin.x = originalOrigin.x;
1798 ptOrigin.y+= nLineHeight;
1802 class IntArray : public CArray<int, int>
1805 explicit IntArray(int len) { SetSize(len); }
1808 std::vector<TEXTBLOCK> CCrystalTextView::
1809 MergeTextBlocks (const std::vector<TEXTBLOCK>& blocks1, const std::vector<TEXTBLOCK>& blocks2) const
1813 std::vector<TEXTBLOCK> mergedBlocks(blocks1.size() + blocks2.size());
1815 for (i = 0, j = 0, k = 0; ; k++)
1817 if (i >= blocks1.size() && j >= blocks2.size())
1821 else if ((i < blocks1.size()&& j < blocks2.size()) &&
1822 (blocks1[i].m_nCharPos == blocks2[j].m_nCharPos))
1824 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1825 if (blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1826 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1828 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1829 if (blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1830 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1832 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1836 else if (j >= blocks2.size() || (i < blocks1.size() &&
1837 blocks1[i].m_nCharPos < blocks2[j].m_nCharPos))
1839 mergedBlocks[k].m_nCharPos = blocks1[i].m_nCharPos;
1840 if (blocks2.size() == 0 || blocks2[j - 1].m_nColorIndex == COLORINDEX_NONE)
1841 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1843 mergedBlocks[k].m_nColorIndex = blocks2[j - 1].m_nColorIndex;
1844 if (blocks2.size() == 0 || blocks2[j - 1].m_nBgColorIndex == COLORINDEX_NONE)
1845 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1847 mergedBlocks[k].m_nBgColorIndex = blocks2[j - 1].m_nBgColorIndex;
1850 else if (i >= blocks1.size() || (j < blocks2.size() && blocks1[i].m_nCharPos > blocks2[j].m_nCharPos))
1852 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1853 if (i > 0 && blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1854 mergedBlocks[k].m_nColorIndex = blocks1[i - 1].m_nColorIndex;
1856 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1857 if (i > 0 && blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1858 mergedBlocks[k].m_nBgColorIndex = blocks1[i - 1].m_nBgColorIndex;
1860 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1866 for (i = 0; i < k; ++i)
1869 (mergedBlocks[i - 1].m_nColorIndex != mergedBlocks[i].m_nColorIndex ||
1870 mergedBlocks[i - 1].m_nBgColorIndex != mergedBlocks[i].m_nBgColorIndex))
1872 mergedBlocks[j] = mergedBlocks[i];
1877 mergedBlocks.resize(j);
1878 return mergedBlocks;
1881 std::vector<TEXTBLOCK>
1882 CCrystalTextView::GetMarkerTextBlocks(int nLineIndex) const
1884 std::vector<TEXTBLOCK> allblocks;
1885 if (!m_pMarkers->GetEnabled())
1888 int nLength = GetViewableLineLength (nLineIndex);
1890 for (const auto& marker : m_pMarkers->GetMarkers())
1892 if (!marker.second.bVisible)
1895 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1896 blocks[0].m_nCharPos = 0;
1897 blocks[0].m_nColorIndex = COLORINDEX_NONE;
1898 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1900 const TCHAR *pszChars = GetLineChars(nLineIndex);
1901 int nLineLength = GetLineLength(nLineIndex);
1902 if (pszChars != nullptr)
1904 RxNode *node = nullptr;
1905 for (const TCHAR *p = pszChars; p < pszChars + nLineLength; )
1909 size_t nPos = ::FindStringHelper(pszChars, nLineLength, p, marker.second.sFindWhat, marker.second.dwFlags | FIND_NO_WRAP, nMatchLen, node, &matches);
1912 if (nLineLength < static_cast<int>((p - pszChars) + nPos) + nMatchLen)
1913 nMatchLen = static_cast<int>(nLineLength - (p - pszChars));
1914 ASSERT(((p - pszChars) + nPos) < INT_MAX);
1915 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos);
1916 blocks[nBlocks].m_nBgColorIndex = marker.second.nBgColorIndex | COLORINDEX_APPLYFORCE;
1917 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1919 ASSERT(((p - pszChars) + nPos + nMatchLen) < INT_MAX);
1920 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos + nMatchLen);
1921 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1922 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1924 p += nPos + (nMatchLen == 0 ? 1 : nMatchLen);
1927 blocks.resize(nBlocks);
1928 allblocks = MergeTextBlocks(allblocks, blocks);
1935 std::vector<TEXTBLOCK>
1936 CCrystalTextView::GetTextBlocks(int nLineIndex)
1938 int nLength = GetViewableLineLength (nLineIndex);
1941 DWORD dwCookie = GetParseCookie(nLineIndex - 1);
1942 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1944 // insert at least one textblock of normal color at the beginning
1945 blocks[0].m_nCharPos = 0;
1946 blocks[0].m_nColorIndex = COLORINDEX_NORMALTEXT;
1947 blocks[0].m_nBgColorIndex = COLORINDEX_BKGND;
1949 (*m_ParseCookies)[nLineIndex] = ParseLine(dwCookie, GetLineChars(nLineIndex), GetLineLength(nLineIndex), blocks.data(), nBlocks);
1950 ASSERT((*m_ParseCookies)[nLineIndex] != -1);
1951 blocks.resize(nBlocks);
1953 return MergeTextBlocks(blocks,
1954 (m_pMarkers && m_pMarkers->GetEnabled() && m_pMarkers->GetMarkers().size() > 0) ?
1955 MergeTextBlocks(GetAdditionalTextBlocks(nLineIndex), GetMarkerTextBlocks(nLineIndex)) :
1956 GetAdditionalTextBlocks(nLineIndex));
1959 void CCrystalTextView::
1960 DrawSingleLine (const CRect & rc, int nLineIndex)
1962 const int nCharWidth = GetCharWidth();
1963 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
1965 if (nLineIndex == -1)
1967 // Draw line beyond the text
1968 m_pCrystalRenderer->FillSolidRectangle (rc, GetColor (COLORINDEX_WHITESPACE));
1972 // Acquire the background color for the current line
1973 bool bDrawWhitespace = false;
1974 COLORREF crBkgnd, crText;
1975 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
1977 int nLength = GetViewableLineLength (nLineIndex);
1979 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
1981 int nActualItem = 0;
1982 int nActualOffset = 0;
1984 std::vector<int> anBreaks(nLength);
1987 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
1989 // Draw the line text
1990 CPoint origin (rc.left - m_nOffsetChar * nCharWidth, rc.top);
1991 if (crBkgnd != CLR_NONE)
1992 m_pCrystalRenderer->SetBkColor (crBkgnd);
1993 if (crText != CLR_NONE)
1994 m_pCrystalRenderer->SetTextColor (crText);
1996 const TextLayoutMode layoutMode = GetTextLayoutMode ();
1997 if (layoutMode == TEXTLAYOUT_TABLE_WORDWRAP)
1999 anBreaks.push_back (-nLength);
2000 CPoint originOrg = origin;
2003 blocks, nActualItem,
2004 crText, crBkgnd, bDrawWhitespace,
2005 nLineIndex, 0, abs(anBreaks[0]),
2006 nActualOffset, CPoint( 0, nLineIndex ) );
2008 for( int i = 0, j = 0; i < static_cast<int> (anBreaks.size ()) - 1; i++, j++ )
2010 if (anBreaks[i] < 0)
2014 CRect frect( origin.x, originOrg.y + (j + 1) * GetLineHeight (),
2015 origin.x + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth, rc.bottom );
2016 if (frect.left < rc.left)
2017 frect.left = rc.left;
2018 if (frect.right > rc.left)
2019 m_pCrystalRenderer->FillSolidRectangle (frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2021 origin.y = originOrg.y;
2022 origin.x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth;
2027 blocks, nActualItem,
2028 crText, crBkgnd, bDrawWhitespace,
2029 nLineIndex, abs(anBreaks[i]), abs(anBreaks[i + 1]) - abs(anBreaks[i]),
2030 nActualOffset, CPoint( abs(anBreaks[i]), nLineIndex ) );
2033 else if (layoutMode == TEXTLAYOUT_WORDWRAP && nBreaks > 0)
2035 // Draw all the screen lines of the wrapped line
2036 ASSERT( anBreaks[0] < nLength );
2038 // draw start of line to first break
2041 blocks, nActualItem,
2042 crText, crBkgnd, bDrawWhitespace,
2043 nLineIndex, 0, anBreaks[0], nActualOffset, CPoint( 0, nLineIndex ) );
2045 // draw from first break to last break
2047 for( i = 0; i < nBreaks - 1; i++ )
2049 ASSERT( anBreaks[i] >= 0 && anBreaks[i] < nLength );
2052 blocks, nActualItem,
2053 crText, crBkgnd, bDrawWhitespace,
2054 nLineIndex, anBreaks[i], anBreaks[i + 1] - anBreaks[i],
2055 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
2058 // draw from last break till end of line
2061 blocks, nActualItem,
2062 crText, crBkgnd, bDrawWhitespace,
2063 nLineIndex, anBreaks[i], nLength - anBreaks[i],
2064 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
2069 blocks, nActualItem,
2070 crText, crBkgnd, bDrawWhitespace,
2071 nLineIndex, 0, nLength, nActualOffset, CPoint(0, nLineIndex));
2073 // Draw empty sublines
2074 int nEmptySubLines = GetEmptySubLines(nLineIndex);
2075 if (nEmptySubLines > 0)
2078 frect.top = frect.bottom - nEmptySubLines * GetLineHeight();
2079 m_pCrystalRenderer->FillSolidRectangle(frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2084 * @brief Escape special characters
2085 * @param [in] strText The text to escape
2086 * @param [in, out] bLastCharSpace Whether last char processed was white space
2087 * @param [in, out] nNonbreakChars The number of non-break characters in the text
2088 * @param [in] nScreenChars The maximum number of characters to display per line
2089 * @return The escaped text
2092 EscapeHTML (const CString & strText, bool & bLastCharSpace, int & nNonbreakChars, int nScreenChars)
2095 int len = strText.GetLength ();
2096 for (int i = 0; i < len; ++i)
2098 TCHAR ch = strText[i];
2102 strHTML += _T("&");
2103 bLastCharSpace = false;
2107 strHTML += _T("<");
2108 bLastCharSpace = false;
2112 strHTML += _T(">");
2113 bLastCharSpace = false;
2119 strHTML += _T("<wbr>");
2120 bLastCharSpace = false;
2124 if (i == 0 || bLastCharSpace)
2126 strHTML += _T(" ");
2127 bLastCharSpace = false;
2132 bLastCharSpace = true;
2138 bLastCharSpace = false;
2142 if (IsDBCSLeadByte (ch))
2143 strHTML += strText[++i];
2145 if ((nNonbreakChars % nScreenChars) == nScreenChars - 1)
2147 strHTML += _T("<wbr>");
2155 // Make a CString from printf-style args (single call version of CString::Format)
2156 static CString Fmt(LPCTSTR fmt, ...)
2160 va_start(args, fmt);
2161 str.FormatV(fmt, args);
2167 * @brief Return all the styles necessary to render this view as HTML code
2168 * @return The HTML styles
2170 CString CCrystalTextView::
2173 int arColorIndices[] = {
2174 COLORINDEX_NORMALTEXT,
2177 COLORINDEX_FUNCNAME,
2180 COLORINDEX_OPERATOR,
2182 COLORINDEX_PREPROCESSOR,
2183 COLORINDEX_HIGHLIGHTTEXT1,
2184 COLORINDEX_HIGHLIGHTTEXT2,
2188 int arBgColorIndices[] = {
2190 COLORINDEX_SELBKGND,
2191 COLORINDEX_HIGHLIGHTBKGND1,
2192 COLORINDEX_HIGHLIGHTBKGND2,
2193 COLORINDEX_HIGHLIGHTBKGND3,
2194 COLORINDEX_HIGHLIGHTBKGND4,
2198 for (int f = 0; f < sizeof(arColorIndices)/sizeof(int); f++)
2200 int nColorIndex = arColorIndices[f];
2201 for (int b = 0; b < sizeof(arBgColorIndices)/sizeof(int); b++)
2203 int nBgColorIndex = arBgColorIndices[b];
2206 strStyles += Fmt (_T(".sf%db%d {"), nColorIndex, nBgColorIndex);
2207 clr = GetColor (nColorIndex);
2208 strStyles += Fmt (_T("color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2209 clr = GetColor (nBgColorIndex);
2210 strStyles += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2211 if (GetBold (nColorIndex))
2212 strStyles += _T("font-weight: bold; ");
2213 if (GetItalic (nColorIndex))
2214 strStyles += _T("font-style: italic; ");
2215 strStyles += _T("}\n");
2218 COLORREF clrSelMargin = GetColor(COLORINDEX_SELMARGIN);
2219 COLORREF clrNormalText = GetColor(COLORINDEX_NORMALTEXT);
2220 strStyles += Fmt(_T(".ln {text-align: right; word-break: normal; color: #%02x%02x%02x; background-color: #%02x%02x%02x;}\n"),
2221 GetRValue(clrNormalText), GetGValue(clrNormalText), GetBValue(clrNormalText),
2222 GetRValue(clrSelMargin), GetGValue(clrSelMargin), GetBValue(clrSelMargin));
2227 * @brief Return the HTML attribute associated with the specified colors
2228 * @param [in] nColorIndex Index of text color
2229 * @param [in] nBgColorIndex Index of background color
2230 * @param [in] crText Base text color
2231 * @param [in] crBkgnd Base background color
2232 * @return The HTML attribute
2234 CString CCrystalTextView::
2235 GetHTMLAttribute (int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd)
2240 if ((crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE)) &&
2241 (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE)))
2242 return Fmt(_T("class=\"sf%db%d\""), nColorIndex & ~COLORINDEX_APPLYFORCE, nBgColorIndex & ~COLORINDEX_APPLYFORCE);
2244 if (crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE))
2245 clr = GetColor (nColorIndex);
2248 strAttr += Fmt (_T("style=\"color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2250 if (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE))
2251 clr = GetColor (nBgColorIndex);
2254 strAttr += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2256 if (GetBold (nColorIndex))
2257 strAttr += _T("font-weight: bold; ");
2258 if (GetItalic (nColorIndex))
2259 strAttr += _T("font-style: italic; ");
2261 strAttr += _T("\"");
2267 * @brief Retrieve the html version of the line
2268 * @param [in] nLineIndex Index of line in view
2269 * @param [in] pszTag The HTML tag to enclose the line
2270 * @return The html version of the line
2272 CString CCrystalTextView::
2273 GetHTMLLine (int nLineIndex, LPCTSTR pszTag)
2275 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2277 int nLength = GetViewableLineLength (nLineIndex);
2279 // Acquire the background color for the current line
2280 bool bDrawWhitespace = false;
2281 COLORREF crBkgnd, crText;
2282 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2284 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2287 CString strExpanded;
2289 int nNonbreakChars = 0;
2290 bool bLastCharSpace = false;
2291 const int nScreenChars = 40; // GetScreenChars();
2296 strHTML += GetHTMLAttribute (COLORINDEX_NORMALTEXT, COLORINDEX_BKGND, crText, crBkgnd);
2297 strHTML += _T("><code>");
2299 auto MakeSpan = [&](const TEXTBLOCK& block, const CString& strExpanded) {
2301 strHTML += _T("<span ");
2302 strHTML += GetHTMLAttribute (block.m_nColorIndex, block.m_nBgColorIndex, crText, crBkgnd);
2304 strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2305 strHTML += _T("</span>");
2309 for (i = 0; i < blocks.size() - 1; i++)
2311 ExpandChars (nLineIndex, blocks[i].m_nCharPos, blocks[i + 1].m_nCharPos - blocks[i].m_nCharPos, strExpanded, 0);
2312 if (!strExpanded.IsEmpty())
2313 strHTML += MakeSpan(blocks[i], strExpanded);
2315 if (blocks.size() > 0)
2317 ExpandChars (nLineIndex, blocks[i].m_nCharPos, nLength - blocks[i].m_nCharPos, strExpanded, 0);
2318 if (!strExpanded.IsEmpty())
2319 strHTML += MakeSpan(blocks[i], strExpanded);
2320 if (strExpanded.Compare (CString (' ', strExpanded.GetLength())) == 0)
2321 strHTML += _T(" ");
2323 strHTML += _T("</code></");
2330 COLORREF CCrystalTextView::
2331 GetColor (int nColorIndex)
2333 if (m_pColors != nullptr)
2335 nColorIndex &= ~COLORINDEX_APPLYFORCE;
2336 return m_pColors->GetColor(nColorIndex);
2339 return RGB(0, 0, 0);
2342 DWORD CCrystalTextView::
2343 GetLineFlags (int nLineIndex) const
2345 if (m_pTextBuffer == nullptr)
2347 return m_pTextBuffer->GetLineFlags (nLineIndex);
2350 void CCrystalTextView::
2351 DrawTopMargin (const CRect& rect)
2356 auto getColumnName = [](int nColumn) -> CString
2359 for (int i = 0; ; ++i)
2361 TCHAR c = 'A' + (nColumn % 26) - (i == 0 ? 0 : 1);
2362 columnName.Insert (0, c);
2370 m_pCrystalRenderer->SetBkColor (GetColor (COLORINDEX_SELMARGIN));
2371 m_pCrystalRenderer->FillRectangle (rect);
2372 m_pCrystalRenderer->SetTextColor (GetColor (COLORINDEX_NORMALTEXT));
2373 if (m_pTextBuffer->GetTableEditing ())
2375 const int nCharWidth = GetCharWidth ();
2376 const int nMarginWidth = GetMarginWidth ();
2377 CString columnNames;
2378 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth; x < rect.Width (); ++nColumn)
2380 int nColumnWidth = m_pTextBuffer->GetColumnWidth (nColumn);
2381 CString columnName = getColumnName (nColumn);
2382 int columnNameLen = columnName.GetLength ();
2383 if (nColumnWidth < columnNameLen)
2384 columnNames += columnName.Right (nColumnWidth);
2387 int leftspaces = (nColumnWidth - columnNameLen) / 2;
2388 columnNames += CString (' ', leftspaces) + columnName + CString (' ', nColumnWidth - leftspaces - columnNameLen);
2390 x += nColumnWidth * nCharWidth;
2392 columnNames = columnNames.Mid (m_nOffsetChar).Left(rect.Width () / nCharWidth + 1);
2394 std::vector<int> nWidths (columnNames.GetLength (), nCharWidth);
2395 m_pCrystalRenderer->SwitchFont (false, false);
2396 m_pCrystalRenderer->DrawText (nMarginWidth, 0, rect, columnNames, columnNames.GetLength (), nWidths.data ());
2399 m_pCrystalRenderer->DrawRuler (GetMarginWidth (), 0, rect.Width (), rect.Height (), GetCharWidth (), m_nOffsetChar);
2403 * @brief Draw selection margin.
2404 * @param [in] pdc Pointer to draw context.
2405 * @param [in] rect The rectangle to draw.
2406 * @param [in] nLineIndex Index of line in view.
2407 * @param [in] nLineNumber Line number to display. if -1, it's not displayed.
2409 void CCrystalTextView::
2410 DrawMargin (const CRect & rect, int nLineIndex, int nLineNumber)
2412 if (!m_bSelMargin && !m_bViewLineNumbers)
2413 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_BKGND));
2415 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_SELMARGIN));
2416 m_pCrystalRenderer->FillRectangle(rect);
2418 if (m_bViewLineNumbers && nLineNumber > 0)
2420 m_pCrystalRenderer->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
2421 m_pCrystalRenderer->DrawMarginLineNumber(rect.right, rect.top, nLineNumber);
2424 // Draw line revision mark (or background) whenever we have valid lineindex
2425 COLORREF clrRevisionMark = GetColor(COLORINDEX_WHITESPACE);
2426 if (nLineIndex >= 0 && m_pTextBuffer != nullptr)
2428 // get line revision marks color
2429 DWORD dwRevisionNumber = m_pTextBuffer->GetLineRevisionNumber(nLineIndex);
2430 if (dwRevisionNumber > 0)
2432 if (m_pTextBuffer->m_dwRevisionNumberOnSave < dwRevisionNumber)
2433 clrRevisionMark = UNSAVED_REVMARK_CLR;
2435 clrRevisionMark = SAVED_REVMARK_CLR;
2439 // draw line revision marks
2440 CRect rc(rect.right - MARGIN_REV_WIDTH, rect.top, rect.right, rect.bottom);
2441 m_pCrystalRenderer->FillSolidRectangle (rc, clrRevisionMark);
2446 int nImageIndex = -1;
2447 if (nLineIndex >= 0)
2449 DWORD dwLineFlags = GetLineFlags (nLineIndex);
2450 static const DWORD adwFlags[] =
2454 LF_COMPILATION_ERROR,
2466 LF_INVALID_BREAKPOINT
2468 for (int I = 0; I < sizeof (adwFlags) / sizeof (adwFlags[0]); I++)
2470 if ((dwLineFlags & adwFlags[I]) != 0)
2477 if (nImageIndex >= 0)
2479 const int iconsize = GetMarginIconSize();
2480 m_pCrystalRenderer->DrawMarginIcon(
2481 rect.left + 2, rect.top + (GetLineHeight() - iconsize) / 2, nImageIndex, iconsize);
2484 // draw wrapped-line-icon
2485 if (nLineNumber > 0)
2487 const int iconsize = GetMarginIconSize();
2489 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2490 for (int i = 0; i < nBreaks; i++)
2492 m_pCrystalRenderer->DrawMarginIcon(
2493 rect.right - iconsize, rect.top + (GetLineHeight()
2494 - iconsize) / 2 + (i+1) * GetLineHeight(), ICON_INDEX_WRAPLINE, iconsize);
2499 bool CCrystalTextView::
2500 IsInsideSelBlock (CPoint ptTextPos)
2503 ASSERT_VALIDTEXTPOS (ptTextPos);
2504 if (ptTextPos.y < m_ptDrawSelStart.y)
2506 if (ptTextPos.y > m_ptDrawSelEnd.y)
2508 if (m_bRectangularSelection)
2509 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2510 if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
2512 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
2514 if (ptTextPos.y == m_ptDrawSelEnd.y)
2515 return ptTextPos.x < m_ptDrawSelEnd.x;
2516 ASSERT (ptTextPos.y == m_ptDrawSelStart.y);
2517 return ptTextPos.x >= m_ptDrawSelStart.x;
2519 ASSERT (m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
2520 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2523 bool CCrystalTextView::
2524 IsInsideSelection (const CPoint & ptTextPos)
2526 PrepareSelBounds ();
2527 return IsInsideSelBlock (ptTextPos);
2531 * @brief : class the selection extremities in ascending order
2533 * @note : Updates m_ptDrawSelStart and m_ptDrawSelEnd
2534 * This function must be called before reading these values
2536 void CCrystalTextView::
2539 if (m_ptSelStart.y < m_ptSelEnd.y ||
2540 (m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
2542 m_ptDrawSelStart = m_ptSelStart;
2543 m_ptDrawSelEnd = m_ptSelEnd;
2547 m_ptDrawSelStart = m_ptSelEnd;
2548 m_ptDrawSelEnd = m_ptSelStart;
2552 void CCrystalTextView::
2555 // We use the same GDI objects for both drawing and printing.
2556 // So when printing is in progress, we do nothing in this function.
2561 GetClientRect (rcClient);
2563 if (m_pTextBuffer == nullptr)
2565 m_pCrystalRenderer->BindDC(*pdc, rcClient);
2566 m_pCrystalRenderer->BeginDraw();
2567 m_pCrystalRenderer->SetBkColor(GetSysColor(COLOR_WINDOW));
2568 m_pCrystalRenderer->FillRectangle(rcClient);
2569 m_pCrystalRenderer->EndDraw();
2573 const int nLineCount = GetLineCount ();
2574 const int nLineHeight = GetLineHeight ();
2575 PrepareSelBounds ();
2577 // if the private arrays (m_ParseCookies and m_pnActualLineLength)
2578 // are defined, check they are in phase with the text buffer
2579 if (m_ParseCookies->size())
2580 ASSERT(m_ParseCookies->size() == static_cast<size_t>(nLineCount));
2581 if (m_pnActualLineLength->size())
2582 ASSERT(m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
2585 VERIFY (cacheDC.CreateCompatibleDC (pdc));
2586 if (m_pCacheBitmap == nullptr)
2588 m_pCacheBitmap = new CBitmap;
2589 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height()));
2591 CBitmap *pOldBitmap = cacheDC.SelectObject (m_pCacheBitmap);
2594 int nSubLineOffset = GetSubLineIndex( m_nTopLine ) - m_nTopSubLine;
2595 int nCursorY = TextToClient (m_ptCursorPos).y;
2598 CRect rcTopMargin(rcClient.left, rcClient.top, rcClient.right, rcClient.top + GetTopMarginHeight());
2600 rcLine.top = rcTopMargin.bottom + nSubLineOffset * nLineHeight;
2601 CRect rcMargin (rcLine.left, rcLine.top, rcLine.left + GetMarginWidth (), rcLine.top + nLineHeight);
2602 rcLine.left = rcMargin.right;
2604 m_pCrystalRenderer->BindDC(cacheDC, rcClient);
2605 m_pCrystalRenderer->BeginDraw();
2607 int nLastLineBottom = 0;
2608 int nCurrentLine = m_nTopLine;
2609 while (rcLine.top < rcClient.bottom)
2612 if( nCurrentLine < nLineCount /*&& GetLineLength( nCurrentLine ) > nMaxLineChars*/ )
2613 nSubLines = GetSubLines(nCurrentLine);
2615 rcLine.bottom = rcLine.top + nSubLines * nLineHeight;
2616 rcMargin.bottom = rcLine.bottom;
2618 CRect rcMarginAndLine(rcClient.left, rcLine.top, rcClient.right, rcLine.bottom);
2619 if (pdc->RectVisible(rcMarginAndLine))
2621 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
2623 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
2624 DrawSingleLine (rcLine, nCurrentLine);
2625 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
2626 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.bottom-1);
2627 if (m_pTextBuffer->GetTableEditing ())
2628 m_pCrystalRenderer->DrawGridLine (rcMargin.left, rcMargin.bottom-1, rcLine.right, rcMargin.bottom-1);
2629 if (nCurrentLine == m_ptCursorPos.y)
2630 m_pCrystalRenderer->DrawLineCursor (rcMargin.left, rcLine.right,
2631 nCursorY + nLineHeight - 1, 1);
2632 nLastLineBottom = rcMargin.bottom;
2636 DrawMargin (rcMargin, -1, -1);
2637 DrawSingleLine (rcLine, -1);
2642 rcLine.top = rcLine.bottom;
2643 rcMargin.top = rcLine.top;
2646 if (pdc->RectVisible (rcTopMargin))
2647 DrawTopMargin (rcTopMargin);
2649 if (m_pTextBuffer->GetTableEditing ())
2651 int nCharWidth = GetCharWidth ();
2652 int nMarginWidth = GetMarginWidth ();
2653 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth;
2654 x < rcClient.Width();
2655 x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
2657 if (x >= nMarginWidth && nColumn > 0)
2658 m_pCrystalRenderer->DrawGridLine (x, rcClient.top, x, nLastLineBottom);
2662 m_pCrystalRenderer->EndDraw();
2664 VERIFY (pdc->BitBlt (rcClient.left, rcClient.top, rcClient.Width (),
2665 rcClient.Height (), &cacheDC, 0, 0, SRCCOPY));
2667 cacheDC.SelectObject (pOldBitmap);
2668 cacheDC.DeleteDC ();
2671 void CCrystalTextView::
2674 // m_bWordWrap = false;
2680 m_nScreenLines = -1;
2681 m_nScreenChars = -1;
2682 m_nIdealCharPos = -1;
2685 InvalidateLineCache( 0, -1 );
2686 m_ParseCookies->clear();
2687 m_pnActualLineLength->clear();
2688 m_ptCursorPos.x = 0;
2689 m_ptCursorPos.y = 0;
2690 m_ptSelStart = m_ptSelEnd = m_ptCursorPos;
2691 if (m_bDragSelection)
2694 KillTimer (m_nDragSelTimer);
2696 m_bDragSelection = false;
2697 m_bVertScrollBarLocked = false;
2698 m_bHorzScrollBarLocked = false;
2699 if (::IsWindow (m_hWnd))
2701 m_bShowInactiveSelection = true; // FP: reverted because I like it
2702 m_bPrintHeader = false;
2703 m_bPrintFooter = true;
2705 m_bMultipleSearch = false; // More search
2709 void CCrystalTextView::
2712 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
2713 if (m_bFocused && !m_bCursorHidden &&
2714 CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x) >= m_nOffsetChar)
2716 int nCaretHeight = GetLineVisible(m_ptCursorPos.y) ? GetLineHeight () : 0;
2717 if (m_bOverrideCaret) //UPDATE
2718 CreateSolidCaret(GetCharWidth(), nCaretHeight);
2720 CreateSolidCaret (2, nCaretHeight);
2722 SetCaretPos (TextToClient (m_ptCursorPos));
2724 UpdateCompositionWindowPos(); /* IME */
2733 void CCrystalTextView::
2738 int CCrystalTextView::
2741 if (m_pTextBuffer != nullptr)
2743 return m_pTextBuffer->GetCRLFMode ();
2748 void CCrystalTextView::
2749 SetCRLFMode (CRLFSTYLE nCRLFMode)
2751 if (m_pTextBuffer != nullptr)
2753 m_pTextBuffer->SetCRLFMode (nCRLFMode);
2757 int CCrystalTextView::
2760 if (m_pTextBuffer == nullptr)
2763 return m_pTextBuffer->GetTabSize();
2767 void CCrystalTextView::
2768 SetTabSize (int nTabSize)
2770 ASSERT (nTabSize >= 0 && nTabSize <= 64);
2771 if (m_pTextBuffer == nullptr)
2774 if (m_pTextBuffer->GetTabSize() != nTabSize)
2776 m_pTextBuffer->SetTabSize( nTabSize );
2778 m_pnActualLineLength->clear();
2779 RecalcHorzScrollBar ();
2785 void CCrystalTextView::
2788 CSize szCharExt = m_pCrystalRenderer->GetCharWidthHeight();
2789 m_nLineHeight = szCharExt.cy;
2790 if (m_nLineHeight < 1)
2792 m_nCharWidth = szCharExt.cx;
2795 int CCrystalTextView::
2798 if (m_nLineHeight == -1)
2800 return m_nLineHeight;
2803 int CCrystalTextView::GetSubLines( int nLineIndex )
2805 // get a number of lines this wrapped lines contains
2807 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2809 return GetEmptySubLines(nLineIndex) + nBreaks + 1;
2812 bool CCrystalTextView::IsEmptySubLineIndex( int nSubLineIndex )
2816 GetLineBySubLine(nSubLineIndex, nLineIndex, dummy);
2817 int nSubLineIndexNextLine = GetSubLineIndex(nLineIndex) + GetSubLines(nLineIndex);
2818 if (nSubLineIndexNextLine - GetEmptySubLines(nLineIndex) <= nSubLineIndex && nSubLineIndex < nSubLineIndexNextLine)
2824 int CCrystalTextView::CharPosToPoint( int nLineIndex, int nCharPos, CPoint &charPoint, int* pnColumn )
2826 // if we do not wrap lines, y is allways 0 and x is equl to nCharPos
2829 charPoint.x = nCharPos;
2834 vector<int> anBreaks(GetLineLength (nLineIndex) + 1);
2837 WrapLineCached (nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
2839 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_WORDWRAP)
2844 for (; i < anBreaks.size () && abs (anBreaks[i]) <= nCharPos ; ++i)
2846 if (anBreaks[i] < 0)
2854 charPoint.x = (i > 0) ? nCharPos - abs (anBreaks[i - 1]) : nCharPos;
2857 *pnColumn = nColumn;
2859 return (i > 0)? abs (anBreaks[i - 1]) : 0;
2863 int i = (nBreaks <= 0) ? -1 : nBreaks - 1;
2864 for (; i >= 0 && nCharPos < anBreaks[i]; i--)
2867 charPoint.x = (i >= 0)? nCharPos - anBreaks[i] : nCharPos;
2868 charPoint.y = i + 1;
2870 int nReturnVal = (i >= 0)? anBreaks[i] : 0;
2876 /** Does character introduce a multicharacter character? */
2877 static inline bool IsLeadByte(TCHAR ch)
2882 return _getmbcp() && IsDBCSLeadByte(ch);
2886 int CCrystalTextView::CursorPointToCharPos( int nLineIndex, const CPoint &curPoint )
2888 // calculate char pos out of point
2889 const int nLength = GetLineLength( nLineIndex );
2890 const int nScreenChars = GetScreenChars();
2891 LPCTSTR szLine = GetLineChars( nLineIndex );
2894 vector<int> anBreaks(nLength + 1);
2897 WrapLineCached( nLineIndex, nScreenChars, &anBreaks, nBreaks );
2899 // find char pos that matches cursor position
2903 const int nTabSize = GetTabSize();
2905 int nIndex=0, nPrevIndex = 0;
2906 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(szLine, nLength);
2907 switch (GetTextLayoutMode ())
2909 case TEXTLAYOUT_TABLE_NOWORDWRAP:
2911 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
2912 int nColumnTotalWidth = 0;
2914 bool bInQuote = false;
2915 const int sep = m_pTextBuffer->GetFieldDelimiter ();
2916 const int quote = m_pTextBuffer->GetFieldEnclosure ();
2917 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
2920 if (!bInQuote && szLine[nIndex] == sep)
2922 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
2923 nOffset = nColumnTotalWidth - nXPos;
2927 if (szLine[nIndex] == quote)
2928 bInQuote = !bInQuote;
2929 if (szLine[nIndex] == '\t')
2932 nOffset = GetCharCellCountFromChar (szLine + nIndex);
2933 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
2934 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nXPos;
2939 if( nXPos > curPoint.x && nYPos == curPoint.y )
2941 else if( nYPos > curPoint.y )
2943 nIndex = nPrevIndex;
2947 nPrevIndex = nIndex;
2951 case TEXTLAYOUT_TABLE_WORDWRAP:
2955 int nColumnSumWidth = 0;
2956 int nColumnCurPoint = INT_MAX;
2957 if (curPoint.x < m_pTextBuffer->GetColumnWidth (0))
2958 nColumnCurPoint = 0;
2959 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next ())
2961 if (i < static_cast<int>(anBreaks.size ()) && nIndex == abs (anBreaks[i]))
2963 if (anBreaks[i++] < 0)
2966 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
2967 nXPos = nColumnSumWidth;
2968 if (nColumnSumWidth <= curPoint.x && curPoint.x < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
2969 nColumnCurPoint = nColumn;
2973 nXPos = nColumnSumWidth;
2979 if (szLine[nIndex] == '\t')
2982 nOffset = GetCharCellCountFromChar (szLine + nIndex);
2986 if( nXPos > curPoint.x && nYPos == curPoint.y )
2988 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
2990 nIndex = nPrevIndex;
2993 else if ( nYPos == curPoint.y)
2994 nPrevIndex = nIndex;
2996 if (nIndex == nLength && nYPos != curPoint.y)
2997 nIndex = nPrevIndex;
3002 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
3004 if( nBreaks > 0 && nYPos < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[nYPos] )
3011 if (szLine[nIndex] == _T('\t'))
3012 nOffset = nTabSize - nCurPos % nTabSize;
3014 nOffset = GetCharCellCountFromChar(szLine + nIndex);
3018 if( nXPos > curPoint.x && nYPos == curPoint.y )
3020 else if( nYPos > curPoint.y )
3022 nIndex = nPrevIndex;
3026 nPrevIndex = nIndex;
3033 void CCrystalTextView::SubLineCursorPosToTextPos( const CPoint &subLineCurPos, CPoint &textPos )
3036 int nSubLineOffset, nLine;
3038 GetLineBySubLine( subLineCurPos.y, nLine, nSubLineOffset );
3040 // compute cursor-position
3041 textPos.x = CursorPointToCharPos( nLine, CPoint( subLineCurPos.x, nSubLineOffset ) );
3046 * @brief Calculate last character position in (sub)line.
3047 * @param [in] nLineIndex Linenumber to check.
3048 * @param [in] nSublineOffset Subline index in wrapped line.
3049 * @return Position of the last character.
3051 int CCrystalTextView::SubLineEndToCharPos(int nLineIndex, int nSubLineOffset)
3053 const int nLength = GetLineLength(nLineIndex);
3055 // if word wrapping is disabled, the end is equal to the length of the line -1
3060 vector<int> anBreaks(nLength + 1);
3063 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3065 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3067 int nBreakLast = -1;
3068 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3070 if (anBreaks[i] < 0)
3072 if (j == nSubLineOffset)
3075 if (nBreakLast < static_cast<int>(anBreaks.size ()) - 1)
3076 return abs (anBreaks[nBreakLast + 1]) - 1;
3080 // if there is no break inside the line or the given subline is the last
3081 // one in this line...
3082 if (nBreaks <= 0 || nSubLineOffset == nBreaks)
3087 // compute character position for end of subline
3088 ASSERT(nSubLineOffset >= 0 && nSubLineOffset <= nBreaks);
3090 int nReturnVal = anBreaks[nSubLineOffset] - 1;
3096 * @brief Calculate first character position in (sub)line.
3097 * @param [in] nLineIndex Linenumber to check.
3098 * @param [in] nSublineOffset Subline index in wrapped line.
3099 * @return Position of the first character.
3101 int CCrystalTextView::SubLineHomeToCharPos(int nLineIndex, int nSubLineOffset)
3103 // if word wrapping is disabled, the start is 0
3104 if (!m_bWordWrap || nSubLineOffset == 0)
3108 int nLength = GetLineLength(nLineIndex);
3109 vector<int> anBreaks(nLength + 1);
3112 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3114 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3116 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3118 if (anBreaks[i] < 0)
3120 if (j == nSubLineOffset)
3121 return abs (anBreaks[i]);
3126 // if there is no break inside the line...
3132 // compute character position for end of subline
3133 ASSERT(nSubLineOffset > 0 && nSubLineOffset <= nBreaks);
3135 int nReturnVal = anBreaks[nSubLineOffset - 1];
3141 int CCrystalTextView::
3144 if (m_nCharWidth == -1)
3146 return m_nCharWidth;
3149 int CCrystalTextView::
3150 GetMaxLineLength (int nTopLine, int nLines)
3152 int nMaxLineLength = 0;
3153 const int nLineCount = (std::min)(nTopLine + nLines, GetLineCount ());
3154 for (int I = nTopLine; I < nLineCount; I++)
3156 int nActualLength = GetLineActualLength (I);
3157 if (nMaxLineLength < nActualLength)
3158 nMaxLineLength = nActualLength;
3160 return nMaxLineLength;
3163 CCrystalTextView *CCrystalTextView::
3164 GetSiblingView (int nRow, int nCol)
3166 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3167 if (pSplitter == nullptr)
3169 CWnd *pWnd = CWnd::FromHandlePermanent (
3170 ::GetDlgItem (pSplitter->m_hWnd, pSplitter->IdFromRowCol (nRow, nCol)));
3171 if (pWnd == nullptr || !pWnd->IsKindOf (RUNTIME_CLASS (CCrystalTextView)))
3173 return static_cast<CCrystalTextView *>(pWnd);
3176 void CCrystalTextView::
3177 GoToLine (int nLine, bool bRelative)
3179 int nLines = m_pTextBuffer->GetLineCount () - 1;
3180 CPoint ptCursorPos = GetCursorPos ();
3183 nLine += ptCursorPos.y;
3195 int nChars = m_pTextBuffer->GetLineLength (nLine);
3200 if (ptCursorPos.x > nChars)
3202 ptCursorPos.x = nChars;
3204 if (ptCursorPos.x >= 0)
3206 ptCursorPos.y = nLine;
3207 ASSERT_VALIDTEXTPOS (ptCursorPos);
3208 SetAnchor (ptCursorPos);
3209 SetSelection (ptCursorPos, ptCursorPos);
3210 SetCursorPos (ptCursorPos);
3211 EnsureVisible (ptCursorPos);
3216 void CCrystalTextView::
3219 CView::OnInitialUpdate ();
3220 CString sDoc = GetDocument ()->GetPathName (), sExt = GetExt (sDoc);
3221 if (!sExt.IsEmpty())
3223 AttachToBuffer (nullptr);
3225 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3226 if (pSplitter != nullptr)
3228 // See CSplitterWnd::IdFromRowCol() implementation
3229 int nRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3230 int nCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3231 ASSERT (nRow >= 0 && nRow < pSplitter->GetRowCount ());
3232 ASSERT (nCol >= 0 && nCol < pSplitter->GetColumnCount ());
3236 CCrystalTextView *pSiblingView = GetSiblingView (0, nCol);
3237 if (pSiblingView != nullptr && pSiblingView != this)
3239 m_nOffsetChar = pSiblingView->m_nOffsetChar;
3240 ASSERT (m_nOffsetChar >= 0 && m_nOffsetChar <= GetMaxLineLength (m_nTopLine, GetScreenLines()));
3246 CCrystalTextView *pSiblingView = GetSiblingView (nRow, 0);
3247 if (pSiblingView != nullptr && pSiblingView != this)
3249 m_nTopLine = pSiblingView->m_nTopLine;
3250 ASSERT (m_nTopLine >= 0 && m_nTopLine < GetLineCount ());
3254 SetFont (m_LogFont);
3255 if (m_bRememberLastPos && !sDoc.IsEmpty ())
3258 CString sKey = REG_EDITPAD;
3259 sKey += _T ("\\Remembered");
3261 if (reg.Open (HKEY_CURRENT_USER, sKey, KEY_READ) &&
3262 reg.LoadBinary (sDoc, (LPBYTE) dwLastPos, sizeof (dwLastPos)))
3265 ptCursorPos.x = dwLastPos[1];
3266 ptCursorPos.y = dwLastPos[2];
3267 if (IsValidTextPosY (ptCursorPos))
3269 if (!IsValidTextPosX (ptCursorPos))
3271 ASSERT_VALIDTEXTPOS (ptCursorPos);
3272 SetCursorPos (ptCursorPos);
3273 SetSelection (ptCursorPos, ptCursorPos);
3274 SetAnchor (ptCursorPos);
3275 EnsureVisible (ptCursorPos);
3281 /////////////////////////////////////////////////////////////////////////////
3282 // CCrystalTextView printing
3284 void CCrystalTextView::
3285 OnPrepareDC (CDC * pDC, CPrintInfo * pInfo /*= nullptr*/)
3287 CView::OnPrepareDC (pDC, pInfo);
3289 if (pInfo != nullptr)
3291 pInfo->m_bContinuePrinting = true;
3292 if (m_nPrintPages != 0 && (int) pInfo->m_nCurPage > m_nPrintPages)
3293 pInfo->m_bContinuePrinting = false;
3297 BOOL CCrystalTextView::
3298 OnPreparePrinting (CPrintInfo * pInfo)
3300 return DoPreparePrinting (pInfo);
3303 void CCrystalTextView::
3304 GetPrintHeaderText (int nPageNum, CString & text)
3306 ASSERT (m_bPrintHeader);
3310 void CCrystalTextView::
3311 GetPrintFooterText (int nPageNum, CString & text)
3313 ASSERT (m_bPrintFooter);
3314 text.Format (_T ("Page %d/%d"), nPageNum, m_nPrintPages);
3317 void CCrystalTextView::
3318 PrintHeader (CDC * pdc, int nPageNum)
3320 CRect rcHeader = m_rcPrintArea;
3321 rcHeader.bottom = rcHeader.top;
3322 rcHeader.top -= (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3325 GetPrintHeaderText (nPageNum, text);
3326 if (!text.IsEmpty ())
3327 pdc->DrawText (text, &rcHeader, DT_CENTER | DT_NOPREFIX | DT_TOP | DT_SINGLELINE);
3330 void CCrystalTextView::
3331 PrintFooter (CDC * pdc, int nPageNum)
3333 CRect rcFooter = m_rcPrintArea;
3334 rcFooter.top = rcFooter.bottom;
3335 rcFooter.bottom += (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3338 GetPrintFooterText (nPageNum, text);
3339 if (!text.IsEmpty ())
3340 pdc->DrawText (text, &rcFooter, DT_CENTER | DT_NOPREFIX | DT_BOTTOM | DT_SINGLELINE);
3344 * @brief Retrieves the print margins
3345 * @param nLeft [out] Left margin
3346 * @param nTop [out] Top margin
3347 * @param nRight [out] right margin
3348 * @param nBottom [out] Bottom margin
3350 void CCrystalTextView::
3351 GetPrintMargins (long & nLeft, long & nTop, long & nRight, long & nBottom)
3353 nLeft = DEFAULT_PRINT_MARGIN;
3354 nTop = DEFAULT_PRINT_MARGIN;
3355 nRight = DEFAULT_PRINT_MARGIN;
3356 nBottom = DEFAULT_PRINT_MARGIN;
3358 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
3361 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
3363 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
3365 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
3367 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
3372 void CCrystalTextView::
3373 RecalcPageLayouts (CDC * pdc, CPrintInfo * pInfo)
3375 m_ptPageArea = pInfo->m_rectDraw;
3376 m_ptPageArea.NormalizeRect ();
3378 m_nPrintLineHeight = pdc->GetTextExtent (_T ("X")).cy;
3380 m_rcPrintArea = m_ptPageArea;
3381 CSize szTopLeft, szBottomRight;
3382 CWinApp *pApp = AfxGetApp ();
3383 ASSERT (pApp != nullptr);
3384 GetPrintMargins (szTopLeft.cx, szTopLeft.cy, szBottomRight.cx, szBottomRight.cy);
3385 pdc->HIMETRICtoLP (&szTopLeft);
3386 pdc->HIMETRICtoLP (&szBottomRight);
3387 m_rcPrintArea.left += szTopLeft.cx;
3388 m_rcPrintArea.right -= szBottomRight.cx;
3389 m_rcPrintArea.top += szTopLeft.cy;
3390 m_rcPrintArea.bottom -= szBottomRight.cy;
3392 m_rcPrintArea.top += m_nPrintLineHeight + m_nPrintLineHeight / 2;
3394 m_rcPrintArea.bottom -= m_nPrintLineHeight + m_nPrintLineHeight / 2;
3396 InvalidateLineCache (0, -1);
3398 m_nScreenChars = (m_rcPrintArea.Width () - GetMarginWidth (pdc)) / GetCharWidth ();
3399 m_nScreenLines = m_rcPrintArea.Height () / GetLineHeight ();
3402 void CCrystalTextView::
3403 OnBeginPrinting (CDC * pdc, CPrintInfo * pInfo)
3405 ASSERT (m_pPrintFont == nullptr);
3406 LOGFONT lf = m_lfBaseFont;
3407 CDC *pDisplayDC = GetDC ();
3408 lf.lfHeight = MulDiv (lf.lfHeight, pdc->GetDeviceCaps (LOGPIXELSY), pDisplayDC->GetDeviceCaps (LOGPIXELSY));
3409 lf.lfWidth = MulDiv (lf.lfWidth, pdc->GetDeviceCaps (LOGPIXELSX), pDisplayDC->GetDeviceCaps (LOGPIXELSX));
3410 ReleaseDC (pDisplayDC);
3412 m_pCrystalRendererSaved = m_pCrystalRenderer.release();
3413 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
3415 m_pPrintFont = new CFont;
3416 if (!m_pPrintFont->CreateFontIndirect (&lf))
3418 delete m_pPrintFont;
3419 m_pPrintFont = nullptr;
3423 GetFont (m_lfSavedBaseFont);
3424 m_pPrintFont->GetLogFont (&lf);
3431 void CCrystalTextView::
3432 OnEndPrinting (CDC * pdc, CPrintInfo * pInfo)
3434 if (m_pCrystalRendererSaved)
3436 m_pCrystalRenderer.reset(m_pCrystalRendererSaved);
3437 m_pCrystalRendererSaved = nullptr;
3439 if (m_pPrintFont != nullptr)
3441 delete m_pPrintFont;
3442 m_pPrintFont = nullptr;
3443 SetFont(m_lfSavedBaseFont);
3446 m_nPrintLineHeight = 0;
3447 m_bPrinting = false;
3450 void CCrystalTextView::
3451 OnPrint (CDC * pdc, CPrintInfo * pInfo)
3453 pdc->SelectObject (m_pPrintFont);
3455 const COLORREF defaultLineColor = RGB(0,0,0);
3456 const COLORREF defaultBgColor = RGB(255,255,255);
3458 RecalcPageLayouts (pdc, pInfo);
3460 m_nPrintPages = (GetSubLineCount () + GetScreenLines () - 1) / GetScreenLines ();
3462 ASSERT (pInfo->m_nCurPage >= 1 && (int) pInfo->m_nCurPage <= m_nPrintPages);
3464 int nTopSubLine = (pInfo->m_nCurPage - 1) * GetScreenLines ();
3465 int nEndSubLine = nTopSubLine + GetScreenLines () - 1;
3466 if (nEndSubLine >= GetSubLineCount ())
3467 nEndSubLine = GetSubLineCount () - 1;
3469 int nTopLine, nEndLine;
3470 GetLineBySubLine (nTopSubLine, nTopLine, nSubLines);
3471 GetLineBySubLine (nEndSubLine, nEndLine, nSubLines);
3473 TRACE (_T ("Printing page %d of %d, lines %d - %d\n"),
3474 pInfo->m_nCurPage, m_nPrintPages, nTopLine, nEndLine);
3476 m_pCrystalRenderer->BindDC(*pdc, m_rcPrintArea);
3477 m_pCrystalRenderer->BeginDraw();
3479 m_pCrystalRenderer->SetTextColor(defaultLineColor);
3480 m_pCrystalRenderer->SetBkColor(defaultBgColor);
3484 PrintHeader (pdc, pInfo->m_nCurPage);
3489 PrintFooter (pdc, pInfo->m_nCurPage);
3492 // set clipping region
3493 // see http://support.microsoft.com/kb/128334
3494 CRect rectClip = m_rcPrintArea;
3495 rectClip.right = rectClip.left + GetMarginWidth (pdc) + GetScreenChars () * GetCharWidth ();
3496 rectClip.bottom = rectClip.top + GetScreenLines () * GetLineHeight ();
3497 if (!!pdc->IsKindOf (RUNTIME_CLASS (CPreviewDC)))
3499 CPreviewDC *pPrevDC = (CPreviewDC *)pdc;
3501 pPrevDC->PrinterDPtoScreenDP (&rectClip.TopLeft ());
3502 pPrevDC->PrinterDPtoScreenDP (&rectClip.BottomRight ());
3505 ::GetViewportOrgEx (pdc->m_hDC,&ptOrg);
3509 rgn.CreateRectRgnIndirect (&rectClip);
3510 pdc->SelectClipRgn (&rgn);
3514 CRect rcLine = m_rcPrintArea;
3515 int nLineHeight = GetLineHeight ();
3516 rcLine.bottom = rcLine.top + nLineHeight;
3518 rcMargin.right = rcMargin.left + GetMarginWidth (pdc);
3519 rcLine.left = rcMargin.right;
3521 int nSubLineOffset = GetSubLineIndex (nTopLine) - nTopSubLine;
3522 if( nSubLineOffset < 0 )
3524 rcLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
3527 int nLineCount = GetLineCount();
3529 for (nCurrentLine = nTopLine; nCurrentLine <= nEndLine; nCurrentLine++)
3531 rcLine.bottom = rcLine.top + GetSubLines (nCurrentLine) * nLineHeight;
3532 rcMargin.bottom = rcLine.bottom;
3534 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
3536 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
3537 DrawSingleLine (rcLine, nCurrentLine);
3538 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
3539 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.bottom-1);
3542 rcLine.top = rcLine.bottom;
3543 rcMargin.top = rcLine.bottom;
3546 m_pCrystalRenderer->EndDraw();
3548 pdc->SelectClipRgn (nullptr);
3552 /////////////////////////////////////////////////////////////////////////////
3553 // CCrystalTextView message handlers
3555 int CCrystalTextView::
3558 if (m_pTextBuffer == nullptr)
3559 return 1; // Single empty line
3561 int nLineCount = m_pTextBuffer->GetLineCount ();
3562 ASSERT (nLineCount > 0);
3567 int CCrystalTextView::GetSubLineCount()
3569 const int nLineCount = GetLineCount();
3571 // if we do not wrap words, number of sub lines is
3572 // equal to number of lines
3573 if( !m_bWordWrap && !m_bHideLines )
3576 // calculate number of sub lines
3577 if (nLineCount <= 0)
3579 return CCrystalTextView::GetSubLineIndex( nLineCount - 1 ) + GetSubLines( nLineCount - 1 );
3582 int CCrystalTextView::GetSubLineIndex( int nLineIndex )
3584 // if we do not wrap words, subline index of this line is equal to its index
3585 if( !m_bWordWrap && !m_bHideLines )
3588 // calculate subline index of the line
3589 int nSubLineCount = 0;
3590 int nLineCount = GetLineCount();
3592 if( nLineIndex >= nLineCount )
3593 nLineIndex = nLineCount - 1;
3595 // return cached subline index of the line if it is already cached.
3596 if (nLineIndex <= m_nLastLineIndexCalculatedSubLineIndex)
3597 return (*m_panSubLineIndexCache)[nLineIndex];
3599 // calculate subline index of the line and cache it.
3600 if (m_nLastLineIndexCalculatedSubLineIndex >= 0)
3601 nSubLineCount = (*m_panSubLineIndexCache)[m_nLastLineIndexCalculatedSubLineIndex];
3604 m_nLastLineIndexCalculatedSubLineIndex = 0;
3605 m_panSubLineIndexCache->SetAtGrow( 0, 0 );
3608 // TODO: Rethink this, it is very time consuming
3609 for( int i = m_nLastLineIndexCalculatedSubLineIndex; i < nLineIndex; i++ )
3611 m_panSubLineIndexCache->SetAtGrow( i, nSubLineCount);
3612 nSubLineCount+= GetSubLines( i );
3614 m_panSubLineIndexCache->SetAtGrow( nLineIndex, nSubLineCount);
3615 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex;
3617 return nSubLineCount;
3620 // See comment in the header file
3621 void CCrystalTextView::GetLineBySubLine(int nSubLineIndex, int &nLine, int &nSubLine)
3623 if (GetSubLineCount() == 0)
3630 ASSERT( nSubLineIndex < GetSubLineCount() );
3632 // if we do not wrap words, nLine is equal to nSubLineIndex and nSubLine is allways 0
3633 if ( !m_bWordWrap && !m_bHideLines )
3635 nLine = nSubLineIndex;
3641 const int nLineCount = GetLineCount();
3644 int base = 0, i = 0, nSubLineIndex2 = 0;
3645 for (int lim = nLineCount; lim != 0; lim >>= 1)
3647 i = base + (lim >> 1);
3648 nSubLineIndex2 = GetSubLineIndex(i);
3649 if (nSubLineIndex >= nSubLineIndex2 && nSubLineIndex < nSubLineIndex2 + GetSubLines(i))
3651 else if (nSubLineIndex2 <= nSubLineIndex) /* key > p: move right */
3655 } /* else move left */
3658 ASSERT(i < nLineCount);
3660 nSubLine = nSubLineIndex - nSubLineIndex2;
3663 int CCrystalTextView::
3664 GetLineLength (int nLineIndex) const
3666 if (m_pTextBuffer == nullptr)
3668 return m_pTextBuffer->GetLineLength (nLineIndex);
3671 int CCrystalTextView::
3672 GetFullLineLength (int nLineIndex) const
3674 if (m_pTextBuffer == nullptr)
3676 return m_pTextBuffer->GetFullLineLength (nLineIndex);
3679 // How many bytes of line are displayed on-screen?
3680 int CCrystalTextView::
3681 GetViewableLineLength (int nLineIndex) const
3684 return GetFullLineLength(nLineIndex);
3686 return GetLineLength(nLineIndex);
3689 LPCTSTR CCrystalTextView::
3690 GetLineChars (int nLineIndex) const
3692 if (m_pTextBuffer == nullptr)
3694 return m_pTextBuffer->GetLineChars (nLineIndex);
3698 * @brief Reattach buffer after deleting/inserting ghost lines :
3700 * @note no need to reinitialize the horizontal scrollbar
3701 * no need to reset the editor options (m_bOvrMode, m_bLastReplace)
3703 void CCrystalTextView::
3704 ReAttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3706 if (m_pTextBuffer != nullptr)
3707 m_pTextBuffer->RemoveView (this);
3708 if (pBuf == nullptr)
3710 pBuf = LocateTextBuffer ();
3713 m_pTextBuffer = pBuf;
3714 if (m_pTextBuffer != nullptr)
3715 m_pTextBuffer->AddView (this);
3716 // don't reset CCrystalEditView options
3717 CCrystalTextView::ResetView ();
3719 // Init scrollbars arrows
3720 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3721 if (pVertScrollBarCtrl != nullptr)
3722 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3723 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3724 // Update vertical scrollbar only
3725 RecalcVertScrollBar ();
3729 * @brief Attach buffer (maybe for the first time)
3730 * initialize the view and initialize both scrollbars
3732 void CCrystalTextView::
3733 AttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3735 if (m_pTextBuffer != nullptr)
3736 m_pTextBuffer->RemoveView (this);
3737 if (pBuf == nullptr)
3739 pBuf = LocateTextBuffer ();
3742 m_pTextBuffer = pBuf;
3743 if (m_pTextBuffer != nullptr)
3744 m_pTextBuffer->AddView (this);
3748 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3749 if (pVertScrollBarCtrl != nullptr)
3750 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3751 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3752 CScrollBar *pHorzScrollBarCtrl = GetScrollBarCtrl (SB_HORZ);
3753 if (pHorzScrollBarCtrl != nullptr)
3754 pHorzScrollBarCtrl->EnableScrollBar (GetScreenChars () >= GetMaxLineLength (m_nTopLine, GetScreenLines())?
3755 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3757 // Update scrollbars
3758 RecalcVertScrollBar ();
3759 RecalcHorzScrollBar ();
3762 void CCrystalTextView::
3765 if (m_pTextBuffer != nullptr)
3767 m_pTextBuffer->RemoveView (this);
3768 m_pTextBuffer = nullptr;
3769 // don't reset CCrystalEditView options
3770 CCrystalTextView::ResetView ();
3774 int CCrystalTextView::
3777 if (m_nScreenLines == -1)
3780 GetClientRect (&rect);
3781 m_nScreenLines = (rect.Height () - GetTopMarginHeight ()) / GetLineHeight ();
3783 return m_nScreenLines;
3786 bool CCrystalTextView::
3787 GetItalic (int nColorIndex)
3789 // WINMERGE - since italic text has problems,
3790 // lets disable it. E.g. "_" chars disappear and last
3791 // char may be cropped.
3794 // return nColorIndex == COLORINDEX_COMMENT;
3797 bool CCrystalTextView::
3798 GetBold (int nColorIndex)
3800 if (m_pColors != nullptr)
3802 nColorIndex &= ~COLORINDEX_APPLYFORCE;
3803 return m_pColors->GetBold(nColorIndex);
3809 int CCrystalTextView::
3812 if (m_nScreenChars == -1)
3815 GetClientRect (&rect);
3816 m_nScreenChars = (rect.Width () - GetMarginWidth ()) / GetCharWidth ();
3818 return m_nScreenChars;
3821 void CCrystalTextView::
3824 DetachFromBuffer ();
3827 CView::OnDestroy ();
3829 if (m_pCacheBitmap != nullptr)
3831 delete m_pCacheBitmap;
3832 m_pCacheBitmap = nullptr;
3836 BOOL CCrystalTextView::
3837 OnEraseBkgnd (CDC * pdc)
3839 UNREFERENCED_PARAMETER(pdc);
3843 void CCrystalTextView::
3844 OnSize (UINT nType, int cx, int cy)
3846 CView::OnSize (nType, cx, cy);
3849 // get char position of top left visible character with old cached word wrap
3851 SubLineCursorPosToTextPos( CPoint( 0, m_nTopSubLine ), topPos );
3855 // we have to recompute the line wrapping
3856 InvalidateScreenRect(false);
3858 // compute new top sub line
3860 CharPosToPoint( topPos.y, topPos.x, topSubLine );
3861 m_nTopSubLine = GetSubLineIndex(topPos.y) + topSubLine.y;
3863 ScrollToSubLine( m_nTopSubLine );
3865 // set caret to right position
3869 RecalcVertScrollBar (false, false);
3870 RecalcHorzScrollBar (false, false);
3873 void CCrystalTextView::
3874 UpdateSiblingScrollPos (bool bHorz)
3876 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
3877 if (pSplitterWnd != nullptr)
3879 // See CSplitterWnd::IdFromRowCol() implementation for details
3880 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3881 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3882 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
3883 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
3887 int nCols = pSplitterWnd->GetColumnCount ();
3888 for (int nCol = 0; nCol < nCols; nCol++)
3890 if (nCol != nCurrentCol) // We don't need to update ourselves
3892 CCrystalTextView *pSiblingView = GetSiblingView (nCurrentRow, nCol);
3893 if (pSiblingView != nullptr)
3894 pSiblingView->OnUpdateSibling (this, false);
3900 int nRows = pSplitterWnd->GetRowCount ();
3901 for (int nRow = 0; nRow < nRows; nRow++)
3903 if (nRow != nCurrentRow) // We don't need to update ourselves
3905 CCrystalTextView *pSiblingView = GetSiblingView (nRow, nCurrentCol);
3906 if (pSiblingView != nullptr)
3907 pSiblingView->OnUpdateSibling (this, false);
3914 void CCrystalTextView::
3915 OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
3917 if (pUpdateSource != this)
3919 ASSERT (pUpdateSource != nullptr);
3920 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
3923 ASSERT (pUpdateSource->m_nTopLine >= 0);
3924 ASSERT (pUpdateSource->m_nTopLine < GetLineCount ());
3925 if (pUpdateSource->m_nTopLine != m_nTopLine)
3927 ScrollToLine (pUpdateSource->m_nTopLine, true, false);
3933 ASSERT (pUpdateSource->m_nOffsetChar >= 0);
3934 ASSERT (pUpdateSource->m_nOffsetChar < GetMaxLineLength (m_nTopLine, GetScreenLines()));
3935 if (pUpdateSource->m_nOffsetChar != m_nOffsetChar)
3937 ScrollToChar (pUpdateSource->m_nOffsetChar, true, false);
3944 void CCrystalTextView::
3945 RecalcVertScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
3947 SCROLLINFO si = {0};
3948 si.cbSize = sizeof (si);
3952 si.nPos = m_nTopSubLine;
3956 const int nScreenLines = GetScreenLines();
3957 if( m_nTopSubLine > 0 && nScreenLines >= GetSubLineCount() )
3963 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3965 si.nMax = GetSubLineCount() - 1;
3966 si.nPage = nScreenLines;
3967 si.nPos = m_nTopSubLine;
3969 VERIFY (SetScrollInfo (SB_VERT, &si, bRedraw));
3972 void CCrystalTextView::
3973 OnVScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
3975 CView::OnVScroll (nSBCode, nPos, pScrollBar);
3977 // Note we cannot use nPos because of its 16-bit nature
3978 SCROLLINFO si = {0};
3979 si.cbSize = sizeof (si);
3980 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3981 VERIFY (GetScrollInfo (SB_VERT, &si));
3983 // Get the minimum and maximum scroll-bar positions.
3984 int nMinPos = si.nMin;
3985 int nMaxPos = si.nMax;
3987 // Get the current position of scroll box.
3988 int nCurPos = si.nPos;
3990 bool bDisableSmooth = true;
3993 case SB_TOP: // Scroll to top.
3995 bDisableSmooth = false;
3998 case SB_BOTTOM: // Scroll to bottom.
4000 bDisableSmooth = false;
4003 case SB_LINEUP: // Scroll one line up.
4004 if (nCurPos > nMinPos)
4008 case SB_LINEDOWN: // Scroll one line down.
4009 if (nCurPos < nMaxPos)
4013 case SB_PAGEUP: // Scroll one page up.
4014 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4015 bDisableSmooth = false;
4018 case SB_PAGEDOWN: // Scroll one page down.
4019 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4020 bDisableSmooth = false;
4023 case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position
4024 nCurPos = si.nTrackPos; // of the scroll box at the end of the drag operation.
4027 case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the
4028 nCurPos = si.nTrackPos; // position that the scroll box has been dragged to.
4031 ScrollToSubLine(nCurPos, bDisableSmooth);
4034 void CCrystalTextView::
4035 RecalcHorzScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
4037 SCROLLINFO si = {0};
4038 si.cbSize = sizeof (si);
4040 const int nScreenChars = GetScreenChars();
4042 if (GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
4044 if (m_nOffsetChar > nScreenChars)
4050 // Disable horizontal scroll bar
4051 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4052 SetScrollInfo (SB_HORZ, &si);
4056 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
4061 si.nPos = m_nOffsetChar;
4065 if (nScreenChars >= nMaxLineLen && m_nOffsetChar > 0)
4071 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4074 // Horiz scroll limit to longest line + one screenwidth
4075 si.nMax = nMaxLineLen + nScreenChars;
4076 si.nPage = nScreenChars;
4077 si.nPos = m_nOffsetChar;
4079 VERIFY (SetScrollInfo (SB_HORZ, &si, bRedraw));
4082 void CCrystalTextView::
4083 OnHScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
4085 // Default handler not needed
4086 //CView::OnHScroll (nSBCode, nPos, pScrollBar);
4088 // Again, we cannot use nPos because it's 16-bit
4089 SCROLLINFO si = {0};
4090 si.cbSize = sizeof (si);
4091 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
4092 VERIFY (GetScrollInfo (SB_HORZ, &si));
4094 // Get the minimum and maximum scroll-bar positions.
4095 int nMinPos = si.nMin;
4096 int nMaxPos = si.nMax;
4098 // Get the current position of scroll box.
4099 int nCurPos = si.nPos;
4103 case SB_LEFT: // Scroll to far left.
4107 case SB_RIGHT: // Scroll to far right.
4111 case SB_ENDSCROLL: // End scroll.
4114 case SB_LINELEFT: // Scroll left.
4115 if (nCurPos > nMinPos)
4119 case SB_LINERIGHT: // Scroll right.
4120 if (nCurPos < nMaxPos)
4124 case SB_PAGELEFT: // Scroll one page left.
4125 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4128 case SB_PAGERIGHT: // Scroll one page right.
4129 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4132 case SB_THUMBPOSITION: // Scroll to absolute position. The current position is
4133 nCurPos = si.nTrackPos; // specified by the nPos parameter.
4136 case SB_THUMBTRACK: // Drag scroll box to specified position. The current
4137 nCurPos = si.nTrackPos; // position is specified by the nPos parameter
4138 // The SB_THUMBTRACK scroll-bar code typically is used by applications that give
4139 // some feedback while the scroll box is being dragged.
4142 ScrollToChar (nCurPos, true);
4143 // This is needed, but why ? OnVScroll don't need to call UpdateCaret
4147 BOOL CCrystalTextView::
4148 OnSetCursor (CWnd * pWnd, UINT nHitTest, UINT message)
4150 if (nHitTest == HTCLIENT)
4153 ::GetCursorPos (&pt);
4154 ScreenToClient (&pt);
4155 if (pt.y < GetTopMarginHeight ())
4157 const int nColumnResizing = ClientToColumnResizing (pt.x);
4158 ::SetCursor (::LoadCursor (nullptr, nColumnResizing >= 0 ? IDC_SIZEWE : IDC_ARROW));
4160 else if (pt.x < GetMarginWidth ())
4162 ::SetCursor (::LoadCursor (AfxGetInstanceHandle (), MAKEINTRESOURCE (IDR_MARGIN_CURSOR)));
4166 CPoint ptText = ClientToText (pt);
4167 PrepareSelBounds ();
4168 if (IsInsideSelBlock (ptText))
4170 // [JRT]: Support For Disabling Drag and Drop...
4171 if (!m_bDisableDragAndDrop) // If Drag And Drop Not Disabled
4173 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW)); // Set To Arrow Cursor
4177 ::SetCursor (::LoadCursor (nullptr, IDC_IBEAM));
4181 return CView::OnSetCursor (pWnd, nHitTest, message);
4184 int CCrystalTextView::
4185 ClientToIdealTextPos (int x)
4188 if (x > GetMarginWidth ())
4189 nPos = m_nOffsetChar + (x - GetMarginWidth ()) / GetCharWidth ();
4196 * @brief Converts client area point to text position.
4197 * @param [in] point Client area point.
4198 * @return Text position (line index, char index in line).
4199 * @note For gray selection area char index is 0.
4201 CPoint CCrystalTextView::
4202 ClientToText (const CPoint & point)
4205 const int nSubLineCount = GetSubLineCount();
4206 const int nLineCount = GetLineCount();
4209 pt.y = m_nTopSubLine + (point.y - GetTopMarginHeight ()) / GetLineHeight();
4210 if (pt.y >= nSubLineCount)
4211 pt.y = nSubLineCount - 1;
4218 GetLineBySubLine( pt.y, nLine, nSubLineOffset );
4221 LPCTSTR pszLine = nullptr;
4223 vector<int> anBreaks(1);
4226 if (pt.y >= 0 && pt.y < nLineCount)
4228 nLength = GetLineLength( pt.y );
4229 anBreaks.resize(nLength + 1);
4230 pszLine = GetLineChars(pt.y);
4231 WrapLineCached( pt.y, GetScreenChars(), &anBreaks, nBreaks );
4233 if (nBreaks > nSubLineOffset && GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
4234 nLength = anBreaks[nSubLineOffset] - 1;
4237 // Char index for margin area is 0
4238 int nPos = ClientToIdealTextPos (point.x);
4243 const int nTabSize = GetTabSize();
4245 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, nLength);
4246 switch (GetTextLayoutMode ())
4248 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4250 int nColumnCount = m_pTextBuffer->GetColumnCount (nLine);
4251 int nColumnTotalWidth = 0;
4253 bool bInQuote = false;
4254 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4255 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4256 while (nIndex < nLength)
4259 if (!bInQuote && pszLine[nIndex] == sep)
4261 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4262 nOffset = nColumnTotalWidth - nCurPos;
4266 if (pszLine[nIndex] == quote)
4267 bInQuote = !bInQuote;
4268 if (pszLine[nIndex] == '\t')
4271 nOffset = GetCharCellCountFromChar (pszLine + nIndex);
4272 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4273 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
4278 if (n > nPos && i == nSubLineOffset)
4281 nIndex = pIterChar->next ();
4285 case TEXTLAYOUT_TABLE_WORDWRAP:
4289 int nColumnSumWidth = 0;
4290 int nColumnCurPoint = INT_MAX;
4292 if (nPos < m_pTextBuffer->GetColumnWidth (0))
4293 nColumnCurPoint = 0;
4294 while (nIndex < nLength)
4296 if (i < static_cast<int>(anBreaks.size()) && nIndex == abs(anBreaks[i]))
4298 if (anBreaks[i++] < 0)
4301 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4302 n = nColumnSumWidth;
4303 if (nColumnSumWidth <= nPos && nPos < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4304 nColumnCurPoint = nColumn;
4308 n = nColumnSumWidth;
4314 if (pszLine[nIndex] == '\t')
4317 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4321 if (n > nPos && j == nSubLineOffset)
4323 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
4325 nIndex = nPrevIndex;
4328 else if ( j == nSubLineOffset)
4329 nPrevIndex = nIndex;
4331 nIndex = pIterChar->next();
4333 if (nIndex == nLength && j != nSubLineOffset)
4334 nIndex = nPrevIndex;
4339 while (nIndex < nLength)
4341 if (nBreaks && i < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[i])
4348 if (pszLine[nIndex] == '\t')
4349 nOffset = nTabSize - nCurPos % nTabSize;
4351 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4355 if (n > nPos && i == nSubLineOffset)
4358 nIndex = pIterChar->next();
4364 ASSERT(nIndex >= 0 && nIndex <= nLength);
4369 int CCrystalTextView::
4370 ClientToColumn (int x)
4373 GetClientRect (&rcClient);
4374 int nCharWidth = GetCharWidth ();
4375 int nMarginWidth = GetMarginWidth ();
4376 for (int nColumn = 0, columnleft = nMarginWidth - m_nOffsetChar * nCharWidth;
4377 columnleft < rcClient.Width ();
4378 columnleft += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
4380 if (columnleft <= x && x < columnleft + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth)
4386 int CCrystalTextView::
4387 ClientToColumnResizing (int x)
4389 const int nColumn = ClientToColumn (x);
4390 const int nColumnL = ClientToColumn (x - 4);
4391 const int nColumnR = ClientToColumn (x + 4);
4392 if (nColumn != nColumnL || nColumn != nColumnR)
4394 return (nColumn != nColumnL) ? nColumnL : nColumn;
4399 void CCrystalTextView::
4400 AssertValidTextPos (const CPoint & point)
4402 if (GetLineCount () > 0)
4404 ASSERT (m_nTopLine >= 0 && m_nOffsetChar >= 0);
4405 ASSERT (point.y >= 0 && point.y < GetLineCount ());
4406 ASSERT (point.x >= 0 && point.x <= GetViewableLineLength (point.y));
4411 bool CCrystalTextView::
4412 IsValidTextPos (const CPoint &point)
4414 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4415 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4418 bool CCrystalTextView::
4419 IsValidTextPosX (const CPoint &point)
4421 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4422 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4425 bool CCrystalTextView::
4426 IsValidTextPosY (const CPoint &point)
4428 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4429 point.y >= 0 && point.y < GetLineCount ();
4432 CPoint CCrystalTextView::
4433 TextToClient (const CPoint & point)
4435 ASSERT_VALIDTEXTPOS (point);
4436 LPCTSTR pszLine = GetLineChars (point.y);
4438 int nColumnIndex = 0;
4442 int nSubLineStart = CharPosToPoint( point.y, point.x, charPoint, &nColumnIndex );
4443 charPoint.y+= GetSubLineIndex( point.y );
4445 // compute y-position
4446 pt.y = (charPoint.y - m_nTopSubLine) * GetLineHeight() + GetTopMarginHeight ();
4448 // if pt.x is null, we know the result
4449 if( charPoint.x == 0 && nColumnIndex == 0)
4451 pt.x = GetMarginWidth();
4455 // we have to calculate x-position
4458 pt.y = (point.y - m_nTopLine) * GetLineHeight();
4462 int nTabSize = GetTabSize ();
4463 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, point.x);
4464 switch (GetTextLayoutMode ())
4466 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4468 int nColumnCount = m_pTextBuffer->GetColumnCount (point.y);
4469 int nColumnTotalWidth = 0;
4471 bool bInQuote = false;
4472 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4473 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4474 for (int nIndex = 0, nTabs = 0; nIndex < point.x; nIndex = pIterChar->next())
4476 if (!bInQuote && pszLine[nIndex] == sep)
4478 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4479 pt.x = nColumnTotalWidth;
4483 if (pszLine[nIndex] == quote)
4484 bInQuote = !bInQuote;
4485 if (pszLine[nIndex] == _T ('\t'))
4488 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4489 if (nColumn < nColumnCount && pt.x > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4490 pt.x = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4493 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4497 case TEXTLAYOUT_TABLE_WORDWRAP:
4500 for (int i = 0; i < nColumnIndex; ++i)
4501 pt.x += m_pTextBuffer->GetColumnWidth (i);
4502 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4504 if( nIndex >= nSubLineStart )
4506 if (pszLine[nIndex] == '\t')
4509 pt.x += GetCharCellCountFromChar (pszLine + nIndex);
4512 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4518 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4521 if( nIndex == nSubLineStart )
4524 if (pszLine[nIndex] == _T ('\t'))
4525 pt.x += (nTabSize - pt.x % nTabSize);
4527 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4533 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4539 int CCrystalTextView::
4540 ColumnToClient (int nColumn)
4543 GetClientRect (&rcClient);
4544 int nCharWidth = GetCharWidth ();
4545 int columnleft = GetMarginWidth () - m_nOffsetChar * nCharWidth;
4546 for (int nColumn2 = 0; nColumn2 != nColumn && columnleft < rcClient.Width ();
4547 columnleft += m_pTextBuffer->GetColumnWidth (nColumn2++) * nCharWidth)
4552 void CCrystalTextView::
4553 InvalidateLines (int nLine1, int nLine2, bool bInvalidateMargin /*= false*/ )
4555 bInvalidateMargin = true;
4556 const int nTopMarginHeight = GetTopMarginHeight ();
4557 const int nLineHeight = GetLineHeight();
4561 GetClientRect (&rcInvalid);
4562 if (!bInvalidateMargin)
4563 rcInvalid.left += GetMarginWidth ();
4565 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4567 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4570 InvalidateRect (&rcInvalid, false);
4574 if (nLine2 < nLine1)
4581 GetClientRect (&rcInvalid);
4582 if (!bInvalidateMargin)
4583 rcInvalid.left += GetMarginWidth ();
4585 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4586 rcInvalid.bottom = (GetSubLineIndex( nLine2 ) - m_nTopSubLine + GetSubLines( nLine2 )) * nLineHeight + nTopMarginHeight;
4588 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4589 rcInvalid.bottom = (nLine2 - m_nTopLine + 1) * GetLineHeight();
4592 InvalidateRect (&rcInvalid, false);
4596 void CCrystalTextView::
4597 SetSelection (const CPoint & ptStart, const CPoint & ptEnd, bool bUpdateView /* = true */)
4599 ASSERT_VALIDTEXTPOS (ptStart);
4600 ASSERT_VALIDTEXTPOS (ptEnd);
4601 if (m_ptSelStart == ptStart && !m_bRectangularSelection)
4603 if (m_ptSelEnd != ptEnd)
4604 InvalidateLines (ptEnd.y, m_ptSelEnd.y);
4608 InvalidateLines (ptStart.y, ptEnd.y);
4609 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4611 m_ptSelStart = ptStart;
4615 void CCrystalTextView::
4616 AdjustTextPoint (CPoint & point)
4618 point.x += GetCharWidth () / 2; //todo
4622 void CCrystalTextView::
4623 OnSetFocus (CWnd * pOldWnd)
4625 CView::OnSetFocus (pOldWnd);
4628 if (m_ptSelStart != m_ptSelEnd)
4629 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4633 DWORD CCrystalTextView::
4634 ParseLine (DWORD dwCookie, const TCHAR *pszChars, int nLength, TEXTBLOCK * pBuf, int &nActualItems)
4636 return m_CurSourceDef->ParseLineX (dwCookie, pszChars, nLength, pBuf, nActualItems);
4639 int CCrystalTextView::
4640 CalculateActualOffset (int nLineIndex, int nCharIndex, bool bAccumulate)
4642 const int nLength = GetLineLength (nLineIndex);
4643 ASSERT (nCharIndex >= 0 && nCharIndex <= nLength);
4644 LPCTSTR pszChars = GetLineChars (nLineIndex);
4646 const int nTabSize = GetTabSize ();
4647 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nCharIndex);
4649 switch (GetTextLayoutMode ())
4651 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4653 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4654 int nColumnTotalWidth = 0;
4656 bool bInQuote = false;
4657 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4658 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4659 for (I = 0; I < nCharIndex; I = pIterChar->next())
4661 if (!bInQuote && pszChars[I] == sep)
4663 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4664 nOffset = nColumnTotalWidth;
4668 if (pszChars[I] == quote)
4669 bInQuote = !bInQuote;
4670 else if (pszChars[I] == '\t')
4673 nOffset += GetCharCellCountFromChar (pszChars + I);
4674 if (nColumn < nColumnCount && nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4675 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4681 case TEXTLAYOUT_TABLE_WORDWRAP:
4683 int nColumnIndex = 0;
4685 int nSubLineStart = CharPosToPoint( nLineIndex, nCharIndex, charPoint, &nColumnIndex );
4686 for (int i = 0; i < nColumnIndex; ++i)
4687 nOffset += m_pTextBuffer->GetColumnWidth (i);
4688 for (int nIndex = 0; nIndex < nCharIndex; nIndex = pIterChar->next())
4690 if( nIndex >= nSubLineStart )
4692 if (pszChars[nIndex] == '\t')
4695 nOffset += GetCharCellCountFromChar (pszChars + nIndex);
4704 vector<int> anBreaks(nLength + 1);
4707 /*if( nLength > GetScreenChars() )*/
4708 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
4716 for( J = nBreaks - 1; J >= 0 && nCharIndex < anBreaks[J]; J-- );
4717 nPreBreak = (J >= 0) ? anBreaks[J] : 0;
4720 for (I = 0; I < nCharIndex; I = pIterChar->next())
4723 if( nPreBreak == I && nBreaks )
4724 nPreOffset = nOffset;
4726 if (pszChars[I] == _T ('\t'))
4727 nOffset += (nTabSize - nOffset % nTabSize);
4729 nOffset += GetCharCellCountFromChar(pszChars + I);
4734 if( nPreBreak == I && nBreaks > 0)
4737 return nOffset - nPreOffset;
4745 int CCrystalTextView::
4746 ApproxActualOffset (int nLineIndex, int nOffset)
4751 int nLength = GetLineLength (nLineIndex);
4752 LPCTSTR pszChars = GetLineChars (nLineIndex);
4753 int nCurrentOffset = 0;
4754 int nTabSize = GetTabSize ();
4755 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
4756 switch (GetTextLayoutMode ())
4758 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4759 case TEXTLAYOUT_TABLE_WORDWRAP:
4761 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4762 int nColumnTotalWidth = 0;
4763 bool bInQuote = false;
4764 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4765 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4766 for (int I = 0, nColumn = 0; I < nLength; I = pIterChar->next())
4768 if (!bInQuote && pszChars[I] ==sep)
4770 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4771 nCurrentOffset = nColumnTotalWidth;
4775 if (pszChars[I] == quote)
4776 bInQuote = !bInQuote;
4777 if (pszChars[I] == '\t')
4780 nCurrentOffset += GetCharCellCountFromChar (pszChars + I);
4781 if (nColumn < nColumnCount && nCurrentOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4782 nCurrentOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4784 if (nCurrentOffset >= nOffset)
4786 if (nOffset <= nCurrentOffset - nTabSize / 2)
4788 return pIterChar->next ();
4795 for (int I = 0; I < nLength; I = pIterChar->next())
4797 if (pszChars[I] == _T ('\t'))
4798 nCurrentOffset += (nTabSize - nCurrentOffset % nTabSize);
4801 nCurrentOffset += GetCharCellCountFromChar(pszChars + I);
4803 if (nCurrentOffset >= nOffset)
4805 if (nOffset <= nCurrentOffset - nTabSize / 2)
4807 return pIterChar->next();
4815 void CCrystalTextView::
4816 EnsureVisible (CPoint pt)
4818 EnsureVisible(pt, pt);
4821 void CCrystalTextView::
4822 OnKillFocus (CWnd * pNewWnd)
4824 CView::OnKillFocus (pNewWnd);
4828 if (m_ptSelStart != m_ptSelEnd)
4829 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4830 if (m_bDragSelection)
4833 KillTimer (m_nDragSelTimer);
4834 m_bDragSelection = false;
4838 void CCrystalTextView::
4841 CView::OnSysColorChange ();
4845 void CCrystalTextView::
4846 GetText (const CPoint & ptStart, const CPoint & ptEnd, CString & text, bool bExcludeInvisibleLines /*= true*/)
4848 if (m_pTextBuffer != nullptr)
4849 m_pTextBuffer->GetText (ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, text);
4854 void CCrystalTextView::
4855 GetTextInColumnSelection (CString & text, bool bExcludeInvisibleLines /*= true*/)
4857 if (m_pTextBuffer == nullptr)
4863 PrepareSelBounds ();
4865 CString sEol = m_pTextBuffer->GetStringEol (CRLF_STYLE_DOS);
4868 for (int L = m_ptDrawSelStart.y; L <= m_ptDrawSelEnd.y; L++)
4869 nBufSize += GetLineLength (L) + sEol.GetLength ();
4870 LPTSTR pszBuf = text.GetBuffer (nBufSize);
4872 for (int I = m_ptDrawSelStart.y; I <= m_ptDrawSelEnd.y; I++)
4874 if (bExcludeInvisibleLines && (GetLineFlags (I) & LF_INVISIBLE))
4876 int nSelLeft, nSelRight;
4877 GetColumnSelection (I, nSelLeft, nSelRight);
4878 memcpy (pszBuf, GetLineChars (I) + nSelLeft, sizeof (TCHAR) * (nSelRight - nSelLeft));
4879 pszBuf += (nSelRight - nSelLeft);
4880 memcpy (pszBuf, sEol, sizeof (TCHAR) * sEol.GetLength ());
4881 pszBuf += sEol.GetLength ();
4884 text.ReleaseBuffer ();
4888 void CCrystalTextView::
4889 UpdateView (CCrystalTextView * pSource, CUpdateContext * pContext,
4890 DWORD dwFlags, int nLineIndex /*= -1*/ )
4892 // SetTextType (GetExt (GetDocument ()->GetPathName ()));
4893 if (dwFlags & UPDATE_RESET)
4896 RecalcVertScrollBar ();
4897 RecalcHorzScrollBar ();
4901 int nLineCount = GetLineCount ();
4902 ASSERT (nLineCount > 0);
4903 ASSERT (nLineIndex >= -1 && nLineIndex < nLineCount);
4904 if ((dwFlags & UPDATE_SINGLELINE) != 0)
4906 ASSERT (nLineIndex != -1);
4907 // All text below this line should be reparsed
4908 const int cookiesSize = (int) m_ParseCookies->size();
4909 if (cookiesSize > 0)
4911 ASSERT (cookiesSize == nLineCount);
4912 // must be reinitialized to invalid value (DWORD) - 1
4913 for (int i = nLineIndex; i < cookiesSize; ++i)
4914 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4916 // This line'th actual length must be recalculated
4917 if (m_pnActualLineLength->size())
4919 ASSERT (m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
4920 // must be initialized to invalid code -1
4921 (*m_pnActualLineLength)[nLineIndex] = -1;
4923 InvalidateLineCache( nLineIndex, nLineIndex );
4926 // Repaint the lines
4927 InvalidateLines (nLineIndex, -1, true);
4931 if (m_bViewLineNumbers)
4932 // if enabling linenumber, we must invalidate all line-cache in visible area because selection margin width changes dynamically.
4933 nLineIndex = m_nTopLine < nLineIndex ? m_nTopLine : nLineIndex;
4935 if (nLineIndex == -1)
4936 nLineIndex = 0; // Refresh all text
4938 // All text below this line should be reparsed
4939 if (m_ParseCookies->size())
4941 size_t arrSize = m_ParseCookies->size();
4942 if (arrSize != static_cast<size_t>(nLineCount))
4944 size_t oldsize = arrSize;
4945 m_ParseCookies->resize(nLineCount);
4946 arrSize = nLineCount;
4947 // must be initialized to invalid value (DWORD) - 1
4948 for (size_t i = oldsize; i < arrSize; ++i)
4949 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4951 for (size_t i = nLineIndex; i < arrSize; ++i)
4952 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4955 // Recalculate actual length for all lines below this
4956 if (m_pnActualLineLength->size())
4958 size_t arrsize = m_pnActualLineLength->size();
4959 if (arrsize != static_cast<size_t>(nLineCount))
4961 // Reallocate actual length array
4962 size_t oldsize = arrsize;
4963 m_pnActualLineLength->resize(nLineCount);
4964 arrsize = nLineCount;
4965 // must be initialized to invalid code -1
4966 for (size_t i = oldsize; i < arrsize; ++i)
4967 (*m_pnActualLineLength)[i] = -1;
4969 for (size_t i = nLineIndex; i < arrsize; ++i)
4970 (*m_pnActualLineLength)[i] = -1;
4973 InvalidateLineCache( nLineIndex, -1 );
4975 // Repaint the lines
4976 InvalidateLines (nLineIndex, -1, true);
4979 // All those points must be recalculated and validated
4980 if (pContext != nullptr)
4982 pContext->RecalcPoint (m_ptCursorPos);
4983 pContext->RecalcPoint (m_ptSelStart);
4984 pContext->RecalcPoint (m_ptSelEnd);
4985 pContext->RecalcPoint (m_ptAnchor);
4986 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
4987 ASSERT_VALIDTEXTPOS (m_ptSelStart);
4988 ASSERT_VALIDTEXTPOS (m_ptSelEnd);
4989 ASSERT_VALIDTEXTPOS (m_ptAnchor);
4990 if (m_bDraggingText)
4992 pContext->RecalcPoint (m_ptDraggedTextBegin);
4993 pContext->RecalcPoint (m_ptDraggedTextEnd);
4994 ASSERT_VALIDTEXTPOS (m_ptDraggedTextBegin);
4995 ASSERT_VALIDTEXTPOS (m_ptDraggedTextEnd);
4997 CPoint ptTopLine (0, m_nTopLine);
4998 pContext->RecalcPoint (ptTopLine);
4999 ASSERT_VALIDTEXTPOS (ptTopLine);
5000 m_nTopLine = ptTopLine.y;
5004 // Recalculate vertical scrollbar, if needed
5005 if ((dwFlags & UPDATE_VERTRANGE) != 0)
5007 if (!m_bVertScrollBarLocked)
5008 RecalcVertScrollBar ();
5011 // Recalculate horizontal scrollbar, if needed
5012 if ((dwFlags & UPDATE_HORZRANGE) != 0)
5014 if (!m_bHorzScrollBarLocked)
5015 RecalcHorzScrollBar ();
5019 HINSTANCE CCrystalTextView::
5020 GetResourceHandle ()
5022 #ifdef CRYSEDIT_RES_HANDLE
5023 return CRYSEDIT_RES_HANDLE;
5025 if (s_hResourceInst != nullptr)
5026 return s_hResourceInst;
5027 return AfxGetResourceHandle ();
5031 int CCrystalTextView::
5032 OnCreate (LPCREATESTRUCT lpCreateStruct)
5035 _tcscpy_s (m_lfBaseFont.lfFaceName, _T ("FixedSys"));
5036 m_lfBaseFont.lfHeight = 0;
5037 m_lfBaseFont.lfWeight = FW_NORMAL;
5038 m_lfBaseFont.lfItalic = false;
5039 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
5040 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
5041 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5042 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
5043 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
5045 if (CView::OnCreate (lpCreateStruct) == -1)
5048 ASSERT (m_hAccel == nullptr);
5049 // vvv GetResourceHandle () ???
5050 HINSTANCE hInst = AfxFindResourceHandle (MAKEINTRESOURCE(IDR_DEFAULT_ACCEL), RT_ACCELERATOR);
5051 ASSERT (hInst != nullptr);
5052 m_hAccel =::LoadAccelerators (hInst, MAKEINTRESOURCE (IDR_DEFAULT_ACCEL));
5053 ASSERT (m_hAccel != nullptr);
5057 void CCrystalTextView::
5058 SetAnchor (const CPoint & ptNewAnchor)
5060 ASSERT_VALIDTEXTPOS (ptNewAnchor);
5061 m_ptAnchor = ptNewAnchor;
5064 void CCrystalTextView::
5065 OnEditOperation (int nAction, LPCTSTR pszText, size_t cchText)
5069 BOOL CCrystalTextView::
5070 PreTranslateMessage (MSG * pMsg)
5072 if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
5074 if (m_hAccel != nullptr)
5076 if (::TranslateAccelerator (m_hWnd, m_hAccel, pMsg))
5080 else if (pMsg->message == WM_LBUTTONDBLCLK)
5081 m_dwLastDblClickTime = GetTickCount();
5082 else if (pMsg->message == WM_LBUTTONDOWN && (GetTickCount() - GetDoubleClickTime()) < m_dwLastDblClickTime)
5084 m_dwLastDblClickTime = 0;
5085 OnLButtonTrippleClk(static_cast<UINT>(pMsg->wParam), { GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam) });
5088 return CView::PreTranslateMessage (pMsg);
5091 void CCrystalTextView::
5092 SetCursorPos (const CPoint & ptCursorPos)
5094 ASSERT_VALIDTEXTPOS (ptCursorPos);
5095 m_ptCursorPos = ptCursorPos;
5096 m_nIdealCharPos = CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x);
5100 void CCrystalTextView::
5101 UpdateCompositionWindowPos() /* IME */
5103 HIMC hIMC = ImmGetContext(m_hWnd);
5104 COMPOSITIONFORM compform;
5106 compform.dwStyle = CFS_FORCE_POSITION;
5107 compform.ptCurrentPos = GetCaretPos();
5108 ImmSetCompositionWindow(hIMC, &compform);
5110 ImmReleaseContext(m_hWnd, hIMC);
5113 void CCrystalTextView::
5114 UpdateCompositionWindowFont() /* IME */
5116 HIMC hIMC = ImmGetContext(m_hWnd);
5118 ImmSetCompositionFont(hIMC, &m_lfBaseFont);
5120 ImmReleaseContext(m_hWnd, hIMC);
5123 void CCrystalTextView::
5124 SetTopMargin (bool bTopMargin)
5126 if (m_bTopMargin != bTopMargin)
5128 m_bTopMargin = bTopMargin;
5129 if (::IsWindow (m_hWnd))
5132 m_nScreenLines = -1;
5133 RecalcVertScrollBar ();
5139 void CCrystalTextView::
5140 SetSelectionMargin (bool bSelMargin)
5142 if (m_bSelMargin != bSelMargin)
5144 m_bSelMargin = bSelMargin;
5145 if (::IsWindow (m_hWnd))
5147 InvalidateScreenRect ();
5148 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5149 RecalcHorzScrollBar ();
5155 void CCrystalTextView::
5156 SetViewLineNumbers (bool bViewLineNumbers)
5158 if (m_bViewLineNumbers != bViewLineNumbers)
5160 m_bViewLineNumbers = bViewLineNumbers;
5161 if (::IsWindow (m_hWnd))
5163 InvalidateScreenRect ();
5164 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5165 RecalcHorzScrollBar ();
5171 void CCrystalTextView::
5172 SetFont (const LOGFONT & lf)
5177 m_pCrystalRenderer->SetFont(lf);
5178 if (::IsWindow (m_hWnd))
5180 InvalidateScreenRect();
5181 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5182 RecalcVertScrollBar ();
5183 RecalcHorzScrollBar ();
5191 void CCrystalTextView::
5192 OnUpdateIndicatorPosition (CCmdUI * pCmdUI)
5194 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
5196 // VVV m_ptCursorPos.x + 1 ???
5197 stat.Format (_T ("Ln %d, Col %d"), m_ptCursorPos.y + 1, m_nIdealCharPos + 1);
5198 pCmdUI->SetText (stat);
5200 if( pCmdUI->m_pOther != nullptr && !!pCmdUI->m_pOther->IsKindOf( RUNTIME_CLASS(CStatusBar) ) )
5201 OnUpdateStatusMessage( (CStatusBar*)pCmdUI->m_pOther );
5205 void CCrystalTextView::
5206 OnUpdateIndicatorCRLF (CCmdUI * pCmdUI)
5208 if (m_pTextBuffer != nullptr)
5210 std::basic_string<TCHAR> eol;
5211 CRLFSTYLE crlfMode = m_pTextBuffer->GetCRLFMode ();
5214 case CRLF_STYLE_DOS:
5215 eol = LoadResString (IDS_EOL_DOS);
5216 pCmdUI->SetText (eol.c_str());
5217 pCmdUI->Enable (true);
5219 case CRLF_STYLE_UNIX:
5220 eol = LoadResString (IDS_EOL_UNIX);
5221 pCmdUI->SetText (eol.c_str());
5222 pCmdUI->Enable (true);
5224 case CRLF_STYLE_MAC:
5225 eol = LoadResString (IDS_EOL_MAC);
5226 pCmdUI->SetText (eol.c_str());
5227 pCmdUI->Enable (true);
5229 case CRLF_STYLE_MIXED:
5230 eol = LoadResString (IDS_EOL_MIXED);
5231 pCmdUI->SetText (eol.c_str());
5232 pCmdUI->Enable (true);
5235 pCmdUI->SetText (nullptr);
5236 pCmdUI->Enable (false);
5241 pCmdUI->SetText (nullptr);
5242 pCmdUI->Enable (false);
5246 void CCrystalTextView::
5247 OnToggleBookmark (UINT nCmdID)
5249 int nBookmarkID = nCmdID - ID_EDIT_TOGGLE_BOOKMARK0;
5250 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5251 if (m_pTextBuffer != nullptr)
5253 DWORD dwFlags = GetLineFlags (m_ptCursorPos.y);
5254 DWORD dwMask = LF_BOOKMARK (nBookmarkID);
5255 m_pTextBuffer->SetLineFlag (m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0);
5259 void CCrystalTextView::
5260 OnGoBookmark (UINT nCmdID)
5262 int nBookmarkID = nCmdID - ID_EDIT_GO_BOOKMARK0;
5263 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5264 if (m_pTextBuffer != nullptr)
5266 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5269 CPoint pt (0, nLine);
5270 ASSERT_VALIDTEXTPOS (pt);
5272 SetSelection (pt, pt);
5279 void CCrystalTextView::
5282 if (m_pTextBuffer != nullptr)
5284 for (int nBookmarkID = 0; nBookmarkID <= 9; nBookmarkID++)
5286 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5289 m_pTextBuffer->SetLineFlag (nLine, LF_BOOKMARK (nBookmarkID), false);
5296 void CCrystalTextView::
5299 m_bCursorHidden = false;
5303 void CCrystalTextView::
5306 m_bCursorHidden = true;
5310 void CCrystalTextView::
5311 OnDropSource (DROPEFFECT de)
5313 ASSERT (de == DROPEFFECT_COPY);
5316 HGLOBAL CCrystalTextView::
5319 PrepareSelBounds ();
5320 if (m_ptDrawSelStart == m_ptDrawSelEnd)
5324 GetText (m_ptDrawSelStart, m_ptDrawSelEnd, text);
5325 int cchText = text.GetLength();
5326 SIZE_T cbData = (cchText + 1) * sizeof(TCHAR);
5327 HGLOBAL hData =::GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, cbData);
5328 if (hData == nullptr)
5331 LPTSTR pszData = (LPTSTR)::GlobalLock (hData);
5332 if (pszData == nullptr)
5334 ::GlobalFree(hData);
5337 memcpy (pszData, text, cbData);
5338 ::GlobalUnlock (hData);
5340 m_ptDraggedTextBegin = m_ptDrawSelStart;
5341 m_ptDraggedTextEnd = m_ptDrawSelEnd;
5345 static const TCHAR *memstr(const TCHAR *str1, size_t str1len, const TCHAR *str2, size_t str2len)
5347 ASSERT(str1 && str2 && str2len > 0);
5348 for (const TCHAR *p = str1; p < str1 + str1len; ++p)
5352 if (memcmp(p, str2, str2len * sizeof(TCHAR)) == 0)
5359 static const TCHAR *memistr(const TCHAR *str1, size_t str1len, const TCHAR *str2, size_t str2len)
5361 ASSERT(str1 && str2 && str2len > 0);
5362 for (const TCHAR *p = str1; p < str1 + str1len; ++p)
5364 if (toupper(*p) == toupper(*str2))
5367 for (i = 0; i < str2len; ++i)
5369 if (toupper(p[i]) != toupper(str2[i]))
5380 FindStringHelper (LPCTSTR pszLineBegin, size_t nLineLength, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch)
5382 if (dwFlags & FIND_REGEXP)
5389 if (pszFindWhat[0] == '^' && pszLineBegin != pszFindWhere)
5391 rxnode = RxCompile (pszFindWhat, (dwFlags & FIND_MATCH_CASE) != 0 ? RX_CASE : 0);
5392 if (rxnode && RxExec (rxnode, pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhere, rxmatch))
5394 pos = rxmatch->Open[0];
5395 ASSERT((rxmatch->Close[0] - rxmatch->Open[0]) < INT_MAX);
5396 nLen = static_cast<int>(rxmatch->Close[0] - rxmatch->Open[0]);
5402 ASSERT (pszFindWhere != nullptr);
5403 ASSERT (pszFindWhat != nullptr);
5405 int nLength = (int) _tcslen (pszFindWhat);
5406 LPCTSTR pszFindWhereOrig = pszFindWhere;
5411 if (dwFlags & FIND_MATCH_CASE)
5412 pszPos = memstr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5414 pszPos = memistr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5415 if (pszPos == nullptr)
5417 if ((dwFlags & FIND_WHOLE_WORD) == 0)
5418 return nCur + (int) (pszPos - pszFindWhere);
5419 if (pszPos > pszFindWhereOrig && xisalnum (pszPos[-1]))
5421 nCur += (int) (pszPos - pszFindWhere + 1);
5422 pszFindWhere = pszPos + 1;
5425 if (xisalnum (pszPos[nLength]))
5427 nCur += (int) (pszPos - pszFindWhere + 1);
5428 pszFindWhere = pszPos + 1;
5431 return nCur + (int) (pszPos - pszFindWhere);
5434 //~ ASSERT (false); // Unreachable
5438 * @brief Select text in editor.
5439 * @param [in] ptStartPos Star position for highlight.
5440 * @param [in] nLength Count of characters to highlight.
5441 * @param [in] bCursorToLeft If true cursor is positioned to Left-end of text
5442 * selection, if false cursor is positioned to right-end.
5444 bool CCrystalTextView::
5445 HighlightText (const CPoint & ptStartPos, int nLength,
5446 bool bCursorToLeft /*= false*/)
5448 ASSERT_VALIDTEXTPOS (ptStartPos);
5449 CPoint ptEndPos = ptStartPos;
5450 int nCount = GetLineLength (ptEndPos.y) - ptEndPos.x;
5451 if (nLength <= nCount)
5453 ptEndPos.x += nLength;
5457 while (nLength > nCount)
5459 nLength -= nCount + 1;
5460 nCount = GetLineLength (++ptEndPos.y);
5462 ptEndPos.x = nLength;
5464 ASSERT_VALIDTEXTPOS (m_ptCursorPos); // Probably 'nLength' is bigger than expected...
5466 m_ptCursorPos = bCursorToLeft ? ptStartPos : ptEndPos;
5467 m_ptAnchor = bCursorToLeft ? ptEndPos : ptStartPos;
5468 SetSelection (ptStartPos, ptEndPos);
5471 // Scrolls found text to middle of screen if out-of-screen
5472 int nScreenLines = GetScreenLines();
5473 if (ptStartPos.y < m_nTopLine || ptEndPos.y > m_nTopLine + nScreenLines)
5475 if (ptStartPos.y > nScreenLines / 2)
5476 ScrollToLine(ptStartPos.y - nScreenLines / 2);
5478 ScrollToLine(ptStartPos.y);
5479 UpdateSiblingScrollPos (false);
5481 EnsureVisible (ptStartPos, ptEndPos);
5485 bool CCrystalTextView::
5486 FindText (LPCTSTR pszText, const CPoint & ptStartPos, DWORD dwFlags,
5487 bool bWrapSearch, CPoint * pptFoundPos)
5489 if (m_pMarkers != nullptr)
5491 m_pMarkers->SetMarker(_T("EDITOR_MARKER"), pszText, dwFlags, COLORINDEX_MARKERBKGND0, false);
5492 if (m_pMarkers->GetEnabled())
5493 m_pMarkers->UpdateViews();
5495 int nLineCount = GetLineCount ();
5496 return FindTextInBlock (pszText, ptStartPos, CPoint (0, 0),
5497 CPoint (GetLineLength (nLineCount - 1), nLineCount - 1),
5498 dwFlags, bWrapSearch, pptFoundPos);
5501 int HowManyStr (LPCTSTR s, LPCTSTR m)
5505 const int l = (int) _tcslen (m);
5506 while ((p = _tcsstr (p, m)) != nullptr)
5514 int HowManyStr (LPCTSTR s, TCHAR c)
5518 while ((p = _tcschr (p, c)) != nullptr)
5526 bool CCrystalTextView::
5527 FindTextInBlock (LPCTSTR pszText, const CPoint & ptStartPosition,
5528 const CPoint & ptBlockBegin, const CPoint & ptBlockEnd,
5529 DWORD dwFlags, bool bWrapSearch, CPoint * pptFoundPos)
5531 CPoint ptCurrentPos = ptStartPosition;
5533 ASSERT (pszText != nullptr && _tcslen (pszText) > 0);
5534 ASSERT_VALIDTEXTPOS (ptCurrentPos);
5535 ASSERT_VALIDTEXTPOS (ptBlockBegin);
5536 ASSERT_VALIDTEXTPOS (ptBlockEnd);
5537 ASSERT (ptBlockBegin.y < ptBlockEnd.y || ptBlockBegin.y == ptBlockEnd.y &&
5538 ptBlockBegin.x <= ptBlockEnd.x);
5539 if (ptBlockBegin == ptBlockEnd)
5541 CWaitCursor waitCursor;
5542 if (ptCurrentPos.y < ptBlockBegin.y || ptCurrentPos.y == ptBlockBegin.y &&
5543 ptCurrentPos.x < ptBlockBegin.x)
5544 ptCurrentPos = ptBlockBegin;
5546 CString what = pszText;
5548 if (dwFlags & FIND_REGEXP)
5550 nEolns = HowManyStr (what, _T("\\n"));
5556 if (dwFlags & FIND_DIRECTION_UP)
5558 // Let's check if we deal with whole text.
5559 // At this point, we cannot search *up* in selection
5560 ASSERT (ptBlockBegin.x == 0 && ptBlockBegin.y == 0);
5561 ASSERT (ptBlockEnd.x == GetLineLength (GetLineCount () - 1) &&
5562 ptBlockEnd.y == GetLineCount () - 1);
5564 // Proceed as if we have whole text search.
5567 while (ptCurrentPos.y >= 0)
5571 if (dwFlags & FIND_REGEXP)
5573 for (int i = 0; i <= nEolns && ptCurrentPos.y >= i; i++)
5576 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y - i);
5579 nLineLength = GetLineLength (ptCurrentPos.y - i);
5581 line = _T ('\n') + line;
5585 nLineLength = ptCurrentPos.x != -1 ? ptCurrentPos.x : GetLineLength (ptCurrentPos.y - i);
5587 if (nLineLength > 0)
5589 item.SetString(pszChars, nLineLength);
5593 nLineLength = line.GetLength ();
5594 if (ptCurrentPos.x == -1)
5599 nLineLength = GetLineLength(ptCurrentPos.y);
5600 if (ptCurrentPos.x == -1)
5602 ptCurrentPos.x = nLineLength;
5604 else if( ptCurrentPos.x > nLineLength )
5605 ptCurrentPos.x = nLineLength;
5606 if (ptCurrentPos.x == -1)
5609 line.SetString (GetLineChars (ptCurrentPos.y), ptCurrentPos.x);
5612 ptrdiff_t nFoundPos = -1;
5613 int nMatchLen = what.GetLength();
5614 int nLineLen = line.GetLength();
5618 size_t nPosRel = ::FindStringHelper(line, nLineLen, static_cast<LPCTSTR>(line) + nPos, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5621 nFoundPos = nPos + nPosRel;
5622 nMatchLen = m_nLastFindWhatLen;
5623 nPos += nMatchLen == 0 ? 1 : nMatchLen;
5626 if( nFoundPos != -1 ) // Found text!
5628 ptCurrentPos.x = static_cast<int>(nFoundPos);
5629 *pptFoundPos = ptCurrentPos;
5634 if( ptCurrentPos.y >= 0 )
5635 ptCurrentPos.x = GetLineLength( ptCurrentPos.y );
5638 // Beginning of text reached
5642 // Start again from the end of text
5643 bWrapSearch = false;
5644 ptCurrentPos = CPoint (GetLineLength (GetLineCount () - 1), GetLineCount () - 1);
5651 while (ptCurrentPos.y <= ptBlockEnd.y)
5655 if (dwFlags & FIND_REGEXP)
5657 int nLines = m_pTextBuffer->GetLineCount ();
5658 for (int i = 0; i <= nEolns && ptCurrentPos.y + i < nLines; i++)
5660 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y + i);
5661 nLineLength = GetLineLength (ptCurrentPos.y + i);
5666 if (nLineLength > 0)
5668 int nLineLengthOld = line.GetLength();
5669 memcpy(line.GetBufferSetLength(nLineLengthOld + nLineLength) + nLineLengthOld, pszChars, nLineLength * sizeof(TCHAR));
5672 nLineLength = line.GetLength ();
5676 nLineLength = GetLineLength (ptCurrentPos.y) - ptCurrentPos.x;
5677 if (nLineLength <= 0)
5684 line.SetString(GetLineChars(ptCurrentPos.y), GetLineLength(ptCurrentPos.y));
5687 // Perform search in the line
5688 size_t nPos = ::FindStringHelper (line, line.GetLength (), static_cast<LPCTSTR>(line) + ptCurrentPos.x, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5691 if (m_pszMatched != nullptr)
5693 m_pszMatched = _tcsdup (line);
5696 CString item = line.Left (static_cast<LONG>(nPos));
5697 LPCTSTR current = _tcsrchr (item, _T('\n'));
5702 nEolns = HowManyStr (item, _T('\n'));
5705 ptCurrentPos.y += nEolns;
5706 ptCurrentPos.x = static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5710 ptCurrentPos.x += static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5712 if (ptCurrentPos.x < 0)
5717 ptCurrentPos.x += static_cast<LONG>(nPos);
5719 // Check of the text found is outside the block.
5720 if (ptCurrentPos.y == ptBlockEnd.y && ptCurrentPos.x >= ptBlockEnd.x)
5723 *pptFoundPos = ptCurrentPos;
5728 if (m_pszMatched != nullptr)
5730 m_pszMatched = nullptr;
5733 // Go further, text was not found
5738 // End of text reached
5742 // Start from the beginning
5743 bWrapSearch = false;
5744 ptCurrentPos = ptBlockBegin;
5748 //~ ASSERT (false); // Unreachable
5751 static DWORD ConvertSearchInfosToSearchFlags(const LastSearchInfos *lastSearch)
5753 DWORD dwSearchFlags = 0;
5754 if (lastSearch->m_bMatchCase)
5755 dwSearchFlags |= FIND_MATCH_CASE;
5756 if (lastSearch->m_bWholeWord)
5757 dwSearchFlags |= FIND_WHOLE_WORD;
5758 if (lastSearch->m_bRegExp)
5759 dwSearchFlags |= FIND_REGEXP;
5760 if (lastSearch->m_nDirection == 0)
5761 dwSearchFlags |= FIND_DIRECTION_UP;
5762 if (lastSearch->m_bNoWrap)
5763 dwSearchFlags |= FIND_NO_WRAP;
5764 if (lastSearch->m_bNoClose)
5765 dwSearchFlags |= FIND_NO_CLOSE;
5766 return dwSearchFlags;
5769 static void ConvertSearchFlagsToLastSearchInfos(LastSearchInfos *lastSearch, DWORD dwFlags)
5771 lastSearch->m_bMatchCase = (dwFlags & FIND_MATCH_CASE) != 0;
5772 lastSearch->m_bWholeWord = (dwFlags & FIND_WHOLE_WORD) != 0;
5773 lastSearch->m_bRegExp = (dwFlags & FIND_REGEXP) != 0;
5774 lastSearch->m_nDirection = (dwFlags & FIND_DIRECTION_UP) == 0;
5775 lastSearch->m_bNoWrap = (dwFlags & FIND_NO_WRAP) != 0;
5776 lastSearch->m_bNoClose = (dwFlags & FIND_NO_CLOSE) != 0;
5779 CPoint CCrystalTextView::
5780 GetSearchPos(DWORD dwSearchFlags)
5785 CPoint ptStart, ptEnd;
5786 GetSelection(ptStart, ptEnd);
5787 if( dwSearchFlags & FIND_DIRECTION_UP)
5788 ptSearchPos = ptStart;
5790 ptSearchPos = ptEnd;
5793 ptSearchPos = m_ptCursorPos;
5797 bool CCrystalTextView::
5798 FindText (const LastSearchInfos * lastSearch)
5801 DWORD dwSearchFlags = ConvertSearchInfosToSearchFlags(lastSearch);
5802 if (!FindText (lastSearch->m_sText, GetSearchPos(dwSearchFlags), dwSearchFlags, !lastSearch->m_bNoWrap,
5808 bool bCursorToLeft = (lastSearch->m_nDirection == 0);
5809 HighlightText (ptTextPos, m_nLastFindWhatLen, bCursorToLeft);
5811 // Save search parameters for 'F3' command
5812 m_bLastSearch = true;
5813 if (m_pszLastFindWhat != nullptr)
5814 free (m_pszLastFindWhat);
5815 m_pszLastFindWhat = _tcsdup (lastSearch->m_sText);
5816 m_dwLastSearchFlags = dwSearchFlags;
5818 // Save search parameters to registry
5819 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), m_dwLastSearchFlags));
5824 void CCrystalTextView::
5827 CWinApp *pApp = AfxGetApp ();
5828 ASSERT (pApp != nullptr);
5830 if (m_pFindTextDlg == nullptr)
5831 m_pFindTextDlg = new CFindTextDlg (this);
5833 LastSearchInfos * lastSearch = m_pFindTextDlg->GetLastSearchInfos();
5837 // Get the latest search parameters
5838 ConvertSearchFlagsToLastSearchInfos(lastSearch, m_dwLastSearchFlags);
5839 if (m_pszLastFindWhat != nullptr)
5840 lastSearch->m_sText = m_pszLastFindWhat;
5845 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), &dwFlags))
5847 ConvertSearchFlagsToLastSearchInfos(lastSearch, dwFlags);
5849 m_pFindTextDlg->UseLastSearch ();
5851 // Take the current selection, if any
5854 CPoint ptSelStart, ptSelEnd;
5855 GetSelection (ptSelStart, ptSelEnd);
5856 if (ptSelStart.y == ptSelEnd.y)
5857 GetText (ptSelStart, ptSelEnd, m_pFindTextDlg->m_sText);
5861 CPoint ptCursorPos = GetCursorPos ();
5862 CPoint ptStart = WordToLeft (ptCursorPos);
5863 CPoint ptEnd = WordToRight (ptCursorPos);
5864 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5865 GetText (ptStart, ptEnd, m_pFindTextDlg->m_sText);
5868 // Execute Find dialog
5870 // m_bShowInactiveSelection = true; // FP: removed because I like it
5871 m_pFindTextDlg->UpdateData(FALSE);
5872 m_pFindTextDlg->ShowWindow(SW_SHOW);
5873 // m_bShowInactiveSelection = false; // FP: removed because I like it
5877 void CCrystalTextView::
5880 bool bEnable = m_bLastSearch;
5881 // Show dialog if no last find text
5882 if (m_pszLastFindWhat == nullptr || _tcslen(m_pszLastFindWhat) == 0)
5886 sText = m_pszLastFindWhat;
5889 // If last find-text exists, cut it to first line
5890 bEnable = !sText.IsEmpty ();
5893 int pos = sText.FindOneOf (_T("\r\n"));
5895 sText = sText.Left (pos);
5899 // CTRL-F3 will find selected text..
5900 bool bControlKey = (::GetAsyncKeyState(VK_CONTROL)& 0x8000) != 0;
5901 // CTRL-SHIFT-F3 will find selected text, but opposite direction
5902 bool bShiftKey = (::GetAsyncKeyState(VK_SHIFT)& 0x8000) != 0;
5907 CPoint ptSelStart, ptSelEnd;
5908 GetSelection (ptSelStart, ptSelEnd);
5909 GetText (ptSelStart, ptSelEnd, sText);
5913 CPoint ptCursorPos = GetCursorPos ();
5914 CPoint ptStart = WordToLeft (ptCursorPos);
5915 CPoint ptEnd = WordToRight (ptCursorPos);
5916 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5917 GetText (ptStart, ptEnd, sText);
5919 if (!sText.IsEmpty())
5922 free(m_pszLastFindWhat);
5923 m_pszLastFindWhat = _tcsdup (sText);
5924 m_bLastSearch = true;
5928 m_dwLastSearchFlags |= FIND_DIRECTION_UP;
5930 m_dwLastSearchFlags &= ~FIND_DIRECTION_UP;
5935 // for correct backward search we need some changes:
5936 if (! FindText(sText, GetSearchPos(m_dwLastSearchFlags), m_dwLastSearchFlags,
5937 (m_dwLastSearchFlags & FIND_NO_WRAP) == 0, &ptFoundPos))
5940 prompt.Format (LoadResString(IDS_EDIT_TEXT_NOT_FOUND).c_str(), (LPCTSTR)sText);
5941 AfxMessageBox (prompt, MB_ICONINFORMATION);
5944 HighlightText (ptFoundPos, m_nLastFindWhatLen, (m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0);
5945 m_bMultipleSearch = true; // More search
5948 OnEditFind(); // No previous find, open Find-dialog
5951 void CCrystalTextView::
5952 OnUpdateEditRepeat (CCmdUI * pCmdUI)
5954 pCmdUI->Enable (true);
5957 void CCrystalTextView::
5962 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), &dwFlags))
5965 // Take the current selection, if any
5968 CPoint ptSelStart, ptSelEnd;
5969 GetSelection (ptSelStart, ptSelEnd);
5970 if (ptSelStart.y == ptSelEnd.y)
5971 GetText (ptSelStart, ptSelEnd, sText);
5975 CPoint ptCursorPos = GetCursorPos ();
5976 CPoint ptStart = WordToLeft (ptCursorPos);
5977 CPoint ptEnd = WordToRight (ptCursorPos);
5978 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5979 GetText (ptStart, ptEnd, sText);
5982 CTextMarkerDlg markerDlg(*m_pMarkers, sText, dwFlags);
5984 if (markerDlg.DoModal() == IDOK)
5986 // Save search parameters to registry
5987 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), markerDlg.GetLastSearchFlags()));
5988 m_pMarkers->SaveToRegistry();
5992 void CCrystalTextView::
5995 CWinApp *pApp = AfxGetApp ();
5996 ASSERT (pApp != nullptr);
5998 CPageSetupDialog dlg;
6000 if (!pApp->GetPrinterDeviceDefaults (&pd))
6003 dlg.m_psd.hDevMode = pd.hDevMode;
6004 dlg.m_psd.hDevNames = pd.hDevNames;
6005 dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS|PSD_MARGINS;
6006 dlg.m_psd.rtMargin.left = DEFAULT_PRINT_MARGIN;
6007 dlg.m_psd.rtMargin.right = DEFAULT_PRINT_MARGIN;
6008 dlg.m_psd.rtMargin.top = DEFAULT_PRINT_MARGIN;
6009 dlg.m_psd.rtMargin.bottom = DEFAULT_PRINT_MARGIN;
6011 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
6014 if (reg.LoadNumber (_T ("PageWidth"), &dwTemp))
6015 dlg.m_psd.ptPaperSize.x = dwTemp;
6016 if (reg.LoadNumber (_T ("PageHeight"), &dwTemp))
6017 dlg.m_psd.ptPaperSize.y = dwTemp;
6018 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
6019 dlg.m_psd.rtMargin.left = dwTemp;
6020 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
6021 dlg.m_psd.rtMargin.right = dwTemp;
6022 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
6023 dlg.m_psd.rtMargin.top = dwTemp;
6024 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
6025 dlg.m_psd.rtMargin.bottom = dwTemp;
6027 if (dlg.DoModal () == IDOK)
6030 if (reg1.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
6032 VERIFY (reg1.SaveNumber (_T ("PageWidth"), dlg.m_psd.ptPaperSize.x));
6033 VERIFY (reg1.SaveNumber (_T ("PageHeight"), dlg.m_psd.ptPaperSize.y));
6034 VERIFY (reg1.SaveNumber (_T ("PageLeft"), dlg.m_psd.rtMargin.left));
6035 VERIFY (reg1.SaveNumber (_T ("PageRight"), dlg.m_psd.rtMargin.right));
6036 VERIFY (reg1.SaveNumber (_T ("PageTop"), dlg.m_psd.rtMargin.top));
6037 VERIFY (reg1.SaveNumber (_T ("PageBottom"), dlg.m_psd.rtMargin.bottom));
6039 pApp->SelectPrinter (dlg.m_psd.hDevNames, dlg.m_psd.hDevMode, false);
6044 * @brief Adds/removes bookmark on given line.
6045 * This functions adds bookmark or removes bookmark on given line.
6046 * @param [in] Index (0-based) of line to add/remove bookmark.
6048 void CCrystalTextView::ToggleBookmark(int nLine)
6050 ASSERT(nLine >= 0 && nLine < GetLineCount());
6051 if (m_pTextBuffer != nullptr)
6053 DWORD dwFlags = GetLineFlags (nLine);
6054 DWORD dwMask = LF_BOOKMARKS;
6055 m_pTextBuffer->SetLineFlag (nLine, dwMask, (dwFlags & dwMask) == 0, false);
6056 const int nBookmarkLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARKS);
6057 if (nBookmarkLine >= 0)
6058 m_bBookmarkExist = true;
6060 m_bBookmarkExist = false;
6064 * @brief Called when Toggle Bookmark is selected from the GUI.
6066 void CCrystalTextView::
6069 ToggleBookmark(m_ptCursorPos.y);
6072 void CCrystalTextView::
6075 if (m_pTextBuffer != nullptr)
6077 int nLine = m_pTextBuffer->FindNextBookmarkLine (m_ptCursorPos.y);
6080 CPoint pt (0, nLine);
6081 ASSERT_VALIDTEXTPOS (pt);
6083 SetSelection (pt, pt);
6090 void CCrystalTextView::
6093 if (m_pTextBuffer != nullptr)
6095 int nLine = m_pTextBuffer->FindPrevBookmarkLine (m_ptCursorPos.y);
6098 CPoint pt (0, nLine);
6099 ASSERT_VALIDTEXTPOS (pt);
6101 SetSelection (pt, pt);
6108 void CCrystalTextView::
6109 OnClearAllBookmarks ()
6111 if (m_pTextBuffer != nullptr)
6113 int nLineCount = GetLineCount ();
6114 for (int I = 0; I < nLineCount; I++)
6116 if (m_pTextBuffer->GetLineFlags (I) & LF_BOOKMARKS)
6117 m_pTextBuffer->SetLineFlag (I, LF_BOOKMARKS, false);
6119 m_bBookmarkExist = false;
6123 void CCrystalTextView::
6124 OnUpdateNextBookmark (CCmdUI * pCmdUI)
6126 pCmdUI->Enable (m_bBookmarkExist);
6129 void CCrystalTextView::
6130 OnUpdatePrevBookmark (CCmdUI * pCmdUI)
6132 pCmdUI->Enable (m_bBookmarkExist);
6135 void CCrystalTextView::
6136 OnUpdateClearAllBookmarks (CCmdUI * pCmdUI)
6138 pCmdUI->Enable (m_bBookmarkExist);
6141 void CCrystalTextView::
6142 SetViewTabs (bool bViewTabs)
6144 if (bViewTabs != m_bViewTabs)
6146 m_bViewTabs = bViewTabs;
6147 if (::IsWindow (m_hWnd))
6152 void CCrystalTextView::
6153 SetViewEols (bool bViewEols, bool bDistinguishEols)
6155 if (bViewEols != m_bViewEols || bDistinguishEols != m_bDistinguishEols)
6157 m_bViewEols = bViewEols;
6158 m_bDistinguishEols = bDistinguishEols;
6159 if (::IsWindow (m_hWnd))
6164 void CCrystalTextView::
6165 SetFlags (DWORD dwFlags)
6167 if (m_dwFlags != dwFlags)
6169 m_dwFlags = dwFlags;
6170 if (::IsWindow (m_hWnd))
6175 int CCrystalTextView::
6176 GetTopMarginHeight()
6180 return GetLineHeight();
6184 * @brief Calculate margin area width.
6185 * This function calculates needed margin width. Needed width is (approx.)
6186 * one char-width for bookmark etc markers and rest to linenumbers (if
6187 * visible). If we have linenumbers visible we need to adjust width so that
6188 * biggest number fits.
6189 * @return Margin area width in pixels.
6191 int CCrystalTextView::
6192 GetMarginWidth (CDC *pdc /*= nullptr*/)
6194 int nMarginWidth = 0;
6196 if (m_bViewLineNumbers)
6198 const int nLines = GetLineCount();
6201 for (n = 1; n <= nLines; n *= 10)
6203 nMarginWidth += GetCharWidth () * nNumbers;
6205 nMarginWidth += 2; // Small gap when symbol part disabled
6210 if (pdc == nullptr || !pdc->IsPrinting ())
6211 nMarginWidth += GetMarginIconSize () + 7; // Width for icon markers and some margin
6215 if (pdc == nullptr || !pdc->IsPrinting ())
6216 nMarginWidth += MARGIN_REV_WIDTH; // Space for revision marks
6219 return nMarginWidth;
6222 void CCrystalTextView::CopyProperties (CCrystalTextView *pSource)
6224 m_nTopLine = pSource->m_nTopLine;
6225 m_nTopSubLine = pSource->m_nTopSubLine;
6226 m_bViewTabs = pSource->m_bViewTabs;
6227 m_bViewEols = pSource->m_bViewEols;
6228 m_bDistinguishEols = pSource->m_bDistinguishEols;
6229 m_bTopMargin = pSource->m_bTopMargin;
6230 m_bSelMargin = pSource->m_bSelMargin;
6231 m_bViewLineNumbers = pSource->m_bViewLineNumbers;
6232 m_bSmoothScroll = pSource->m_bSmoothScroll;
6233 m_bWordWrap = pSource->m_bWordWrap;
6234 m_pColors = pSource->m_pColors;
6235 m_pMarkers = pSource->m_pMarkers;
6236 m_bDisableDragAndDrop = pSource->m_bDisableDragAndDrop;
6237 SetTextType(pSource->m_CurSourceDef);
6238 SetFont (pSource->m_lfBaseFont);
6242 // Mouse wheel event. zDelta is in multiples of 120.
6243 // Divide by 40 so each click is 3 lines. I know some
6244 // drivers let you set the ammount of scroll, but I
6245 // don't know how to retrieve this or if they just
6246 // adjust the zDelta you get here.
6247 BOOL CCrystalTextView::
6248 OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
6250 SCROLLINFO si = {0};
6251 si.cbSize = sizeof (si);
6252 si.fMask = SIF_PAGE | SIF_RANGE;
6253 VERIFY (GetScrollInfo (SB_VERT, &si));
6255 int nNewTopSubLine= m_nTopSubLine - zDelta / 40;
6257 if (nNewTopSubLine < 0)
6259 if (nNewTopSubLine > (si.nMax - (signed int)si.nPage + 1))
6260 nNewTopSubLine = si.nMax - si.nPage + 1;
6262 ScrollToSubLine(nNewTopSubLine, true);
6263 UpdateSiblingScrollPos(false);
6265 return CView::OnMouseWheel (nFlags, zDelta, pt);
6268 void CCrystalTextView::
6269 OnMouseHWheel (UINT nFlags, short zDelta, CPoint pt)
6271 SCROLLINFO si = { sizeof(si) };
6272 si.fMask = SIF_POS | SIF_RANGE;
6273 VERIFY (GetScrollInfo (SB_HORZ, &si));
6275 int nCurPos = si.nPos + zDelta / 40;
6276 if (nCurPos < si.nMin)
6278 else if (nCurPos > si.nMax)
6281 ScrollToChar (nCurPos, true);
6283 UpdateSiblingScrollPos (true);
6285 CView::OnMouseHWheel (nFlags, zDelta, pt);
6288 void CCrystalTextView::
6289 OnSourceType (UINT nId)
6291 SetTextType ((CrystalLineParser::TextType) (nId - ID_SOURCE_PLAIN));
6295 void CCrystalTextView::
6296 OnUpdateSourceType (CCmdUI * pCmdUI)
6298 pCmdUI->SetRadio (CrystalLineParser::m_SourceDefs + (pCmdUI->m_nID - ID_SOURCE_PLAIN) == m_CurSourceDef);
6304 static LPCTSTR braces = _T("{}()[]<>");
6305 LPCTSTR pos = _tcschr (braces, c);
6306 return pos != nullptr ? (int) (pos - braces) + 1 : 0;
6310 bracetype (LPCTSTR s)
6314 return bracetype (*s);
6317 void CCrystalTextView::
6320 CPoint ptCursorPos = GetCursorPos ();
6321 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6322 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y), pszEnd = pszText + ptCursorPos.x;
6323 bool bAfter = false;
6325 if (ptCursorPos.x < nLength)
6327 nType = bracetype (*pszEnd);
6332 else if (ptCursorPos.x > 0)
6334 nType = bracetype (pszEnd[-1]);
6338 else if (ptCursorPos.x > 0)
6340 nType = bracetype (pszEnd[-1]);
6345 int nOther, nCount = 0, nComment = 0;
6348 nOther = ((nType - 1) ^ 1) + 1;
6354 nOther = ((nType - 1) ^ 1) + 1;
6358 LPCTSTR pszOpenComment = m_CurSourceDef->opencomment,
6359 pszCloseComment = m_CurSourceDef->closecomment,
6360 pszCommentLine = m_CurSourceDef->commentline, pszTest;
6361 int nOpenComment = (int) _tcslen (pszOpenComment),
6362 nCloseComment = (int) _tcslen (pszCloseComment),
6363 nCommentLine = (int) _tcslen (pszCommentLine);
6368 while (--pszEnd >= pszText)
6370 pszTest = pszEnd - nOpenComment + 1;
6371 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszOpenComment, nOpenComment))
6375 if (--pszEnd < pszText)
6380 pszTest = pszEnd - nCloseComment + 1;
6381 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCloseComment, nCloseComment))
6385 if (--pszEnd < pszText)
6392 pszTest = pszEnd - nCommentLine + 1;
6393 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCommentLine, nCommentLine))
6397 if (bracetype (*pszEnd) == nType)
6401 else if (bracetype (*pszEnd) == nOther)
6405 ptCursorPos.x = (LONG) (pszEnd - pszText);
6408 SetCursorPos (ptCursorPos);
6409 SetSelection (ptCursorPos, ptCursorPos);
6410 SetAnchor (ptCursorPos);
6411 EnsureVisible (ptCursorPos);
6419 ptCursorPos.x = m_pTextBuffer->GetLineLength (--ptCursorPos.y);
6420 pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6421 pszEnd = pszText + ptCursorPos.x;
6429 LPCTSTR pszBegin = pszText;
6431 pszEnd = pszBegin + nLength;
6432 int nLines = m_pTextBuffer->GetLineCount ();
6435 while (pszText < pszEnd)
6437 pszTest = pszText + nCloseComment;
6438 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCloseComment, nCloseComment))
6442 if (pszText > pszEnd)
6447 pszTest = pszText + nOpenComment;
6448 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszOpenComment, nOpenComment))
6452 if (pszText > pszEnd)
6459 pszTest = pszText + nCommentLine;
6460 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCommentLine, nCommentLine))
6464 if (bracetype (*pszText) == nType)
6468 else if (bracetype (*pszText) == nOther)
6472 ptCursorPos.x = (LONG) (pszText - pszBegin);
6475 SetCursorPos (ptCursorPos);
6476 SetSelection (ptCursorPos, ptCursorPos);
6477 SetAnchor (ptCursorPos);
6478 EnsureVisible (ptCursorPos);
6485 if (ptCursorPos.y < nLines)
6488 nLength = m_pTextBuffer->GetLineLength (++ptCursorPos.y);
6489 pszBegin = pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6490 pszEnd = pszBegin + nLength;
6499 void CCrystalTextView::
6500 OnUpdateMatchBrace (CCmdUI * pCmdUI)
6502 CPoint ptCursorPos = GetCursorPos ();
6503 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6504 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y) + ptCursorPos.x;
6505 pCmdUI->Enable (ptCursorPos.x < nLength && (bracetype (*pszText) || ptCursorPos.x > 0 && bracetype (pszText[-1])) || ptCursorPos.x > 0 && bracetype (pszText[-1]));
6508 void CCrystalTextView::
6511 CGotoDlg dlg (this);
6515 void CCrystalTextView::
6516 OnUpdateToggleSourceHeader (CCmdUI * pCmdUI)
6518 pCmdUI->Enable (m_CurSourceDef->type == CrystalLineParser::SRC_C);
6521 void CCrystalTextView::
6522 OnToggleSourceHeader ()
6524 if (m_CurSourceDef->type == CrystalLineParser::SRC_C)
6526 CDocument *pDoc = GetDocument ();
6527 ASSERT (pDoc != nullptr);
6528 CString sFilePath = pDoc->GetPathName (), sOriginalPath = sFilePath;
6529 if (!_tcsicmp (sFilePath.Right (2), _T (".c")))
6531 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ('h');
6533 else if (!_tcsicmp (sFilePath.Right (4), _T (".cpp")))
6535 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('h');
6537 else if (!_tcsicmp (sFilePath.Right (4), _T (".inl")))
6539 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6540 if (!FileExist(sFilePath))
6542 sFilePath = sFilePath + _T ("pp");
6545 else if (!_tcsicmp (sFilePath.Right (4), _T (".hpp")))
6547 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6548 if (!FileExist(sFilePath))
6550 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6551 if (!FileExist(sFilePath))
6553 sFilePath = sFilePath + _T ("pp");
6557 else if (!_tcsicmp (sFilePath.Right (2), _T (".h")))
6559 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ("hpp");
6560 if (!FileExist(sFilePath))
6562 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6563 if (!FileExist(sFilePath))
6565 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6566 if (!FileExist(sFilePath))
6568 sFilePath = sFilePath + _T ("pp");
6573 if (FileExist(sFilePath))
6575 if (!m_bSingle || !pDoc->IsModified () || pDoc->DoSave (sOriginalPath))
6577 AfxGetApp ()->OpenDocumentFile (sFilePath);
6580 m_ptCursorLast.x = m_ptCursorLast.y = 0;
6581 ASSERT_VALIDTEXTPOS (m_ptCursorLast);
6582 CPoint ptCursorPos = m_ptCursorLast;
6583 SetCursorPos (ptCursorPos);
6584 SetSelection (ptCursorPos, ptCursorPos);
6585 SetAnchor (ptCursorPos);
6586 EnsureVisible (ptCursorPos);
6594 void CCrystalTextView::
6595 OnUpdateTopMargin (CCmdUI * pCmdUI)
6597 pCmdUI->SetCheck (m_bTopMargin);
6600 void CCrystalTextView::
6603 ASSERT (m_CurSourceDef != nullptr);
6605 m_CurSourceDef->flags &= ~SRCOPT_TOPMARGIN;
6607 m_CurSourceDef->flags |= SRCOPT_TOPMARGIN;
6608 SetTopMargin (!m_bTopMargin);
6611 void CCrystalTextView::
6612 OnUpdateSelMargin (CCmdUI * pCmdUI)
6614 pCmdUI->SetCheck (m_bSelMargin);
6617 void CCrystalTextView::
6620 ASSERT (m_CurSourceDef != nullptr);
6622 m_CurSourceDef->flags &= ~SRCOPT_SELMARGIN;
6624 m_CurSourceDef->flags |= SRCOPT_SELMARGIN;
6625 SetSelectionMargin (!m_bSelMargin);
6628 void CCrystalTextView::
6629 OnUpdateWordWrap (CCmdUI * pCmdUI)
6631 pCmdUI->SetCheck (m_bWordWrap);
6634 void CCrystalTextView::
6637 ASSERT (m_CurSourceDef != nullptr);
6640 m_CurSourceDef->flags &= ~SRCOPT_WORDWRAP;
6641 SetWordWrapping (false);
6645 m_CurSourceDef->flags |= SRCOPT_WORDWRAP;
6646 SetWordWrapping (true);
6650 void CCrystalTextView::
6654 RedrawWindow (nullptr, nullptr, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ERASENOW);
6657 void CCrystalTextView::
6658 OnToggleColumnSelection ()
6660 m_bRectangularSelection = !m_bRectangularSelection;
6664 void CCrystalTextView::SetRenderingMode(RENDERING_MODE nRenderingMode)
6667 if (nRenderingMode == RENDERING_MODE_GDI)
6668 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
6670 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(nRenderingMode));
6671 m_pCrystalRenderer->SetFont(m_lfBaseFont);
6673 m_nRenderingMode = nRenderingMode;
6677 bool CCrystalTextView::GetWordWrapping() const
6682 void CCrystalTextView::SetWordWrapping( bool bWordWrap )
6684 m_bWordWrap = bWordWrap;
6686 if( IsWindow( m_hWnd ) )
6689 InvalidateScreenRect();
6693 CCrystalParser *CCrystalTextView::SetParser( CCrystalParser *pParser )
6695 CCrystalParser *pOldParser = m_pParser;
6697 m_pParser = pParser;
6699 if( pParser != nullptr )
6700 pParser->m_pTextView = this;
6707 * @brief Return whether a line is visible.
6709 bool CCrystalTextView::GetLineVisible (int nLineIndex) const
6711 return !m_bHideLines || !(GetLineFlags (nLineIndex) & LF_INVISIBLE);
6715 // incremental search imlementation
6716 BOOL CCrystalTextView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO *pHandlerInfo )
6718 // just look for commands
6719 if( nCode != CN_COMMAND || pExtra != nullptr )
6720 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6723 // each command that is not related to incremental search
6724 // ends the incremental search
6725 if( nID == ID_EDIT_FIND_INCREMENTAL_FORWARD ||
6726 nID == ID_EDIT_FIND_INCREMENTAL_BACKWARD ||
6727 nID == ID_EDIT_DELETE_BACK )
6728 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6730 if( nID >= ID_EDIT_FIRST && nID <= ID_EDIT_LAST )
6731 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6733 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6736 void CCrystalTextView::OnChar( wchar_t nChar, UINT nRepCnt, UINT nFlags )
6738 CView::OnChar( nChar, nRepCnt, nFlags );
6740 // we only have to handle character-input, if we are in incremental search mode
6741 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6744 // exit incremental search, when Escape is pressed
6745 if( nChar == VK_ESCAPE )
6747 // if not end incremental search
6748 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6749 SetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6750 SetCursorPos( m_cursorPosBeforeIncrementalSearch );
6751 EnsureVisible( m_cursorPosBeforeIncrementalSearch );
6755 // exit incremental search without destroying selection
6756 if( nChar == VK_RETURN )
6758 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6762 // is the character valid for incremental search?
6763 if( !_istgraph( nChar ) && !(nChar == _T(' ')) && !(nChar == _T('\t')) )
6765 // if not end incremental search
6766 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6770 // if last search was not successfull do not add a new character
6771 if( !m_bIncrementalFound )
6773 MessageBeep( MB_OK );
6777 // add character to incremental search string and search
6778 *m_pstrIncrementalSearchString += (TCHAR) nChar;
6779 OnEditFindIncremental();
6782 LRESULT CCrystalTextView::OnImeStartComposition(WPARAM wParam, LPARAM lParam) /* IME */
6784 UpdateCompositionWindowFont();
6785 UpdateCompositionWindowPos();
6787 return DefWindowProc(WM_IME_STARTCOMPOSITION, wParam, lParam);
6790 void CCrystalTextView::OnEditDeleteBack()
6792 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6795 // remove last character from search string
6796 if( m_pstrIncrementalSearchString->IsEmpty() )
6799 *m_pstrIncrementalSearchString = m_pstrIncrementalSearchString->Left( m_pstrIncrementalSearchString->GetLength() - 1 );
6800 OnEditFindIncremental();
6804 void CCrystalTextView::OnEditFindIncremental( bool bFindNextOccurence /*= false*/ )
6806 // when string is empty, then goto position where the search starts
6807 if( m_pstrIncrementalSearchString->IsEmpty() )
6809 SetSelection( m_incrementalSearchStartPos, m_incrementalSearchStartPos );
6810 SetCursorPos( m_incrementalSearchStartPos );
6811 EnsureVisible( m_incrementalSearchStartPos );
6815 // otherwise search next occurence of search string,
6816 // starting at current cursor position
6817 CPoint matchStart, matchEnd;
6819 // calculate start point for search
6820 if( bFindNextOccurence )
6822 CPoint selStart, selEnd;
6823 GetSelection( selStart, selEnd );
6824 m_incrementalSearchStartPos = (m_bIncrementalSearchBackward)? selStart : selEnd;
6827 m_bIncrementalFound = FindText(
6828 *m_pstrIncrementalSearchString,
6829 m_incrementalSearchStartPos,
6830 m_bIncrementalSearchBackward? FIND_DIRECTION_UP : 0,
6834 if( !m_bIncrementalFound )
6836 MessageBeep( MB_OK );
6840 // select found text and set cursor to end of match
6841 matchEnd = matchStart;
6842 matchEnd.x+= m_pstrIncrementalSearchString->GetLength();
6843 SetSelection( matchStart, matchEnd );
6844 SetCursorPos( matchEnd );
6845 EnsureVisible( matchEnd );
6850 void CCrystalTextView::OnEditFindIncrementalForward()
6852 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6855 if( !m_pstrIncrementalSearchString->IsEmpty() )
6856 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6857 m_pstrIncrementalSearchString->Empty();
6858 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6859 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6861 else if( m_bIncrementalSearchForward )
6863 if( m_pstrIncrementalSearchString->IsEmpty() )
6865 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6866 m_pstrIncrementalSearchStringOld->Empty();
6867 OnEditFindIncremental();
6870 OnEditFindIncremental( true );
6875 m_bIncrementalSearchForward = true;
6876 m_bIncrementalSearchBackward = false;
6877 m_bIncrementalFound = true;
6878 OnEditFindIncremental();
6881 void CCrystalTextView::OnEditFindIncrementalBackward()
6883 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6886 if( !m_pstrIncrementalSearchString->IsEmpty() )
6887 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6888 m_pstrIncrementalSearchString->Empty();
6889 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6890 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6892 else if( m_bIncrementalSearchBackward )
6894 if( m_pstrIncrementalSearchString->IsEmpty() )
6896 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6897 m_pstrIncrementalSearchStringOld->Empty();
6898 OnEditFindIncremental();
6901 OnEditFindIncremental( true );
6906 m_bIncrementalSearchForward = false;
6907 m_bIncrementalSearchBackward = true;
6908 m_bIncrementalFound = true;
6909 OnEditFindIncremental();
6912 void CCrystalTextView::OnUpdateEditFindIncrementalForward(CCmdUI* pCmdUI)
6914 if (m_pTextBuffer != nullptr)
6916 int nLines = m_pTextBuffer->GetLineCount ();
6917 int nChars = m_pTextBuffer->GetLineLength (m_ptCursorPos.y);
6918 pCmdUI->Enable(m_ptCursorPos.y < nLines - 1 || m_ptCursorPos.x < nChars);
6921 pCmdUI->Enable(false);
6924 void CCrystalTextView::OnUpdateEditFindIncrementalBackward(CCmdUI* pCmdUI)
6926 if (m_pTextBuffer != nullptr)
6928 pCmdUI->Enable(m_ptCursorPos.y > 0 || m_ptCursorPos.x > 0);
6931 pCmdUI->Enable(false);
6934 void CCrystalTextView::OnUpdateStatusMessage( CStatusBar *pStatusBar )
6936 static bool bUpdatedAtLastCall = false;
6938 ASSERT( pStatusBar != nullptr && IsWindow( pStatusBar->m_hWnd ) );
6939 if( pStatusBar == nullptr || !IsWindow( pStatusBar->m_hWnd ) )
6942 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6944 if( bUpdatedAtLastCall )
6945 pStatusBar->SetPaneText( 0, LoadResString(AFX_IDS_IDLEMESSAGE).c_str() );
6947 bUpdatedAtLastCall = false;
6954 if( !m_bIncrementalFound )
6955 formatid = IDS_FIND_INCREMENTAL_FAILED;
6956 else if( m_bIncrementalSearchForward )
6957 formatid = IDS_FIND_INCREMENTAL_FORWARD;
6958 else if( m_bIncrementalSearchBackward )
6959 formatid = IDS_FIND_INCREMENTAL_BACKWARD;
6962 strFormat.Format( LoadResString(formatid).c_str(), (LPCTSTR)*m_pstrIncrementalSearchString );
6964 pStatusBar->SetPaneText( 0, strFormat );
6965 bUpdatedAtLastCall = false;
6969 bool CCrystalTextView::IsTextBufferInitialized () const
6971 return m_pTextBuffer && m_pTextBuffer->IsTextBufferInitialized();
6974 CString CCrystalTextView::GetTextBufferEol(int nLine) const
6976 return m_pTextBuffer->GetLineEol(nLine);
6979 void CCrystalTextView::SetMarkersContext(CCrystalTextMarkers * pMarkers)
6981 pMarkers->AddView(this);
6982 m_pMarkers = pMarkers;
6986 int CCrystalTextView::GetCharCellCountUnicodeChar(const wchar_t *pch)
6989 if (!m_bChWidthsCalculated[ch/256])
6991 if (U16_IS_SURROGATE(ch) && U16_IS_SURROGATE_LEAD(ch))
6993 return wcwidth(U16_GET_SUPPLEMENTARY(ch, pch[1]));
6997 int nWidthArray[256];
6998 wchar_t nStart = ch/256*256;
6999 wchar_t nEnd = nStart + 255;
7000 m_pCrystalRenderer->GetCharWidth(nStart, nEnd, nWidthArray);
7001 int nCharWidth = GetCharWidth();
7002 for (int i = 0; i < 256; i++)
7004 if (nCharWidth * 15 < nWidthArray[i] * 10)
7005 m_iChDoubleWidthFlags[(nStart+i)/32] |= 1 << (i % 32);
7008 wchar_t ch2 = static_cast<wchar_t>(nStart + i);
7009 if (wcwidth(ch2) > 1)
7010 m_iChDoubleWidthFlags[(nStart + i) / 32] |= 1 << (i % 32);
7013 m_bChWidthsCalculated[ch / 256] = true;
7016 if (m_iChDoubleWidthFlags[ch / 32] & (1 << (ch % 32)))
7023 /** @brief Reset computed unicode character widths. */
7024 void CCrystalTextView::ResetCharWidths ()
7027 ZeroMemory(m_bChWidthsCalculated, sizeof(m_bChWidthsCalculated));
7028 ZeroMemory(m_iChDoubleWidthFlags, sizeof(m_iChDoubleWidthFlags));
7032 // This function assumes selection is in one line
7033 void CCrystalTextView::EnsureVisible (CPoint ptStart, CPoint ptEnd)
7035 // Scroll vertically
7037 int nSubLineCount = GetSubLineCount();
7038 int nNewTopSubLine = m_nTopSubLine;
7041 CharPosToPoint( ptStart.y, ptStart.x, subLinePos );
7042 subLinePos.y += GetSubLineIndex( ptStart.y );
7044 if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
7045 nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
7046 if( subLinePos.y < nNewTopSubLine )
7047 nNewTopSubLine = subLinePos.y;
7049 if( nNewTopSubLine < 0 )
7051 if( nNewTopSubLine >= nSubLineCount )
7052 nNewTopSubLine = nSubLineCount - 1;
7054 if ( !m_bWordWrap && !m_bHideLines )
7056 // WINMERGE: This line fixes (cursor) slowdown after merges!
7057 // I don't know exactly why, but propably we are setting
7058 // m_nTopLine to zero in ResetView() and are not setting to
7059 // valid value again. Maybe this is a good place to set it?
7060 m_nTopLine = nNewTopSubLine;
7065 GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
7068 if( nNewTopSubLine != m_nTopSubLine )
7070 ScrollToSubLine( nNewTopSubLine );
7072 UpdateSiblingScrollPos( false );
7075 // Scroll horizontally
7077 // we do not need horizontally scrolling, if we wrap the words
7078 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
7081 int nActualPos = CalculateActualOffset (ptStart.y, ptStart.x);
7082 int nNewOffset = m_nOffsetChar;
7083 const int nScreenChars = GetScreenChars ();
7085 if (ptStart == ptEnd)
7087 // Keep 5 chars visible right to cursor
7088 if (nActualPos > nNewOffset + nScreenChars - 5)
7090 // Add 10 chars width space after line
7091 nNewOffset = nActualPos - nScreenChars + 10;
7093 // Keep 5 chars visible left to cursor
7094 if (nActualPos < nNewOffset + 5)
7096 // Jump by 10 char steps, so user sees previous letters too
7097 nNewOffset = nActualPos - 10;
7102 int nActualEndPos = CalculateActualOffset (ptEnd.y, ptEnd.x);
7103 const int nBeginOffset = nActualPos - m_nOffsetChar;
7104 const int nEndOffset = nActualEndPos - m_nOffsetChar;
7105 const int nSelLen = nActualEndPos - nActualPos;
7107 // Selection fits to screen, scroll whole selection visible
7108 if (nSelLen < nScreenChars)
7110 // Begin of selection not visible
7111 if (nBeginOffset > nScreenChars)
7113 // Scroll so that there is max 5 chars margin at end
7114 if (nScreenChars - nSelLen > 5)
7115 nNewOffset = nActualPos + 5 - nScreenChars + nSelLen;
7117 nNewOffset = nActualPos - 5;
7119 else if (nBeginOffset < 0)
7121 // Scroll so that there is max 5 chars margin at begin
7122 if (nScreenChars - nSelLen >= 5)
7123 nNewOffset = nActualPos - 5;
7125 nNewOffset = nActualPos - 5 - nScreenChars + nSelLen;
7127 // End of selection not visible
7128 else if (nEndOffset > nScreenChars ||
7131 nNewOffset = nActualPos - 5;
7134 else // Selection does not fit screen so scroll to begin of selection
7136 nNewOffset = nActualPos - 5;
7140 // Horiz scroll limit to longest line + one screenwidth
7141 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
7142 if (nNewOffset >= nMaxLineLen + nScreenChars)
7143 nNewOffset = nMaxLineLen + nScreenChars - 1;
7147 if (m_nOffsetChar != nNewOffset)
7149 ScrollToChar (nNewOffset);
7151 UpdateSiblingScrollPos (true);
7155 // Analyze the first line of file to detect its type
7156 // Mainly it works for xml files
7157 bool CCrystalTextView::
7158 SetTextTypeByContent (LPCTSTR pszContent)
7160 RxNode *rxnode = nullptr;
7163 if (::FindStringHelper(pszContent, _tcslen(pszContent), pszContent, _T("^\\s*\\<\\?xml\\s+.+?\\?\\>\\s*$"),
7164 FIND_REGEXP, nLen, rxnode, &rxmatch) == 0)
7168 return SetTextType(CrystalLineParser::SRC_XML);
7175 void CCrystalTextView::
7176 AutoFitColumn (int nColumn)
7178 int nLastColumn = 0;
7179 int nLastColumnWidth = 0;
7180 const int nTabSize = GetTabSize ();
7181 std::vector<int> aColumnWidths;
7182 const int nScreenChars = GetScreenChars ();
7183 const int nMaxColumnWidth = nScreenChars < 1 ? 1 : nScreenChars - 1;
7184 for (auto& pbuf : m_pTextBuffer->GetTextBufferList ())
7186 const TCHAR sep = pbuf->GetFieldDelimiter ();
7187 const int quote = pbuf->GetFieldEnclosure ();
7188 const int nLineCount = pbuf->GetLineCount ();
7189 for (int i = 0; i < nLineCount; ++i)
7191 bool bInQuote = false;
7193 int nColumnWidth = 0;
7194 const TCHAR* pszChars = pbuf->GetLineChars (i);
7195 const size_t nLineLength = pbuf->GetFullLineLength (i);
7196 for (size_t j = 0; j < nLineLength; j += U16_IS_SURROGATE (pszChars[j]) ? 2 : 1)
7198 bool bDelimiterOrNewLine = false;
7199 TCHAR c = pszChars[j];
7201 bInQuote = !bInQuote;
7202 if (!bInQuote && c == sep)
7204 bDelimiterOrNewLine = true;
7207 else if (c == '\r' || c == '\n')
7213 if (j == nLineLength - 1 || pszChars[j + 1] != '\n')
7216 bDelimiterOrNewLine = true;
7221 if (j > 0 && pszChars[j - 1] == '\r')
7225 bDelimiterOrNewLine = true;
7229 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7234 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7236 if (bDelimiterOrNewLine)
7238 if (nColumnWidth > nMaxColumnWidth)
7239 nColumnWidth = nMaxColumnWidth;
7240 if (static_cast<int>(aColumnWidths.size ()) < nColumn2 + 1)
7241 aColumnWidths.resize (nColumn2 + 1, nTabSize);
7242 if (aColumnWidths[nColumn2] < nColumnWidth)
7243 aColumnWidths[nColumn2] = nColumnWidth;
7249 if (nLastColumn < nColumn2)
7251 nLastColumn = nColumn2;
7252 nLastColumnWidth = 0;
7254 if (nLastColumnWidth < nColumnWidth)
7255 nLastColumnWidth = nColumnWidth;
7259 aColumnWidths.resize (nLastColumn + 1, nTabSize);
7260 if (aColumnWidths[nLastColumn] < nLastColumnWidth)
7261 aColumnWidths[nLastColumn] = nLastColumnWidth;
7263 for (size_t nColumn2 = 0; nColumn2 < aColumnWidths.size (); ++nColumn2)
7265 if (nColumn == -1 || nColumn == static_cast<int>(nColumn2))
7266 m_pTextBuffer->SetColumnWidth (static_cast<int>(nColumn2), aColumnWidths[nColumn2]);
7268 m_pTextBuffer->InvalidateColumns ();
7271 CCrystalTextView::TextLayoutMode CCrystalTextView::GetTextLayoutMode () const
7273 if (m_pTextBuffer && m_pTextBuffer->GetTableEditing ())
7274 return m_bWordWrap ? TEXTLAYOUT_TABLE_WORDWRAP : TEXTLAYOUT_TABLE_NOWORDWRAP;
7275 return m_bWordWrap ? TEXTLAYOUT_WORDWRAP : TEXTLAYOUT_NOWORDWRAP;
7278 ////////////////////////////////////////////////////////////////////////////