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;
148 /** @brief Width of icons printed in the margin. */
149 const UINT MARGIN_ICON_WIDTH = 12;
150 /** @brief Height of icons printed in the margin. */
151 const UINT MARGIN_ICON_HEIGHT = 12;
153 /** @brief Color of unsaved line revision mark (dark yellow). */
154 const COLORREF UNSAVED_REVMARK_CLR = RGB(0xD7, 0xD7, 0x00);
155 /** @brief Color of saved line revision mark (green). */
156 const COLORREF SAVED_REVMARK_CLR = RGB(0x00, 0xFF, 0x00);
158 #define SMOOTH_SCROLL_FACTOR 6
160 #define ICON_INDEX_WRAPLINE 15
162 ////////////////////////////////////////////////////////////////////////////
165 LOGFONT CCrystalTextView::m_LogFont;
167 IMPLEMENT_DYNCREATE (CCrystalTextView, CView)
169 HINSTANCE CCrystalTextView::s_hResourceInst = nullptr;
170 CCrystalTextView::RENDERING_MODE CCrystalTextView::s_nRenderingModeDefault = RENDERING_MODE_GDI;
172 static ptrdiff_t FindStringHelper(LPCTSTR pszLineBegin, size_t nLineLength, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch);
174 BEGIN_MESSAGE_MAP (CCrystalTextView, CView)
175 //{{AFX_MSG_MAP(CCrystalTextView)
188 ON_WM_LBUTTONDBLCLK ()
189 ON_COMMAND (ID_EDIT_COPY, OnEditCopy)
190 ON_UPDATE_COMMAND_UI (ID_EDIT_COPY, OnUpdateEditCopy)
191 ON_COMMAND (ID_EDIT_SELECT_ALL, OnEditSelectAll)
193 ON_WM_SYSCOLORCHANGE ()
195 ON_COMMAND (ID_EDIT_FIND, OnEditFind)
196 ON_COMMAND (ID_EDIT_REPEAT, OnEditRepeat)
197 ON_UPDATE_COMMAND_UI (ID_EDIT_REPEAT, OnUpdateEditRepeat)
198 ON_COMMAND (ID_EDIT_MARK, OnEditMark)
201 ON_MESSAGE (WM_IME_STARTCOMPOSITION, OnImeStartComposition) /* IME */
203 ON_COMMAND (ID_EDIT_CHAR_LEFT, OnCharLeft)
204 ON_COMMAND (ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
205 ON_COMMAND (ID_EDIT_CHAR_RIGHT, OnCharRight)
206 ON_COMMAND (ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
207 ON_COMMAND (ID_EDIT_WORD_LEFT, OnWordLeft)
208 ON_COMMAND (ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
209 ON_COMMAND (ID_EDIT_WORD_RIGHT, OnWordRight)
210 ON_COMMAND (ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
211 ON_COMMAND (ID_EDIT_LINE_UP, OnLineUp)
212 ON_COMMAND (ID_EDIT_EXT_LINE_UP, OnExtLineUp)
213 ON_COMMAND (ID_EDIT_LINE_DOWN, OnLineDown)
214 ON_COMMAND (ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
215 ON_COMMAND (ID_EDIT_SCROLL_UP, ScrollUp)
216 ON_COMMAND (ID_EDIT_SCROLL_DOWN, ScrollDown)
217 ON_COMMAND (ID_EDIT_PAGE_UP, OnPageUp)
218 ON_COMMAND (ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
219 ON_COMMAND (ID_EDIT_PAGE_DOWN, OnPageDown)
220 ON_COMMAND (ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
221 ON_COMMAND (ID_EDIT_LINE_END, OnLineEnd)
222 ON_COMMAND (ID_EDIT_EXT_LINE_END, OnExtLineEnd)
223 ON_COMMAND (ID_EDIT_HOME, OnHome)
224 ON_COMMAND (ID_EDIT_EXT_HOME, OnExtHome)
225 ON_COMMAND (ID_EDIT_TEXT_BEGIN, OnTextBegin)
226 ON_COMMAND (ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
227 ON_COMMAND (ID_EDIT_TEXT_END, OnTextEnd)
228 ON_COMMAND (ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
229 // Standard printing commands
230 ON_COMMAND (ID_FILE_PAGE_SETUP, OnFilePageSetup)
231 ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint)
232 ON_COMMAND (ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
233 ON_COMMAND (ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
235 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
236 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
238 ON_COMMAND_RANGE (ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
239 ON_COMMAND_RANGE (ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
240 ON_COMMAND (ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
242 ON_COMMAND (ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
243 ON_COMMAND (ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
244 ON_COMMAND (ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
245 ON_COMMAND (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
246 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
247 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
248 ON_UPDATE_COMMAND_UI (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
249 // Ferdi's source type chnages
250 ON_COMMAND_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnSourceType)
251 ON_UPDATE_COMMAND_UI_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnUpdateSourceType)
252 ON_COMMAND (ID_EDIT_MATCHBRACE, OnMatchBrace)
253 ON_UPDATE_COMMAND_UI (ID_EDIT_MATCHBRACE, OnUpdateMatchBrace)
254 ON_COMMAND (ID_EDIT_GOTO, OnEditGoTo)
255 ON_UPDATE_COMMAND_UI (ID_VIEW_TOGGLE_SRC_HDR, OnUpdateToggleSourceHeader)
256 ON_COMMAND (ID_VIEW_TOGGLE_SRC_HDR, OnToggleSourceHeader)
257 ON_UPDATE_COMMAND_UI (ID_VIEW_TOPMARGIN, OnUpdateTopMargin)
258 ON_COMMAND (ID_VIEW_TOPMARGIN, OnTopMargin)
259 ON_UPDATE_COMMAND_UI (ID_VIEW_SELMARGIN, OnUpdateSelMargin)
260 ON_COMMAND (ID_VIEW_SELMARGIN, OnSelMargin)
261 ON_UPDATE_COMMAND_UI (ID_VIEW_WORDWRAP, OnUpdateWordWrap)
262 ON_COMMAND (ID_VIEW_WORDWRAP, OnWordWrap)
263 ON_COMMAND (ID_FORCE_REDRAW, OnForceRedraw)
265 // incremental search
266 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnEditFindIncrementalForward)
267 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnEditFindIncrementalBackward)
268 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnUpdateEditFindIncrementalForward)
269 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnUpdateEditFindIncrementalBackward)
271 ON_COMMAND (ID_EDIT_TOGGLE_COLUMNSELECTION, OnToggleColumnSelection)
274 #define EXPAND_PRIMITIVE(impl, func) \
275 void CCrystalTextView::On##func() { m_bRectangularSelection = false; impl(false); } \
276 void CCrystalTextView::OnExt##func() { impl(true); }
277 EXPAND_PRIMITIVE (MoveLeft, CharLeft)
278 EXPAND_PRIMITIVE (MoveRight, CharRight)
279 EXPAND_PRIMITIVE (MoveWordLeft, WordLeft)
280 EXPAND_PRIMITIVE (MoveWordRight, WordRight)
281 EXPAND_PRIMITIVE (MoveUp, LineUp)
282 EXPAND_PRIMITIVE (MoveDown, LineDown)
283 EXPAND_PRIMITIVE (MovePgUp, PageUp)
284 EXPAND_PRIMITIVE (MovePgDn, PageDown)
285 EXPAND_PRIMITIVE (MoveHome, Home)
286 EXPAND_PRIMITIVE (MoveEnd, LineEnd)
287 EXPAND_PRIMITIVE (MoveCtrlHome, TextBegin)
288 EXPAND_PRIMITIVE (MoveCtrlEnd, TextEnd)
289 #undef EXPAND_PRIMITIVE
291 /////////////////////////////////////////////////////////////////////////////
292 // CCrystalTextView construction/destruction
294 bool CCrystalTextView::
295 DoSetTextType (CrystalLineParser::TextDefinition *def)
297 m_CurSourceDef = def;
298 SetFlags (def->flags);
301 // EOL is determined from file, tabsize and viewtabs are
302 // global WinMerge settings, selection margin is not needed
303 // and wordwrapping must be false always
305 SetWordWrapping ((def->flags & SRCOPT_WORDWRAP) != false);
306 SetSelectionMargin ((def->flags & SRCOPT_SELMARGIN) != false);
307 SetTabSize (def->tabsize);
308 SetViewTabs ((def->flags & SRCOPT_SHOWTABS) != false);
310 if (def->flags & SRCOPT_EOLNDOS)
314 else if (def->flags & SRCOPT_EOLNUNIX)
318 else if (def->flags & SRCOPT_EOLNMAC)
331 bool CCrystalTextView::
332 SetTextType (LPCTSTR pszExt)
334 m_CurSourceDef = CrystalLineParser::m_SourceDefs;
336 CrystalLineParser::TextDefinition *def = CrystalLineParser::GetTextType (pszExt);
338 return SetTextType (def);
341 bool CCrystalTextView::
342 SetTextType (CrystalLineParser::TextType enuType)
344 CrystalLineParser::TextDefinition *def;
346 m_CurSourceDef = def = CrystalLineParser::m_SourceDefs;
347 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
349 if (def->type == enuType)
351 return SetTextType (def);
357 bool CCrystalTextView::
358 SetTextType (CrystalLineParser::TextDefinition *def)
361 if (m_CurSourceDef != def)
362 return DoSetTextType (def);
368 void CCrystalTextView::
371 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
374 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
376 reg.LoadNumber (_T ("DefaultEncoding"), (DWORD*) &CCrystalTextBuffer::m_nDefaultEncoding);
377 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
380 if (reg1.Open (reg.hKey, def->name, KEY_READ))
382 reg1.LoadString (_T ("Extensions"), def->exts, _countof (def->exts));
383 reg1.LoadNumber (_T ("Flags"), &def->flags);
384 // reg1.LoadNumber (_T ("TabSize"), &def->tabsize);
385 reg1.LoadString (_T ("OpenComment"), def->opencomment, _countof (def->opencomment));
386 reg1.LoadString (_T ("CloseComment"), def->closecomment, _countof (def->closecomment));
387 reg1.LoadString (_T ("CommentLine"), def->commentline, _countof (def->commentline));
388 reg1.LoadNumber (_T ("DefaultEncoding"), &def->encoding);
391 bFontLoaded = reg.LoadBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont));
397 CWindowDC dc (CWnd::GetDesktopWindow ());
398 NONCLIENTMETRICS info;
399 info.cbSize = sizeof(info);
400 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
401 memcpy (&m_LogFont, &info.lfMessageFont, sizeof (LOGFONT));
402 m_LogFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
403 m_LogFont.lfWeight = FW_NORMAL;
404 m_LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
405 m_LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
406 m_LogFont.lfQuality = DEFAULT_QUALITY;
407 m_LogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
408 _tcscpy_s (m_LogFont.lfFaceName, _T ("Courier New"));
412 void CCrystalTextView::
415 CrystalLineParser::TextDefinition *def = CrystalLineParser::m_SourceDefs;
417 if (reg.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
419 VERIFY (reg.SaveNumber (_T ("DefaultEncoding"), (DWORD) CCrystalTextBuffer::m_nDefaultEncoding));
420 for (int i = 0; i < _countof (CrystalLineParser::m_SourceDefs); i++, def++)
423 if (reg1.Create (reg.hKey, def->name, KEY_WRITE))
425 VERIFY (reg1.SaveString (_T ("Extensions"), def->exts));
426 VERIFY (reg1.SaveNumber (_T ("Flags"), def->flags));
427 // VERIFY (reg1.SaveNumber (_T ("TabSize"), def->tabsize));
428 VERIFY (reg1.SaveString (_T ("OpenComment"), def->opencomment));
429 VERIFY (reg1.SaveString (_T ("CloseComment"), def->closecomment));
430 VERIFY (reg1.SaveString (_T ("CommentLine"), def->commentline));
431 VERIFY (reg1.SaveNumber (_T ("DefaultEncoding"), def->encoding));
434 VERIFY (reg.SaveBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont)));
438 CCrystalTextView::CCrystalTextView ()
440 , m_pFindTextDlg(nullptr)
441 , m_CurSourceDef(nullptr)
442 , m_dwLastDblClickTime(0)
444 , m_pszMatched(nullptr)
445 , m_bTopMargin(false)
447 , m_bViewLineNumbers(false)
449 , m_bHideLines(false)
450 , m_bLastSearch(false)
451 , m_bBookmarkExist(false)
452 , m_bSingle(false) // needed to be set in descendat classes
453 , m_bRememberLastPos(false)
455 , m_nLastLineIndexCalculatedSubLineIndex(-1)
457 , m_pTextBuffer(nullptr)
458 , m_pCacheBitmap(nullptr)
459 , m_pszLastFindWhat(nullptr)
460 , m_dwLastSearchFlags(0)
461 , m_bMultipleSearch(false)
462 , m_bCursorHidden(false)
467 , m_bDistinguishEols(false)
470 , m_pMarkers(nullptr)
471 , m_panSubLines(new CArray<int, int>())
472 , m_panSubLineIndexCache(new CArray<int, int>())
473 , m_pstrIncrementalSearchString(new CString)
474 , m_pstrIncrementalSearchStringOld(new CString)
475 , m_ParseCookies(new vector<DWORD>)
476 , m_pnActualLineLength(new vector<int>)
480 , m_lfSavedBaseFont{}
482 , m_pPrintFont(nullptr)
484 , m_bChWidthsCalculated{}
485 , m_iChDoubleWidthFlags{}
487 , m_bPreparingToDrag(false)
488 , m_bDraggingText(false)
489 , m_bDragSelection(false)
490 , m_bWordSelection(false)
491 , m_bLineSelection(false)
492 , m_bRectangularSelection(false)
493 , m_bColumnSelection(false)
495 , m_bOverrideCaret(false)
496 , m_nLastFindWhatLen(0)
498 , m_nPrintLineHeight(0)
499 , m_bPrintFooter(false)
500 , m_bPrintHeader(false)
505 , m_bSmoothScroll(false)
506 , m_bVertScrollBarLocked(false)
507 , m_bHorzScrollBarLocked(false)
508 , m_bShowInactiveSelection(false)
509 , m_bDisableDragAndDrop(false)
510 , m_bIncrementalSearchForward(false)
511 , m_bIncrementalSearchBackward(false)
512 , m_bIncrementalFound(false)
514 , m_nRenderingMode(s_nRenderingModeDefault)
515 , m_pCrystalRendererSaved(nullptr)
516 , m_nColumnResizing(-1)
519 if (m_nRenderingMode == RENDERING_MODE_GDI)
520 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
522 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(m_nRenderingMode));
524 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
527 m_panSubLines->SetSize( 0, 4096 );
528 m_panSubLineIndexCache->SetSize( 0, 4096 );
531 CCrystalTextView::ResetView ();
532 SetTextType (CrystalLineParser::SRC_PLAIN);
535 CCrystalTextView::~CCrystalTextView ()
537 ASSERT (m_hAccel == nullptr);
538 ASSERT (m_pCacheBitmap == nullptr);
539 ASSERT (m_pTextBuffer == nullptr); // Must be correctly detached
541 delete m_pFindTextDlg;
543 free (m_pszLastFindWhat);
544 m_pszLastFindWhat=nullptr;
549 free(m_pszMatched); // Allocated by _tcsdup()
550 m_pszMatched = nullptr;
553 delete m_panSubLines;
554 m_panSubLines = nullptr;
556 delete m_panSubLineIndexCache;
557 m_panSubLineIndexCache = nullptr;
559 delete m_pstrIncrementalSearchString;
560 m_pstrIncrementalSearchString = nullptr;
562 delete m_pstrIncrementalSearchStringOld;
563 m_pstrIncrementalSearchStringOld = nullptr;
566 ASSERT(m_ParseCookies != nullptr);
567 delete m_ParseCookies;
568 m_ParseCookies = nullptr;
569 ASSERT(m_pnActualLineLength != nullptr);
570 delete m_pnActualLineLength;
571 m_pnActualLineLength = nullptr;
572 if (m_pMarkers != nullptr)
573 m_pMarkers->DeleteView(this);
576 BOOL CCrystalTextView::
577 PreCreateWindow (CREATESTRUCT & cs)
579 CWnd *pParentWnd = CWnd::FromHandlePermanent (cs.hwndParent);
580 if (pParentWnd == nullptr || !pParentWnd->IsKindOf (RUNTIME_CLASS (CSplitterWnd)))
582 // View must always create its own scrollbars,
583 // if only it's not used within splitter
585 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
586 // we do not need a horizontal scroll bar, if we wrap the lines
587 cs.style|= WS_VSCROLL;
589 cs.style |= (WS_HSCROLL | WS_VSCROLL);
591 cs.style |= (WS_HSCROLL | WS_VSCROLL);
595 cs.lpszClass = AfxRegisterWndClass (CS_DBLCLKS);
596 return CView::PreCreateWindow (cs);
600 /////////////////////////////////////////////////////////////////////////////
601 // CCrystalTextView drawing
603 void CCrystalTextView::
604 GetSelection (CPoint & ptStart, CPoint & ptEnd)
607 ptStart = m_ptDrawSelStart;
608 ptEnd = m_ptDrawSelEnd;
611 bool CCrystalTextView::
612 GetColumnSelection (int nLineIndex, int & nSelBegin, int & nSelEnd)
614 int nSelTop, nSelBottom;
615 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
617 nSelTop = m_ptDrawSelStart.y;
618 nSelBottom = m_ptDrawSelEnd.y;
622 nSelTop = m_ptDrawSelEnd.y;
623 nSelBottom = m_ptDrawSelStart.y;
626 if (nSelTop > nLineIndex || nLineIndex > nSelBottom)
634 int nStartCharPos = CalculateActualOffset (m_ptDrawSelStart.y, m_ptDrawSelStart.x, true);
635 int nEndCharPos = CalculateActualOffset (m_ptDrawSelEnd.y, m_ptDrawSelEnd.x, true);
636 int nLeftCharPos, nRightCharPos;
637 if (nStartCharPos > nEndCharPos)
639 nLeftCharPos = nEndCharPos;
640 nRightCharPos = nStartCharPos;
644 nLeftCharPos = nStartCharPos;
645 nRightCharPos = nEndCharPos;
647 if (nRightCharPos < m_nIdealCharPos)
648 nRightCharPos = m_nIdealCharPos;
649 nSelBegin = ApproxActualOffset (nLineIndex, nLeftCharPos);
650 nSelEnd = ApproxActualOffset (nLineIndex, nRightCharPos);
655 void CCrystalTextView::
656 GetFullySelectedLines(int & firstLine, int & lastLine)
660 GetSelection(ptStart, ptEnd);
663 firstLine = ptStart.y;
665 firstLine = ptStart.y + 1;
666 if (ptEnd.x == GetLineLength(ptEnd.y))
669 lastLine = ptEnd.y-1;
672 CCrystalTextBuffer *CCrystalTextView::
679 * @brief : Get the line length, for cursor movement
681 * @note : there are at least 4 line lengths :
682 * - number of characters (memory, no EOL)
683 * - number of characters (memory, with EOL)
684 * - number of characters for cursor position (tabs are expanded, no EOL)
685 * - number of displayed characters (tabs are expanded, with EOL)
686 * Corresponding functions :
688 * - GetFullLineLength
689 * - GetLineActualLength
690 * - ExpandChars (returns the line to be displayed as a CString)
692 int CCrystalTextView::
693 GetLineActualLength (int nLineIndex)
695 const int nLineCount = GetLineCount ();
696 ASSERT (nLineCount > 0);
697 ASSERT (nLineIndex >= 0 && nLineIndex < nLineCount);
698 if (!m_pnActualLineLength->size())
700 m_pnActualLineLength->assign(nLineCount, -1);
703 if ((*m_pnActualLineLength)[nLineIndex] != - 1)
704 return (*m_pnActualLineLength)[nLineIndex];
706 // Actual line length is not determined yet, let's calculate a little
707 int nActualLength = 0;
708 int nLength = GetLineLength (nLineIndex);
711 LPCTSTR pszChars = GetLineChars (nLineIndex);
712 const int nTabSize = GetTabSize ();
713 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
714 switch ( GetTextLayoutMode ())
716 case TEXTLAYOUT_TABLE_NOWORDWRAP:
717 case TEXTLAYOUT_TABLE_WORDWRAP:
719 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
720 int nColumnTotalWidth = 0;
721 bool bInQuote = false;
722 const int sep = m_pTextBuffer->GetFieldDelimiter ();
723 const int quote = m_pTextBuffer->GetFieldEnclosure ();
724 for (int i = 0, nColumn = 0; i < nLength; i = pIterChar->next())
726 TCHAR c = pszChars[i];
727 if (!bInQuote && c == sep)
729 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
730 nActualLength = nColumnTotalWidth;
735 bInQuote = !bInQuote;
739 nActualLength += GetCharCellCountFromChar (pszChars + i);
740 if (nColumn < nColumnCount && nActualLength > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
741 nActualLength = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
748 for (int i = 0; i < nLength; i = pIterChar->next())
750 TCHAR c = pszChars[i];
752 nActualLength += (nTabSize - nActualLength % nTabSize);
754 nActualLength += GetCharCellCountFromChar(pszChars + i);
760 (*m_pnActualLineLength)[nLineIndex] = nActualLength;
761 return nActualLength;
764 void CCrystalTextView::
765 ScrollToChar (int nNewOffsetChar, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
768 // no horizontal scrolling, when word wrapping is enabled
769 if (GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
773 // For now, ignoring bNoSmoothScroll and m_bSmoothScroll
774 if (m_nOffsetChar != nNewOffsetChar)
776 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
777 m_nOffsetChar = nNewOffsetChar;
779 GetClientRect (&rcScroll);
780 rcScroll.left += GetMarginWidth ();
781 ScrollWindow (nScrollChars * GetCharWidth (), 0, &rcScroll, &rcScroll);
784 RecalcHorzScrollBar (true);
789 * @brief Scroll view to given line.
790 * Scrolls view so that given line is first line in the view. We limit
791 * scrolling so that there is only one empty line visible after the last
792 * line at max. So we don't allow user to scroll last line being at top or
793 * even at middle of the screen. This is how many editors behave and I
794 * (Kimmo) think it is good for us too.
795 * @param [in] nNewTopSubLine New top line for view.
796 * @param [in] bNoSmoothScroll if true don't use smooth scrolling.
797 * @param [in] bTrackScrollBar if true scrollbar is updated after scroll.
799 void CCrystalTextView::ScrollToSubLine( int nNewTopSubLine,
800 bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
802 if (m_nTopSubLine != nNewTopSubLine)
805 GetClientRect (&rcScroll);
806 rcScroll.top += GetTopMarginHeight ();
808 if (bNoSmoothScroll || ! m_bSmoothScroll)
810 // Limit scrolling so that we show one empty line at end of file
811 const int nScreenLines = GetScreenLines();
812 const int nLineCount = GetSubLineCount();
813 if (nNewTopSubLine > (nLineCount - nScreenLines))
815 nNewTopSubLine = nLineCount - nScreenLines;
816 if (nNewTopSubLine < 0)
820 const int nScrollLines = m_nTopSubLine - nNewTopSubLine;
821 m_nTopSubLine = nNewTopSubLine;
822 // OnDraw() uses m_nTopLine to determine topline
824 GetLineBySubLine(m_nTopSubLine, m_nTopLine, dummy);
825 ScrollWindow(0, nScrollLines * GetLineHeight(), nullptr, rcScroll);
829 RecalcVertScrollBar(true);
830 RecalcHorzScrollBar();
835 // Do smooth scrolling
836 int nLineHeight = GetLineHeight();
837 if (m_nTopSubLine > nNewTopSubLine)
839 int nIncrement = (m_nTopSubLine - nNewTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
840 while (m_nTopSubLine != nNewTopSubLine)
842 int nTopSubLine = m_nTopSubLine - nIncrement;
843 if (nTopSubLine < nNewTopSubLine)
844 nTopSubLine = nNewTopSubLine;
845 const int nScrollLines = nTopSubLine - m_nTopSubLine;
846 m_nTopSubLine = nTopSubLine;
847 ScrollWindow(0, - nLineHeight * nScrollLines, nullptr, rcScroll);
851 RecalcVertScrollBar(true);
852 RecalcHorzScrollBar();
858 int nIncrement = (nNewTopSubLine - m_nTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
859 while (m_nTopSubLine != nNewTopSubLine)
861 int nTopSubLine = m_nTopSubLine + nIncrement;
862 if (nTopSubLine > nNewTopSubLine)
863 nTopSubLine = nNewTopSubLine;
864 const int nScrollLines = nTopSubLine - m_nTopSubLine;
865 m_nTopSubLine = nTopSubLine;
866 ScrollWindow(0, - nLineHeight * nScrollLines, rcScroll, rcScroll);
870 RecalcVertScrollBar(true);
871 RecalcHorzScrollBar();
877 GetLineBySubLine( m_nTopSubLine, m_nTopLine, nDummy );
881 void CCrystalTextView::
882 ScrollToLine (int nNewTopLine, bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
884 if( m_nTopLine != nNewTopLine )
885 ScrollToSubLine( GetSubLineIndex( nNewTopLine ), bNoSmoothScroll, bTrackScrollBar );
888 /** Append szadd to string str, and advance position curpos */
889 static void AppendStringAdv(CString & str, int & curpos, LPCTSTR szadd)
892 curpos += (int) _tcslen(szadd);
895 /** Append escaped control char to string str, and advance position curpos */
896 static void AppendEscapeAdv(CString & str, int & curpos, TCHAR c)
898 int curlen = str.GetLength();
899 LPTSTR szadd = str.GetBufferSetLength(curlen + 3) + curlen;
900 curpos += wsprintf(szadd, _T("\t%02X"), static_cast<int>(c));
903 int CCrystalTextView::
904 ExpandChars (int nLineIndex, int nOffset, int nCount, CString & line, int nActualOffset)
907 // Request whitespace characters for codepage ACP
908 // because that is the codepage used by ExtTextOut
909 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE_GDI);
916 const int nTabSize = GetTabSize ();
918 LPCTSTR pszChars = GetLineChars(nLineIndex);
920 int nLength = nCount;
922 for (int i = 0; i < nLength; i++)
924 TCHAR c = pszChars[i];
926 nCount += nTabSize - 1;
927 else if (c >= _T('\x00') && c <= _T('\x1F') && c != _T('\r') && c != _T('\n'))
931 // Preallocate line buffer, to avoid reallocations as we add characters
932 line.GetBuffer(nCount + 1); // at least this many characters
933 line.ReleaseBuffer(0);
935 const bool bTableEditing = m_pTextBuffer->GetTableEditing ();
937 if (nCount > nLength || m_bViewTabs || m_bViewEols)
939 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
940 for (int i = 0, next = 0; i < nLength; i = next)
942 next = pIterChar->next();
943 if (pszChars[i] == _T('\t'))
945 int nSpaces = bTableEditing ? 1 : (nTabSize - (nActualOffset + nCurPos) % nTabSize);
948 AppendStringAdv(line, nCurPos, lpspc->c_tab);
958 else if (pszChars[i] == ' ' && m_bViewTabs)
959 AppendStringAdv(line, nCurPos, lpspc->c_space);
960 else if (pszChars[i] == '\r' || pszChars[i] == '\n')
964 if (pszChars[i] == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
966 // Ignore \n after \r
970 if (pszChars[i] == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
972 AppendStringAdv(line, nCurPos, lpspc->c_eol);
975 else if (pszChars[i] == '\r' && m_bDistinguishEols)
976 AppendStringAdv(line, nCurPos, lpspc->c_cr);
977 else if (pszChars[i] == '\n' && m_bDistinguishEols)
978 AppendStringAdv(line, nCurPos, lpspc->c_lf);
981 AppendStringAdv(line, nCurPos, lpspc->c_eol);
986 else if (pszChars[i] >= _T('\x00') && pszChars[i] <= _T('\x1F'))
988 AppendEscapeAdv(line, nCurPos, pszChars[i]);
992 nCurPos += GetCharCellCountFromChar(pszChars + i);
993 for (; i < next; ++i)
1000 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
1001 for (int i1=0, next=0; i1<nLength; i1 = next)
1003 next = pIterChar->next();
1004 nCurPos += GetCharCellCountFromChar(pszChars + i1);
1005 for (; i1 < next; ++i1)
1006 line += pszChars[i1];
1012 int CCrystalTextView::
1013 ExpandCharsTableEditingNoWrap(int nLineIndex, int nOffset, int nCount, CString& line, int nActualOffset)
1015 if (m_pTextBuffer == nullptr || nCount <= 0)
1018 LPCTSTR pszChars = GetLineChars(nLineIndex);
1020 // Request whitespace characters for codepage ACP
1021 // because that is the codepage used by ExtTextOut
1022 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP(), m_nRenderingMode != RENDERING_MODE_GDI);
1024 int nLength = nCount;
1026 int nColumnCount = 0;
1027 int nCurColumn = -1;
1028 int nColumnTotalWidth = 0;
1029 int nColumnBegin = 0;
1030 int nColumnTotalWidthBegin = 0;
1031 const int nLineLength = GetFullLineLength (nLineIndex);
1032 const int nTabSize = GetTabSize ();
1033 const int sep = m_pTextBuffer->GetFieldDelimiter ();
1034 const int quote = m_pTextBuffer->GetFieldEnclosure ();
1035 const int eollen = nLineLength - GetLineLength (nLineIndex);
1036 bool bInQuote = false;
1037 bool bInQuoteBegin = false;
1039 for (int i = 0; i < nLineLength; i++)
1041 TCHAR c = pszChars[i];
1044 nColumnBegin = nColumn;
1045 nColumnTotalWidthBegin = nColumnTotalWidth;
1046 bInQuoteBegin = bInQuote;
1048 if (nLineIndex == m_ptCursorPos.y && i == m_ptCursorPos.x)
1049 nCurColumn = nColumn;
1050 if (!bInQuote && c == sep)
1052 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
1053 nCount = nColumnTotalWidth;
1057 else if (c == quote)
1059 bInQuote = !bInQuote;
1061 else if (c >= '\x00' && c <= '\x1F' && c != '\r' && c != '\n')
1064 nColumnCount = nColumn + 1;
1066 // Preallocate line buffer, to avoid reallocations as we add characters
1067 line.GetBuffer (nCount + 1); // at least this many characters
1068 line.ReleaseBuffer (0);
1069 int nCurPos = nActualOffset;
1071 pszChars += nOffset;
1073 int curColumnTextCellWidth = 0;
1074 bool beforeCursorPos = (nLineIndex == m_ptCursorPos.y && nOffset < m_ptCursorPos.x);
1075 CString curColumnText;
1076 std::vector<std::pair<int, int>> curColumnByteLenCellWidth;
1077 nColumn = nColumnBegin;
1078 nColumnTotalWidth = nColumnTotalWidthBegin;
1079 bInQuote = bInQuoteBegin;
1080 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator (pszChars, nLength);
1081 auto nextColumnDistance = [&](int nCurPos)
1083 return (nColumn == nColumnCount - 1) ? INT_MAX : nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
1085 auto appendChars = [&](int i, int next, int pos, CString& text, int& textwidth)
1087 TCHAR c = pszChars[i];
1088 if ((c == '\r' || c == '\n') && i >= nLineLength - nOffset - eollen)
1092 if (c == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
1094 // Ignore \n after \r
1098 int prevtextwidth = textwidth;
1099 if (c == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
1100 AppendStringAdv (text, textwidth, lpspc->c_eol);
1101 else if (c == '\r' && m_bDistinguishEols)
1102 AppendStringAdv (text, textwidth, lpspc->c_cr);
1103 else if (c == '\n' && m_bDistinguishEols)
1105 if (i == 0 || pszChars[i - 1] != '\r')
1106 AppendStringAdv (text, textwidth, lpspc->c_lf);
1109 AppendStringAdv (text, textwidth, lpspc->c_eol);
1110 if (textwidth - prevtextwidth > 0)
1111 curColumnByteLenCellWidth.push_back ({ textwidth - prevtextwidth, textwidth - prevtextwidth});
1118 if (sep != '\t' || bInQuote)
1121 if (nSpaces > nextColumnDistance (pos))
1122 nSpaces = nextColumnDistance (pos);
1124 if (nSpaces >= 1 && m_bViewTabs)
1126 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1127 AppendStringAdv (text, textwidth, lpspc->c_tab);
1132 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1138 else if (c == ' ' && m_bViewTabs)
1140 curColumnByteLenCellWidth.push_back ({ 1, 1 });
1141 AppendStringAdv (text, textwidth, lpspc->c_space);
1143 else if (c >= '\x00' && c <= '\x1F')
1145 curColumnByteLenCellWidth.push_back ({ 3, 3 });
1146 AppendEscapeAdv (text, textwidth, c);
1147 if (c == '\r' && pszChars[i + 1] == '\n')
1148 AppendEscapeAdv (text, textwidth, pszChars[i + 1]);
1152 int nLen = GetCharCellCountFromChar (pszChars + i);
1153 curColumnByteLenCellWidth.push_back ({ nLen, next - i });
1155 for (; i < next; ++i)
1156 text += pszChars[i];
1158 if (!bInQuote && c == sep)
1160 int nSpaces = nextColumnDistance (pos + 1);
1167 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1171 for (int i = 0, next = 0; i < nLength; i = next)
1173 next = pIterChar->next ();
1174 const TCHAR c = pszChars[i];
1176 bInQuote = !bInQuote;
1177 int nLen = GetCharCellCountFromChar (pszChars + i);
1178 if (nColumn == nCurColumn && beforeCursorPos)
1180 appendChars (i, next, nCurPos + curColumnTextCellWidth, curColumnText, curColumnTextCellWidth);
1181 if (next + nOffset == m_ptCursorPos.x || next >= nLength)
1183 beforeCursorPos = false;
1184 if (curColumnTextCellWidth > nextColumnDistance (nCurPos))
1186 for (size_t k = 0; k < curColumnByteLenCellWidth.size () && curColumnTextCellWidth > nextColumnDistance (nCurPos); ++k)
1188 curColumnTextCellWidth -= curColumnByteLenCellWidth[k].first;
1189 curColumnText = curColumnText.Mid (curColumnByteLenCellWidth[k].second);
1191 int nSpaces = nextColumnDistance (nCurPos) - curColumnTextCellWidth;
1194 CString spaces (' ', nSpaces);
1195 curColumnText.Insert (0, spaces);
1196 curColumnTextCellWidth = m_pTextBuffer->GetColumnWidth (nColumn);
1199 line += curColumnText;
1200 nCurPos += curColumnTextCellWidth;
1205 if (nLen <= nextColumnDistance (nCurPos))
1207 appendChars (i, next, nCurPos, line, nCurPos);
1208 curColumnByteLenCellWidth.clear ();
1212 int nSpaces = nextColumnDistance (nCurPos);
1219 if (!bInQuote && c == sep)
1221 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn);
1227 return nCurPos - nActualOffset;
1232 * @brief Draw a chunk of text (one color, one line, full or part of line)
1234 * @note In ANSI build, this routine is buggy for multibytes or double-width characters
1236 void CCrystalTextView::
1237 DrawLineHelperImpl (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex,
1238 int nBgColorIndex, COLORREF crText, COLORREF crBkgnd, int nLineIndex, int nOffset, int nCount, int &nActualOffset)
1240 ASSERT (nCount >= 0);
1244 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_NOWORDWRAP)
1245 nActualOffset += ExpandCharsTableEditingNoWrap (nLineIndex, nOffset, nCount, line, nActualOffset);
1247 nActualOffset += ExpandChars (nLineIndex, nOffset, nCount, line, nActualOffset);
1248 const int lineLen = line.GetLength();
1249 const int nCharWidth = GetCharWidth();
1250 const int nCharWidthNarrowed = nCharWidth / 2;
1251 const int nCharWidthWidened = nCharWidth * 2 - nCharWidthNarrowed;
1252 const int nLineHeight = GetLineHeight();
1253 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator((LPCTSTR)line, lineLen);
1255 // i the character index, from 0 to lineLen-1
1258 // Pass if the text begins after the right end of the clipping region
1259 if (ptOrigin.x < rcClip.right)
1261 // Because ExtTextOut is buggy when ptOrigin.x < - 4095 * charWidth
1262 // or when nCount >= 4095
1263 // and because this is not well documented,
1264 // we decide to do the left & right clipping here
1266 // Update the position after the left clipped characters
1267 // stop for i = first visible character, at least partly
1268 const int clipLeft = rcClip.left - nCharWidth * 2;
1269 for ( ; i < lineLen; i = pIterChar->next())
1271 int pnWidthsCurrent = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1272 ptOrigin.x += pnWidthsCurrent;
1273 if (ptOrigin.x >= clipLeft)
1275 ptOrigin.x -= pnWidthsCurrent;
1282 //CSize sz = pdc->GetTextExtent(line, nCount);
1283 //ASSERT(sz.cx == m_nCharWidth * nCount);
1288 // We have to draw some characters
1292 int nWidth = rcClip.right - ptOrigin.x;
1294 // Table of charwidths as CCrystalEditor thinks they are
1295 // Seems that CrystalEditor's and ExtTextOut()'s charwidths aren't
1296 // same with some fonts and text is drawn only partially
1297 // if this table is not used.
1298 vector<int> nWidths(nWidth / nCharWidth * 2 + 2);
1299 bool bdisphex = false;
1300 for (int next = i; i < lineLen && nSumWidth < nWidth ; i = next)
1302 if (line[i] == '\t') // Escape sequence leadin?
1305 // Substitute a space narrowed to half the width of a character cell.
1307 size_t idx = i - ibegin;
1308 if (idx >= nWidths.size())
1309 nWidths.resize(nWidths.size() * 2);
1310 nSumWidth += nWidths[idx] = nCharWidthNarrowed;
1311 // 1st hex digit has normal width.
1312 idx = pIterChar->next() - ibegin;
1313 if (idx >= nWidths.size())
1314 nWidths.resize(nWidths.size() * 2);
1315 nSumWidth += nWidths[idx] = nCharWidth;
1316 // 2nd hex digit is padded by half the width of a character cell.
1317 idx = pIterChar->next() - ibegin;
1318 if (idx >= nWidths.size())
1319 nWidths.resize(nWidths.size() * 2);
1320 nSumWidth += nWidths[idx] = nCharWidthWidened;
1324 size_t idx = i - ibegin;
1325 if (idx >= nWidths.size())
1326 nWidths.resize(nWidths.size() * 2);
1327 nSumWidth += nWidths[idx] = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1329 next = pIterChar->next();
1331 int nCount1 = i - ibegin;
1333 if (ptOrigin.x + nSumWidth > rcClip.left)
1335 if (crText == CLR_NONE || nColorIndex & COLORINDEX_APPLYFORCE)
1336 m_pCrystalRenderer->SetTextColor(GetColor(nColorIndex));
1338 m_pCrystalRenderer->SetTextColor(crText);
1339 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1340 m_pCrystalRenderer->SetBkColor(GetColor(nBgColorIndex));
1342 m_pCrystalRenderer->SetBkColor(crBkgnd);
1344 m_pCrystalRenderer->SwitchFont(GetItalic(nColorIndex), GetBold(nColorIndex));
1345 // we are sure to have less than 4095 characters because all the chars are visible
1347 RECT rcTextBlock = {ptOrigin.x, ptOrigin.y, ptOrigin.x + nSumWidth + 2, ptOrigin.y + nLineHeight};
1348 IntersectRect(&rcIntersect, &rcClip, &rcTextBlock);
1349 m_pCrystalRenderer->DrawText(ptOrigin.x, ptOrigin.y, rcIntersect, LPCTSTR(line) + ibegin, nCount1, &nWidths[0]);
1352 // Draw rounded rectangles around control characters
1353 m_pCrystalRenderer->PushAxisAlignedClip(rcClip);
1355 for (int j = 0 ; j < nCount1 ; ++j)
1357 // Assume narrowed space is converted escape sequence leadin.
1358 if (line[ibegin + j] == ' ' && nWidths[j] < nCharWidth)
1360 m_pCrystalRenderer->DrawRoundRectangle(x + 2, ptOrigin.y + 1,
1361 x + 3 * nCharWidth - 2, ptOrigin.y + nLineHeight - 1,
1362 nCharWidth / 2, nLineHeight / 2);
1366 m_pCrystalRenderer->PopAxisAlignedClip();
1370 // Update the final position after the visible characters
1371 ptOrigin.x += nSumWidth;
1375 // Update the final position after the right clipped characters
1376 for ( ; i < lineLen; i = pIterChar->next())
1378 ptOrigin.x += GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1383 void CCrystalTextView::
1384 DrawLineHelper (CPoint & ptOrigin, const CRect & rcClip, int nColorIndex, int nBgColorIndex,
1385 COLORREF crText, COLORREF crBkgnd, int nLineIndex, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos)
1389 if (m_bFocused || m_bShowInactiveSelection)
1391 int nSelBegin = 0, nSelEnd = 0;
1392 if ( !m_bRectangularSelection )
1394 if (m_ptDrawSelStart.y > ptTextPos.y)
1398 else if (m_ptDrawSelStart.y == ptTextPos.y)
1400 nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
1403 if (nSelBegin > nCount)
1406 if (m_ptDrawSelEnd.y > ptTextPos.y)
1410 else if (m_ptDrawSelEnd.y == ptTextPos.y)
1412 nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
1415 if (nSelEnd > nCount)
1421 int nSelLeft, nSelRight;
1422 GetColumnSelection (ptTextPos.y, nSelLeft, nSelRight);
1423 nSelBegin = nSelLeft - ptTextPos.x;
1424 nSelEnd = nSelRight - ptTextPos.x;
1425 if (nSelBegin < 0) nSelBegin = 0;
1426 if (nSelBegin > nCount) nSelBegin = nCount;
1427 if (nSelEnd < 0) nSelEnd = 0;
1428 if (nSelEnd > nCount) nSelEnd = nCount;
1431 ASSERT (nSelBegin >= 0 && nSelBegin <= nCount);
1432 ASSERT (nSelEnd >= 0 && nSelEnd <= nCount);
1433 ASSERT (nSelBegin <= nSelEnd);
1435 // Draw part of the text before selection
1438 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nSelBegin, nActualOffset);
1440 if (nSelBegin < nSelEnd)
1442 DrawLineHelperImpl (ptOrigin, rcClip,
1443 nColorIndex & ~COLORINDEX_APPLYFORCE,
1444 nBgColorIndex & ~COLORINDEX_APPLYFORCE,
1445 GetColor (COLORINDEX_SELTEXT),
1446 GetColor (COLORINDEX_SELBKGND),
1448 nOffset + nSelBegin, nSelEnd - nSelBegin, nActualOffset);
1450 if (nSelEnd < nCount)
1452 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset + nSelEnd, nCount - nSelEnd, nActualOffset);
1457 DrawLineHelperImpl (ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset);
1462 void CCrystalTextView::
1463 GetLineColors (int nLineIndex, COLORREF & crBkgnd,
1464 COLORREF & crText, bool & bDrawWhitespace)
1466 DWORD dwLineFlags = GetLineFlags (nLineIndex);
1467 bDrawWhitespace = true;
1468 crText = RGB (255, 255, 255);
1469 if (dwLineFlags & LF_EXECUTION)
1471 crBkgnd = RGB (0, 128, 0);
1474 if (dwLineFlags & LF_BREAKPOINT)
1476 crBkgnd = RGB (255, 0, 0);
1479 if (dwLineFlags & LF_INVALID_BREAKPOINT)
1481 crBkgnd = RGB (128, 128, 0);
1486 bDrawWhitespace = false;
1489 DWORD CCrystalTextView::
1490 GetParseCookie (int nLineIndex)
1492 const int nLineCount = GetLineCount ();
1493 if (m_ParseCookies->size() == 0)
1495 // must be initialized to invalid value (DWORD) -1
1496 m_ParseCookies->assign(nLineCount, static_cast<DWORD>(-1));
1501 if ((*m_ParseCookies)[nLineIndex] != - 1)
1502 return (*m_ParseCookies)[nLineIndex];
1505 while (L >= 0 && (*m_ParseCookies)[L] == - 1)
1510 while (L <= nLineIndex)
1514 dwCookie = (*m_ParseCookies)[L - 1];
1515 ASSERT (dwCookie != - 1);
1516 (*m_ParseCookies)[L] = ParseLine (dwCookie, GetLineChars(L), GetLineLength(L), nullptr, nBlocks);
1517 ASSERT ((*m_ParseCookies)[L] != - 1);
1521 return (*m_ParseCookies)[nLineIndex];
1524 std::vector<TEXTBLOCK> CCrystalTextView::
1525 GetAdditionalTextBlocks (int nLineIndex)
1531 void CCrystalTextView::WrapLine( int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1533 // There must be a parser attached to this view
1534 if( m_pParser == nullptr )
1537 m_pParser->WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1541 void CCrystalTextView::WrapLineCached(
1542 int nLineIndex, int nMaxLineWidth, std::vector<int> *anBreaks, int &nBreaks )
1544 if( !GetLineVisible (nLineIndex) )
1550 // If the word wrap is not active, there is no breaks in the line
1557 // word wrap is active
1558 if( nLineIndex < m_panSubLines->GetSize() && !anBreaks && (*m_panSubLines)[nLineIndex] > -1 )
1559 // return cached data
1560 nBreaks = (*m_panSubLines)[nLineIndex] - 1;
1563 // recompute line wrap
1565 WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1568 ASSERT( nBreaks > -1 );
1569 m_panSubLines->SetAtGrow( nLineIndex, nBreaks + 1 );
1571 // RecalcVertScrollBar();
1576 void CCrystalTextView::InvalidateLineCache( int nLineIndex1, int nLineIndex2 /*= -1*/ )
1578 // invalidate cached sub line index
1579 InvalidateSubLineIndexCache( nLineIndex1 );
1581 // invalidate cached sub line count
1583 if( nLineIndex2 == -1 && nLineIndex1 < m_panSubLines->GetSize() )
1584 for( int i = nLineIndex1; i < m_panSubLines->GetSize(); i++ )
1585 (*m_panSubLines)[i] = -1;
1588 if( nLineIndex1 > nLineIndex2 )
1590 int nStorage = nLineIndex1;
1591 nLineIndex1 = nLineIndex2;
1592 nLineIndex2 = nStorage;
1595 if( nLineIndex1 >= m_panSubLines->GetSize() )
1598 if( nLineIndex2 >= m_panSubLines->GetSize() )
1599 nLineIndex2 = (int) m_panSubLines->GetUpperBound();
1601 for( int i = nLineIndex1; i <= nLineIndex2; i++ )
1602 if( i >= 0 && i < m_panSubLines->GetSize() )
1603 (*m_panSubLines)[i] = -1;
1608 * @brief Invalidate sub line index cache from the specified index to the end of file.
1609 * @param [in] nLineIndex Index of the first line to invalidate
1611 void CCrystalTextView::InvalidateSubLineIndexCache( int nLineIndex )
1613 if (m_nLastLineIndexCalculatedSubLineIndex > nLineIndex)
1614 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex - 1;
1618 * @brief Invalidate items related screen size.
1620 void CCrystalTextView::InvalidateScreenRect(bool bInvalidateView)
1622 if (m_pCacheBitmap != nullptr)
1624 delete m_pCacheBitmap;
1625 m_pCacheBitmap = nullptr;
1627 m_nScreenChars = -1;
1628 m_nScreenLines = -1;
1629 InvalidateLineCache(0, -1);
1630 if (bInvalidateView)
1633 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
1634 RecalcVertScrollBar ();
1635 RecalcHorzScrollBar ();
1640 void CCrystalTextView::DrawScreenLine( CPoint &ptOrigin, const CRect &rcClip,
1641 const std::vector<TEXTBLOCK>& blocks, int &nActualItem,
1642 COLORREF crText, COLORREF crBkgnd, bool bDrawWhitespace,
1643 int nLineIndex, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos )
1645 CPoint originalOrigin = ptOrigin;
1646 CPoint ptOriginZeroWidthBlock;
1647 CRect frect = rcClip;
1648 const int nLineLength = GetViewableLineLength( ptTextPos.y );
1649 const int nLineHeight = GetLineHeight();
1650 int nBgColorIndexZeorWidthBlock = COLORINDEX_NONE;
1651 bool bPrevZeroWidthBlock = false;
1652 static const int ZEROWIDTHBLOCK_WIDTH = 2;
1654 frect.top = ptOrigin.y;
1655 frect.bottom = frect.top + nLineHeight;
1657 int nBlockSize = static_cast<int>(blocks.size());
1658 ASSERT( nActualItem < nBlockSize );
1660 if( nBlockSize > 0 && nActualItem < nBlockSize - 1 &&
1661 blocks[nActualItem + 1].m_nCharPos >= nOffset &&
1662 blocks[nActualItem + 1].m_nCharPos <= nOffset + nCount )
1664 ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1665 blocks[nActualItem].m_nCharPos <= nLineLength);
1668 for (I = nActualItem; I < blocks.size() - 1 &&
1669 blocks[I + 1].m_nCharPos <= nOffset + nCount; I ++)
1671 const TEXTBLOCK& blk = blocks[I];
1672 ASSERT(blk.m_nCharPos >= 0 && blk.m_nCharPos <= nLineLength);
1674 int nOffsetToUse = (nOffset > blk.m_nCharPos) ?
1675 nOffset : blk.m_nCharPos;
1676 if (blocks[I + 1].m_nCharPos - nOffsetToUse > 0)
1678 int nOldActualOffset = nActualOffset;
1679 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex, crText, crBkgnd, nLineIndex,
1680 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1681 blocks[I + 1].m_nCharPos - nOffsetToUse,
1682 nActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1683 if (bPrevZeroWidthBlock)
1685 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1686 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock, crText, crBkgnd, nLineIndex,
1687 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1688 blocks[I + 1].m_nCharPos - nOffsetToUse,
1689 nOldActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1690 bPrevZeroWidthBlock = false;
1695 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1697 int nBgColorIndex = blk.m_nBgColorIndex;
1698 COLORREF clrBkColor;
1699 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1700 clrBkColor = GetColor(nBgColorIndex);
1702 clrBkColor = crBkgnd;
1703 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1704 m_pCrystalRenderer->SetBkColor(clrBkColor);
1705 m_pCrystalRenderer->FillRectangle(rc);
1706 ptOriginZeroWidthBlock = ptOrigin;
1707 nBgColorIndexZeorWidthBlock = blk.m_nBgColorIndex;
1708 bPrevZeroWidthBlock = true;
1711 if (ptOrigin.x > rcClip.right)
1715 nActualItem = static_cast<int>(I);
1717 const TEXTBLOCK& blk = blocks[nActualItem];
1718 ASSERT(blk.m_nCharPos >= 0 &&
1719 blk.m_nCharPos <= nLineLength);
1721 if (nOffset + nCount - blk.m_nCharPos > 0)
1723 int nOldActualOffset = nActualOffset;
1724 DrawLineHelper(ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex,
1725 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1726 nOffset + nCount - blk.m_nCharPos,
1727 nActualOffset, CPoint(blk.m_nCharPos, ptTextPos.y));
1728 if (bPrevZeroWidthBlock)
1730 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1731 DrawLineHelper(ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock,
1732 crText, crBkgnd, nLineIndex, blk.m_nCharPos,
1733 nOffset + nCount - blk.m_nCharPos,
1734 nOldActualOffset, CPoint(blk.m_nCharPos, ptTextPos.y));
1735 bPrevZeroWidthBlock = false;
1740 if (!bPrevZeroWidthBlock && (blk.m_nCharPos < nOffset + nCount || nOffset + nCount == nLineLength))
1742 int nBgColorIndex = blk.m_nBgColorIndex;
1743 COLORREF clrBkColor;
1744 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1745 clrBkColor = GetColor(nBgColorIndex);
1747 clrBkColor = crBkgnd;
1748 CRect rc(ptOrigin.x, ptOrigin.y, ptOrigin.x + ZEROWIDTHBLOCK_WIDTH, ptOrigin.y + GetLineHeight());
1749 m_pCrystalRenderer->SetBkColor(clrBkColor);
1750 m_pCrystalRenderer->FillRectangle(rc);
1751 bPrevZeroWidthBlock = true;
1758 ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1759 crText, crBkgnd, nLineIndex, nOffset, nCount, nActualOffset, ptTextPos);
1762 // Draw space on the right of the text
1764 frect.left = ptOrigin.x + (bPrevZeroWidthBlock ? ZEROWIDTHBLOCK_WIDTH : 0);
1766 if ((m_bFocused || m_bShowInactiveSelection)
1767 && !m_bRectangularSelection
1768 && IsInsideSelBlock(CPoint(nLineLength, ptTextPos.y))
1769 && (nOffset + nCount) == nLineLength )
1771 if (frect.left >= rcClip.left)
1773 const int nCharWidth = GetCharWidth();
1774 CRect rc(frect.left, frect.top, frect.left + nCharWidth, frect.bottom);
1775 m_pCrystalRenderer->SetBkColor(GetColor(COLORINDEX_SELBKGND));
1776 m_pCrystalRenderer->FillRectangle(rc);
1777 frect.left += nCharWidth;
1780 if (frect.left < rcClip.left)
1781 frect.left = rcClip.left;
1783 if (frect.right > frect.left)
1785 m_pCrystalRenderer->SetBkColor(bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
1786 m_pCrystalRenderer->FillRectangle(frect);
1789 // set origin to beginning of next screen line
1790 ptOrigin.x = originalOrigin.x;
1791 ptOrigin.y+= nLineHeight;
1795 class IntArray : public CArray<int, int>
1798 explicit IntArray(int len) { SetSize(len); }
1801 std::vector<TEXTBLOCK> CCrystalTextView::
1802 MergeTextBlocks (const std::vector<TEXTBLOCK>& blocks1, const std::vector<TEXTBLOCK>& blocks2) const
1806 std::vector<TEXTBLOCK> mergedBlocks(blocks1.size() + blocks2.size());
1808 for (i = 0, j = 0, k = 0; ; k++)
1810 if (i >= blocks1.size() && j >= blocks2.size())
1814 else if ((i < blocks1.size()&& j < blocks2.size()) &&
1815 (blocks1[i].m_nCharPos == blocks2[j].m_nCharPos))
1817 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1818 if (blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1819 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1821 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1822 if (blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1823 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1825 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1829 else if (j >= blocks2.size() || (i < blocks1.size() &&
1830 blocks1[i].m_nCharPos < blocks2[j].m_nCharPos))
1832 mergedBlocks[k].m_nCharPos = blocks1[i].m_nCharPos;
1833 if (blocks2.size() == 0 || blocks2[j - 1].m_nColorIndex == COLORINDEX_NONE)
1834 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1836 mergedBlocks[k].m_nColorIndex = blocks2[j - 1].m_nColorIndex;
1837 if (blocks2.size() == 0 || blocks2[j - 1].m_nBgColorIndex == COLORINDEX_NONE)
1838 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1840 mergedBlocks[k].m_nBgColorIndex = blocks2[j - 1].m_nBgColorIndex;
1843 else if (i >= blocks1.size() || (j < blocks2.size() && blocks1[i].m_nCharPos > blocks2[j].m_nCharPos))
1845 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1846 if (i > 0 && blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1847 mergedBlocks[k].m_nColorIndex = blocks1[i - 1].m_nColorIndex;
1849 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1850 if (i > 0 && blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1851 mergedBlocks[k].m_nBgColorIndex = blocks1[i - 1].m_nBgColorIndex;
1853 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1859 for (i = 0; i < k; ++i)
1862 (mergedBlocks[i - 1].m_nColorIndex != mergedBlocks[i].m_nColorIndex ||
1863 mergedBlocks[i - 1].m_nBgColorIndex != mergedBlocks[i].m_nBgColorIndex))
1865 mergedBlocks[j] = mergedBlocks[i];
1870 mergedBlocks.resize(j);
1871 return mergedBlocks;
1874 std::vector<TEXTBLOCK>
1875 CCrystalTextView::GetMarkerTextBlocks(int nLineIndex) const
1877 std::vector<TEXTBLOCK> allblocks;
1878 if (!m_pMarkers->GetEnabled())
1881 int nLength = GetViewableLineLength (nLineIndex);
1883 for (const auto& marker : m_pMarkers->GetMarkers())
1885 if (!marker.second.bVisible)
1888 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1889 blocks[0].m_nCharPos = 0;
1890 blocks[0].m_nColorIndex = COLORINDEX_NONE;
1891 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1893 const TCHAR *pszChars = GetLineChars(nLineIndex);
1894 int nLineLength = GetLineLength(nLineIndex);
1895 if (pszChars != nullptr)
1897 RxNode *node = nullptr;
1898 for (const TCHAR *p = pszChars; p < pszChars + nLineLength; )
1902 size_t nPos = ::FindStringHelper(pszChars, nLineLength, p, marker.second.sFindWhat, marker.second.dwFlags | FIND_NO_WRAP, nMatchLen, node, &matches);
1905 if (nLineLength < static_cast<int>((p - pszChars) + nPos) + nMatchLen)
1906 nMatchLen = static_cast<int>(nLineLength - (p - pszChars));
1907 ASSERT(((p - pszChars) + nPos) < INT_MAX);
1908 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos);
1909 blocks[nBlocks].m_nBgColorIndex = marker.second.nBgColorIndex | COLORINDEX_APPLYFORCE;
1910 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1912 ASSERT(((p - pszChars) + nPos + nMatchLen) < INT_MAX);
1913 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos + nMatchLen);
1914 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1915 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1917 p += nPos + (nMatchLen == 0 ? 1 : nMatchLen);
1920 blocks.resize(nBlocks);
1921 allblocks = MergeTextBlocks(allblocks, blocks);
1928 std::vector<TEXTBLOCK>
1929 CCrystalTextView::GetTextBlocks(int nLineIndex)
1931 int nLength = GetViewableLineLength (nLineIndex);
1934 DWORD dwCookie = GetParseCookie(nLineIndex - 1);
1935 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1937 // insert at least one textblock of normal color at the beginning
1938 blocks[0].m_nCharPos = 0;
1939 blocks[0].m_nColorIndex = COLORINDEX_NORMALTEXT;
1940 blocks[0].m_nBgColorIndex = COLORINDEX_BKGND;
1942 (*m_ParseCookies)[nLineIndex] = ParseLine(dwCookie, GetLineChars(nLineIndex), GetLineLength(nLineIndex), blocks.data(), nBlocks);
1943 ASSERT((*m_ParseCookies)[nLineIndex] != -1);
1944 blocks.resize(nBlocks);
1946 return MergeTextBlocks(blocks,
1947 (m_pMarkers && m_pMarkers->GetEnabled() && m_pMarkers->GetMarkers().size() > 0) ?
1948 MergeTextBlocks(GetAdditionalTextBlocks(nLineIndex), GetMarkerTextBlocks(nLineIndex)) :
1949 GetAdditionalTextBlocks(nLineIndex));
1952 void CCrystalTextView::
1953 DrawSingleLine (const CRect & rc, int nLineIndex)
1955 const int nCharWidth = GetCharWidth();
1956 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
1958 if (nLineIndex == -1)
1960 // Draw line beyond the text
1961 m_pCrystalRenderer->FillSolidRectangle (rc, GetColor (COLORINDEX_WHITESPACE));
1965 // Acquire the background color for the current line
1966 bool bDrawWhitespace = false;
1967 COLORREF crBkgnd, crText;
1968 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
1970 int nLength = GetViewableLineLength (nLineIndex);
1972 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
1974 int nActualItem = 0;
1975 int nActualOffset = 0;
1977 std::vector<int> anBreaks(nLength);
1980 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
1982 // Draw the line text
1983 CPoint origin (rc.left - m_nOffsetChar * nCharWidth, rc.top);
1984 if (crBkgnd != CLR_NONE)
1985 m_pCrystalRenderer->SetBkColor (crBkgnd);
1986 if (crText != CLR_NONE)
1987 m_pCrystalRenderer->SetTextColor (crText);
1989 const TextLayoutMode layoutMode = GetTextLayoutMode ();
1990 if (layoutMode == TEXTLAYOUT_TABLE_WORDWRAP)
1992 anBreaks.push_back (-nLength);
1993 CPoint originOrg = origin;
1996 blocks, nActualItem,
1997 crText, crBkgnd, bDrawWhitespace,
1998 nLineIndex, 0, abs(anBreaks[0]),
1999 nActualOffset, CPoint( 0, nLineIndex ) );
2001 for( int i = 0, j = 0; i < static_cast<int> (anBreaks.size ()) - 1; i++, j++ )
2003 if (anBreaks[i] < 0)
2007 CRect frect( origin.x, originOrg.y + (j + 1) * GetLineHeight (),
2008 origin.x + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth, rc.bottom );
2009 if (frect.left < rc.left)
2010 frect.left = rc.left;
2011 if (frect.right > rc.left)
2012 m_pCrystalRenderer->FillSolidRectangle (frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2014 origin.y = originOrg.y;
2015 origin.x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth;
2020 blocks, nActualItem,
2021 crText, crBkgnd, bDrawWhitespace,
2022 nLineIndex, abs(anBreaks[i]), abs(anBreaks[i + 1]) - abs(anBreaks[i]),
2023 nActualOffset, CPoint( abs(anBreaks[i]), nLineIndex ) );
2026 else if (layoutMode == TEXTLAYOUT_WORDWRAP && nBreaks > 0)
2028 // Draw all the screen lines of the wrapped line
2029 ASSERT( anBreaks[0] < nLength );
2031 // draw start of line to first break
2034 blocks, nActualItem,
2035 crText, crBkgnd, bDrawWhitespace,
2036 nLineIndex, 0, anBreaks[0], nActualOffset, CPoint( 0, nLineIndex ) );
2038 // draw from first break to last break
2040 for( i = 0; i < nBreaks - 1; i++ )
2042 ASSERT( anBreaks[i] >= 0 && anBreaks[i] < nLength );
2045 blocks, nActualItem,
2046 crText, crBkgnd, bDrawWhitespace,
2047 nLineIndex, anBreaks[i], anBreaks[i + 1] - anBreaks[i],
2048 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
2051 // draw from last break till end of line
2054 blocks, nActualItem,
2055 crText, crBkgnd, bDrawWhitespace,
2056 nLineIndex, anBreaks[i], nLength - anBreaks[i],
2057 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
2062 blocks, nActualItem,
2063 crText, crBkgnd, bDrawWhitespace,
2064 nLineIndex, 0, nLength, nActualOffset, CPoint(0, nLineIndex));
2066 // Draw empty sublines
2067 int nEmptySubLines = GetEmptySubLines(nLineIndex);
2068 if (nEmptySubLines > 0)
2071 frect.top = frect.bottom - nEmptySubLines * GetLineHeight();
2072 m_pCrystalRenderer->FillSolidRectangle(frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
2077 * @brief Escape special characters
2078 * @param [in] strText The text to escape
2079 * @param [in, out] bLastCharSpace Whether last char processed was white space
2080 * @param [in, out] nNonbreakChars The number of non-break characters in the text
2081 * @param [in] nScreenChars The maximum number of characters to display per line
2082 * @return The escaped text
2085 EscapeHTML (const CString & strText, bool & bLastCharSpace, int & nNonbreakChars, int nScreenChars)
2088 int len = strText.GetLength ();
2089 for (int i = 0; i < len; ++i)
2091 TCHAR ch = strText[i];
2095 strHTML += _T("&");
2096 bLastCharSpace = false;
2100 strHTML += _T("<");
2101 bLastCharSpace = false;
2105 strHTML += _T(">");
2106 bLastCharSpace = false;
2112 strHTML += _T("<wbr>");
2113 bLastCharSpace = false;
2117 if (i == 0 || bLastCharSpace)
2119 strHTML += _T(" ");
2120 bLastCharSpace = false;
2125 bLastCharSpace = true;
2131 bLastCharSpace = false;
2135 if (IsDBCSLeadByte (ch))
2136 strHTML += strText[++i];
2138 if ((nNonbreakChars % nScreenChars) == nScreenChars - 1)
2140 strHTML += _T("<wbr>");
2148 // Make a CString from printf-style args (single call version of CString::Format)
2149 static CString Fmt(LPCTSTR fmt, ...)
2153 va_start(args, fmt);
2154 str.FormatV(fmt, args);
2160 * @brief Return all the styles necessary to render this view as HTML code
2161 * @return The HTML styles
2163 CString CCrystalTextView::
2166 int arColorIndices[] = {
2167 COLORINDEX_NORMALTEXT,
2170 COLORINDEX_FUNCNAME,
2173 COLORINDEX_OPERATOR,
2175 COLORINDEX_PREPROCESSOR,
2176 COLORINDEX_HIGHLIGHTTEXT1,
2177 COLORINDEX_HIGHLIGHTTEXT2,
2181 int arBgColorIndices[] = {
2183 COLORINDEX_SELBKGND,
2184 COLORINDEX_HIGHLIGHTBKGND1,
2185 COLORINDEX_HIGHLIGHTBKGND2,
2186 COLORINDEX_HIGHLIGHTBKGND3,
2187 COLORINDEX_HIGHLIGHTBKGND4,
2191 for (int f = 0; f < sizeof(arColorIndices)/sizeof(int); f++)
2193 int nColorIndex = arColorIndices[f];
2194 for (int b = 0; b < sizeof(arBgColorIndices)/sizeof(int); b++)
2196 int nBgColorIndex = arBgColorIndices[b];
2199 strStyles += Fmt (_T(".sf%db%d {"), nColorIndex, nBgColorIndex);
2200 clr = GetColor (nColorIndex);
2201 strStyles += Fmt (_T("color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2202 clr = GetColor (nBgColorIndex);
2203 strStyles += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2204 if (GetBold (nColorIndex))
2205 strStyles += _T("font-weight: bold; ");
2206 if (GetItalic (nColorIndex))
2207 strStyles += _T("font-style: italic; ");
2208 strStyles += _T("}\n");
2211 COLORREF clrSelMargin = GetColor(COLORINDEX_SELMARGIN);
2212 COLORREF clrNormalText = GetColor(COLORINDEX_NORMALTEXT);
2213 strStyles += Fmt(_T(".ln {text-align: right; word-break: normal; color: #%02x%02x%02x; background-color: #%02x%02x%02x;}\n"),
2214 GetRValue(clrNormalText), GetGValue(clrNormalText), GetBValue(clrNormalText),
2215 GetRValue(clrSelMargin), GetGValue(clrSelMargin), GetBValue(clrSelMargin));
2220 * @brief Return the HTML attribute associated with the specified colors
2221 * @param [in] nColorIndex Index of text color
2222 * @param [in] nBgColorIndex Index of background color
2223 * @param [in] crText Base text color
2224 * @param [in] crBkgnd Base background color
2225 * @return The HTML attribute
2227 CString CCrystalTextView::
2228 GetHTMLAttribute (int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd)
2233 if ((crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE)) &&
2234 (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE)))
2235 return Fmt(_T("class=\"sf%db%d\""), nColorIndex & ~COLORINDEX_APPLYFORCE, nBgColorIndex & ~COLORINDEX_APPLYFORCE);
2237 if (crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE))
2238 clr = GetColor (nColorIndex);
2241 strAttr += Fmt (_T("style=\"color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2243 if (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE))
2244 clr = GetColor (nBgColorIndex);
2247 strAttr += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2249 if (GetBold (nColorIndex))
2250 strAttr += _T("font-weight: bold; ");
2251 if (GetItalic (nColorIndex))
2252 strAttr += _T("font-style: italic; ");
2254 strAttr += _T("\"");
2260 * @brief Retrieve the html version of the line
2261 * @param [in] nLineIndex Index of line in view
2262 * @param [in] pszTag The HTML tag to enclose the line
2263 * @return The html version of the line
2265 CString CCrystalTextView::
2266 GetHTMLLine (int nLineIndex, LPCTSTR pszTag)
2268 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2270 int nLength = GetViewableLineLength (nLineIndex);
2272 // Acquire the background color for the current line
2273 bool bDrawWhitespace = false;
2274 COLORREF crBkgnd, crText;
2275 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2277 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2280 CString strExpanded;
2282 int nNonbreakChars = 0;
2283 bool bLastCharSpace = false;
2284 const int nScreenChars = 40; // GetScreenChars();
2289 strHTML += GetHTMLAttribute (COLORINDEX_NORMALTEXT, COLORINDEX_BKGND, crText, crBkgnd);
2290 strHTML += _T("><code>");
2292 auto MakeSpan = [&](const TEXTBLOCK& block, const CString& strExpanded) {
2294 strHTML += _T("<span ");
2295 strHTML += GetHTMLAttribute (block.m_nColorIndex, block.m_nBgColorIndex, crText, crBkgnd);
2297 strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2298 strHTML += _T("</span>");
2302 for (i = 0; i < blocks.size() - 1; i++)
2304 ExpandChars (nLineIndex, blocks[i].m_nCharPos, blocks[i + 1].m_nCharPos - blocks[i].m_nCharPos, strExpanded, 0);
2305 if (!strExpanded.IsEmpty())
2306 strHTML += MakeSpan(blocks[i], strExpanded);
2308 if (blocks.size() > 0)
2310 ExpandChars (nLineIndex, blocks[i].m_nCharPos, nLength - blocks[i].m_nCharPos, strExpanded, 0);
2311 if (!strExpanded.IsEmpty())
2312 strHTML += MakeSpan(blocks[i], strExpanded);
2313 if (strExpanded.Compare (CString (' ', strExpanded.GetLength())) == 0)
2314 strHTML += _T(" ");
2316 strHTML += _T("</code></");
2323 COLORREF CCrystalTextView::
2324 GetColor (int nColorIndex)
2326 if (m_pColors != nullptr)
2328 nColorIndex &= ~COLORINDEX_APPLYFORCE;
2329 return m_pColors->GetColor(nColorIndex);
2332 return RGB(0, 0, 0);
2335 DWORD CCrystalTextView::
2336 GetLineFlags (int nLineIndex) const
2338 if (m_pTextBuffer == nullptr)
2340 return m_pTextBuffer->GetLineFlags (nLineIndex);
2343 void CCrystalTextView::
2344 DrawTopMargin (const CRect& rect)
2349 auto getColumnName = [](int nColumn) -> CString
2352 for (int i = 0; ; ++i)
2354 TCHAR c = 'A' + (nColumn % 26) - (i == 0 ? 0 : 1);
2355 columnName.Insert (0, c);
2363 m_pCrystalRenderer->SetBkColor (GetColor (COLORINDEX_SELMARGIN));
2364 m_pCrystalRenderer->FillRectangle (rect);
2365 m_pCrystalRenderer->SetTextColor (GetColor (COLORINDEX_NORMALTEXT));
2366 if (m_pTextBuffer->GetTableEditing ())
2368 const int nCharWidth = GetCharWidth ();
2369 const int nMarginWidth = GetMarginWidth ();
2370 CString columnNames;
2371 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth; x < rect.Width (); ++nColumn)
2373 int nColumnWidth = m_pTextBuffer->GetColumnWidth (nColumn);
2374 CString columnName = getColumnName (nColumn);
2375 int columnNameLen = columnName.GetLength ();
2376 if (nColumnWidth < columnNameLen)
2377 columnNames += columnName.Right (nColumnWidth);
2380 int leftspaces = (nColumnWidth - columnNameLen) / 2;
2381 columnNames += CString (' ', leftspaces) + columnName + CString (' ', nColumnWidth - leftspaces - columnNameLen);
2383 x += nColumnWidth * nCharWidth;
2385 columnNames = columnNames.Mid (m_nOffsetChar).Left(rect.Width () / nCharWidth + 1);
2387 std::vector<int> nWidths (columnNames.GetLength (), nCharWidth);
2388 m_pCrystalRenderer->SwitchFont (false, false);
2389 m_pCrystalRenderer->DrawText (nMarginWidth, 0, rect, columnNames, columnNames.GetLength (), nWidths.data ());
2392 m_pCrystalRenderer->DrawRuler (GetMarginWidth (), 0, rect.Width (), rect.Height (), GetCharWidth (), m_nOffsetChar);
2396 * @brief Draw selection margin.
2397 * @param [in] pdc Pointer to draw context.
2398 * @param [in] rect The rectangle to draw.
2399 * @param [in] nLineIndex Index of line in view.
2400 * @param [in] nLineNumber Line number to display. if -1, it's not displayed.
2402 void CCrystalTextView::
2403 DrawMargin (const CRect & rect, int nLineIndex, int nLineNumber)
2405 if (!m_bSelMargin && !m_bViewLineNumbers)
2406 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_BKGND));
2408 m_pCrystalRenderer->SetBkColor(GetColor (COLORINDEX_SELMARGIN));
2409 m_pCrystalRenderer->FillRectangle(rect);
2411 if (m_bViewLineNumbers && nLineNumber > 0)
2413 m_pCrystalRenderer->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
2414 m_pCrystalRenderer->DrawMarginLineNumber(rect.right, rect.top, nLineNumber);
2417 // Draw line revision mark (or background) whenever we have valid lineindex
2418 COLORREF clrRevisionMark = GetColor(COLORINDEX_WHITESPACE);
2419 if (nLineIndex >= 0 && m_pTextBuffer != nullptr)
2421 // get line revision marks color
2422 DWORD dwRevisionNumber = m_pTextBuffer->GetLineRevisionNumber(nLineIndex);
2423 if (dwRevisionNumber > 0)
2425 if (m_pTextBuffer->m_dwRevisionNumberOnSave < dwRevisionNumber)
2426 clrRevisionMark = UNSAVED_REVMARK_CLR;
2428 clrRevisionMark = SAVED_REVMARK_CLR;
2432 // draw line revision marks
2433 CRect rc(rect.right - MARGIN_REV_WIDTH, rect.top, rect.right, rect.bottom);
2434 m_pCrystalRenderer->FillSolidRectangle (rc, clrRevisionMark);
2439 int nImageIndex = -1;
2440 if (nLineIndex >= 0)
2442 DWORD dwLineFlags = GetLineFlags (nLineIndex);
2443 static const DWORD adwFlags[] =
2447 LF_COMPILATION_ERROR,
2459 LF_INVALID_BREAKPOINT
2461 for (int I = 0; I < sizeof (adwFlags) / sizeof (adwFlags[0]); I++)
2463 if ((dwLineFlags & adwFlags[I]) != 0)
2470 if (nImageIndex >= 0)
2472 m_pCrystalRenderer->DrawMarginIcon(
2473 rect.left + 2, rect.top + (GetLineHeight() - CCrystalRenderer::MARGIN_ICON_HEIGHT) / 2, nImageIndex);
2476 // draw wrapped-line-icon
2477 if (nLineNumber > 0)
2480 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2481 for (int i = 0; i < nBreaks; i++)
2483 m_pCrystalRenderer->DrawMarginIcon(
2484 rect.right - CCrystalRenderer::MARGIN_ICON_WIDTH, rect.top + (GetLineHeight()
2485 - CCrystalRenderer::MARGIN_ICON_WIDTH) / 2 + (i+1) * GetLineHeight(), ICON_INDEX_WRAPLINE);
2490 bool CCrystalTextView::
2491 IsInsideSelBlock (CPoint ptTextPos)
2494 ASSERT_VALIDTEXTPOS (ptTextPos);
2495 if (ptTextPos.y < m_ptDrawSelStart.y)
2497 if (ptTextPos.y > m_ptDrawSelEnd.y)
2499 if (m_bRectangularSelection)
2500 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2501 if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
2503 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
2505 if (ptTextPos.y == m_ptDrawSelEnd.y)
2506 return ptTextPos.x < m_ptDrawSelEnd.x;
2507 ASSERT (ptTextPos.y == m_ptDrawSelStart.y);
2508 return ptTextPos.x >= m_ptDrawSelStart.x;
2510 ASSERT (m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
2511 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2514 bool CCrystalTextView::
2515 IsInsideSelection (const CPoint & ptTextPos)
2517 PrepareSelBounds ();
2518 return IsInsideSelBlock (ptTextPos);
2522 * @brief : class the selection extremities in ascending order
2524 * @note : Updates m_ptDrawSelStart and m_ptDrawSelEnd
2525 * This function must be called before reading these values
2527 void CCrystalTextView::
2530 if (m_ptSelStart.y < m_ptSelEnd.y ||
2531 (m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
2533 m_ptDrawSelStart = m_ptSelStart;
2534 m_ptDrawSelEnd = m_ptSelEnd;
2538 m_ptDrawSelStart = m_ptSelEnd;
2539 m_ptDrawSelEnd = m_ptSelStart;
2543 void CCrystalTextView::
2546 // We use the same GDI objects for both drawing and printing.
2547 // So when printing is in progress, we do nothing in this function.
2552 GetClientRect (rcClient);
2554 if (m_pTextBuffer == nullptr)
2556 m_pCrystalRenderer->BindDC(*pdc, rcClient);
2557 m_pCrystalRenderer->BeginDraw();
2558 m_pCrystalRenderer->SetBkColor(GetSysColor(COLOR_WINDOW));
2559 m_pCrystalRenderer->FillRectangle(rcClient);
2560 m_pCrystalRenderer->EndDraw();
2564 const int nLineCount = GetLineCount ();
2565 const int nLineHeight = GetLineHeight ();
2566 PrepareSelBounds ();
2568 // if the private arrays (m_ParseCookies and m_pnActualLineLength)
2569 // are defined, check they are in phase with the text buffer
2570 if (m_ParseCookies->size())
2571 ASSERT(m_ParseCookies->size() == static_cast<size_t>(nLineCount));
2572 if (m_pnActualLineLength->size())
2573 ASSERT(m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
2576 VERIFY (cacheDC.CreateCompatibleDC (pdc));
2577 if (m_pCacheBitmap == nullptr)
2579 m_pCacheBitmap = new CBitmap;
2580 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height()));
2582 CBitmap *pOldBitmap = cacheDC.SelectObject (m_pCacheBitmap);
2585 int nSubLineOffset = GetSubLineIndex( m_nTopLine ) - m_nTopSubLine;
2586 int nCursorY = TextToClient (m_ptCursorPos).y;
2589 CRect rcTopMargin(rcClient.left, rcClient.top, rcClient.right, rcClient.top + GetTopMarginHeight());
2591 rcLine.top = rcTopMargin.bottom + nSubLineOffset * nLineHeight;
2592 CRect rcMargin (rcLine.left, rcLine.top, rcLine.left + GetMarginWidth (), rcLine.top + nLineHeight);
2593 rcLine.left = rcMargin.right;
2595 m_pCrystalRenderer->BindDC(cacheDC, rcClient);
2596 m_pCrystalRenderer->BeginDraw();
2598 int nLastLineBottom = 0;
2599 int nCurrentLine = m_nTopLine;
2600 while (rcLine.top < rcClient.bottom)
2603 if( nCurrentLine < nLineCount /*&& GetLineLength( nCurrentLine ) > nMaxLineChars*/ )
2604 nSubLines = GetSubLines(nCurrentLine);
2606 rcLine.bottom = rcLine.top + nSubLines * nLineHeight;
2607 rcMargin.bottom = rcLine.bottom;
2609 CRect rcMarginAndLine(rcClient.left, rcLine.top, rcClient.right, rcLine.bottom);
2610 if (pdc->RectVisible(rcMarginAndLine))
2612 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
2614 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
2615 DrawSingleLine (rcLine, nCurrentLine);
2616 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
2617 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.bottom-1);
2618 if (m_pTextBuffer->GetTableEditing ())
2619 m_pCrystalRenderer->DrawGridLine (rcMargin.left, rcMargin.bottom-1, rcLine.right, rcMargin.bottom-1);
2620 if (nCurrentLine == m_ptCursorPos.y)
2621 m_pCrystalRenderer->DrawLineCursor (rcMargin.left, rcLine.right,
2622 nCursorY + nLineHeight - 1, 1);
2623 nLastLineBottom = rcMargin.bottom;
2627 DrawMargin (rcMargin, -1, -1);
2628 DrawSingleLine (rcLine, -1);
2633 rcLine.top = rcLine.bottom;
2634 rcMargin.top = rcLine.top;
2637 if (pdc->RectVisible (rcTopMargin))
2638 DrawTopMargin (rcTopMargin);
2640 if (m_pTextBuffer->GetTableEditing ())
2642 int nCharWidth = GetCharWidth ();
2643 int nMarginWidth = GetMarginWidth ();
2644 for (int nColumn = 0, x = nMarginWidth - m_nOffsetChar * nCharWidth;
2645 x < rcClient.Width();
2646 x += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
2648 if (x >= nMarginWidth && nColumn > 0)
2649 m_pCrystalRenderer->DrawGridLine (x, rcClient.top, x, nLastLineBottom);
2653 m_pCrystalRenderer->EndDraw();
2655 VERIFY (pdc->BitBlt (rcClient.left, rcClient.top, rcClient.Width (),
2656 rcClient.Height (), &cacheDC, 0, 0, SRCCOPY));
2658 cacheDC.SelectObject (pOldBitmap);
2659 cacheDC.DeleteDC ();
2662 void CCrystalTextView::
2665 // m_bWordWrap = false;
2671 m_nScreenLines = -1;
2672 m_nScreenChars = -1;
2673 m_nIdealCharPos = -1;
2676 InvalidateLineCache( 0, -1 );
2677 m_ParseCookies->clear();
2678 m_pnActualLineLength->clear();
2679 m_ptCursorPos.x = 0;
2680 m_ptCursorPos.y = 0;
2681 m_ptSelStart = m_ptSelEnd = m_ptCursorPos;
2682 if (m_bDragSelection)
2685 KillTimer (m_nDragSelTimer);
2687 m_bDragSelection = false;
2688 m_bVertScrollBarLocked = false;
2689 m_bHorzScrollBarLocked = false;
2690 if (::IsWindow (m_hWnd))
2692 m_bShowInactiveSelection = true; // FP: reverted because I like it
2693 m_bPrintHeader = false;
2694 m_bPrintFooter = true;
2696 m_bMultipleSearch = false; // More search
2700 void CCrystalTextView::
2703 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
2704 if (m_bFocused && !m_bCursorHidden &&
2705 CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x) >= m_nOffsetChar)
2707 int nCaretHeight = GetLineVisible(m_ptCursorPos.y) ? GetLineHeight () : 0;
2708 if (m_bOverrideCaret) //UPDATE
2709 CreateSolidCaret(GetCharWidth(), nCaretHeight);
2711 CreateSolidCaret (2, nCaretHeight);
2713 SetCaretPos (TextToClient (m_ptCursorPos));
2715 UpdateCompositionWindowPos(); /* IME */
2724 void CCrystalTextView::
2729 int CCrystalTextView::
2732 if (m_pTextBuffer != nullptr)
2734 return m_pTextBuffer->GetCRLFMode ();
2739 void CCrystalTextView::
2740 SetCRLFMode (CRLFSTYLE nCRLFMode)
2742 if (m_pTextBuffer != nullptr)
2744 m_pTextBuffer->SetCRLFMode (nCRLFMode);
2748 int CCrystalTextView::
2751 if (m_pTextBuffer == nullptr)
2754 return m_pTextBuffer->GetTabSize();
2758 void CCrystalTextView::
2759 SetTabSize (int nTabSize)
2761 ASSERT (nTabSize >= 0 && nTabSize <= 64);
2762 if (m_pTextBuffer == nullptr)
2765 if (m_pTextBuffer->GetTabSize() != nTabSize)
2767 m_pTextBuffer->SetTabSize( nTabSize );
2769 m_pnActualLineLength->clear();
2770 RecalcHorzScrollBar ();
2776 void CCrystalTextView::
2779 CSize szCharExt = m_pCrystalRenderer->GetCharWidthHeight();
2780 m_nLineHeight = szCharExt.cy;
2781 if (m_nLineHeight < 1)
2783 m_nCharWidth = szCharExt.cx;
2786 int CCrystalTextView::
2789 if (m_nLineHeight == -1)
2791 return m_nLineHeight;
2794 int CCrystalTextView::GetSubLines( int nLineIndex )
2796 // get a number of lines this wrapped lines contains
2798 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2800 return GetEmptySubLines(nLineIndex) + nBreaks + 1;
2803 int CCrystalTextView::GetEmptySubLines( int nLineIndex )
2808 bool CCrystalTextView::IsEmptySubLineIndex( int nSubLineIndex )
2812 GetLineBySubLine(nSubLineIndex, nLineIndex, dummy);
2813 int nSubLineIndexNextLine = GetSubLineIndex(nLineIndex) + GetSubLines(nLineIndex);
2814 if (nSubLineIndexNextLine - GetEmptySubLines(nLineIndex) <= nSubLineIndex && nSubLineIndex < nSubLineIndexNextLine)
2820 int CCrystalTextView::CharPosToPoint( int nLineIndex, int nCharPos, CPoint &charPoint, int* pnColumn )
2822 // if we do not wrap lines, y is allways 0 and x is equl to nCharPos
2825 charPoint.x = nCharPos;
2830 vector<int> anBreaks(GetLineLength (nLineIndex) + 1);
2833 WrapLineCached (nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
2835 if (GetTextLayoutMode () == TEXTLAYOUT_TABLE_WORDWRAP)
2840 for (; i < anBreaks.size () && abs (anBreaks[i]) <= nCharPos ; ++i)
2842 if (anBreaks[i] < 0)
2850 charPoint.x = (i > 0) ? nCharPos - abs (anBreaks[i - 1]) : nCharPos;
2853 *pnColumn = nColumn;
2855 return (i > 0)? abs (anBreaks[i - 1]) : 0;
2859 int i = (nBreaks <= 0) ? -1 : nBreaks - 1;
2860 for (; i >= 0 && nCharPos < anBreaks[i]; i--)
2863 charPoint.x = (i >= 0)? nCharPos - anBreaks[i] : nCharPos;
2864 charPoint.y = i + 1;
2866 int nReturnVal = (i >= 0)? anBreaks[i] : 0;
2872 /** Does character introduce a multicharacter character? */
2873 static inline bool IsLeadByte(TCHAR ch)
2878 return _getmbcp() && IsDBCSLeadByte(ch);
2882 int CCrystalTextView::CursorPointToCharPos( int nLineIndex, const CPoint &curPoint )
2884 // calculate char pos out of point
2885 const int nLength = GetLineLength( nLineIndex );
2886 const int nScreenChars = GetScreenChars();
2887 LPCTSTR szLine = GetLineChars( nLineIndex );
2890 vector<int> anBreaks(nLength + 1);
2893 WrapLineCached( nLineIndex, nScreenChars, &anBreaks, nBreaks );
2895 // find char pos that matches cursor position
2899 const int nTabSize = GetTabSize();
2901 int nIndex=0, nPrevIndex = 0;
2902 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(szLine, nLength);
2903 switch (GetTextLayoutMode ())
2905 case TEXTLAYOUT_TABLE_NOWORDWRAP:
2907 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
2908 int nColumnTotalWidth = 0;
2910 bool bInQuote = false;
2911 const int sep = m_pTextBuffer->GetFieldDelimiter ();
2912 const int quote = m_pTextBuffer->GetFieldEnclosure ();
2913 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
2916 if (!bInQuote && szLine[nIndex] == sep)
2918 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
2919 nOffset = nColumnTotalWidth - nXPos;
2923 if (szLine[nIndex] == quote)
2924 bInQuote = !bInQuote;
2925 if (szLine[nIndex] == '\t')
2928 nOffset = GetCharCellCountFromChar (szLine + nIndex);
2929 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
2930 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nXPos;
2935 if( nXPos > curPoint.x && nYPos == curPoint.y )
2937 else if( nYPos > curPoint.y )
2939 nIndex = nPrevIndex;
2943 nPrevIndex = nIndex;
2947 case TEXTLAYOUT_TABLE_WORDWRAP:
2951 int nColumnSumWidth = 0;
2952 int nColumnCurPoint = INT_MAX;
2953 if (curPoint.x < m_pTextBuffer->GetColumnWidth (0))
2954 nColumnCurPoint = 0;
2955 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next ())
2957 if (i < static_cast<int>(anBreaks.size ()) && nIndex == abs (anBreaks[i]))
2959 if (anBreaks[i++] < 0)
2962 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
2963 nXPos = nColumnSumWidth;
2964 if (nColumnSumWidth <= curPoint.x && curPoint.x < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
2965 nColumnCurPoint = nColumn;
2969 nXPos = nColumnSumWidth;
2975 if (szLine[nIndex] == '\t')
2978 nOffset = GetCharCellCountFromChar (szLine + nIndex);
2982 if( nXPos > curPoint.x && nYPos == curPoint.y )
2984 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
2986 nIndex = nPrevIndex;
2989 else if ( nYPos == curPoint.y)
2990 nPrevIndex = nIndex;
2992 if (nIndex == nLength && nYPos != curPoint.y)
2993 nIndex = nPrevIndex;
2998 for( nIndex = 0; nIndex < nLength; nIndex = pIterChar->next())
3000 if( nBreaks > 0 && nYPos < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[nYPos] )
3007 if (szLine[nIndex] == _T('\t'))
3008 nOffset = nTabSize - nCurPos % nTabSize;
3010 nOffset = GetCharCellCountFromChar(szLine + nIndex);
3014 if( nXPos > curPoint.x && nYPos == curPoint.y )
3016 else if( nYPos > curPoint.y )
3018 nIndex = nPrevIndex;
3022 nPrevIndex = nIndex;
3029 void CCrystalTextView::SubLineCursorPosToTextPos( const CPoint &subLineCurPos, CPoint &textPos )
3032 int nSubLineOffset, nLine;
3034 GetLineBySubLine( subLineCurPos.y, nLine, nSubLineOffset );
3036 // compute cursor-position
3037 textPos.x = CursorPointToCharPos( nLine, CPoint( subLineCurPos.x, nSubLineOffset ) );
3042 * @brief Calculate last character position in (sub)line.
3043 * @param [in] nLineIndex Linenumber to check.
3044 * @param [in] nSublineOffset Subline index in wrapped line.
3045 * @return Position of the last character.
3047 int CCrystalTextView::SubLineEndToCharPos(int nLineIndex, int nSubLineOffset)
3049 const int nLength = GetLineLength(nLineIndex);
3051 // if word wrapping is disabled, the end is equal to the length of the line -1
3056 vector<int> anBreaks(nLength + 1);
3059 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3061 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3063 int nBreakLast = -1;
3064 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3066 if (anBreaks[i] < 0)
3068 if (j == nSubLineOffset)
3071 if (nBreakLast < static_cast<int>(anBreaks.size ()) - 1)
3072 return abs (anBreaks[nBreakLast + 1]) - 1;
3076 // if there is no break inside the line or the given subline is the last
3077 // one in this line...
3078 if (nBreaks <= 0 || nSubLineOffset == nBreaks)
3083 // compute character position for end of subline
3084 ASSERT(nSubLineOffset >= 0 && nSubLineOffset <= nBreaks);
3086 int nReturnVal = anBreaks[nSubLineOffset] - 1;
3092 * @brief Calculate first character position in (sub)line.
3093 * @param [in] nLineIndex Linenumber to check.
3094 * @param [in] nSublineOffset Subline index in wrapped line.
3095 * @return Position of the first character.
3097 int CCrystalTextView::SubLineHomeToCharPos(int nLineIndex, int nSubLineOffset)
3099 // if word wrapping is disabled, the start is 0
3100 if (!m_bWordWrap || nSubLineOffset == 0)
3104 int nLength = GetLineLength(nLineIndex);
3105 vector<int> anBreaks(nLength + 1);
3108 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks, nBreaks);
3110 if (GetTextLayoutMode() == TEXTLAYOUT_TABLE_WORDWRAP)
3112 for (int i = 0, j = 1; i < static_cast<int>(anBreaks.size ()); ++i, ++j)
3114 if (anBreaks[i] < 0)
3116 if (j == nSubLineOffset)
3117 return abs (anBreaks[i]);
3122 // if there is no break inside the line...
3128 // compute character position for end of subline
3129 ASSERT(nSubLineOffset > 0 && nSubLineOffset <= nBreaks);
3131 int nReturnVal = anBreaks[nSubLineOffset - 1];
3137 int CCrystalTextView::
3140 if (m_nCharWidth == -1)
3142 return m_nCharWidth;
3145 int CCrystalTextView::
3146 GetMaxLineLength (int nTopLine, int nLines)
3148 int nMaxLineLength = 0;
3149 const int nLineCount = (std::min)(nTopLine + nLines, GetLineCount ());
3150 for (int I = nTopLine; I < nLineCount; I++)
3152 int nActualLength = GetLineActualLength (I);
3153 if (nMaxLineLength < nActualLength)
3154 nMaxLineLength = nActualLength;
3156 return nMaxLineLength;
3159 CCrystalTextView *CCrystalTextView::
3160 GetSiblingView (int nRow, int nCol)
3162 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3163 if (pSplitter == nullptr)
3165 CWnd *pWnd = CWnd::FromHandlePermanent (
3166 ::GetDlgItem (pSplitter->m_hWnd, pSplitter->IdFromRowCol (nRow, nCol)));
3167 if (pWnd == nullptr || !pWnd->IsKindOf (RUNTIME_CLASS (CCrystalTextView)))
3169 return static_cast<CCrystalTextView *>(pWnd);
3172 void CCrystalTextView::
3173 GoToLine (int nLine, bool bRelative)
3175 int nLines = m_pTextBuffer->GetLineCount () - 1;
3176 CPoint ptCursorPos = GetCursorPos ();
3179 nLine += ptCursorPos.y;
3191 int nChars = m_pTextBuffer->GetLineLength (nLine);
3196 if (ptCursorPos.x > nChars)
3198 ptCursorPos.x = nChars;
3200 if (ptCursorPos.x >= 0)
3202 ptCursorPos.y = nLine;
3203 ASSERT_VALIDTEXTPOS (ptCursorPos);
3204 SetAnchor (ptCursorPos);
3205 SetSelection (ptCursorPos, ptCursorPos);
3206 SetCursorPos (ptCursorPos);
3207 EnsureVisible (ptCursorPos);
3212 void CCrystalTextView::
3215 CView::OnInitialUpdate ();
3216 CString sDoc = GetDocument ()->GetPathName (), sExt = GetExt (sDoc);
3217 if (!sExt.IsEmpty())
3219 AttachToBuffer (nullptr);
3221 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
3222 if (pSplitter != nullptr)
3224 // See CSplitterWnd::IdFromRowCol() implementation
3225 int nRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3226 int nCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3227 ASSERT (nRow >= 0 && nRow < pSplitter->GetRowCount ());
3228 ASSERT (nCol >= 0 && nCol < pSplitter->GetColumnCount ());
3232 CCrystalTextView *pSiblingView = GetSiblingView (0, nCol);
3233 if (pSiblingView != nullptr && pSiblingView != this)
3235 m_nOffsetChar = pSiblingView->m_nOffsetChar;
3236 ASSERT (m_nOffsetChar >= 0 && m_nOffsetChar <= GetMaxLineLength (m_nTopLine, GetScreenLines()));
3242 CCrystalTextView *pSiblingView = GetSiblingView (nRow, 0);
3243 if (pSiblingView != nullptr && pSiblingView != this)
3245 m_nTopLine = pSiblingView->m_nTopLine;
3246 ASSERT (m_nTopLine >= 0 && m_nTopLine < GetLineCount ());
3250 SetFont (m_LogFont);
3251 if (m_bRememberLastPos && !sDoc.IsEmpty ())
3254 CString sKey = REG_EDITPAD;
3255 sKey += _T ("\\Remembered");
3257 if (reg.Open (HKEY_CURRENT_USER, sKey, KEY_READ) &&
3258 reg.LoadBinary (sDoc, (LPBYTE) dwLastPos, sizeof (dwLastPos)))
3261 ptCursorPos.x = dwLastPos[1];
3262 ptCursorPos.y = dwLastPos[2];
3263 if (IsValidTextPosY (ptCursorPos))
3265 if (!IsValidTextPosX (ptCursorPos))
3267 ASSERT_VALIDTEXTPOS (ptCursorPos);
3268 SetCursorPos (ptCursorPos);
3269 SetSelection (ptCursorPos, ptCursorPos);
3270 SetAnchor (ptCursorPos);
3271 EnsureVisible (ptCursorPos);
3277 /////////////////////////////////////////////////////////////////////////////
3278 // CCrystalTextView printing
3280 void CCrystalTextView::
3281 OnPrepareDC (CDC * pDC, CPrintInfo * pInfo /*= nullptr*/)
3283 CView::OnPrepareDC (pDC, pInfo);
3285 if (pInfo != nullptr)
3287 pInfo->m_bContinuePrinting = true;
3288 if (m_nPrintPages != 0 && (int) pInfo->m_nCurPage > m_nPrintPages)
3289 pInfo->m_bContinuePrinting = false;
3293 BOOL CCrystalTextView::
3294 OnPreparePrinting (CPrintInfo * pInfo)
3296 return DoPreparePrinting (pInfo);
3299 void CCrystalTextView::
3300 GetPrintHeaderText (int nPageNum, CString & text)
3302 ASSERT (m_bPrintHeader);
3306 void CCrystalTextView::
3307 GetPrintFooterText (int nPageNum, CString & text)
3309 ASSERT (m_bPrintFooter);
3310 text.Format (_T ("Page %d/%d"), nPageNum, m_nPrintPages);
3313 void CCrystalTextView::
3314 PrintHeader (CDC * pdc, int nPageNum)
3316 CRect rcHeader = m_rcPrintArea;
3317 rcHeader.bottom = rcHeader.top;
3318 rcHeader.top -= (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3321 GetPrintHeaderText (nPageNum, text);
3322 if (!text.IsEmpty ())
3323 pdc->DrawText (text, &rcHeader, DT_CENTER | DT_NOPREFIX | DT_TOP | DT_SINGLELINE);
3326 void CCrystalTextView::
3327 PrintFooter (CDC * pdc, int nPageNum)
3329 CRect rcFooter = m_rcPrintArea;
3330 rcFooter.top = rcFooter.bottom;
3331 rcFooter.bottom += (m_nPrintLineHeight + m_nPrintLineHeight / 2);
3334 GetPrintFooterText (nPageNum, text);
3335 if (!text.IsEmpty ())
3336 pdc->DrawText (text, &rcFooter, DT_CENTER | DT_NOPREFIX | DT_BOTTOM | DT_SINGLELINE);
3340 * @brief Retrieves the print margins
3341 * @param nLeft [out] Left margin
3342 * @param nTop [out] Top margin
3343 * @param nRight [out] right margin
3344 * @param nBottom [out] Bottom margin
3346 void CCrystalTextView::
3347 GetPrintMargins (long & nLeft, long & nTop, long & nRight, long & nBottom)
3349 nLeft = DEFAULT_PRINT_MARGIN;
3350 nTop = DEFAULT_PRINT_MARGIN;
3351 nRight = DEFAULT_PRINT_MARGIN;
3352 nBottom = DEFAULT_PRINT_MARGIN;
3354 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
3357 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
3359 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
3361 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
3363 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
3368 void CCrystalTextView::
3369 RecalcPageLayouts (CDC * pdc, CPrintInfo * pInfo)
3371 m_ptPageArea = pInfo->m_rectDraw;
3372 m_ptPageArea.NormalizeRect ();
3374 m_nPrintLineHeight = pdc->GetTextExtent (_T ("X")).cy;
3376 m_rcPrintArea = m_ptPageArea;
3377 CSize szTopLeft, szBottomRight;
3378 CWinApp *pApp = AfxGetApp ();
3379 ASSERT (pApp != nullptr);
3380 GetPrintMargins (szTopLeft.cx, szTopLeft.cy, szBottomRight.cx, szBottomRight.cy);
3381 pdc->HIMETRICtoLP (&szTopLeft);
3382 pdc->HIMETRICtoLP (&szBottomRight);
3383 m_rcPrintArea.left += szTopLeft.cx;
3384 m_rcPrintArea.right -= szBottomRight.cx;
3385 m_rcPrintArea.top += szTopLeft.cy;
3386 m_rcPrintArea.bottom -= szBottomRight.cy;
3388 m_rcPrintArea.top += m_nPrintLineHeight + m_nPrintLineHeight / 2;
3390 m_rcPrintArea.bottom -= m_nPrintLineHeight + m_nPrintLineHeight / 2;
3392 InvalidateLineCache (0, -1);
3394 m_nScreenChars = (m_rcPrintArea.Width () - GetMarginWidth (pdc)) / GetCharWidth ();
3395 m_nScreenLines = m_rcPrintArea.Height () / GetLineHeight ();
3398 void CCrystalTextView::
3399 OnBeginPrinting (CDC * pdc, CPrintInfo * pInfo)
3401 ASSERT (m_pPrintFont == nullptr);
3402 LOGFONT lf = m_lfBaseFont;
3403 CDC *pDisplayDC = GetDC ();
3404 lf.lfHeight = MulDiv (lf.lfHeight, pdc->GetDeviceCaps (LOGPIXELSY), pDisplayDC->GetDeviceCaps (LOGPIXELSY));
3405 lf.lfWidth = MulDiv (lf.lfWidth, pdc->GetDeviceCaps (LOGPIXELSX), pDisplayDC->GetDeviceCaps (LOGPIXELSX));
3406 ReleaseDC (pDisplayDC);
3408 m_pCrystalRendererSaved = m_pCrystalRenderer.release();
3409 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
3411 m_pPrintFont = new CFont;
3412 if (!m_pPrintFont->CreateFontIndirect (&lf))
3414 delete m_pPrintFont;
3415 m_pPrintFont = nullptr;
3419 GetFont (m_lfSavedBaseFont);
3420 m_pPrintFont->GetLogFont (&lf);
3427 void CCrystalTextView::
3428 OnEndPrinting (CDC * pdc, CPrintInfo * pInfo)
3430 if (m_pCrystalRendererSaved)
3432 m_pCrystalRenderer.reset(m_pCrystalRendererSaved);
3433 m_pCrystalRendererSaved = nullptr;
3435 if (m_pPrintFont != nullptr)
3437 delete m_pPrintFont;
3438 m_pPrintFont = nullptr;
3439 SetFont(m_lfSavedBaseFont);
3442 m_nPrintLineHeight = 0;
3443 m_bPrinting = false;
3446 void CCrystalTextView::
3447 OnPrint (CDC * pdc, CPrintInfo * pInfo)
3449 pdc->SelectObject (m_pPrintFont);
3451 const COLORREF defaultLineColor = RGB(0,0,0);
3452 const COLORREF defaultBgColor = RGB(255,255,255);
3454 RecalcPageLayouts (pdc, pInfo);
3456 m_nPrintPages = (GetSubLineCount () + GetScreenLines () - 1) / GetScreenLines ();
3458 ASSERT (pInfo->m_nCurPage >= 1 && (int) pInfo->m_nCurPage <= m_nPrintPages);
3460 int nTopSubLine = (pInfo->m_nCurPage - 1) * GetScreenLines ();
3461 int nEndSubLine = nTopSubLine + GetScreenLines () - 1;
3462 if (nEndSubLine >= GetSubLineCount ())
3463 nEndSubLine = GetSubLineCount () - 1;
3465 int nTopLine, nEndLine;
3466 GetLineBySubLine (nTopSubLine, nTopLine, nSubLines);
3467 GetLineBySubLine (nEndSubLine, nEndLine, nSubLines);
3469 TRACE (_T ("Printing page %d of %d, lines %d - %d\n"),
3470 pInfo->m_nCurPage, m_nPrintPages, nTopLine, nEndLine);
3472 m_pCrystalRenderer->BindDC(*pdc, m_rcPrintArea);
3473 m_pCrystalRenderer->BeginDraw();
3475 m_pCrystalRenderer->SetTextColor(defaultLineColor);
3476 m_pCrystalRenderer->SetBkColor(defaultBgColor);
3480 PrintHeader (pdc, pInfo->m_nCurPage);
3485 PrintFooter (pdc, pInfo->m_nCurPage);
3488 // set clipping region
3489 // see http://support.microsoft.com/kb/128334
3490 CRect rectClip = m_rcPrintArea;
3491 rectClip.right = rectClip.left + GetMarginWidth (pdc) + GetScreenChars () * GetCharWidth ();
3492 rectClip.bottom = rectClip.top + GetScreenLines () * GetLineHeight ();
3493 if (!!pdc->IsKindOf (RUNTIME_CLASS (CPreviewDC)))
3495 CPreviewDC *pPrevDC = (CPreviewDC *)pdc;
3497 pPrevDC->PrinterDPtoScreenDP (&rectClip.TopLeft ());
3498 pPrevDC->PrinterDPtoScreenDP (&rectClip.BottomRight ());
3501 ::GetViewportOrgEx (pdc->m_hDC,&ptOrg);
3505 rgn.CreateRectRgnIndirect (&rectClip);
3506 pdc->SelectClipRgn (&rgn);
3510 CRect rcLine = m_rcPrintArea;
3511 int nLineHeight = GetLineHeight ();
3512 rcLine.bottom = rcLine.top + nLineHeight;
3514 rcMargin.right = rcMargin.left + GetMarginWidth (pdc);
3515 rcLine.left = rcMargin.right;
3517 int nSubLineOffset = GetSubLineIndex (nTopLine) - nTopSubLine;
3518 if( nSubLineOffset < 0 )
3520 rcLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
3523 int nLineCount = GetLineCount();
3525 for (nCurrentLine = nTopLine; nCurrentLine <= nEndLine; nCurrentLine++)
3527 rcLine.bottom = rcLine.top + GetSubLines (nCurrentLine) * nLineHeight;
3528 rcMargin.bottom = rcLine.bottom;
3530 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
3532 DrawMargin (rcMargin, nCurrentLine, nCurrentLine + 1);
3533 DrawSingleLine (rcLine, nCurrentLine);
3534 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
3535 m_pCrystalRenderer->DrawBoundaryLine (rcMargin.left, rcLine.right, rcMargin.bottom-1);
3538 rcLine.top = rcLine.bottom;
3539 rcMargin.top = rcLine.bottom;
3542 m_pCrystalRenderer->EndDraw();
3544 pdc->SelectClipRgn (nullptr);
3548 /////////////////////////////////////////////////////////////////////////////
3549 // CCrystalTextView message handlers
3551 int CCrystalTextView::
3554 if (m_pTextBuffer == nullptr)
3555 return 1; // Single empty line
3557 int nLineCount = m_pTextBuffer->GetLineCount ();
3558 ASSERT (nLineCount > 0);
3563 int CCrystalTextView::GetSubLineCount()
3565 const int nLineCount = GetLineCount();
3567 // if we do not wrap words, number of sub lines is
3568 // equal to number of lines
3569 if( !m_bWordWrap && !m_bHideLines )
3572 // calculate number of sub lines
3573 if (nLineCount <= 0)
3575 return CCrystalTextView::GetSubLineIndex( nLineCount - 1 ) + GetSubLines( nLineCount - 1 );
3578 int CCrystalTextView::GetSubLineIndex( int nLineIndex )
3580 // if we do not wrap words, subline index of this line is equal to its index
3581 if( !m_bWordWrap && !m_bHideLines )
3584 // calculate subline index of the line
3585 int nSubLineCount = 0;
3586 int nLineCount = GetLineCount();
3588 if( nLineIndex >= nLineCount )
3589 nLineIndex = nLineCount - 1;
3591 // return cached subline index of the line if it is already cached.
3592 if (nLineIndex <= m_nLastLineIndexCalculatedSubLineIndex)
3593 return (*m_panSubLineIndexCache)[nLineIndex];
3595 // calculate subline index of the line and cache it.
3596 if (m_nLastLineIndexCalculatedSubLineIndex >= 0)
3597 nSubLineCount = (*m_panSubLineIndexCache)[m_nLastLineIndexCalculatedSubLineIndex];
3600 m_nLastLineIndexCalculatedSubLineIndex = 0;
3601 m_panSubLineIndexCache->SetAtGrow( 0, 0 );
3604 // TODO: Rethink this, it is very time consuming
3605 for( int i = m_nLastLineIndexCalculatedSubLineIndex; i < nLineIndex; i++ )
3607 m_panSubLineIndexCache->SetAtGrow( i, nSubLineCount);
3608 nSubLineCount+= GetSubLines( i );
3610 m_panSubLineIndexCache->SetAtGrow( nLineIndex, nSubLineCount);
3611 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex;
3613 return nSubLineCount;
3616 // See comment in the header file
3617 void CCrystalTextView::GetLineBySubLine(int nSubLineIndex, int &nLine, int &nSubLine)
3619 if (GetSubLineCount() == 0)
3626 ASSERT( nSubLineIndex < GetSubLineCount() );
3628 // if we do not wrap words, nLine is equal to nSubLineIndex and nSubLine is allways 0
3629 if ( !m_bWordWrap && !m_bHideLines )
3631 nLine = nSubLineIndex;
3637 const int nLineCount = GetLineCount();
3640 int base = 0, i = 0, nSubLineIndex2 = 0;
3641 for (int lim = nLineCount; lim != 0; lim >>= 1)
3643 i = base + (lim >> 1);
3644 nSubLineIndex2 = GetSubLineIndex(i);
3645 if (nSubLineIndex >= nSubLineIndex2 && nSubLineIndex < nSubLineIndex2 + GetSubLines(i))
3647 else if (nSubLineIndex2 <= nSubLineIndex) /* key > p: move right */
3651 } /* else move left */
3654 ASSERT(i < nLineCount);
3656 nSubLine = nSubLineIndex - nSubLineIndex2;
3659 int CCrystalTextView::
3660 GetLineLength (int nLineIndex) const
3662 if (m_pTextBuffer == nullptr)
3664 return m_pTextBuffer->GetLineLength (nLineIndex);
3667 int CCrystalTextView::
3668 GetFullLineLength (int nLineIndex) const
3670 if (m_pTextBuffer == nullptr)
3672 return m_pTextBuffer->GetFullLineLength (nLineIndex);
3675 // How many bytes of line are displayed on-screen?
3676 int CCrystalTextView::
3677 GetViewableLineLength (int nLineIndex) const
3680 return GetFullLineLength(nLineIndex);
3682 return GetLineLength(nLineIndex);
3685 LPCTSTR CCrystalTextView::
3686 GetLineChars (int nLineIndex) const
3688 if (m_pTextBuffer == nullptr)
3690 return m_pTextBuffer->GetLineChars (nLineIndex);
3694 * @brief Reattach buffer after deleting/inserting ghost lines :
3696 * @note no need to reinitialize the horizontal scrollbar
3697 * no need to reset the editor options (m_bOvrMode, m_bLastReplace)
3699 void CCrystalTextView::
3700 ReAttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3702 if (m_pTextBuffer != nullptr)
3703 m_pTextBuffer->RemoveView (this);
3704 if (pBuf == nullptr)
3706 pBuf = LocateTextBuffer ();
3709 m_pTextBuffer = pBuf;
3710 if (m_pTextBuffer != nullptr)
3711 m_pTextBuffer->AddView (this);
3712 // don't reset CCrystalEditView options
3713 CCrystalTextView::ResetView ();
3715 // Init scrollbars arrows
3716 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3717 if (pVertScrollBarCtrl != nullptr)
3718 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3719 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3720 // Update vertical scrollbar only
3721 RecalcVertScrollBar ();
3725 * @brief Attach buffer (maybe for the first time)
3726 * initialize the view and initialize both scrollbars
3728 void CCrystalTextView::
3729 AttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3731 if (m_pTextBuffer != nullptr)
3732 m_pTextBuffer->RemoveView (this);
3733 if (pBuf == nullptr)
3735 pBuf = LocateTextBuffer ();
3738 m_pTextBuffer = pBuf;
3739 if (m_pTextBuffer != nullptr)
3740 m_pTextBuffer->AddView (this);
3744 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3745 if (pVertScrollBarCtrl != nullptr)
3746 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3747 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3748 CScrollBar *pHorzScrollBarCtrl = GetScrollBarCtrl (SB_HORZ);
3749 if (pHorzScrollBarCtrl != nullptr)
3750 pHorzScrollBarCtrl->EnableScrollBar (GetScreenChars () >= GetMaxLineLength (m_nTopLine, GetScreenLines())?
3751 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3753 // Update scrollbars
3754 RecalcVertScrollBar ();
3755 RecalcHorzScrollBar ();
3758 void CCrystalTextView::
3761 if (m_pTextBuffer != nullptr)
3763 m_pTextBuffer->RemoveView (this);
3764 m_pTextBuffer = nullptr;
3765 // don't reset CCrystalEditView options
3766 CCrystalTextView::ResetView ();
3770 int CCrystalTextView::
3773 if (m_nScreenLines == -1)
3776 GetClientRect (&rect);
3777 m_nScreenLines = (rect.Height () - GetTopMarginHeight ()) / GetLineHeight ();
3779 return m_nScreenLines;
3782 bool CCrystalTextView::
3783 GetItalic (int nColorIndex)
3785 // WINMERGE - since italic text has problems,
3786 // lets disable it. E.g. "_" chars disappear and last
3787 // char may be cropped.
3790 // return nColorIndex == COLORINDEX_COMMENT;
3793 bool CCrystalTextView::
3794 GetBold (int nColorIndex)
3796 if (m_pColors != nullptr)
3798 nColorIndex &= ~COLORINDEX_APPLYFORCE;
3799 return m_pColors->GetBold(nColorIndex);
3805 int CCrystalTextView::
3808 if (m_nScreenChars == -1)
3811 GetClientRect (&rect);
3812 m_nScreenChars = (rect.Width () - GetMarginWidth ()) / GetCharWidth ();
3814 return m_nScreenChars;
3817 void CCrystalTextView::
3820 DetachFromBuffer ();
3823 CView::OnDestroy ();
3825 if (m_pCacheBitmap != nullptr)
3827 delete m_pCacheBitmap;
3828 m_pCacheBitmap = nullptr;
3832 BOOL CCrystalTextView::
3833 OnEraseBkgnd (CDC * pdc)
3835 UNREFERENCED_PARAMETER(pdc);
3839 void CCrystalTextView::
3840 OnSize (UINT nType, int cx, int cy)
3842 CView::OnSize (nType, cx, cy);
3845 // get char position of top left visible character with old cached word wrap
3847 SubLineCursorPosToTextPos( CPoint( 0, m_nTopSubLine ), topPos );
3851 // we have to recompute the line wrapping
3852 InvalidateScreenRect(false);
3854 // compute new top sub line
3856 CharPosToPoint( topPos.y, topPos.x, topSubLine );
3857 m_nTopSubLine = GetSubLineIndex(topPos.y) + topSubLine.y;
3859 ScrollToSubLine( m_nTopSubLine );
3861 // set caret to right position
3865 RecalcVertScrollBar (false, false);
3866 RecalcHorzScrollBar (false, false);
3869 void CCrystalTextView::
3870 UpdateSiblingScrollPos (bool bHorz)
3872 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
3873 if (pSplitterWnd != nullptr)
3875 // See CSplitterWnd::IdFromRowCol() implementation for details
3876 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3877 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3878 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
3879 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
3883 int nCols = pSplitterWnd->GetColumnCount ();
3884 for (int nCol = 0; nCol < nCols; nCol++)
3886 if (nCol != nCurrentCol) // We don't need to update ourselves
3888 CCrystalTextView *pSiblingView = GetSiblingView (nCurrentRow, nCol);
3889 if (pSiblingView != nullptr)
3890 pSiblingView->OnUpdateSibling (this, false);
3896 int nRows = pSplitterWnd->GetRowCount ();
3897 for (int nRow = 0; nRow < nRows; nRow++)
3899 if (nRow != nCurrentRow) // We don't need to update ourselves
3901 CCrystalTextView *pSiblingView = GetSiblingView (nRow, nCurrentCol);
3902 if (pSiblingView != nullptr)
3903 pSiblingView->OnUpdateSibling (this, false);
3910 void CCrystalTextView::
3911 OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
3913 if (pUpdateSource != this)
3915 ASSERT (pUpdateSource != nullptr);
3916 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
3919 ASSERT (pUpdateSource->m_nTopLine >= 0);
3920 ASSERT (pUpdateSource->m_nTopLine < GetLineCount ());
3921 if (pUpdateSource->m_nTopLine != m_nTopLine)
3923 ScrollToLine (pUpdateSource->m_nTopLine, true, false);
3929 ASSERT (pUpdateSource->m_nOffsetChar >= 0);
3930 ASSERT (pUpdateSource->m_nOffsetChar < GetMaxLineLength (m_nTopLine, GetScreenLines()));
3931 if (pUpdateSource->m_nOffsetChar != m_nOffsetChar)
3933 ScrollToChar (pUpdateSource->m_nOffsetChar, true, false);
3940 void CCrystalTextView::
3941 RecalcVertScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
3943 SCROLLINFO si = {0};
3944 si.cbSize = sizeof (si);
3948 si.nPos = m_nTopSubLine;
3952 const int nScreenLines = GetScreenLines();
3953 if( m_nTopSubLine > 0 && nScreenLines >= GetSubLineCount() )
3959 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3961 si.nMax = GetSubLineCount() - 1;
3962 si.nPage = nScreenLines;
3963 si.nPos = m_nTopSubLine;
3965 VERIFY (SetScrollInfo (SB_VERT, &si, bRedraw));
3968 void CCrystalTextView::
3969 OnVScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
3971 CView::OnVScroll (nSBCode, nPos, pScrollBar);
3973 // Note we cannot use nPos because of its 16-bit nature
3974 SCROLLINFO si = {0};
3975 si.cbSize = sizeof (si);
3976 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3977 VERIFY (GetScrollInfo (SB_VERT, &si));
3979 // Get the minimum and maximum scroll-bar positions.
3980 int nMinPos = si.nMin;
3981 int nMaxPos = si.nMax;
3983 // Get the current position of scroll box.
3984 int nCurPos = si.nPos;
3986 bool bDisableSmooth = true;
3989 case SB_TOP: // Scroll to top.
3991 bDisableSmooth = false;
3994 case SB_BOTTOM: // Scroll to bottom.
3996 bDisableSmooth = false;
3999 case SB_LINEUP: // Scroll one line up.
4000 if (nCurPos > nMinPos)
4004 case SB_LINEDOWN: // Scroll one line down.
4005 if (nCurPos < nMaxPos)
4009 case SB_PAGEUP: // Scroll one page up.
4010 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4011 bDisableSmooth = false;
4014 case SB_PAGEDOWN: // Scroll one page down.
4015 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4016 bDisableSmooth = false;
4019 case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position
4020 nCurPos = si.nTrackPos; // of the scroll box at the end of the drag operation.
4023 case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the
4024 nCurPos = si.nTrackPos; // position that the scroll box has been dragged to.
4027 ScrollToSubLine(nCurPos, bDisableSmooth);
4030 void CCrystalTextView::
4031 RecalcHorzScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
4033 SCROLLINFO si = {0};
4034 si.cbSize = sizeof (si);
4036 const int nScreenChars = GetScreenChars();
4038 if (GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
4040 if (m_nOffsetChar > nScreenChars)
4046 // Disable horizontal scroll bar
4047 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4048 SetScrollInfo (SB_HORZ, &si);
4052 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
4057 si.nPos = m_nOffsetChar;
4061 if (nScreenChars >= nMaxLineLen && m_nOffsetChar > 0)
4067 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
4070 // Horiz scroll limit to longest line + one screenwidth
4071 si.nMax = nMaxLineLen + nScreenChars;
4072 si.nPage = nScreenChars;
4073 si.nPos = m_nOffsetChar;
4075 VERIFY (SetScrollInfo (SB_HORZ, &si, bRedraw));
4078 void CCrystalTextView::
4079 OnHScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
4081 // Default handler not needed
4082 //CView::OnHScroll (nSBCode, nPos, pScrollBar);
4084 // Again, we cannot use nPos because it's 16-bit
4085 SCROLLINFO si = {0};
4086 si.cbSize = sizeof (si);
4087 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
4088 VERIFY (GetScrollInfo (SB_HORZ, &si));
4090 // Get the minimum and maximum scroll-bar positions.
4091 int nMinPos = si.nMin;
4092 int nMaxPos = si.nMax;
4094 // Get the current position of scroll box.
4095 int nCurPos = si.nPos;
4099 case SB_LEFT: // Scroll to far left.
4103 case SB_RIGHT: // Scroll to far right.
4107 case SB_ENDSCROLL: // End scroll.
4110 case SB_LINELEFT: // Scroll left.
4111 if (nCurPos > nMinPos)
4115 case SB_LINERIGHT: // Scroll right.
4116 if (nCurPos < nMaxPos)
4120 case SB_PAGELEFT: // Scroll one page left.
4121 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
4124 case SB_PAGERIGHT: // Scroll one page right.
4125 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
4128 case SB_THUMBPOSITION: // Scroll to absolute position. The current position is
4129 nCurPos = si.nTrackPos; // specified by the nPos parameter.
4132 case SB_THUMBTRACK: // Drag scroll box to specified position. The current
4133 nCurPos = si.nTrackPos; // position is specified by the nPos parameter
4134 // The SB_THUMBTRACK scroll-bar code typically is used by applications that give
4135 // some feedback while the scroll box is being dragged.
4138 ScrollToChar (nCurPos, true);
4139 // This is needed, but why ? OnVScroll don't need to call UpdateCaret
4143 BOOL CCrystalTextView::
4144 OnSetCursor (CWnd * pWnd, UINT nHitTest, UINT message)
4146 if (nHitTest == HTCLIENT)
4149 ::GetCursorPos (&pt);
4150 ScreenToClient (&pt);
4151 if (pt.y < GetTopMarginHeight ())
4153 const int nColumnResizing = ClientToColumnResizing (pt.x);
4154 ::SetCursor (::LoadCursor (nullptr, nColumnResizing >= 0 ? IDC_SIZEWE : IDC_ARROW));
4156 else if (pt.x < GetMarginWidth ())
4158 ::SetCursor (::LoadCursor (AfxGetInstanceHandle (), MAKEINTRESOURCE (IDR_MARGIN_CURSOR)));
4162 CPoint ptText = ClientToText (pt);
4163 PrepareSelBounds ();
4164 if (IsInsideSelBlock (ptText))
4166 // [JRT]: Support For Disabling Drag and Drop...
4167 if (!m_bDisableDragAndDrop) // If Drag And Drop Not Disabled
4169 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW)); // Set To Arrow Cursor
4173 ::SetCursor (::LoadCursor (nullptr, IDC_IBEAM));
4177 return CView::OnSetCursor (pWnd, nHitTest, message);
4180 int CCrystalTextView::
4181 ClientToIdealTextPos (int x)
4184 if (x > GetMarginWidth ())
4185 nPos = m_nOffsetChar + (x - GetMarginWidth ()) / GetCharWidth ();
4192 * @brief Converts client area point to text position.
4193 * @param [in] point Client area point.
4194 * @return Text position (line index, char index in line).
4195 * @note For gray selection area char index is 0.
4197 CPoint CCrystalTextView::
4198 ClientToText (const CPoint & point)
4201 const int nSubLineCount = GetSubLineCount();
4202 const int nLineCount = GetLineCount();
4205 pt.y = m_nTopSubLine + (point.y - GetTopMarginHeight ()) / GetLineHeight();
4206 if (pt.y >= nSubLineCount)
4207 pt.y = nSubLineCount - 1;
4214 GetLineBySubLine( pt.y, nLine, nSubLineOffset );
4217 LPCTSTR pszLine = nullptr;
4219 vector<int> anBreaks(1);
4222 if (pt.y >= 0 && pt.y < nLineCount)
4224 nLength = GetLineLength( pt.y );
4225 anBreaks.resize(nLength + 1);
4226 pszLine = GetLineChars(pt.y);
4227 WrapLineCached( pt.y, GetScreenChars(), &anBreaks, nBreaks );
4229 if (nBreaks > nSubLineOffset && GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP)
4230 nLength = anBreaks[nSubLineOffset] - 1;
4233 // Char index for margin area is 0
4234 int nPos = ClientToIdealTextPos (point.x);
4239 const int nTabSize = GetTabSize();
4241 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, nLength);
4242 switch (GetTextLayoutMode ())
4244 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4246 int nColumnCount = m_pTextBuffer->GetColumnCount (nLine);
4247 int nColumnTotalWidth = 0;
4249 bool bInQuote = false;
4250 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4251 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4252 while (nIndex < nLength)
4255 if (!bInQuote && pszLine[nIndex] == sep)
4257 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4258 nOffset = nColumnTotalWidth - nCurPos;
4262 if (pszLine[nIndex] == quote)
4263 bInQuote = !bInQuote;
4264 if (pszLine[nIndex] == '\t')
4267 nOffset = GetCharCellCountFromChar (pszLine + nIndex);
4268 if (nColumn < nColumnCount && nCurPos + nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4269 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn) - nCurPos;
4274 if (n > nPos && i == nSubLineOffset)
4277 nIndex = pIterChar->next ();
4281 case TEXTLAYOUT_TABLE_WORDWRAP:
4285 int nColumnSumWidth = 0;
4286 int nColumnCurPoint = INT_MAX;
4288 if (nPos < m_pTextBuffer->GetColumnWidth (0))
4289 nColumnCurPoint = 0;
4290 while (nIndex < nLength)
4292 if (i < static_cast<int>(anBreaks.size()) && nIndex == abs(anBreaks[i]))
4294 if (anBreaks[i++] < 0)
4297 nColumnSumWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4298 n = nColumnSumWidth;
4299 if (nColumnSumWidth <= nPos && nPos < nColumnSumWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4300 nColumnCurPoint = nColumn;
4304 n = nColumnSumWidth;
4310 if (pszLine[nIndex] == '\t')
4313 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4317 if (n > nPos && j == nSubLineOffset)
4319 else if( nColumnCurPoint < nColumn && nPrevIndex != 0)
4321 nIndex = nPrevIndex;
4324 else if ( j == nSubLineOffset)
4325 nPrevIndex = nIndex;
4327 nIndex = pIterChar->next();
4329 if (nIndex == nLength && j != nSubLineOffset)
4330 nIndex = nPrevIndex;
4335 while (nIndex < nLength)
4337 if (nBreaks && i < static_cast<int>(anBreaks.size ()) && nIndex == anBreaks[i])
4344 if (pszLine[nIndex] == '\t')
4345 nOffset = nTabSize - nCurPos % nTabSize;
4347 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
4351 if (n > nPos && i == nSubLineOffset)
4354 nIndex = pIterChar->next();
4360 ASSERT(nIndex >= 0 && nIndex <= nLength);
4365 int CCrystalTextView::
4366 ClientToColumn (int x)
4369 GetClientRect (&rcClient);
4370 int nCharWidth = GetCharWidth ();
4371 int nMarginWidth = GetMarginWidth ();
4372 for (int nColumn = 0, columnleft = nMarginWidth - m_nOffsetChar * nCharWidth;
4373 columnleft < rcClient.Width ();
4374 columnleft += m_pTextBuffer->GetColumnWidth (nColumn++) * nCharWidth)
4376 if (columnleft <= x && x < columnleft + m_pTextBuffer->GetColumnWidth (nColumn) * nCharWidth)
4382 int CCrystalTextView::
4383 ClientToColumnResizing (int x)
4385 const int nColumn = ClientToColumn (x);
4386 const int nColumnL = ClientToColumn (x - 4);
4387 const int nColumnR = ClientToColumn (x + 4);
4388 if (nColumn != nColumnL || nColumn != nColumnR)
4390 return (nColumn != nColumnL) ? nColumnL : nColumn;
4395 void CCrystalTextView::
4396 AssertValidTextPos (const CPoint & point)
4398 if (GetLineCount () > 0)
4400 ASSERT (m_nTopLine >= 0 && m_nOffsetChar >= 0);
4401 ASSERT (point.y >= 0 && point.y < GetLineCount ());
4402 ASSERT (point.x >= 0 && point.x <= GetViewableLineLength (point.y));
4407 bool CCrystalTextView::
4408 IsValidTextPos (const CPoint &point)
4410 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4411 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4414 bool CCrystalTextView::
4415 IsValidTextPosX (const CPoint &point)
4417 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4418 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
4421 bool CCrystalTextView::
4422 IsValidTextPosY (const CPoint &point)
4424 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
4425 point.y >= 0 && point.y < GetLineCount ();
4428 CPoint CCrystalTextView::
4429 TextToClient (const CPoint & point)
4431 ASSERT_VALIDTEXTPOS (point);
4432 LPCTSTR pszLine = GetLineChars (point.y);
4434 int nColumnIndex = 0;
4438 int nSubLineStart = CharPosToPoint( point.y, point.x, charPoint, &nColumnIndex );
4439 charPoint.y+= GetSubLineIndex( point.y );
4441 // compute y-position
4442 pt.y = (charPoint.y - m_nTopSubLine) * GetLineHeight() + GetTopMarginHeight ();
4444 // if pt.x is null, we know the result
4445 if( charPoint.x == 0 && nColumnIndex == 0)
4447 pt.x = GetMarginWidth();
4451 // we have to calculate x-position
4454 pt.y = (point.y - m_nTopLine) * GetLineHeight();
4458 int nTabSize = GetTabSize ();
4459 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszLine, point.x);
4460 switch (GetTextLayoutMode ())
4462 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4464 int nColumnCount = m_pTextBuffer->GetColumnCount (point.y);
4465 int nColumnTotalWidth = 0;
4467 bool bInQuote = false;
4468 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4469 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4470 for (int nIndex = 0, nTabs = 0; nIndex < point.x; nIndex = pIterChar->next())
4472 if (!bInQuote && pszLine[nIndex] == sep)
4474 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4475 pt.x = nColumnTotalWidth;
4479 if (pszLine[nIndex] == quote)
4480 bInQuote = !bInQuote;
4481 if (pszLine[nIndex] == _T ('\t'))
4484 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4485 if (nColumn < nColumnCount && pt.x > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4486 pt.x = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4489 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4493 case TEXTLAYOUT_TABLE_WORDWRAP:
4496 for (int i = 0; i < nColumnIndex; ++i)
4497 pt.x += m_pTextBuffer->GetColumnWidth (i);
4498 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4500 if( nIndex >= nSubLineStart )
4502 if (pszLine[nIndex] == '\t')
4505 pt.x += GetCharCellCountFromChar (pszLine + nIndex);
4508 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4514 for (int nIndex = 0; nIndex < point.x; nIndex = pIterChar->next())
4517 if( nIndex == nSubLineStart )
4520 if (pszLine[nIndex] == _T ('\t'))
4521 pt.x += (nTabSize - pt.x % nTabSize);
4523 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4529 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4535 int CCrystalTextView::
4536 ColumnToClient (int nColumn)
4539 GetClientRect (&rcClient);
4540 int nCharWidth = GetCharWidth ();
4541 int columnleft = GetMarginWidth () - m_nOffsetChar * nCharWidth;
4542 for (int nColumn2 = 0; nColumn2 != nColumn && columnleft < rcClient.Width ();
4543 columnleft += m_pTextBuffer->GetColumnWidth (nColumn2++) * nCharWidth)
4548 void CCrystalTextView::
4549 InvalidateLines (int nLine1, int nLine2, bool bInvalidateMargin /*= false*/ )
4551 bInvalidateMargin = true;
4552 const int nTopMarginHeight = GetTopMarginHeight ();
4553 const int nLineHeight = GetLineHeight();
4557 GetClientRect (&rcInvalid);
4558 if (!bInvalidateMargin)
4559 rcInvalid.left += GetMarginWidth ();
4561 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4563 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4566 InvalidateRect (&rcInvalid, false);
4570 if (nLine2 < nLine1)
4577 GetClientRect (&rcInvalid);
4578 if (!bInvalidateMargin)
4579 rcInvalid.left += GetMarginWidth ();
4581 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight + nTopMarginHeight;
4582 rcInvalid.bottom = (GetSubLineIndex( nLine2 ) - m_nTopSubLine + GetSubLines( nLine2 )) * nLineHeight + nTopMarginHeight;
4584 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4585 rcInvalid.bottom = (nLine2 - m_nTopLine + 1) * GetLineHeight();
4588 InvalidateRect (&rcInvalid, false);
4592 void CCrystalTextView::
4593 SetSelection (const CPoint & ptStart, const CPoint & ptEnd, bool bUpdateView /* = true */)
4595 ASSERT_VALIDTEXTPOS (ptStart);
4596 ASSERT_VALIDTEXTPOS (ptEnd);
4597 if (m_ptSelStart == ptStart && !m_bRectangularSelection)
4599 if (m_ptSelEnd != ptEnd)
4600 InvalidateLines (ptEnd.y, m_ptSelEnd.y);
4604 InvalidateLines (ptStart.y, ptEnd.y);
4605 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4607 m_ptSelStart = ptStart;
4611 void CCrystalTextView::
4612 AdjustTextPoint (CPoint & point)
4614 point.x += GetCharWidth () / 2; //todo
4618 void CCrystalTextView::
4619 OnSetFocus (CWnd * pOldWnd)
4621 CView::OnSetFocus (pOldWnd);
4624 if (m_ptSelStart != m_ptSelEnd)
4625 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4629 DWORD CCrystalTextView::
4630 ParseLine (DWORD dwCookie, const TCHAR *pszChars, int nLength, TEXTBLOCK * pBuf, int &nActualItems)
4632 return m_CurSourceDef->ParseLineX (dwCookie, pszChars, nLength, pBuf, nActualItems);
4635 int CCrystalTextView::
4636 CalculateActualOffset (int nLineIndex, int nCharIndex, bool bAccumulate)
4638 const int nLength = GetLineLength (nLineIndex);
4639 ASSERT (nCharIndex >= 0 && nCharIndex <= nLength);
4640 LPCTSTR pszChars = GetLineChars (nLineIndex);
4642 const int nTabSize = GetTabSize ();
4643 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nCharIndex);
4645 switch (GetTextLayoutMode ())
4647 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4649 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4650 int nColumnTotalWidth = 0;
4652 bool bInQuote = false;
4653 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4654 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4655 for (I = 0; I < nCharIndex; I = pIterChar->next())
4657 if (!bInQuote && pszChars[I] == sep)
4659 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4660 nOffset = nColumnTotalWidth;
4664 if (pszChars[I] == quote)
4665 bInQuote = !bInQuote;
4666 else if (pszChars[I] == '\t')
4669 nOffset += GetCharCellCountFromChar (pszChars + I);
4670 if (nColumn < nColumnCount && nOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4671 nOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4677 case TEXTLAYOUT_TABLE_WORDWRAP:
4679 int nColumnIndex = 0;
4681 int nSubLineStart = CharPosToPoint( nLineIndex, nCharIndex, charPoint, &nColumnIndex );
4682 for (int i = 0; i < nColumnIndex; ++i)
4683 nOffset += m_pTextBuffer->GetColumnWidth (i);
4684 for (int nIndex = 0; nIndex < nCharIndex; nIndex = pIterChar->next())
4686 if( nIndex >= nSubLineStart )
4688 if (pszChars[nIndex] == '\t')
4691 nOffset += GetCharCellCountFromChar (pszChars + nIndex);
4700 vector<int> anBreaks(nLength + 1);
4703 /*if( nLength > GetScreenChars() )*/
4704 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks, nBreaks );
4712 for( J = nBreaks - 1; J >= 0 && nCharIndex < anBreaks[J]; J-- );
4713 nPreBreak = (J >= 0) ? anBreaks[J] : 0;
4716 for (I = 0; I < nCharIndex; I = pIterChar->next())
4719 if( nPreBreak == I && nBreaks )
4720 nPreOffset = nOffset;
4722 if (pszChars[I] == _T ('\t'))
4723 nOffset += (nTabSize - nOffset % nTabSize);
4725 nOffset += GetCharCellCountFromChar(pszChars + I);
4730 if( nPreBreak == I && nBreaks > 0)
4733 return nOffset - nPreOffset;
4741 int CCrystalTextView::
4742 ApproxActualOffset (int nLineIndex, int nOffset)
4747 int nLength = GetLineLength (nLineIndex);
4748 LPCTSTR pszChars = GetLineChars (nLineIndex);
4749 int nCurrentOffset = 0;
4750 int nTabSize = GetTabSize ();
4751 auto pIterChar = ICUBreakIterator::getCharacterBreakIterator(pszChars, nLength);
4752 switch (GetTextLayoutMode ())
4754 case TEXTLAYOUT_TABLE_NOWORDWRAP:
4755 case TEXTLAYOUT_TABLE_WORDWRAP:
4757 int nColumnCount = m_pTextBuffer->GetColumnCount (nLineIndex);
4758 int nColumnTotalWidth = 0;
4759 bool bInQuote = false;
4760 const int sep = m_pTextBuffer->GetFieldDelimiter ();
4761 const int quote = m_pTextBuffer->GetFieldEnclosure ();
4762 for (int I = 0, nColumn = 0; I < nLength; I = pIterChar->next())
4764 if (!bInQuote && pszChars[I] ==sep)
4766 nColumnTotalWidth += m_pTextBuffer->GetColumnWidth (nColumn++);
4767 nCurrentOffset = nColumnTotalWidth;
4771 if (pszChars[I] == quote)
4772 bInQuote = !bInQuote;
4773 if (pszChars[I] == '\t')
4776 nCurrentOffset += GetCharCellCountFromChar (pszChars + I);
4777 if (nColumn < nColumnCount && nCurrentOffset > nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn))
4778 nCurrentOffset = nColumnTotalWidth + m_pTextBuffer->GetColumnWidth (nColumn);
4780 if (nCurrentOffset >= nOffset)
4782 if (nOffset <= nCurrentOffset - nTabSize / 2)
4784 return pIterChar->next ();
4791 for (int I = 0; I < nLength; I = pIterChar->next())
4793 if (pszChars[I] == _T ('\t'))
4794 nCurrentOffset += (nTabSize - nCurrentOffset % nTabSize);
4797 nCurrentOffset += GetCharCellCountFromChar(pszChars + I);
4799 if (nCurrentOffset >= nOffset)
4801 if (nOffset <= nCurrentOffset - nTabSize / 2)
4803 return pIterChar->next();
4811 void CCrystalTextView::
4812 EnsureVisible (CPoint pt)
4814 EnsureVisible(pt, pt);
4817 void CCrystalTextView::
4818 OnKillFocus (CWnd * pNewWnd)
4820 CView::OnKillFocus (pNewWnd);
4824 if (m_ptSelStart != m_ptSelEnd)
4825 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4826 if (m_bDragSelection)
4829 KillTimer (m_nDragSelTimer);
4830 m_bDragSelection = false;
4834 void CCrystalTextView::
4837 CView::OnSysColorChange ();
4841 void CCrystalTextView::
4842 GetText (const CPoint & ptStart, const CPoint & ptEnd, CString & text, bool bExcludeInvisibleLines /*= true*/)
4844 if (m_pTextBuffer != nullptr)
4845 m_pTextBuffer->GetText (ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, text);
4850 void CCrystalTextView::
4851 GetTextInColumnSelection (CString & text, bool bExcludeInvisibleLines /*= true*/)
4853 if (m_pTextBuffer == nullptr)
4859 PrepareSelBounds ();
4861 CString sEol = m_pTextBuffer->GetStringEol (CRLF_STYLE_DOS);
4864 for (int L = m_ptDrawSelStart.y; L <= m_ptDrawSelEnd.y; L++)
4865 nBufSize += GetLineLength (L) + sEol.GetLength ();
4866 LPTSTR pszBuf = text.GetBuffer (nBufSize);
4868 for (int I = m_ptDrawSelStart.y; I <= m_ptDrawSelEnd.y; I++)
4870 if (bExcludeInvisibleLines && (GetLineFlags (I) & LF_INVISIBLE))
4872 int nSelLeft, nSelRight;
4873 GetColumnSelection (I, nSelLeft, nSelRight);
4874 memcpy (pszBuf, GetLineChars (I) + nSelLeft, sizeof (TCHAR) * (nSelRight - nSelLeft));
4875 pszBuf += (nSelRight - nSelLeft);
4876 memcpy (pszBuf, sEol, sizeof (TCHAR) * sEol.GetLength ());
4877 pszBuf += sEol.GetLength ();
4880 text.ReleaseBuffer ();
4884 void CCrystalTextView::
4885 UpdateView (CCrystalTextView * pSource, CUpdateContext * pContext,
4886 DWORD dwFlags, int nLineIndex /*= -1*/ )
4888 // SetTextType (GetExt (GetDocument ()->GetPathName ()));
4889 if (dwFlags & UPDATE_RESET)
4892 RecalcVertScrollBar ();
4893 RecalcHorzScrollBar ();
4897 int nLineCount = GetLineCount ();
4898 ASSERT (nLineCount > 0);
4899 ASSERT (nLineIndex >= -1 && nLineIndex < nLineCount);
4900 if ((dwFlags & UPDATE_SINGLELINE) != 0)
4902 ASSERT (nLineIndex != -1);
4903 // All text below this line should be reparsed
4904 const int cookiesSize = (int) m_ParseCookies->size();
4905 if (cookiesSize > 0)
4907 ASSERT (cookiesSize == nLineCount);
4908 // must be reinitialized to invalid value (DWORD) - 1
4909 for (int i = nLineIndex; i < cookiesSize; ++i)
4910 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4912 // This line'th actual length must be recalculated
4913 if (m_pnActualLineLength->size())
4915 ASSERT (m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
4916 // must be initialized to invalid code -1
4917 (*m_pnActualLineLength)[nLineIndex] = -1;
4919 InvalidateLineCache( nLineIndex, nLineIndex );
4922 // Repaint the lines
4923 InvalidateLines (nLineIndex, -1, true);
4927 if (m_bViewLineNumbers)
4928 // if enabling linenumber, we must invalidate all line-cache in visible area because selection margin width changes dynamically.
4929 nLineIndex = m_nTopLine < nLineIndex ? m_nTopLine : nLineIndex;
4931 if (nLineIndex == -1)
4932 nLineIndex = 0; // Refresh all text
4934 // All text below this line should be reparsed
4935 if (m_ParseCookies->size())
4937 size_t arrSize = m_ParseCookies->size();
4938 if (arrSize != static_cast<size_t>(nLineCount))
4940 size_t oldsize = arrSize;
4941 m_ParseCookies->resize(nLineCount);
4942 arrSize = nLineCount;
4943 // must be initialized to invalid value (DWORD) - 1
4944 for (size_t i = oldsize; i < arrSize; ++i)
4945 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4947 for (size_t i = nLineIndex; i < arrSize; ++i)
4948 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4951 // Recalculate actual length for all lines below this
4952 if (m_pnActualLineLength->size())
4954 size_t arrsize = m_pnActualLineLength->size();
4955 if (arrsize != static_cast<size_t>(nLineCount))
4957 // Reallocate actual length array
4958 size_t oldsize = arrsize;
4959 m_pnActualLineLength->resize(nLineCount);
4960 arrsize = nLineCount;
4961 // must be initialized to invalid code -1
4962 for (size_t i = oldsize; i < arrsize; ++i)
4963 (*m_pnActualLineLength)[i] = -1;
4965 for (size_t i = nLineIndex; i < arrsize; ++i)
4966 (*m_pnActualLineLength)[i] = -1;
4969 InvalidateLineCache( nLineIndex, -1 );
4971 // Repaint the lines
4972 InvalidateLines (nLineIndex, -1, true);
4975 // All those points must be recalculated and validated
4976 if (pContext != nullptr)
4978 pContext->RecalcPoint (m_ptCursorPos);
4979 pContext->RecalcPoint (m_ptSelStart);
4980 pContext->RecalcPoint (m_ptSelEnd);
4981 pContext->RecalcPoint (m_ptAnchor);
4982 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
4983 ASSERT_VALIDTEXTPOS (m_ptSelStart);
4984 ASSERT_VALIDTEXTPOS (m_ptSelEnd);
4985 ASSERT_VALIDTEXTPOS (m_ptAnchor);
4986 if (m_bDraggingText)
4988 pContext->RecalcPoint (m_ptDraggedTextBegin);
4989 pContext->RecalcPoint (m_ptDraggedTextEnd);
4990 ASSERT_VALIDTEXTPOS (m_ptDraggedTextBegin);
4991 ASSERT_VALIDTEXTPOS (m_ptDraggedTextEnd);
4993 CPoint ptTopLine (0, m_nTopLine);
4994 pContext->RecalcPoint (ptTopLine);
4995 ASSERT_VALIDTEXTPOS (ptTopLine);
4996 m_nTopLine = ptTopLine.y;
5000 // Recalculate vertical scrollbar, if needed
5001 if ((dwFlags & UPDATE_VERTRANGE) != 0)
5003 if (!m_bVertScrollBarLocked)
5004 RecalcVertScrollBar ();
5007 // Recalculate horizontal scrollbar, if needed
5008 if ((dwFlags & UPDATE_HORZRANGE) != 0)
5010 if (!m_bHorzScrollBarLocked)
5011 RecalcHorzScrollBar ();
5015 HINSTANCE CCrystalTextView::
5016 GetResourceHandle ()
5018 #ifdef CRYSEDIT_RES_HANDLE
5019 return CRYSEDIT_RES_HANDLE;
5021 if (s_hResourceInst != nullptr)
5022 return s_hResourceInst;
5023 return AfxGetResourceHandle ();
5027 int CCrystalTextView::
5028 OnCreate (LPCREATESTRUCT lpCreateStruct)
5031 _tcscpy_s (m_lfBaseFont.lfFaceName, _T ("FixedSys"));
5032 m_lfBaseFont.lfHeight = 0;
5033 m_lfBaseFont.lfWeight = FW_NORMAL;
5034 m_lfBaseFont.lfItalic = false;
5035 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
5036 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
5037 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5038 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
5039 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
5041 if (CView::OnCreate (lpCreateStruct) == -1)
5044 ASSERT (m_hAccel == nullptr);
5045 // vvv GetResourceHandle () ???
5046 HINSTANCE hInst = AfxFindResourceHandle (MAKEINTRESOURCE(IDR_DEFAULT_ACCEL), RT_ACCELERATOR);
5047 ASSERT (hInst != nullptr);
5048 m_hAccel =::LoadAccelerators (hInst, MAKEINTRESOURCE (IDR_DEFAULT_ACCEL));
5049 ASSERT (m_hAccel != nullptr);
5053 void CCrystalTextView::
5054 SetAnchor (const CPoint & ptNewAnchor)
5056 ASSERT_VALIDTEXTPOS (ptNewAnchor);
5057 m_ptAnchor = ptNewAnchor;
5060 void CCrystalTextView::
5061 OnEditOperation (int nAction, LPCTSTR pszText, size_t cchText)
5065 BOOL CCrystalTextView::
5066 PreTranslateMessage (MSG * pMsg)
5068 if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
5070 if (m_hAccel != nullptr)
5072 if (::TranslateAccelerator (m_hWnd, m_hAccel, pMsg))
5076 else if (pMsg->message == WM_LBUTTONDBLCLK)
5077 m_dwLastDblClickTime = GetTickCount();
5078 else if (pMsg->message == WM_LBUTTONDOWN && (GetTickCount() - GetDoubleClickTime()) < m_dwLastDblClickTime)
5080 m_dwLastDblClickTime = 0;
5081 OnLButtonTrippleClk(static_cast<UINT>(pMsg->wParam), { GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam) });
5084 return CView::PreTranslateMessage (pMsg);
5087 CPoint CCrystalTextView::
5088 GetCursorPos () const
5090 return m_ptCursorPos;
5093 void CCrystalTextView::
5094 SetCursorPos (const CPoint & ptCursorPos)
5096 ASSERT_VALIDTEXTPOS (ptCursorPos);
5097 m_ptCursorPos = ptCursorPos;
5098 m_nIdealCharPos = CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x);
5102 void CCrystalTextView::
5103 UpdateCompositionWindowPos() /* IME */
5105 HIMC hIMC = ImmGetContext(m_hWnd);
5106 COMPOSITIONFORM compform;
5108 compform.dwStyle = CFS_FORCE_POSITION;
5109 compform.ptCurrentPos = GetCaretPos();
5110 ImmSetCompositionWindow(hIMC, &compform);
5112 ImmReleaseContext(m_hWnd, hIMC);
5115 void CCrystalTextView::
5116 UpdateCompositionWindowFont() /* IME */
5118 HIMC hIMC = ImmGetContext(m_hWnd);
5120 ImmSetCompositionFont(hIMC, &m_lfBaseFont);
5122 ImmReleaseContext(m_hWnd, hIMC);
5125 void CCrystalTextView::
5126 SetTopMargin (bool bTopMargin)
5128 if (m_bTopMargin != bTopMargin)
5130 m_bTopMargin = bTopMargin;
5131 if (::IsWindow (m_hWnd))
5134 m_nScreenLines = -1;
5135 RecalcVertScrollBar ();
5141 void CCrystalTextView::
5142 SetSelectionMargin (bool bSelMargin)
5144 if (m_bSelMargin != bSelMargin)
5146 m_bSelMargin = bSelMargin;
5147 if (::IsWindow (m_hWnd))
5149 InvalidateScreenRect ();
5150 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5151 RecalcHorzScrollBar ();
5157 void CCrystalTextView::
5158 SetViewLineNumbers (bool bViewLineNumbers)
5160 if (m_bViewLineNumbers != bViewLineNumbers)
5162 m_bViewLineNumbers = bViewLineNumbers;
5163 if (::IsWindow (m_hWnd))
5165 InvalidateScreenRect ();
5166 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5167 RecalcHorzScrollBar ();
5173 void CCrystalTextView::
5174 GetFont (LOGFONT & lf)
5179 void CCrystalTextView::
5180 SetFont (const LOGFONT & lf)
5185 m_pCrystalRenderer->SetFont(lf);
5186 if (::IsWindow (m_hWnd))
5188 InvalidateScreenRect();
5189 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
5190 RecalcVertScrollBar ();
5191 RecalcHorzScrollBar ();
5199 void CCrystalTextView::
5200 OnUpdateIndicatorPosition (CCmdUI * pCmdUI)
5202 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
5204 // VVV m_ptCursorPos.x + 1 ???
5205 stat.Format (_T ("Ln %d, Col %d"), m_ptCursorPos.y + 1, m_nIdealCharPos + 1);
5206 pCmdUI->SetText (stat);
5208 if( pCmdUI->m_pOther != nullptr && !!pCmdUI->m_pOther->IsKindOf( RUNTIME_CLASS(CStatusBar) ) )
5209 OnUpdateStatusMessage( (CStatusBar*)pCmdUI->m_pOther );
5213 void CCrystalTextView::
5214 OnUpdateIndicatorCRLF (CCmdUI * pCmdUI)
5216 if (m_pTextBuffer != nullptr)
5218 std::basic_string<TCHAR> eol;
5219 CRLFSTYLE crlfMode = m_pTextBuffer->GetCRLFMode ();
5222 case CRLF_STYLE_DOS:
5223 eol = LoadResString (IDS_EOL_DOS);
5224 pCmdUI->SetText (eol.c_str());
5225 pCmdUI->Enable (true);
5227 case CRLF_STYLE_UNIX:
5228 eol = LoadResString (IDS_EOL_UNIX);
5229 pCmdUI->SetText (eol.c_str());
5230 pCmdUI->Enable (true);
5232 case CRLF_STYLE_MAC:
5233 eol = LoadResString (IDS_EOL_MAC);
5234 pCmdUI->SetText (eol.c_str());
5235 pCmdUI->Enable (true);
5237 case CRLF_STYLE_MIXED:
5238 eol = LoadResString (IDS_EOL_MIXED);
5239 pCmdUI->SetText (eol.c_str());
5240 pCmdUI->Enable (true);
5243 pCmdUI->SetText (nullptr);
5244 pCmdUI->Enable (false);
5249 pCmdUI->SetText (nullptr);
5250 pCmdUI->Enable (false);
5254 void CCrystalTextView::
5255 OnToggleBookmark (UINT nCmdID)
5257 int nBookmarkID = nCmdID - ID_EDIT_TOGGLE_BOOKMARK0;
5258 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5259 if (m_pTextBuffer != nullptr)
5261 DWORD dwFlags = GetLineFlags (m_ptCursorPos.y);
5262 DWORD dwMask = LF_BOOKMARK (nBookmarkID);
5263 m_pTextBuffer->SetLineFlag (m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0);
5267 void CCrystalTextView::
5268 OnGoBookmark (UINT nCmdID)
5270 int nBookmarkID = nCmdID - ID_EDIT_GO_BOOKMARK0;
5271 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
5272 if (m_pTextBuffer != nullptr)
5274 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5277 CPoint pt (0, nLine);
5278 ASSERT_VALIDTEXTPOS (pt);
5280 SetSelection (pt, pt);
5287 void CCrystalTextView::
5290 if (m_pTextBuffer != nullptr)
5292 for (int nBookmarkID = 0; nBookmarkID <= 9; nBookmarkID++)
5294 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
5297 m_pTextBuffer->SetLineFlag (nLine, LF_BOOKMARK (nBookmarkID), false);
5304 void CCrystalTextView::
5307 m_bCursorHidden = false;
5311 void CCrystalTextView::
5314 m_bCursorHidden = true;
5318 DROPEFFECT CCrystalTextView::
5321 return DROPEFFECT_COPY;
5324 void CCrystalTextView::
5325 OnDropSource (DROPEFFECT de)
5327 ASSERT (de == DROPEFFECT_COPY);
5330 HGLOBAL CCrystalTextView::
5333 PrepareSelBounds ();
5334 if (m_ptDrawSelStart == m_ptDrawSelEnd)
5338 GetText (m_ptDrawSelStart, m_ptDrawSelEnd, text);
5339 int cchText = text.GetLength();
5340 SIZE_T cbData = (cchText + 1) * sizeof(TCHAR);
5341 HGLOBAL hData =::GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, cbData);
5342 if (hData == nullptr)
5345 LPTSTR pszData = (LPTSTR)::GlobalLock (hData);
5346 if (pszData == nullptr)
5348 ::GlobalFree(hData);
5351 memcpy (pszData, text, cbData);
5352 ::GlobalUnlock (hData);
5354 m_ptDraggedTextBegin = m_ptDrawSelStart;
5355 m_ptDraggedTextEnd = m_ptDrawSelEnd;
5359 static const TCHAR *memstr(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)
5366 if (memcmp(p, str2, str2len * sizeof(TCHAR)) == 0)
5373 static const TCHAR *memistr(const TCHAR *str1, size_t str1len, const TCHAR *str2, size_t str2len)
5375 ASSERT(str1 && str2 && str2len > 0);
5376 for (const TCHAR *p = str1; p < str1 + str1len; ++p)
5378 if (toupper(*p) == toupper(*str2))
5381 for (i = 0; i < str2len; ++i)
5383 if (toupper(p[i]) != toupper(str2[i]))
5394 FindStringHelper (LPCTSTR pszLineBegin, size_t nLineLength, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch)
5396 if (dwFlags & FIND_REGEXP)
5403 if (pszFindWhat[0] == '^' && pszLineBegin != pszFindWhere)
5405 rxnode = RxCompile (pszFindWhat, (dwFlags & FIND_MATCH_CASE) != 0 ? RX_CASE : 0);
5406 if (rxnode && RxExec (rxnode, pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhere, rxmatch))
5408 pos = rxmatch->Open[0];
5409 ASSERT((rxmatch->Close[0] - rxmatch->Open[0]) < INT_MAX);
5410 nLen = static_cast<int>(rxmatch->Close[0] - rxmatch->Open[0]);
5416 ASSERT (pszFindWhere != nullptr);
5417 ASSERT (pszFindWhat != nullptr);
5419 int nLength = (int) _tcslen (pszFindWhat);
5420 LPCTSTR pszFindWhereOrig = pszFindWhere;
5425 if (dwFlags & FIND_MATCH_CASE)
5426 pszPos = memstr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5428 pszPos = memistr(pszFindWhere, nLineLength - (pszFindWhere - pszLineBegin), pszFindWhat, nLength);
5429 if (pszPos == nullptr)
5431 if ((dwFlags & FIND_WHOLE_WORD) == 0)
5432 return nCur + (int) (pszPos - pszFindWhere);
5433 if (pszPos > pszFindWhereOrig && xisalnum (pszPos[-1]))
5435 nCur += (int) (pszPos - pszFindWhere + 1);
5436 pszFindWhere = pszPos + 1;
5439 if (xisalnum (pszPos[nLength]))
5441 nCur += (int) (pszPos - pszFindWhere + 1);
5442 pszFindWhere = pszPos + 1;
5445 return nCur + (int) (pszPos - pszFindWhere);
5448 //~ ASSERT (false); // Unreachable
5452 * @brief Select text in editor.
5453 * @param [in] ptStartPos Star position for highlight.
5454 * @param [in] nLength Count of characters to highlight.
5455 * @param [in] bCursorToLeft If true cursor is positioned to Left-end of text
5456 * selection, if false cursor is positioned to right-end.
5458 bool CCrystalTextView::
5459 HighlightText (const CPoint & ptStartPos, int nLength,
5460 bool bCursorToLeft /*= false*/)
5462 ASSERT_VALIDTEXTPOS (ptStartPos);
5463 CPoint ptEndPos = ptStartPos;
5464 int nCount = GetLineLength (ptEndPos.y) - ptEndPos.x;
5465 if (nLength <= nCount)
5467 ptEndPos.x += nLength;
5471 while (nLength > nCount)
5473 nLength -= nCount + 1;
5474 nCount = GetLineLength (++ptEndPos.y);
5476 ptEndPos.x = nLength;
5478 ASSERT_VALIDTEXTPOS (m_ptCursorPos); // Probably 'nLength' is bigger than expected...
5480 m_ptCursorPos = bCursorToLeft ? ptStartPos : ptEndPos;
5481 m_ptAnchor = bCursorToLeft ? ptEndPos : ptStartPos;
5482 SetSelection (ptStartPos, ptEndPos);
5485 // Scrolls found text to middle of screen if out-of-screen
5486 int nScreenLines = GetScreenLines();
5487 if (ptStartPos.y < m_nTopLine || ptEndPos.y > m_nTopLine + nScreenLines)
5489 if (ptStartPos.y > nScreenLines / 2)
5490 ScrollToLine(ptStartPos.y - nScreenLines / 2);
5492 ScrollToLine(ptStartPos.y);
5493 UpdateSiblingScrollPos (false);
5495 EnsureVisible (ptStartPos, ptEndPos);
5499 bool CCrystalTextView::
5500 FindText (LPCTSTR pszText, const CPoint & ptStartPos, DWORD dwFlags,
5501 bool bWrapSearch, CPoint * pptFoundPos)
5503 if (m_pMarkers != nullptr)
5505 m_pMarkers->SetMarker(_T("EDITOR_MARKER"), pszText, dwFlags, COLORINDEX_MARKERBKGND0, false);
5506 if (m_pMarkers->GetEnabled())
5507 m_pMarkers->UpdateViews();
5509 int nLineCount = GetLineCount ();
5510 return FindTextInBlock (pszText, ptStartPos, CPoint (0, 0),
5511 CPoint (GetLineLength (nLineCount - 1), nLineCount - 1),
5512 dwFlags, bWrapSearch, pptFoundPos);
5515 int HowManyStr (LPCTSTR s, LPCTSTR m)
5519 const int l = (int) _tcslen (m);
5520 while ((p = _tcsstr (p, m)) != nullptr)
5528 int HowManyStr (LPCTSTR s, TCHAR c)
5532 while ((p = _tcschr (p, c)) != nullptr)
5540 bool CCrystalTextView::
5541 FindTextInBlock (LPCTSTR pszText, const CPoint & ptStartPosition,
5542 const CPoint & ptBlockBegin, const CPoint & ptBlockEnd,
5543 DWORD dwFlags, bool bWrapSearch, CPoint * pptFoundPos)
5545 CPoint ptCurrentPos = ptStartPosition;
5547 ASSERT (pszText != nullptr && _tcslen (pszText) > 0);
5548 ASSERT_VALIDTEXTPOS (ptCurrentPos);
5549 ASSERT_VALIDTEXTPOS (ptBlockBegin);
5550 ASSERT_VALIDTEXTPOS (ptBlockEnd);
5551 ASSERT (ptBlockBegin.y < ptBlockEnd.y || ptBlockBegin.y == ptBlockEnd.y &&
5552 ptBlockBegin.x <= ptBlockEnd.x);
5553 if (ptBlockBegin == ptBlockEnd)
5555 CWaitCursor waitCursor;
5556 if (ptCurrentPos.y < ptBlockBegin.y || ptCurrentPos.y == ptBlockBegin.y &&
5557 ptCurrentPos.x < ptBlockBegin.x)
5558 ptCurrentPos = ptBlockBegin;
5560 CString what = pszText;
5562 if (dwFlags & FIND_REGEXP)
5564 nEolns = HowManyStr (what, _T("\\n"));
5570 if (dwFlags & FIND_DIRECTION_UP)
5572 // Let's check if we deal with whole text.
5573 // At this point, we cannot search *up* in selection
5574 ASSERT (ptBlockBegin.x == 0 && ptBlockBegin.y == 0);
5575 ASSERT (ptBlockEnd.x == GetLineLength (GetLineCount () - 1) &&
5576 ptBlockEnd.y == GetLineCount () - 1);
5578 // Proceed as if we have whole text search.
5581 while (ptCurrentPos.y >= 0)
5585 if (dwFlags & FIND_REGEXP)
5587 for (int i = 0; i <= nEolns && ptCurrentPos.y >= i; i++)
5590 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y - i);
5593 nLineLength = GetLineLength (ptCurrentPos.y - i);
5595 line = _T ('\n') + line;
5599 nLineLength = ptCurrentPos.x != -1 ? ptCurrentPos.x : GetLineLength (ptCurrentPos.y - i);
5601 if (nLineLength > 0)
5603 item.SetString(pszChars, nLineLength);
5607 nLineLength = line.GetLength ();
5608 if (ptCurrentPos.x == -1)
5613 nLineLength = GetLineLength(ptCurrentPos.y);
5614 if (ptCurrentPos.x == -1)
5616 ptCurrentPos.x = nLineLength;
5618 else if( ptCurrentPos.x > nLineLength )
5619 ptCurrentPos.x = nLineLength;
5620 if (ptCurrentPos.x == -1)
5623 line.SetString (GetLineChars (ptCurrentPos.y), ptCurrentPos.x);
5626 ptrdiff_t nFoundPos = -1;
5627 int nMatchLen = what.GetLength();
5628 int nLineLen = line.GetLength();
5632 size_t nPosRel = ::FindStringHelper(line, nLineLen, static_cast<LPCTSTR>(line) + nPos, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5635 nFoundPos = nPos + nPosRel;
5636 nMatchLen = m_nLastFindWhatLen;
5637 nPos += nMatchLen == 0 ? 1 : nMatchLen;
5640 if( nFoundPos != -1 ) // Found text!
5642 ptCurrentPos.x = static_cast<int>(nFoundPos);
5643 *pptFoundPos = ptCurrentPos;
5648 if( ptCurrentPos.y >= 0 )
5649 ptCurrentPos.x = GetLineLength( ptCurrentPos.y );
5652 // Beginning of text reached
5656 // Start again from the end of text
5657 bWrapSearch = false;
5658 ptCurrentPos = CPoint (GetLineLength (GetLineCount () - 1), GetLineCount () - 1);
5665 while (ptCurrentPos.y <= ptBlockEnd.y)
5669 if (dwFlags & FIND_REGEXP)
5671 int nLines = m_pTextBuffer->GetLineCount ();
5672 for (int i = 0; i <= nEolns && ptCurrentPos.y + i < nLines; i++)
5674 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y + i);
5675 nLineLength = GetLineLength (ptCurrentPos.y + i);
5680 if (nLineLength > 0)
5682 int nLineLengthOld = line.GetLength();
5683 memcpy(line.GetBufferSetLength(nLineLengthOld + nLineLength) + nLineLengthOld, pszChars, nLineLength * sizeof(TCHAR));
5686 nLineLength = line.GetLength ();
5690 nLineLength = GetLineLength (ptCurrentPos.y) - ptCurrentPos.x;
5691 if (nLineLength <= 0)
5698 line.SetString(GetLineChars(ptCurrentPos.y), GetLineLength(ptCurrentPos.y));
5701 // Perform search in the line
5702 size_t nPos = ::FindStringHelper (line, line.GetLength (), static_cast<LPCTSTR>(line) + ptCurrentPos.x, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5705 if (m_pszMatched != nullptr)
5707 m_pszMatched = _tcsdup (line);
5710 CString item = line.Left (static_cast<LONG>(nPos));
5711 LPCTSTR current = _tcsrchr (item, _T('\n'));
5716 nEolns = HowManyStr (item, _T('\n'));
5719 ptCurrentPos.y += nEolns;
5720 ptCurrentPos.x = static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5724 ptCurrentPos.x += static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5726 if (ptCurrentPos.x < 0)
5731 ptCurrentPos.x += static_cast<LONG>(nPos);
5733 // Check of the text found is outside the block.
5734 if (ptCurrentPos.y == ptBlockEnd.y && ptCurrentPos.x >= ptBlockEnd.x)
5737 *pptFoundPos = ptCurrentPos;
5742 if (m_pszMatched != nullptr)
5744 m_pszMatched = nullptr;
5747 // Go further, text was not found
5752 // End of text reached
5756 // Start from the beginning
5757 bWrapSearch = false;
5758 ptCurrentPos = ptBlockBegin;
5762 //~ ASSERT (false); // Unreachable
5765 static DWORD ConvertSearchInfosToSearchFlags(const LastSearchInfos *lastSearch)
5767 DWORD dwSearchFlags = 0;
5768 if (lastSearch->m_bMatchCase)
5769 dwSearchFlags |= FIND_MATCH_CASE;
5770 if (lastSearch->m_bWholeWord)
5771 dwSearchFlags |= FIND_WHOLE_WORD;
5772 if (lastSearch->m_bRegExp)
5773 dwSearchFlags |= FIND_REGEXP;
5774 if (lastSearch->m_nDirection == 0)
5775 dwSearchFlags |= FIND_DIRECTION_UP;
5776 if (lastSearch->m_bNoWrap)
5777 dwSearchFlags |= FIND_NO_WRAP;
5778 if (lastSearch->m_bNoClose)
5779 dwSearchFlags |= FIND_NO_CLOSE;
5780 return dwSearchFlags;
5783 static void ConvertSearchFlagsToLastSearchInfos(LastSearchInfos *lastSearch, DWORD dwFlags)
5785 lastSearch->m_bMatchCase = (dwFlags & FIND_MATCH_CASE) != 0;
5786 lastSearch->m_bWholeWord = (dwFlags & FIND_WHOLE_WORD) != 0;
5787 lastSearch->m_bRegExp = (dwFlags & FIND_REGEXP) != 0;
5788 lastSearch->m_nDirection = (dwFlags & FIND_DIRECTION_UP) == 0;
5789 lastSearch->m_bNoWrap = (dwFlags & FIND_NO_WRAP) != 0;
5790 lastSearch->m_bNoClose = (dwFlags & FIND_NO_CLOSE) != 0;
5793 CPoint CCrystalTextView::
5794 GetSearchPos(DWORD dwSearchFlags)
5799 CPoint ptStart, ptEnd;
5800 GetSelection(ptStart, ptEnd);
5801 if( dwSearchFlags & FIND_DIRECTION_UP)
5802 ptSearchPos = ptStart;
5804 ptSearchPos = ptEnd;
5807 ptSearchPos = m_ptCursorPos;
5811 bool CCrystalTextView::
5812 FindText (const LastSearchInfos * lastSearch)
5815 DWORD dwSearchFlags = ConvertSearchInfosToSearchFlags(lastSearch);
5816 if (!FindText (lastSearch->m_sText, GetSearchPos(dwSearchFlags), dwSearchFlags, !lastSearch->m_bNoWrap,
5822 bool bCursorToLeft = (lastSearch->m_nDirection == 0);
5823 HighlightText (ptTextPos, m_nLastFindWhatLen, bCursorToLeft);
5825 // Save search parameters for 'F3' command
5826 m_bLastSearch = true;
5827 if (m_pszLastFindWhat != nullptr)
5828 free (m_pszLastFindWhat);
5829 m_pszLastFindWhat = _tcsdup (lastSearch->m_sText);
5830 m_dwLastSearchFlags = dwSearchFlags;
5832 // Save search parameters to registry
5833 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), m_dwLastSearchFlags));
5838 void CCrystalTextView::
5841 CWinApp *pApp = AfxGetApp ();
5842 ASSERT (pApp != nullptr);
5844 if (m_pFindTextDlg == nullptr)
5845 m_pFindTextDlg = new CFindTextDlg (this);
5847 LastSearchInfos * lastSearch = m_pFindTextDlg->GetLastSearchInfos();
5851 // Get the latest search parameters
5852 ConvertSearchFlagsToLastSearchInfos(lastSearch, m_dwLastSearchFlags);
5853 if (m_pszLastFindWhat != nullptr)
5854 lastSearch->m_sText = m_pszLastFindWhat;
5859 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), &dwFlags))
5861 ConvertSearchFlagsToLastSearchInfos(lastSearch, dwFlags);
5863 m_pFindTextDlg->UseLastSearch ();
5865 // Take the current selection, if any
5868 CPoint ptSelStart, ptSelEnd;
5869 GetSelection (ptSelStart, ptSelEnd);
5870 if (ptSelStart.y == ptSelEnd.y)
5871 GetText (ptSelStart, ptSelEnd, m_pFindTextDlg->m_sText);
5875 CPoint ptCursorPos = GetCursorPos ();
5876 CPoint ptStart = WordToLeft (ptCursorPos);
5877 CPoint ptEnd = WordToRight (ptCursorPos);
5878 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5879 GetText (ptStart, ptEnd, m_pFindTextDlg->m_sText);
5882 // Execute Find dialog
5884 // m_bShowInactiveSelection = true; // FP: removed because I like it
5885 m_pFindTextDlg->UpdateData(FALSE);
5886 m_pFindTextDlg->ShowWindow(SW_SHOW);
5887 // m_bShowInactiveSelection = false; // FP: removed because I like it
5891 void CCrystalTextView::
5894 bool bEnable = m_bLastSearch;
5895 // Show dialog if no last find text
5896 if (m_pszLastFindWhat == nullptr || _tcslen(m_pszLastFindWhat) == 0)
5900 sText = m_pszLastFindWhat;
5903 // If last find-text exists, cut it to first line
5904 bEnable = !sText.IsEmpty ();
5907 int pos = sText.FindOneOf (_T("\r\n"));
5909 sText = sText.Left (pos);
5913 // CTRL-F3 will find selected text..
5914 bool bControlKey = (::GetAsyncKeyState(VK_CONTROL)& 0x8000) != 0;
5915 // CTRL-SHIFT-F3 will find selected text, but opposite direction
5916 bool bShiftKey = (::GetAsyncKeyState(VK_SHIFT)& 0x8000) != 0;
5921 CPoint ptSelStart, ptSelEnd;
5922 GetSelection (ptSelStart, ptSelEnd);
5923 GetText (ptSelStart, ptSelEnd, sText);
5927 CPoint ptCursorPos = GetCursorPos ();
5928 CPoint ptStart = WordToLeft (ptCursorPos);
5929 CPoint ptEnd = WordToRight (ptCursorPos);
5930 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5931 GetText (ptStart, ptEnd, sText);
5933 if (!sText.IsEmpty())
5936 free(m_pszLastFindWhat);
5937 m_pszLastFindWhat = _tcsdup (sText);
5938 m_bLastSearch = true;
5942 m_dwLastSearchFlags |= FIND_DIRECTION_UP;
5944 m_dwLastSearchFlags &= ~FIND_DIRECTION_UP;
5949 // for correct backward search we need some changes:
5950 if (! FindText(sText, GetSearchPos(m_dwLastSearchFlags), m_dwLastSearchFlags,
5951 (m_dwLastSearchFlags & FIND_NO_WRAP) == 0, &ptFoundPos))
5954 prompt.Format (LoadResString(IDS_EDIT_TEXT_NOT_FOUND).c_str(), (LPCTSTR)sText);
5955 AfxMessageBox (prompt, MB_ICONINFORMATION);
5958 HighlightText (ptFoundPos, m_nLastFindWhatLen, (m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0);
5959 m_bMultipleSearch = true; // More search
5962 OnEditFind(); // No previous find, open Find-dialog
5965 void CCrystalTextView::
5966 OnUpdateEditRepeat (CCmdUI * pCmdUI)
5968 pCmdUI->Enable (true);
5971 void CCrystalTextView::
5976 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), &dwFlags))
5979 // Take the current selection, if any
5982 CPoint ptSelStart, ptSelEnd;
5983 GetSelection (ptSelStart, ptSelEnd);
5984 if (ptSelStart.y == ptSelEnd.y)
5985 GetText (ptSelStart, ptSelEnd, sText);
5989 CPoint ptCursorPos = GetCursorPos ();
5990 CPoint ptStart = WordToLeft (ptCursorPos);
5991 CPoint ptEnd = WordToRight (ptCursorPos);
5992 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5993 GetText (ptStart, ptEnd, sText);
5996 CTextMarkerDlg markerDlg(*m_pMarkers, sText, dwFlags);
5998 if (markerDlg.DoModal() == IDOK)
6000 // Save search parameters to registry
6001 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), markerDlg.GetLastSearchFlags()));
6002 m_pMarkers->SaveToRegistry();
6006 void CCrystalTextView::
6009 CWinApp *pApp = AfxGetApp ();
6010 ASSERT (pApp != nullptr);
6012 CPageSetupDialog dlg;
6014 if (!pApp->GetPrinterDeviceDefaults (&pd))
6017 dlg.m_psd.hDevMode = pd.hDevMode;
6018 dlg.m_psd.hDevNames = pd.hDevNames;
6019 dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS|PSD_MARGINS;
6020 dlg.m_psd.rtMargin.left = DEFAULT_PRINT_MARGIN;
6021 dlg.m_psd.rtMargin.right = DEFAULT_PRINT_MARGIN;
6022 dlg.m_psd.rtMargin.top = DEFAULT_PRINT_MARGIN;
6023 dlg.m_psd.rtMargin.bottom = DEFAULT_PRINT_MARGIN;
6025 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
6028 if (reg.LoadNumber (_T ("PageWidth"), &dwTemp))
6029 dlg.m_psd.ptPaperSize.x = dwTemp;
6030 if (reg.LoadNumber (_T ("PageHeight"), &dwTemp))
6031 dlg.m_psd.ptPaperSize.y = dwTemp;
6032 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
6033 dlg.m_psd.rtMargin.left = dwTemp;
6034 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
6035 dlg.m_psd.rtMargin.right = dwTemp;
6036 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
6037 dlg.m_psd.rtMargin.top = dwTemp;
6038 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
6039 dlg.m_psd.rtMargin.bottom = dwTemp;
6041 if (dlg.DoModal () == IDOK)
6044 if (reg1.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
6046 VERIFY (reg1.SaveNumber (_T ("PageWidth"), dlg.m_psd.ptPaperSize.x));
6047 VERIFY (reg1.SaveNumber (_T ("PageHeight"), dlg.m_psd.ptPaperSize.y));
6048 VERIFY (reg1.SaveNumber (_T ("PageLeft"), dlg.m_psd.rtMargin.left));
6049 VERIFY (reg1.SaveNumber (_T ("PageRight"), dlg.m_psd.rtMargin.right));
6050 VERIFY (reg1.SaveNumber (_T ("PageTop"), dlg.m_psd.rtMargin.top));
6051 VERIFY (reg1.SaveNumber (_T ("PageBottom"), dlg.m_psd.rtMargin.bottom));
6053 pApp->SelectPrinter (dlg.m_psd.hDevNames, dlg.m_psd.hDevMode, false);
6058 * @brief Adds/removes bookmark on given line.
6059 * This functions adds bookmark or removes bookmark on given line.
6060 * @param [in] Index (0-based) of line to add/remove bookmark.
6062 void CCrystalTextView::ToggleBookmark(int nLine)
6064 ASSERT(nLine >= 0 && nLine < GetLineCount());
6065 if (m_pTextBuffer != nullptr)
6067 DWORD dwFlags = GetLineFlags (nLine);
6068 DWORD dwMask = LF_BOOKMARKS;
6069 m_pTextBuffer->SetLineFlag (nLine, dwMask, (dwFlags & dwMask) == 0, false);
6070 const int nBookmarkLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARKS);
6071 if (nBookmarkLine >= 0)
6072 m_bBookmarkExist = true;
6074 m_bBookmarkExist = false;
6078 * @brief Called when Toggle Bookmark is selected from the GUI.
6080 void CCrystalTextView::
6083 ToggleBookmark(m_ptCursorPos.y);
6086 void CCrystalTextView::
6089 if (m_pTextBuffer != nullptr)
6091 int nLine = m_pTextBuffer->FindNextBookmarkLine (m_ptCursorPos.y);
6094 CPoint pt (0, nLine);
6095 ASSERT_VALIDTEXTPOS (pt);
6097 SetSelection (pt, pt);
6104 void CCrystalTextView::
6107 if (m_pTextBuffer != nullptr)
6109 int nLine = m_pTextBuffer->FindPrevBookmarkLine (m_ptCursorPos.y);
6112 CPoint pt (0, nLine);
6113 ASSERT_VALIDTEXTPOS (pt);
6115 SetSelection (pt, pt);
6122 void CCrystalTextView::
6123 OnClearAllBookmarks ()
6125 if (m_pTextBuffer != nullptr)
6127 int nLineCount = GetLineCount ();
6128 for (int I = 0; I < nLineCount; I++)
6130 if (m_pTextBuffer->GetLineFlags (I) & LF_BOOKMARKS)
6131 m_pTextBuffer->SetLineFlag (I, LF_BOOKMARKS, false);
6133 m_bBookmarkExist = false;
6137 void CCrystalTextView::
6138 OnUpdateNextBookmark (CCmdUI * pCmdUI)
6140 pCmdUI->Enable (m_bBookmarkExist);
6143 void CCrystalTextView::
6144 OnUpdatePrevBookmark (CCmdUI * pCmdUI)
6146 pCmdUI->Enable (m_bBookmarkExist);
6149 void CCrystalTextView::
6150 OnUpdateClearAllBookmarks (CCmdUI * pCmdUI)
6152 pCmdUI->Enable (m_bBookmarkExist);
6155 bool CCrystalTextView::
6161 void CCrystalTextView::
6162 SetViewTabs (bool bViewTabs)
6164 if (bViewTabs != m_bViewTabs)
6166 m_bViewTabs = bViewTabs;
6167 if (::IsWindow (m_hWnd))
6172 void CCrystalTextView::
6173 SetViewEols (bool bViewEols, bool bDistinguishEols)
6175 if (bViewEols != m_bViewEols || bDistinguishEols != m_bDistinguishEols)
6177 m_bViewEols = bViewEols;
6178 m_bDistinguishEols = bDistinguishEols;
6179 if (::IsWindow (m_hWnd))
6184 DWORD CCrystalTextView::
6190 void CCrystalTextView::
6191 SetFlags (DWORD dwFlags)
6193 if (m_dwFlags != dwFlags)
6195 m_dwFlags = dwFlags;
6196 if (::IsWindow (m_hWnd))
6201 bool CCrystalTextView::
6204 return m_bTopMargin;
6207 bool CCrystalTextView::
6208 GetSelectionMargin ()
6210 return m_bSelMargin;
6213 bool CCrystalTextView::
6214 GetViewLineNumbers () const
6216 return m_bViewLineNumbers;
6219 int CCrystalTextView::
6220 GetTopMarginHeight()
6224 return GetLineHeight();
6228 * @brief Calculate margin area width.
6229 * This function calculates needed margin width. Needed width is (approx.)
6230 * one char-width for bookmark etc markers and rest to linenumbers (if
6231 * visible). If we have linenumbers visible we need to adjust width so that
6232 * biggest number fits.
6233 * @return Margin area width in pixels.
6235 int CCrystalTextView::
6236 GetMarginWidth (CDC *pdc /*= nullptr*/)
6238 int nMarginWidth = 0;
6240 if (m_bViewLineNumbers)
6242 const int nLines = GetLineCount();
6245 for (n = 1; n <= nLines; n *= 10)
6247 nMarginWidth += GetCharWidth () * nNumbers;
6249 nMarginWidth += 2; // Small gap when symbol part disabled
6254 if (pdc == nullptr || !pdc->IsPrinting ())
6255 nMarginWidth += MARGIN_ICON_WIDTH + 7; // Width for icon markers and some margin
6259 if (pdc == nullptr || !pdc->IsPrinting ())
6260 nMarginWidth += MARGIN_REV_WIDTH; // Space for revision marks
6263 return nMarginWidth;
6266 bool CCrystalTextView::
6270 return m_bSmoothScroll;
6273 void CCrystalTextView::SetSmoothScroll (bool bSmoothScroll)
6275 m_bSmoothScroll = bSmoothScroll;
6279 bool CCrystalTextView::
6280 GetDisableDragAndDrop ()
6283 return m_bDisableDragAndDrop;
6287 void CCrystalTextView::SetDisableDragAndDrop (bool bDDAD)
6289 m_bDisableDragAndDrop = bDDAD;
6292 void CCrystalTextView::CopyProperties (CCrystalTextView *pSource)
6294 m_nTopLine = pSource->m_nTopLine;
6295 m_nTopSubLine = pSource->m_nTopSubLine;
6296 m_bViewTabs = pSource->m_bViewTabs;
6297 m_bViewEols = pSource->m_bViewEols;
6298 m_bDistinguishEols = pSource->m_bDistinguishEols;
6299 m_bTopMargin = pSource->m_bTopMargin;
6300 m_bSelMargin = pSource->m_bSelMargin;
6301 m_bViewLineNumbers = pSource->m_bViewLineNumbers;
6302 m_bSmoothScroll = pSource->m_bSmoothScroll;
6303 m_bWordWrap = pSource->m_bWordWrap;
6304 m_pColors = pSource->m_pColors;
6305 m_pMarkers = pSource->m_pMarkers;
6306 m_bDisableDragAndDrop = pSource->m_bDisableDragAndDrop;
6307 SetTextType(pSource->m_CurSourceDef);
6308 SetFont (pSource->m_lfBaseFont);
6312 // Mouse wheel event. zDelta is in multiples of 120.
6313 // Divide by 40 so each click is 3 lines. I know some
6314 // drivers let you set the ammount of scroll, but I
6315 // don't know how to retrieve this or if they just
6316 // adjust the zDelta you get here.
6317 BOOL CCrystalTextView::
6318 OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
6320 SCROLLINFO si = {0};
6321 si.cbSize = sizeof (si);
6322 si.fMask = SIF_PAGE | SIF_RANGE;
6323 VERIFY (GetScrollInfo (SB_VERT, &si));
6325 int nNewTopSubLine= m_nTopSubLine - zDelta / 40;
6327 if (nNewTopSubLine < 0)
6329 if (nNewTopSubLine > (si.nMax - (signed int)si.nPage + 1))
6330 nNewTopSubLine = si.nMax - si.nPage + 1;
6332 ScrollToSubLine(nNewTopSubLine, true);
6333 UpdateSiblingScrollPos(false);
6335 return CView::OnMouseWheel (nFlags, zDelta, pt);
6338 void CCrystalTextView::
6339 OnMouseHWheel (UINT nFlags, short zDelta, CPoint pt)
6341 SCROLLINFO si = { sizeof(si) };
6342 si.fMask = SIF_POS | SIF_RANGE;
6343 VERIFY (GetScrollInfo (SB_HORZ, &si));
6345 int nCurPos = si.nPos + zDelta;
6346 if (nCurPos < si.nMin)
6348 else if (nCurPos > si.nMax)
6351 ScrollToChar (nCurPos, true);
6353 UpdateSiblingScrollPos (true);
6355 CView::OnMouseHWheel (nFlags, zDelta, pt);
6358 void CCrystalTextView::
6359 OnSourceType (UINT nId)
6361 SetTextType ((CrystalLineParser::TextType) (nId - ID_SOURCE_PLAIN));
6365 void CCrystalTextView::
6366 OnUpdateSourceType (CCmdUI * pCmdUI)
6368 pCmdUI->SetRadio (CrystalLineParser::m_SourceDefs + (pCmdUI->m_nID - ID_SOURCE_PLAIN) == m_CurSourceDef);
6374 static LPCTSTR braces = _T("{}()[]<>");
6375 LPCTSTR pos = _tcschr (braces, c);
6376 return pos != nullptr ? (int) (pos - braces) + 1 : 0;
6380 bracetype (LPCTSTR s)
6384 return bracetype (*s);
6387 void CCrystalTextView::
6390 CPoint ptCursorPos = GetCursorPos ();
6391 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6392 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y), pszEnd = pszText + ptCursorPos.x;
6393 bool bAfter = false;
6395 if (ptCursorPos.x < nLength)
6397 nType = bracetype (*pszEnd);
6402 else if (ptCursorPos.x > 0)
6404 nType = bracetype (pszEnd[-1]);
6408 else if (ptCursorPos.x > 0)
6410 nType = bracetype (pszEnd[-1]);
6415 int nOther, nCount = 0, nComment = 0;
6418 nOther = ((nType - 1) ^ 1) + 1;
6424 nOther = ((nType - 1) ^ 1) + 1;
6428 LPCTSTR pszOpenComment = m_CurSourceDef->opencomment,
6429 pszCloseComment = m_CurSourceDef->closecomment,
6430 pszCommentLine = m_CurSourceDef->commentline, pszTest;
6431 int nOpenComment = (int) _tcslen (pszOpenComment),
6432 nCloseComment = (int) _tcslen (pszCloseComment),
6433 nCommentLine = (int) _tcslen (pszCommentLine);
6438 while (--pszEnd >= pszText)
6440 pszTest = pszEnd - nOpenComment + 1;
6441 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszOpenComment, nOpenComment))
6445 if (--pszEnd < pszText)
6450 pszTest = pszEnd - nCloseComment + 1;
6451 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCloseComment, nCloseComment))
6455 if (--pszEnd < pszText)
6462 pszTest = pszEnd - nCommentLine + 1;
6463 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCommentLine, nCommentLine))
6467 if (bracetype (*pszEnd) == nType)
6471 else if (bracetype (*pszEnd) == nOther)
6475 ptCursorPos.x = (LONG) (pszEnd - pszText);
6478 SetCursorPos (ptCursorPos);
6479 SetSelection (ptCursorPos, ptCursorPos);
6480 SetAnchor (ptCursorPos);
6481 EnsureVisible (ptCursorPos);
6489 ptCursorPos.x = m_pTextBuffer->GetLineLength (--ptCursorPos.y);
6490 pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6491 pszEnd = pszText + ptCursorPos.x;
6499 LPCTSTR pszBegin = pszText;
6501 pszEnd = pszBegin + nLength;
6502 int nLines = m_pTextBuffer->GetLineCount ();
6505 while (pszText < pszEnd)
6507 pszTest = pszText + nCloseComment;
6508 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCloseComment, nCloseComment))
6512 if (pszText > pszEnd)
6517 pszTest = pszText + nOpenComment;
6518 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszOpenComment, nOpenComment))
6522 if (pszText > pszEnd)
6529 pszTest = pszText + nCommentLine;
6530 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCommentLine, nCommentLine))
6534 if (bracetype (*pszText) == nType)
6538 else if (bracetype (*pszText) == nOther)
6542 ptCursorPos.x = (LONG) (pszText - pszBegin);
6545 SetCursorPos (ptCursorPos);
6546 SetSelection (ptCursorPos, ptCursorPos);
6547 SetAnchor (ptCursorPos);
6548 EnsureVisible (ptCursorPos);
6555 if (ptCursorPos.y < nLines)
6558 nLength = m_pTextBuffer->GetLineLength (++ptCursorPos.y);
6559 pszBegin = pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
6560 pszEnd = pszBegin + nLength;
6569 void CCrystalTextView::
6570 OnUpdateMatchBrace (CCmdUI * pCmdUI)
6572 CPoint ptCursorPos = GetCursorPos ();
6573 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
6574 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y) + ptCursorPos.x;
6575 pCmdUI->Enable (ptCursorPos.x < nLength && (bracetype (*pszText) || ptCursorPos.x > 0 && bracetype (pszText[-1])) || ptCursorPos.x > 0 && bracetype (pszText[-1]));
6578 void CCrystalTextView::
6581 CGotoDlg dlg (this);
6585 void CCrystalTextView::
6586 OnUpdateToggleSourceHeader (CCmdUI * pCmdUI)
6588 pCmdUI->Enable (m_CurSourceDef->type == CrystalLineParser::SRC_C);
6591 void CCrystalTextView::
6592 OnToggleSourceHeader ()
6594 if (m_CurSourceDef->type == CrystalLineParser::SRC_C)
6596 CDocument *pDoc = GetDocument ();
6597 ASSERT (pDoc != nullptr);
6598 CString sFilePath = pDoc->GetPathName (), sOriginalPath = sFilePath;
6599 if (!_tcsicmp (sFilePath.Right (2), _T (".c")))
6601 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ('h');
6603 else if (!_tcsicmp (sFilePath.Right (4), _T (".cpp")))
6605 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('h');
6607 else if (!_tcsicmp (sFilePath.Right (4), _T (".inl")))
6609 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6610 if (!FileExist(sFilePath))
6612 sFilePath = sFilePath + _T ("pp");
6615 else if (!_tcsicmp (sFilePath.Right (4), _T (".hpp")))
6617 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6618 if (!FileExist(sFilePath))
6620 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6621 if (!FileExist(sFilePath))
6623 sFilePath = sFilePath + _T ("pp");
6627 else if (!_tcsicmp (sFilePath.Right (2), _T (".h")))
6629 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ("hpp");
6630 if (!FileExist(sFilePath))
6632 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6633 if (!FileExist(sFilePath))
6635 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6636 if (!FileExist(sFilePath))
6638 sFilePath = sFilePath + _T ("pp");
6643 if (FileExist(sFilePath))
6645 if (!m_bSingle || !pDoc->IsModified () || pDoc->DoSave (sOriginalPath))
6647 AfxGetApp ()->OpenDocumentFile (sFilePath);
6650 m_ptCursorLast.x = m_ptCursorLast.y = 0;
6651 ASSERT_VALIDTEXTPOS (m_ptCursorLast);
6652 CPoint ptCursorPos = m_ptCursorLast;
6653 SetCursorPos (ptCursorPos);
6654 SetSelection (ptCursorPos, ptCursorPos);
6655 SetAnchor (ptCursorPos);
6656 EnsureVisible (ptCursorPos);
6664 void CCrystalTextView::
6665 OnUpdateTopMargin (CCmdUI * pCmdUI)
6667 pCmdUI->SetCheck (m_bTopMargin);
6670 void CCrystalTextView::
6673 ASSERT (m_CurSourceDef != nullptr);
6675 m_CurSourceDef->flags &= ~SRCOPT_TOPMARGIN;
6677 m_CurSourceDef->flags |= SRCOPT_TOPMARGIN;
6678 SetTopMargin (!m_bTopMargin);
6681 void CCrystalTextView::
6682 OnUpdateSelMargin (CCmdUI * pCmdUI)
6684 pCmdUI->SetCheck (m_bSelMargin);
6687 void CCrystalTextView::
6690 ASSERT (m_CurSourceDef != nullptr);
6692 m_CurSourceDef->flags &= ~SRCOPT_SELMARGIN;
6694 m_CurSourceDef->flags |= SRCOPT_SELMARGIN;
6695 SetSelectionMargin (!m_bSelMargin);
6698 void CCrystalTextView::
6699 OnUpdateWordWrap (CCmdUI * pCmdUI)
6701 pCmdUI->SetCheck (m_bWordWrap);
6704 void CCrystalTextView::
6707 ASSERT (m_CurSourceDef != nullptr);
6710 m_CurSourceDef->flags &= ~SRCOPT_WORDWRAP;
6711 SetWordWrapping (false);
6715 m_CurSourceDef->flags |= SRCOPT_WORDWRAP;
6716 SetWordWrapping (true);
6720 void CCrystalTextView::
6724 RedrawWindow (nullptr, nullptr, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ERASENOW);
6727 void CCrystalTextView::
6728 OnToggleColumnSelection ()
6730 m_bRectangularSelection = !m_bRectangularSelection;
6734 void CCrystalTextView::SetRenderingMode(RENDERING_MODE nRenderingMode)
6737 if (nRenderingMode == RENDERING_MODE_GDI)
6738 m_pCrystalRenderer.reset(new CCrystalRendererGDI());
6740 m_pCrystalRenderer.reset(new CCrystalRendererDirectWrite(nRenderingMode));
6741 m_pCrystalRenderer->SetFont(m_lfBaseFont);
6743 m_nRenderingMode = nRenderingMode;
6747 bool CCrystalTextView::GetWordWrapping() const
6752 void CCrystalTextView::SetWordWrapping( bool bWordWrap )
6754 m_bWordWrap = bWordWrap;
6756 if( IsWindow( m_hWnd ) )
6759 InvalidateScreenRect();
6763 CCrystalParser *CCrystalTextView::SetParser( CCrystalParser *pParser )
6765 CCrystalParser *pOldParser = m_pParser;
6767 m_pParser = pParser;
6769 if( pParser != nullptr )
6770 pParser->m_pTextView = this;
6776 bool CCrystalTextView::GetEnableHideLines () const
6778 return m_bHideLines;
6781 void CCrystalTextView::SetEnableHideLines (bool bHideLines)
6783 m_bHideLines = bHideLines;
6787 * @brief Return whether a line is visible.
6789 bool CCrystalTextView::GetLineVisible (int nLineIndex) const
6791 return !m_bHideLines || !(GetLineFlags (nLineIndex) & LF_INVISIBLE);
6795 // incremental search imlementation
6796 BOOL CCrystalTextView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO *pHandlerInfo )
6798 // just look for commands
6799 if( nCode != CN_COMMAND || pExtra != nullptr )
6800 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6803 // each command that is not related to incremental search
6804 // ends the incremental search
6805 if( nID == ID_EDIT_FIND_INCREMENTAL_FORWARD ||
6806 nID == ID_EDIT_FIND_INCREMENTAL_BACKWARD ||
6807 nID == ID_EDIT_DELETE_BACK )
6808 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6810 if( nID >= ID_EDIT_FIRST && nID <= ID_EDIT_LAST )
6811 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6813 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6816 void CCrystalTextView::OnChar( wchar_t nChar, UINT nRepCnt, UINT nFlags )
6818 CView::OnChar( nChar, nRepCnt, nFlags );
6820 // we only have to handle character-input, if we are in incremental search mode
6821 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6824 // exit incremental search, when Escape is pressed
6825 if( nChar == VK_ESCAPE )
6827 // if not end incremental search
6828 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6829 SetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6830 SetCursorPos( m_cursorPosBeforeIncrementalSearch );
6831 EnsureVisible( m_cursorPosBeforeIncrementalSearch );
6835 // exit incremental search without destroying selection
6836 if( nChar == VK_RETURN )
6838 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6842 // is the character valid for incremental search?
6843 if( !_istgraph( nChar ) && !(nChar == _T(' ')) && !(nChar == _T('\t')) )
6845 // if not end incremental search
6846 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6850 // if last search was not successfull do not add a new character
6851 if( !m_bIncrementalFound )
6853 MessageBeep( MB_OK );
6857 // add character to incremental search string and search
6858 *m_pstrIncrementalSearchString += (TCHAR) nChar;
6859 OnEditFindIncremental();
6862 LRESULT CCrystalTextView::OnImeStartComposition(WPARAM wParam, LPARAM lParam) /* IME */
6864 UpdateCompositionWindowFont();
6865 UpdateCompositionWindowPos();
6867 return DefWindowProc(WM_IME_STARTCOMPOSITION, wParam, lParam);
6870 void CCrystalTextView::OnEditDeleteBack()
6872 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6875 // remove last character from search string
6876 if( m_pstrIncrementalSearchString->IsEmpty() )
6879 *m_pstrIncrementalSearchString = m_pstrIncrementalSearchString->Left( m_pstrIncrementalSearchString->GetLength() - 1 );
6880 OnEditFindIncremental();
6884 void CCrystalTextView::OnEditFindIncremental( bool bFindNextOccurence /*= false*/ )
6886 // when string is empty, then goto position where the search starts
6887 if( m_pstrIncrementalSearchString->IsEmpty() )
6889 SetSelection( m_incrementalSearchStartPos, m_incrementalSearchStartPos );
6890 SetCursorPos( m_incrementalSearchStartPos );
6891 EnsureVisible( m_incrementalSearchStartPos );
6895 // otherwise search next occurence of search string,
6896 // starting at current cursor position
6897 CPoint matchStart, matchEnd;
6899 // calculate start point for search
6900 if( bFindNextOccurence )
6902 CPoint selStart, selEnd;
6903 GetSelection( selStart, selEnd );
6904 m_incrementalSearchStartPos = (m_bIncrementalSearchBackward)? selStart : selEnd;
6907 m_bIncrementalFound = FindText(
6908 *m_pstrIncrementalSearchString,
6909 m_incrementalSearchStartPos,
6910 m_bIncrementalSearchBackward? FIND_DIRECTION_UP : 0,
6914 if( !m_bIncrementalFound )
6916 MessageBeep( MB_OK );
6920 // select found text and set cursor to end of match
6921 matchEnd = matchStart;
6922 matchEnd.x+= m_pstrIncrementalSearchString->GetLength();
6923 SetSelection( matchStart, matchEnd );
6924 SetCursorPos( matchEnd );
6925 EnsureVisible( matchEnd );
6930 void CCrystalTextView::OnEditFindIncrementalForward()
6932 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6935 if( !m_pstrIncrementalSearchString->IsEmpty() )
6936 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6937 m_pstrIncrementalSearchString->Empty();
6938 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6939 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6941 else if( m_bIncrementalSearchForward )
6943 if( m_pstrIncrementalSearchString->IsEmpty() )
6945 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6946 m_pstrIncrementalSearchStringOld->Empty();
6947 OnEditFindIncremental();
6950 OnEditFindIncremental( true );
6955 m_bIncrementalSearchForward = true;
6956 m_bIncrementalSearchBackward = false;
6957 m_bIncrementalFound = true;
6958 OnEditFindIncremental();
6961 void CCrystalTextView::OnEditFindIncrementalBackward()
6963 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6966 if( !m_pstrIncrementalSearchString->IsEmpty() )
6967 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6968 m_pstrIncrementalSearchString->Empty();
6969 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6970 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6972 else if( m_bIncrementalSearchBackward )
6974 if( m_pstrIncrementalSearchString->IsEmpty() )
6976 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6977 m_pstrIncrementalSearchStringOld->Empty();
6978 OnEditFindIncremental();
6981 OnEditFindIncremental( true );
6986 m_bIncrementalSearchForward = false;
6987 m_bIncrementalSearchBackward = true;
6988 m_bIncrementalFound = true;
6989 OnEditFindIncremental();
6992 void CCrystalTextView::OnUpdateEditFindIncrementalForward(CCmdUI* pCmdUI)
6994 if (m_pTextBuffer != nullptr)
6996 int nLines = m_pTextBuffer->GetLineCount ();
6997 int nChars = m_pTextBuffer->GetLineLength (m_ptCursorPos.y);
6998 pCmdUI->Enable(m_ptCursorPos.y < nLines - 1 || m_ptCursorPos.x < nChars);
7001 pCmdUI->Enable(false);
7004 void CCrystalTextView::OnUpdateEditFindIncrementalBackward(CCmdUI* pCmdUI)
7006 if (m_pTextBuffer != nullptr)
7008 pCmdUI->Enable(m_ptCursorPos.y > 0 || m_ptCursorPos.x > 0);
7011 pCmdUI->Enable(false);
7014 void CCrystalTextView::OnUpdateStatusMessage( CStatusBar *pStatusBar )
7016 static bool bUpdatedAtLastCall = false;
7018 ASSERT( pStatusBar != nullptr && IsWindow( pStatusBar->m_hWnd ) );
7019 if( pStatusBar == nullptr || !IsWindow( pStatusBar->m_hWnd ) )
7022 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
7024 if( bUpdatedAtLastCall )
7025 pStatusBar->SetPaneText( 0, LoadResString(AFX_IDS_IDLEMESSAGE).c_str() );
7027 bUpdatedAtLastCall = false;
7034 if( !m_bIncrementalFound )
7035 formatid = IDS_FIND_INCREMENTAL_FAILED;
7036 else if( m_bIncrementalSearchForward )
7037 formatid = IDS_FIND_INCREMENTAL_FORWARD;
7038 else if( m_bIncrementalSearchBackward )
7039 formatid = IDS_FIND_INCREMENTAL_BACKWARD;
7042 strFormat.Format( LoadResString(formatid).c_str(), (LPCTSTR)*m_pstrIncrementalSearchString );
7044 pStatusBar->SetPaneText( 0, strFormat );
7045 bUpdatedAtLastCall = false;
7049 bool CCrystalTextView::IsTextBufferInitialized () const
7051 return m_pTextBuffer && m_pTextBuffer->IsTextBufferInitialized();
7054 CString CCrystalTextView::GetTextBufferEol(int nLine) const
7056 return m_pTextBuffer->GetLineEol(nLine);
7059 void CCrystalTextView::SetMarkersContext(CCrystalTextMarkers * pMarkers)
7061 pMarkers->AddView(this);
7062 m_pMarkers = pMarkers;
7066 int CCrystalTextView::GetCharCellCountUnicodeChar(const wchar_t *pch)
7069 if (!m_bChWidthsCalculated[ch/256])
7071 if (U16_IS_SURROGATE(ch) && U16_IS_SURROGATE_LEAD(ch))
7073 return wcwidth(U16_GET_SUPPLEMENTARY(ch, pch[1]));
7077 int nWidthArray[256];
7078 wchar_t nStart = ch/256*256;
7079 wchar_t nEnd = nStart + 255;
7080 m_pCrystalRenderer->GetCharWidth(nStart, nEnd, nWidthArray);
7081 int nCharWidth = GetCharWidth();
7082 for (int i = 0; i < 256; i++)
7084 if (nCharWidth * 15 < nWidthArray[i] * 10)
7085 m_iChDoubleWidthFlags[(nStart+i)/32] |= 1 << (i % 32);
7088 wchar_t ch2 = static_cast<wchar_t>(nStart + i);
7089 if (wcwidth(ch2) > 1)
7090 m_iChDoubleWidthFlags[(nStart + i) / 32] |= 1 << (i % 32);
7093 m_bChWidthsCalculated[ch / 256] = true;
7096 if (m_iChDoubleWidthFlags[ch / 32] & (1 << (ch % 32)))
7103 /** @brief Reset computed unicode character widths. */
7104 void CCrystalTextView::ResetCharWidths ()
7107 ZeroMemory(m_bChWidthsCalculated, sizeof(m_bChWidthsCalculated));
7108 ZeroMemory(m_iChDoubleWidthFlags, sizeof(m_iChDoubleWidthFlags));
7112 // This function assumes selection is in one line
7113 void CCrystalTextView::EnsureVisible (CPoint ptStart, CPoint ptEnd)
7115 // Scroll vertically
7117 int nSubLineCount = GetSubLineCount();
7118 int nNewTopSubLine = m_nTopSubLine;
7121 CharPosToPoint( ptStart.y, ptStart.x, subLinePos );
7122 subLinePos.y += GetSubLineIndex( ptStart.y );
7124 if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
7125 nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
7126 if( subLinePos.y < nNewTopSubLine )
7127 nNewTopSubLine = subLinePos.y;
7129 if( nNewTopSubLine < 0 )
7131 if( nNewTopSubLine >= nSubLineCount )
7132 nNewTopSubLine = nSubLineCount - 1;
7134 if ( !m_bWordWrap && !m_bHideLines )
7136 // WINMERGE: This line fixes (cursor) slowdown after merges!
7137 // I don't know exactly why, but propably we are setting
7138 // m_nTopLine to zero in ResetView() and are not setting to
7139 // valid value again. Maybe this is a good place to set it?
7140 m_nTopLine = nNewTopSubLine;
7145 GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
7148 if( nNewTopSubLine != m_nTopSubLine )
7150 ScrollToSubLine( nNewTopSubLine );
7152 UpdateSiblingScrollPos( false );
7155 // Scroll horizontally
7157 // we do not need horizontally scrolling, if we wrap the words
7158 if( GetTextLayoutMode () == TEXTLAYOUT_WORDWRAP )
7161 int nActualPos = CalculateActualOffset (ptStart.y, ptStart.x);
7162 int nNewOffset = m_nOffsetChar;
7163 const int nScreenChars = GetScreenChars ();
7165 if (ptStart == ptEnd)
7167 // Keep 5 chars visible right to cursor
7168 if (nActualPos > nNewOffset + nScreenChars - 5)
7170 // Add 10 chars width space after line
7171 nNewOffset = nActualPos - nScreenChars + 10;
7173 // Keep 5 chars visible left to cursor
7174 if (nActualPos < nNewOffset + 5)
7176 // Jump by 10 char steps, so user sees previous letters too
7177 nNewOffset = nActualPos - 10;
7182 int nActualEndPos = CalculateActualOffset (ptEnd.y, ptEnd.x);
7183 const int nBeginOffset = nActualPos - m_nOffsetChar;
7184 const int nEndOffset = nActualEndPos - m_nOffsetChar;
7185 const int nSelLen = nActualEndPos - nActualPos;
7187 // Selection fits to screen, scroll whole selection visible
7188 if (nSelLen < nScreenChars)
7190 // Begin of selection not visible
7191 if (nBeginOffset > nScreenChars)
7193 // Scroll so that there is max 5 chars margin at end
7194 if (nScreenChars - nSelLen > 5)
7195 nNewOffset = nActualPos + 5 - nScreenChars + nSelLen;
7197 nNewOffset = nActualPos - 5;
7199 else if (nBeginOffset < 0)
7201 // Scroll so that there is max 5 chars margin at begin
7202 if (nScreenChars - nSelLen >= 5)
7203 nNewOffset = nActualPos - 5;
7205 nNewOffset = nActualPos - 5 - nScreenChars + nSelLen;
7207 // End of selection not visible
7208 else if (nEndOffset > nScreenChars ||
7211 nNewOffset = nActualPos - 5;
7214 else // Selection does not fit screen so scroll to begin of selection
7216 nNewOffset = nActualPos - 5;
7220 // Horiz scroll limit to longest line + one screenwidth
7221 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
7222 if (nNewOffset >= nMaxLineLen + nScreenChars)
7223 nNewOffset = nMaxLineLen + nScreenChars - 1;
7227 if (m_nOffsetChar != nNewOffset)
7229 ScrollToChar (nNewOffset);
7231 UpdateSiblingScrollPos (true);
7235 // Analyze the first line of file to detect its type
7236 // Mainly it works for xml files
7237 bool CCrystalTextView::
7238 SetTextTypeByContent (LPCTSTR pszContent)
7240 RxNode *rxnode = nullptr;
7243 if (::FindStringHelper(pszContent, _tcslen(pszContent), pszContent, _T("^\\s*\\<\\?xml\\s+.+?\\?\\>\\s*$"),
7244 FIND_REGEXP, nLen, rxnode, &rxmatch) == 0)
7248 return SetTextType(CrystalLineParser::SRC_XML);
7255 void CCrystalTextView::
7256 AutoFitColumn (int nColumn)
7258 int nLastColumn = 0;
7259 int nLastColumnWidth = 0;
7260 const int nTabSize = GetTabSize ();
7261 std::vector<int> aColumnWidths;
7262 const int nScreenChars = GetScreenChars ();
7263 const int nMaxColumnWidth = nScreenChars < 1 ? 1 : nScreenChars - 1;
7264 for (auto& pbuf : m_pTextBuffer->GetTextBufferList ())
7266 const TCHAR sep = pbuf->GetFieldDelimiter ();
7267 const int quote = pbuf->GetFieldEnclosure ();
7268 const int nLineCount = pbuf->GetLineCount ();
7269 for (int i = 0; i < nLineCount; ++i)
7271 bool bInQuote = false;
7273 int nColumnWidth = 0;
7274 const TCHAR* pszChars = pbuf->GetLineChars (i);
7275 const size_t nLineLength = pbuf->GetFullLineLength (i);
7276 for (size_t j = 0; j < nLineLength; j += U16_IS_SURROGATE (pszChars[j]) ? 2 : 1)
7278 bool bDelimiterOrNewLine = false;
7279 TCHAR c = pszChars[j];
7281 bInQuote = !bInQuote;
7282 if (!bInQuote && c == sep)
7284 bDelimiterOrNewLine = true;
7287 else if (c == '\r' || c == '\n')
7293 if (j == nLineLength - 1 || pszChars[j + 1] != '\n')
7296 bDelimiterOrNewLine = true;
7301 if (j > 0 && pszChars[j - 1] == '\r')
7305 bDelimiterOrNewLine = true;
7309 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7314 nColumnWidth += GetCharCellCountFromChar (pszChars + j);
7316 if (bDelimiterOrNewLine)
7318 if (nColumnWidth > nMaxColumnWidth)
7319 nColumnWidth = nMaxColumnWidth;
7320 if (static_cast<int>(aColumnWidths.size ()) < nColumn2 + 1)
7321 aColumnWidths.resize (nColumn2 + 1, nTabSize);
7322 if (aColumnWidths[nColumn2] < nColumnWidth)
7323 aColumnWidths[nColumn2] = nColumnWidth;
7329 if (nLastColumn < nColumn2)
7331 nLastColumn = nColumn2;
7332 nLastColumnWidth = 0;
7334 if (nLastColumnWidth < nColumnWidth)
7335 nLastColumnWidth = nColumnWidth;
7339 aColumnWidths.resize (nLastColumn + 1, nTabSize);
7340 if (aColumnWidths[nLastColumn] < nLastColumnWidth)
7341 aColumnWidths[nLastColumn] = nLastColumnWidth;
7343 for (size_t nColumn2 = 0; nColumn2 < aColumnWidths.size (); ++nColumn2)
7345 if (nColumn == -1 || nColumn == static_cast<int>(nColumn2))
7346 m_pTextBuffer->SetColumnWidth (static_cast<int>(nColumn2), aColumnWidths[nColumn2]);
7348 m_pTextBuffer->InvalidateColumns ();
7351 CCrystalTextView::TextLayoutMode CCrystalTextView::GetTextLayoutMode () const
7353 if (m_pTextBuffer && m_pTextBuffer->GetTableEditing ())
7354 return m_bWordWrap ? TEXTLAYOUT_TABLE_WORDWRAP : TEXTLAYOUT_TABLE_NOWORDWRAP;
7355 return m_bWordWrap ? TEXTLAYOUT_WORDWRAP : TEXTLAYOUT_NOWORDWRAP;
7358 ////////////////////////////////////////////////////////////////////////////