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
87 // ID line follows -- this is updated by SVN
88 // $Id: ccrystaltextview.cpp 7117 2010-02-01 14:24:51Z sdottaka $
94 #include <imm.h> /* IME */
97 #include "ccrystaltextview.h"
98 #include "ccrystaltextbuffer.h"
99 #include "cfindtextdlg.h"
100 #include "ctextmarkerdlg.h"
101 #include "fpattern.h"
103 #include "registry.h"
105 #include "ViewableWhitespace.h"
106 #include "SyntaxColors.h"
107 #include "ccrystaltextmarkers.h"
108 #include "string_util.h"
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 /** @brief Maximum tab-char width. */
143 const UINT MAX_TAB_LEN = 64;
144 /** @brief Width of revision marks. */
145 const UINT MARGIN_REV_WIDTH = 3;
146 /** @brief Width of icons printed in the margin. */
147 const UINT MARGIN_ICON_WIDTH = 12;
148 /** @brief Height of icons printed in the margin. */
149 const UINT MARGIN_ICON_HEIGHT = 12;
151 /** @brief Color of unsaved line revision mark (dark yellow). */
152 const COLORREF UNSAVED_REVMARK_CLR = RGB(0xD7, 0xD7, 0x00);
153 /** @brief Color of saved line revision mark (green). */
154 const COLORREF SAVED_REVMARK_CLR = RGB(0x00, 0xFF, 0x00);
156 #define SMOOTH_SCROLL_FACTOR 6
158 #define ICON_INDEX_WRAPLINE 15
160 ////////////////////////////////////////////////////////////////////////////
163 LOGFONT CCrystalTextView::m_LogFont;
165 IMPLEMENT_DYNCREATE (CCrystalTextView, CView)
167 HINSTANCE CCrystalTextView::s_hResourceInst = nullptr;
169 static ptrdiff_t FindStringHelper(LPCTSTR pszLineBegin, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch);
171 BEGIN_MESSAGE_MAP (CCrystalTextView, CView)
172 //{{AFX_MSG_MAP(CCrystalTextView)
185 ON_WM_LBUTTONDBLCLK ()
186 ON_COMMAND (ID_EDIT_COPY, OnEditCopy)
187 ON_UPDATE_COMMAND_UI (ID_EDIT_COPY, OnUpdateEditCopy)
188 ON_COMMAND (ID_EDIT_SELECT_ALL, OnEditSelectAll)
190 ON_WM_SYSCOLORCHANGE ()
192 ON_COMMAND (ID_EDIT_FIND, OnEditFind)
193 ON_COMMAND (ID_EDIT_REPEAT, OnEditRepeat)
194 ON_UPDATE_COMMAND_UI (ID_EDIT_REPEAT, OnUpdateEditRepeat)
195 ON_COMMAND (ID_EDIT_MARK, OnEditMark)
197 ON_MESSAGE (WM_IME_STARTCOMPOSITION, OnImeStartComposition) /* IME */
199 ON_COMMAND (ID_EDIT_CHAR_LEFT, OnCharLeft)
200 ON_COMMAND (ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
201 ON_COMMAND (ID_EDIT_CHAR_RIGHT, OnCharRight)
202 ON_COMMAND (ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
203 ON_COMMAND (ID_EDIT_WORD_LEFT, OnWordLeft)
204 ON_COMMAND (ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
205 ON_COMMAND (ID_EDIT_WORD_RIGHT, OnWordRight)
206 ON_COMMAND (ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
207 ON_COMMAND (ID_EDIT_LINE_UP, OnLineUp)
208 ON_COMMAND (ID_EDIT_EXT_LINE_UP, OnExtLineUp)
209 ON_COMMAND (ID_EDIT_LINE_DOWN, OnLineDown)
210 ON_COMMAND (ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
211 ON_COMMAND (ID_EDIT_SCROLL_UP, ScrollUp)
212 ON_COMMAND (ID_EDIT_SCROLL_DOWN, ScrollDown)
213 ON_COMMAND (ID_EDIT_PAGE_UP, OnPageUp)
214 ON_COMMAND (ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
215 ON_COMMAND (ID_EDIT_PAGE_DOWN, OnPageDown)
216 ON_COMMAND (ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
217 ON_COMMAND (ID_EDIT_LINE_END, OnLineEnd)
218 ON_COMMAND (ID_EDIT_EXT_LINE_END, OnExtLineEnd)
219 ON_COMMAND (ID_EDIT_HOME, OnHome)
220 ON_COMMAND (ID_EDIT_EXT_HOME, OnExtHome)
221 ON_COMMAND (ID_EDIT_TEXT_BEGIN, OnTextBegin)
222 ON_COMMAND (ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
223 ON_COMMAND (ID_EDIT_TEXT_END, OnTextEnd)
224 ON_COMMAND (ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
225 // Standard printing commands
226 ON_COMMAND (ID_FILE_PAGE_SETUP, OnFilePageSetup)
227 ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint)
228 ON_COMMAND (ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
229 ON_COMMAND (ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
231 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
232 ON_UPDATE_COMMAND_UI (ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
234 ON_COMMAND_RANGE (ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
235 ON_COMMAND_RANGE (ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
236 ON_COMMAND (ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
238 ON_COMMAND (ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
239 ON_COMMAND (ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
240 ON_COMMAND (ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
241 ON_COMMAND (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
242 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
243 ON_UPDATE_COMMAND_UI (ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
244 ON_UPDATE_COMMAND_UI (ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
245 // Ferdi's source type chnages
246 ON_COMMAND_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnSourceType)
247 ON_UPDATE_COMMAND_UI_RANGE (ID_SOURCE_PLAIN, ID_SOURCE_TEX, OnUpdateSourceType)
248 ON_COMMAND (ID_EDIT_MATCHBRACE, OnMatchBrace)
249 ON_UPDATE_COMMAND_UI (ID_EDIT_MATCHBRACE, OnUpdateMatchBrace)
250 ON_COMMAND (ID_EDIT_GOTO, OnEditGoTo)
251 ON_UPDATE_COMMAND_UI (ID_VIEW_TOGGLE_SRC_HDR, OnUpdateToggleSourceHeader)
252 ON_COMMAND (ID_VIEW_TOGGLE_SRC_HDR, OnToggleSourceHeader)
253 ON_UPDATE_COMMAND_UI (ID_VIEW_SELMARGIN, OnUpdateSelMargin)
254 ON_COMMAND (ID_VIEW_SELMARGIN, OnSelMargin)
255 ON_UPDATE_COMMAND_UI (ID_VIEW_WORDWRAP, OnUpdateWordWrap)
256 ON_COMMAND (ID_VIEW_WORDWRAP, OnWordWrap)
257 ON_COMMAND (ID_FORCE_REDRAW, OnForceRedraw)
259 // incremental search
260 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnEditFindIncrementalForward)
261 ON_COMMAND(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnEditFindIncrementalBackward)
262 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_FORWARD, OnUpdateEditFindIncrementalForward)
263 ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_INCREMENTAL_BACKWARD, OnUpdateEditFindIncrementalBackward)
265 ON_COMMAND (ID_EDIT_TOGGLE_COLUMNSELECTION, OnToggleColumnSelection)
268 #define EXPAND_PRIMITIVE(impl, func) \
269 void CCrystalTextView::On##func() { m_bColumnSelection = false; impl(false); } \
270 void CCrystalTextView::OnExt##func() { impl(true); }
271 EXPAND_PRIMITIVE (MoveLeft, CharLeft)
272 EXPAND_PRIMITIVE (MoveRight, CharRight)
273 EXPAND_PRIMITIVE (MoveWordLeft, WordLeft)
274 EXPAND_PRIMITIVE (MoveWordRight, WordRight)
275 EXPAND_PRIMITIVE (MoveUp, LineUp)
276 EXPAND_PRIMITIVE (MoveDown, LineDown)
277 EXPAND_PRIMITIVE (MovePgUp, PageUp)
278 EXPAND_PRIMITIVE (MovePgDn, PageDown)
279 EXPAND_PRIMITIVE (MoveHome, Home)
280 EXPAND_PRIMITIVE (MoveEnd, LineEnd)
281 EXPAND_PRIMITIVE (MoveCtrlHome, TextBegin)
282 EXPAND_PRIMITIVE (MoveCtrlEnd, TextEnd)
283 #undef EXPAND_PRIMITIVE
285 // Tabsize is commented out since we have only GUI setting for it now.
286 // Not removed because we may later want to have per-filetype settings again.
287 // See ccrystaltextview.h for table declaration.
288 CCrystalTextView::TextDefinition CCrystalTextView::m_SourceDefs[] =
290 CCrystalTextView::SRC_PLAIN, _T ("Plain"), _T ("txt,doc,diz"), &CrystalLineParser::ParseLinePlain, SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T (""), (DWORD)-1,
291 CCrystalTextView::SRC_ASP, _T ("ASP"), _T ("asp,ascx"), &CrystalLineParser::ParseLineAsp, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (""), _T (""), _T ("'"), (DWORD)-1,
292 CCrystalTextView::SRC_BASIC, _T ("Basic"), _T ("bas,vb,vbs,frm,dsm,cls,ctl,pag,dsr"), &CrystalLineParser::ParseLineBasic, SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T ("\'"), (DWORD)-1,
293 CCrystalTextView::SRC_BATCH, _T ("Batch"), _T ("bat,btm,cmd"), &CrystalLineParser::ParseLineBatch, SRCOPT_INSERTTABS|SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T ("rem "), (DWORD)-1,
294 CCrystalTextView::SRC_C, _T ("C"), _T ("c,cc,cpp,cxx,h,hpp,hxx,hm,inl,rh,tlh,tli,xs"), &CrystalLineParser::ParseLineC, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
295 CCrystalTextView::SRC_CSHARP, _T ("C#"), _T ("cs"), &CrystalLineParser::ParseLineCSharp, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
296 CCrystalTextView::SRC_CSS, _T ("CSS"), _T ("css"), &CrystalLineParser::ParseLineCss, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T (""), (DWORD)-1,
297 CCrystalTextView::SRC_DCL, _T ("DCL"), _T ("dcl,dcc"), &CrystalLineParser::ParseLineDcl, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
298 CCrystalTextView::SRC_FORTRAN, _T ("Fortran"), _T ("f,f90,f9p,fpp,for,f77"), &CrystalLineParser::ParseLineFortran, SRCOPT_INSERTTABS|SRCOPT_AUTOINDENT, /*8,*/ _T (""), _T (""), _T ("!"), (DWORD)-1,
299 CCrystalTextView::SRC_GO, _T ("Go"), _T ("go"), &CrystalLineParser::ParseLineGo, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
300 CCrystalTextView::SRC_HTML, _T ("HTML"), _T ("html,htm,shtml,ihtml,ssi,stm,stml,jsp"), &CrystalLineParser::ParseLineHtml, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("<!--"), _T ("-->"), _T (""), (DWORD)-1,
301 CCrystalTextView::SRC_INI, _T ("INI"), _T ("ini,reg,vbp,isl"), &CrystalLineParser::ParseLineIni, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU|SRCOPT_EOLNUNIX, /*2,*/ _T (""), _T (""), _T (";"), (DWORD)-1,
302 CCrystalTextView::SRC_INNOSETUP, _T ("InnoSetup"), _T ("iss"), &CrystalLineParser::ParseLineInnoSetup, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("{"), _T ("}"), _T (";"), (DWORD)-1,
303 CCrystalTextView::SRC_INSTALLSHIELD, _T ("InstallShield"), _T ("rul"), &CrystalLineParser::ParseLineIS, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
304 CCrystalTextView::SRC_JAVA, _T ("Java"), _T ("java,jav,js"), &CrystalLineParser::ParseLineJava, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
305 CCrystalTextView::SRC_LISP, _T ("AutoLISP"), _T ("lsp,dsl"), &CrystalLineParser::ParseLineLisp, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (";|"), _T ("|;"), _T (";"), (DWORD)-1,
306 CCrystalTextView::SRC_LUA, _T ("Lua"), _T ("lua"), &CrystalLineParser::ParseLineLua, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("--[["), _T ("]]"), _T ("--"), (DWORD)-1,
307 CCrystalTextView::SRC_NSIS, _T ("NSIS"), _T ("nsi,nsh"), &CrystalLineParser::ParseLineNsis, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T (";"), (DWORD)-1,
308 CCrystalTextView::SRC_PASCAL, _T ("Pascal"), _T ("pas"), &CrystalLineParser::ParseLinePascal, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("{"), _T ("}"), _T (""), (DWORD)-1,
309 CCrystalTextView::SRC_PERL, _T ("Perl"), _T ("pl,pm,plx"), &CrystalLineParser::ParseLinePerl, SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
310 CCrystalTextView::SRC_PHP, _T ("PHP"), _T ("php,php3,php4,php5,phtml"), &CrystalLineParser::ParseLinePhp, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
311 CCrystalTextView::SRC_PO, _T ("PO"), _T ("po,pot"), &CrystalLineParser::ParseLinePo, SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
312 CCrystalTextView::SRC_POWERSHELL, _T ("PowerShell"), _T ("ps1"), &CrystalLineParser::ParseLinePowerShell, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
313 CCrystalTextView::SRC_PYTHON, _T ("Python"), _T ("py"), &CrystalLineParser::ParseLinePython, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
314 CCrystalTextView::SRC_REXX, _T ("REXX"), _T ("rex,rexx"), &CrystalLineParser::ParseLineRexx, SRCOPT_AUTOINDENT, /*4,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
315 CCrystalTextView::SRC_RSRC, _T ("Resources"), _T ("rc,dlg,r16,r32,rc2"), &CrystalLineParser::ParseLineRsrc, SRCOPT_AUTOINDENT, /*4,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
316 CCrystalTextView::SRC_RUBY, _T ("Ruby"), _T ("rb,rbw,rake,gemspec"), &CrystalLineParser::ParseLineRuby, SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
317 CCrystalTextView::SRC_RUST, _T ("Rust"), _T ("rs"), &CrystalLineParser::ParseLineRust, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
318 CCrystalTextView::SRC_SGML, _T ("Sgml"), _T ("sgml"), &CrystalLineParser::ParseLineSgml, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("<!--"), _T ("-->"), _T (""), (DWORD)-1,
319 CCrystalTextView::SRC_SH, _T ("Shell"), _T ("sh,conf"), &CrystalLineParser::ParseLineSh, SRCOPT_INSERTTABS|SRCOPT_AUTOINDENT|SRCOPT_EOLNUNIX, /*4,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
320 CCrystalTextView::SRC_SIOD, _T ("SIOD"), _T ("scm"), &CrystalLineParser::ParseLineSiod, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU, /*2,*/ _T (";|"), _T ("|;"), _T (";"), (DWORD)-1,
321 CCrystalTextView::SRC_SQL, _T ("SQL"), _T ("sql"), &CrystalLineParser::ParseLineSql, SRCOPT_AUTOINDENT, /*4,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
322 CCrystalTextView::SRC_TCL, _T ("TCL"), _T ("tcl"), &CrystalLineParser::ParseLineTcl, SRCOPT_AUTOINDENT|SRCOPT_BRACEGNU|SRCOPT_EOLNUNIX, /*2,*/ _T (""), _T (""), _T ("#"), (DWORD)-1,
323 CCrystalTextView::SRC_TEX, _T ("TEX"), _T ("tex,sty,clo,ltx,fd,dtx"), &CrystalLineParser::ParseLineTex, SRCOPT_AUTOINDENT, /*4,*/ _T (""), _T (""), _T ("%"), (DWORD)-1,
324 CCrystalTextView::SRC_VERILOG, _T ("Verilog"), _T ("v,vh"), &CrystalLineParser::ParseLineVerilog, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("/*"), _T ("*/"), _T ("//"), (DWORD)-1,
325 CCrystalTextView::SRC_VHDL, _T ("VHDL"), _T ("vhd,vhdl,vho"), &CrystalLineParser::ParseLineVhdl, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T (""), _T (""), _T ("--"), (DWORD)-1,
326 CCrystalTextView::SRC_XML, _T ("XML"), _T ("xml"), &CrystalLineParser::ParseLineXml, SRCOPT_AUTOINDENT|SRCOPT_BRACEANSI, /*2,*/ _T ("<!--"), _T ("-->"), _T (""), (DWORD)-1
329 /////////////////////////////////////////////////////////////////////////////
330 // CCrystalTextView construction/destruction
333 MatchType (CString pattern, LPCTSTR lpszExt)
336 int pos, len = pattern.GetLength ();
338 while ((pos = pattern.Find (_T (','))) != -1)
340 part = pattern.Left (pos);
341 if (!part.IsEmpty () && fpattern_isvalid (part))
343 if (fpattern_matchn (part, lpszExt))
349 pattern = pattern.Right (len);
351 if (!pattern.IsEmpty () && fpattern_isvalid (pattern))
353 if (fpattern_matchn (pattern, lpszExt))
361 bool CCrystalTextView::
362 DoSetTextType (TextDefinition *def)
364 m_CurSourceDef = def;
365 SetFlags (def->flags);
368 // EOL is determined from file, tabsize and viewtabs are
369 // global WinMerge settings, selection margin is not needed
370 // and wordwrapping must be false always
372 SetWordWrapping ((def->flags & SRCOPT_WORDWRAP) != false);
373 SetSelectionMargin ((def->flags & SRCOPT_SELMARGIN) != false);
374 SetTabSize (def->tabsize);
375 SetViewTabs ((def->flags & SRCOPT_SHOWTABS) != false);
377 if (def->flags & SRCOPT_EOLNDOS)
381 else if (def->flags & SRCOPT_EOLNUNIX)
385 else if (def->flags & SRCOPT_EOLNMAC)
398 CCrystalTextView::TextDefinition* CCrystalTextView::
399 GetTextType (LPCTSTR pszExt)
402 CString sExt = pszExt;
404 def = CCrystalTextView::m_SourceDefs;
406 for (int i = 0; i < _countof (CCrystalTextView::m_SourceDefs); i++, def++)
407 if (MatchType (def->exts, sExt))
412 bool CCrystalTextView::
413 SetTextType (LPCTSTR pszExt)
415 m_CurSourceDef = m_SourceDefs;
417 TextDefinition *def = GetTextType (pszExt);
419 return SetTextType (def);
422 bool CCrystalTextView::
423 SetTextType (CCrystalTextView::TextType enuType)
427 m_CurSourceDef = def = m_SourceDefs;
428 for (int i = 0; i < _countof (m_SourceDefs); i++, def++)
430 if (def->type == enuType)
432 return SetTextType (def);
438 bool CCrystalTextView::
439 SetTextType (CCrystalTextView::TextDefinition *def)
442 if (m_CurSourceDef != def)
443 return DoSetTextType (def);
449 void CCrystalTextView::
452 TextDefinition *def = m_SourceDefs;
455 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
457 reg.LoadNumber (_T ("DefaultEncoding"), (DWORD*) &CCrystalTextBuffer::m_nDefaultEncoding);
458 for (int i = 0; i < _countof (m_SourceDefs); i++, def++)
461 if (reg1.Open (reg.hKey, def->name, KEY_READ))
463 reg1.LoadString (_T ("Extensions"), def->exts, _countof (def->exts));
464 reg1.LoadNumber (_T ("Flags"), &def->flags);
465 // reg1.LoadNumber (_T ("TabSize"), &def->tabsize);
466 reg1.LoadString (_T ("OpenComment"), def->opencomment, _countof (def->opencomment));
467 reg1.LoadString (_T ("CloseComment"), def->closecomment, _countof (def->closecomment));
468 reg1.LoadString (_T ("CommentLine"), def->commentline, _countof (def->commentline));
469 reg1.LoadNumber (_T ("DefaultEncoding"), &def->encoding);
472 bFontLoaded = reg.LoadBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont));
478 CWindowDC dc (CWnd::GetDesktopWindow ());
479 NONCLIENTMETRICS info;
480 info.cbSize = sizeof(info);
481 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
482 memcpy (&m_LogFont, &info.lfMessageFont, sizeof (LOGFONT));
483 m_LogFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
484 m_LogFont.lfWeight = FW_NORMAL;
485 m_LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
486 m_LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
487 m_LogFont.lfQuality = DEFAULT_QUALITY;
488 m_LogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
489 _tcscpy_s (m_LogFont.lfFaceName, _T ("Courier New"));
493 void CCrystalTextView::
496 TextDefinition *def = m_SourceDefs;
498 if (reg.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
500 VERIFY (reg.SaveNumber (_T ("DefaultEncoding"), (DWORD) CCrystalTextBuffer::m_nDefaultEncoding));
501 for (int i = 0; i < _countof (m_SourceDefs); i++, def++)
504 if (reg1.Create (reg.hKey, def->name, KEY_WRITE))
506 VERIFY (reg1.SaveString (_T ("Extensions"), def->exts));
507 VERIFY (reg1.SaveNumber (_T ("Flags"), def->flags));
508 // VERIFY (reg1.SaveNumber (_T ("TabSize"), def->tabsize));
509 VERIFY (reg1.SaveString (_T ("OpenComment"), def->opencomment));
510 VERIFY (reg1.SaveString (_T ("CloseComment"), def->closecomment));
511 VERIFY (reg1.SaveString (_T ("CommentLine"), def->commentline));
512 VERIFY (reg1.SaveNumber (_T ("DefaultEncoding"), def->encoding));
515 VERIFY (reg.SaveBinary (_T ("LogFont"), (LPBYTE) &m_LogFont, sizeof (m_LogFont)));
519 CCrystalTextView::CCrystalTextView ()
521 , m_pFindTextDlg(nullptr)
522 , m_CurSourceDef(nullptr)
523 , m_dwLastDblClickTime(0)
524 , m_iterChar(UBRK_CHARACTER, "en", nullptr, 0)
525 , m_iterWord(UBRK_WORD, "en", nullptr, 0)
527 , m_pszMatched(nullptr)
529 , m_bViewLineNumbers(false)
531 , m_bHideLines(false)
532 , m_bLastSearch(false)
533 , m_bBookmarkExist(false)
534 , m_bSingle(false) // needed to be set in descendat classes
535 , m_bRememberLastPos(false)
537 , m_nLastLineIndexCalculatedSubLineIndex(-1)
541 , m_pTextBuffer(nullptr)
542 , m_pCacheBitmap(nullptr)
543 , m_pszLastFindWhat(nullptr)
544 , m_dwLastSearchFlags(0)
545 , m_bMultipleSearch(false)
546 , m_bCursorHidden(false)
551 , m_bDistinguishEols(false)
554 , m_pMarkers(nullptr)
555 , m_panSubLines(new CArray<int, int>())
556 , m_panSubLineIndexCache(new CArray<int, int>())
557 , m_pstrIncrementalSearchString(new CString)
558 , m_pstrIncrementalSearchStringOld(new CString)
559 , m_ParseCookies(new vector<DWORD>)
560 , m_pnActualLineLength(new vector<int>)
564 , m_lfSavedBaseFont{}
566 , m_pPrintFont(nullptr)
567 , m_bChWidthsCalculated{}
568 , m_iChDoubleWidthFlags{}
569 , m_bPreparingToDrag(false)
570 , m_bDraggingText(false)
571 , m_bDragSelection(false)
572 , m_bWordSelection(false)
573 , m_bLineSelection(false)
574 , m_bColumnSelection(false)
576 , m_bOverrideCaret(false)
577 , m_nLastFindWhatLen(0)
579 , m_nPrintLineHeight(0)
580 , m_bPrintFooter(false)
581 , m_bPrintHeader(false)
586 , m_bSmoothScroll(false)
587 , m_bVertScrollBarLocked(false)
588 , m_bHorzScrollBarLocked(false)
589 , m_bShowInactiveSelection(false)
590 , m_bDisableDragAndDrop(false)
591 , m_bIncrementalSearchForward(false)
592 , m_bIncrementalSearchBackward(false)
593 , m_bIncrementalFound(false)
596 m_panSubLines->SetSize( 0, 4096 );
597 m_panSubLineIndexCache->SetSize( 0, 4096 );
601 SetTextType (SRC_PLAIN);
604 CCrystalTextView::~CCrystalTextView ()
606 ASSERT (m_hAccel == nullptr);
607 ASSERT (m_pCacheBitmap == nullptr);
608 ASSERT (m_pTextBuffer == nullptr); // Must be correctly detached
610 delete m_pFindTextDlg;
612 if (m_pszLastFindWhat != nullptr)
614 free (m_pszLastFindWhat);
615 m_pszLastFindWhat=nullptr;
617 if (m_rxnode != nullptr)
622 if (m_pszMatched != nullptr)
624 free(m_pszMatched); // Allocated by _tcsdup()
625 m_pszMatched = nullptr;
628 if( m_panSubLines != nullptr )
630 delete m_panSubLines;
631 m_panSubLines = nullptr;
633 if( m_panSubLineIndexCache != nullptr )
635 delete m_panSubLineIndexCache;
636 m_panSubLineIndexCache = nullptr;
638 if( m_pstrIncrementalSearchString != nullptr )
640 delete m_pstrIncrementalSearchString;
641 m_pstrIncrementalSearchString = nullptr;
643 if( m_pstrIncrementalSearchStringOld != nullptr )
645 delete m_pstrIncrementalSearchStringOld;
646 m_pstrIncrementalSearchStringOld = nullptr;
649 ASSERT(m_ParseCookies != nullptr);
650 delete m_ParseCookies;
651 m_ParseCookies = nullptr;
652 ASSERT(m_pnActualLineLength != nullptr);
653 delete m_pnActualLineLength;
654 m_pnActualLineLength = nullptr;
656 if (m_pMarkers != nullptr)
657 m_pMarkers->DeleteView(this);
660 BOOL CCrystalTextView::
661 PreCreateWindow (CREATESTRUCT & cs)
663 CWnd *pParentWnd = CWnd::FromHandlePermanent (cs.hwndParent);
664 if (pParentWnd == nullptr || !pParentWnd->IsKindOf (RUNTIME_CLASS (CSplitterWnd)))
666 // View must always create its own scrollbars,
667 // if only it's not used within splitter
670 // we do not need a horizontal scroll bar, if we wrap the lines
671 cs.style|= WS_VSCROLL;
673 cs.style |= (WS_HSCROLL | WS_VSCROLL);
675 cs.style |= (WS_HSCROLL | WS_VSCROLL);
679 cs.lpszClass = AfxRegisterWndClass (CS_DBLCLKS);
680 return CView::PreCreateWindow (cs);
684 /////////////////////////////////////////////////////////////////////////////
685 // CCrystalTextView drawing
687 void CCrystalTextView::
688 GetSelection (CPoint & ptStart, CPoint & ptEnd)
691 ptStart = m_ptDrawSelStart;
692 ptEnd = m_ptDrawSelEnd;
695 bool CCrystalTextView::
696 GetColumnSelection (int nLineIndex, int & nSelBegin, int & nSelEnd)
698 int nSelTop, nSelBottom;
699 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
701 nSelTop = m_ptDrawSelStart.y;
702 nSelBottom = m_ptDrawSelEnd.y;
706 nSelTop = m_ptDrawSelEnd.y;
707 nSelBottom = m_ptDrawSelStart.y;
710 if (nSelTop > nLineIndex || nLineIndex > nSelBottom)
718 int nStartCharPos = CalculateActualOffset (m_ptDrawSelStart.y, m_ptDrawSelStart.x, true);
719 int nEndCharPos = CalculateActualOffset (m_ptDrawSelEnd.y, m_ptDrawSelEnd.x, true);
720 int nLeftCharPos, nRightCharPos;
721 if (nStartCharPos > nEndCharPos)
723 nLeftCharPos = nEndCharPos;
724 nRightCharPos = nStartCharPos;
728 nLeftCharPos = nStartCharPos;
729 nRightCharPos = nEndCharPos;
731 if (nRightCharPos < m_nIdealCharPos)
732 nRightCharPos = m_nIdealCharPos;
733 nSelBegin = ApproxActualOffset (nLineIndex, nLeftCharPos);
734 nSelEnd = ApproxActualOffset (nLineIndex, nRightCharPos);
739 void CCrystalTextView::
740 GetFullySelectedLines(int & firstLine, int & lastLine)
744 GetSelection(ptStart, ptEnd);
747 firstLine = ptStart.y;
749 firstLine = ptStart.y + 1;
750 if (ptEnd.x == GetLineLength(ptEnd.y))
753 lastLine = ptEnd.y-1;
756 CCrystalTextBuffer *CCrystalTextView::
763 * @brief : Get the line length, for cursor movement
765 * @note : there are at least 4 line lengths :
766 * - number of characters (memory, no EOL)
767 * - number of characters (memory, with EOL)
768 * - number of characters for cursor position (tabs are expanded, no EOL)
769 * - number of displayed characters (tabs are expanded, with EOL)
770 * Corresponding functions :
772 * - GetFullLineLength
773 * - GetLineActualLength
774 * - ExpandChars (returns the line to be displayed as a CString)
776 int CCrystalTextView::
777 GetLineActualLength (int nLineIndex)
779 const int nLineCount = GetLineCount ();
780 ASSERT (nLineCount > 0);
781 ASSERT (nLineIndex >= 0 && nLineIndex < nLineCount);
782 if (!m_pnActualLineLength->size())
784 m_pnActualLineLength->assign(nLineCount, -1);
787 if ((*m_pnActualLineLength)[nLineIndex] != - 1)
788 return (*m_pnActualLineLength)[nLineIndex];
790 // Actual line length is not determined yet, let's calculate a little
791 int nActualLength = 0;
792 int nLength = GetLineLength (nLineIndex);
795 LPCTSTR pszChars = GetLineChars (nLineIndex);
796 const int nTabSize = GetTabSize ();
797 m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
798 for (int i = 0; i < nLength; i = m_iterChar.next())
800 TCHAR c = pszChars[i];
802 nActualLength += (nTabSize - nActualLength % nTabSize);
804 nActualLength += GetCharCellCountFromChar(pszChars + i);
808 (*m_pnActualLineLength)[nLineIndex] = nActualLength;
809 return nActualLength;
812 void CCrystalTextView::
813 ScrollToChar (int nNewOffsetChar, bool bNoSmoothScroll /*= false*/ , bool bTrackScrollBar /*= true*/ )
816 // no horizontal scrolling, when word wrapping is enabled
821 // For now, ignoring bNoSmoothScroll and m_bSmoothScroll
822 if (m_nOffsetChar != nNewOffsetChar)
824 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
825 m_nOffsetChar = nNewOffsetChar;
827 GetClientRect (&rcScroll);
828 rcScroll.left += GetMarginWidth ();
829 ScrollWindow (nScrollChars * GetCharWidth (), 0, &rcScroll, &rcScroll);
832 RecalcHorzScrollBar (true);
837 * @brief Scroll view to given line.
838 * Scrolls view so that given line is first line in the view. We limit
839 * scrolling so that there is only one empty line visible after the last
840 * line at max. So we don't allow user to scroll last line being at top or
841 * even at middle of the screen. This is how many editors behave and I
842 * (Kimmo) think it is good for us too.
843 * @param [in] nNewTopSubLine New top line for view.
844 * @param [in] bNoSmoothScroll if true don't use smooth scrolling.
845 * @param [in] bTrackScrollBar if true scrollbar is updated after scroll.
847 void CCrystalTextView::ScrollToSubLine( int nNewTopSubLine,
848 bool bNoSmoothScroll /*= false*/, bool bTrackScrollBar /*= true*/ )
850 if (m_nTopSubLine != nNewTopSubLine)
852 if (bNoSmoothScroll || ! m_bSmoothScroll)
854 // Limit scrolling so that we show one empty line at end of file
855 const int nScreenLines = GetScreenLines();
856 const int nLineCount = GetSubLineCount();
857 if (nNewTopSubLine > (nLineCount - nScreenLines))
859 nNewTopSubLine = nLineCount - nScreenLines;
860 if (nNewTopSubLine < 0)
864 const int nScrollLines = m_nTopSubLine - nNewTopSubLine;
865 m_nTopSubLine = nNewTopSubLine;
866 // OnDraw() uses m_nTopLine to determine topline
868 GetLineBySubLine(m_nTopSubLine, m_nTopLine, dummy);
869 ScrollWindow(0, nScrollLines * GetLineHeight());
873 RecalcVertScrollBar(true);
874 RecalcHorzScrollBar();
879 // Do smooth scrolling
880 int nLineHeight = GetLineHeight();
881 if (m_nTopSubLine > nNewTopSubLine)
883 int nIncrement = (m_nTopSubLine - nNewTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
884 while (m_nTopSubLine != nNewTopSubLine)
886 int nTopSubLine = m_nTopSubLine - nIncrement;
887 if (nTopSubLine < nNewTopSubLine)
888 nTopSubLine = nNewTopSubLine;
889 const int nScrollLines = nTopSubLine - m_nTopSubLine;
890 m_nTopSubLine = nTopSubLine;
891 ScrollWindow(0, - nLineHeight * nScrollLines);
895 RecalcVertScrollBar(true);
896 RecalcHorzScrollBar();
902 int nIncrement = (nNewTopSubLine - m_nTopSubLine) / SMOOTH_SCROLL_FACTOR + 1;
903 while (m_nTopSubLine != nNewTopSubLine)
905 int nTopSubLine = m_nTopSubLine + nIncrement;
906 if (nTopSubLine > nNewTopSubLine)
907 nTopSubLine = nNewTopSubLine;
908 const int nScrollLines = nTopSubLine - m_nTopSubLine;
909 m_nTopSubLine = nTopSubLine;
910 ScrollWindow(0, - nLineHeight * nScrollLines);
914 RecalcVertScrollBar(true);
915 RecalcHorzScrollBar();
921 GetLineBySubLine( m_nTopSubLine, m_nTopLine, nDummy );
925 void CCrystalTextView::
926 ScrollToLine (int nNewTopLine, bool bNoSmoothScroll /*= false*/ , bool bTrackScrollBar /*= true*/ )
928 if( m_nTopLine != nNewTopLine )
929 ScrollToSubLine( GetSubLineIndex( nNewTopLine ), bNoSmoothScroll, bTrackScrollBar );
932 /** Append szadd to string str, and advance position curpos */
933 static void AppendStringAdv(CString & str, int & curpos, LPCTSTR szadd)
936 curpos += (int) _tcslen(szadd);
939 /** Append escaped control char to string str, and advance position curpos */
940 static void AppendEscapeAdv(CString & str, int & curpos, TCHAR c)
942 int curlen = str.GetLength();
943 LPTSTR szadd = str.GetBufferSetLength(curlen + 3) + curlen;
944 curpos += wsprintf(szadd, _T("\t%02X"), static_cast<int>(c));
947 int CCrystalTextView::
948 ExpandChars (LPCTSTR pszChars, int nOffset, int nCount, CString & line, int nActualOffset)
951 // Request whitespace characters for codepage ACP
952 // because that is the codepage used by ExtTextOut
953 const ViewableWhitespaceChars * lpspc = GetViewableWhitespaceChars(GetACP());
960 const int nTabSize = GetTabSize ();
963 int nLength = nCount;
965 for (int i = 0; i < nLength; i++)
967 TCHAR c = pszChars[i];
969 nCount += nTabSize - 1;
970 else if (c >= _T('\x00') && c <= _T('\x1F') && c != _T('\r') && c != _T('\n'))
974 // Preallocate line buffer, to avoid reallocations as we add characters
975 line.GetBuffer(nCount + 1); // at least this many characters
976 line.ReleaseBuffer(0);
979 if (nCount > nLength || m_bViewTabs || m_bViewEols)
981 m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
982 for (int i = 0, next = 0; i < nLength; i = next)
984 next = m_iterChar.next();
985 if (pszChars[i] == _T('\t'))
987 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
990 AppendStringAdv(line, nCurPos, lpspc->c_tab);
1000 else if (pszChars[i] == ' ' && m_bViewTabs)
1001 AppendStringAdv(line, nCurPos, lpspc->c_space);
1002 else if (pszChars[i] == '\r' || pszChars[i] == '\n')
1006 if (pszChars[i] == '\n' && !m_bDistinguishEols && i+nOffset>0 && pszChars[i-1] == '\r')
1008 // Ignore \n after \r
1012 if (pszChars[i] == '\r' && i < nLength - 1 && pszChars[i+1] == '\n' && m_bDistinguishEols)
1014 AppendStringAdv(line, nCurPos, lpspc->c_eol);
1017 else if (pszChars[i] == '\r' && m_bDistinguishEols)
1018 AppendStringAdv(line, nCurPos, lpspc->c_cr);
1019 else if (pszChars[i] == '\n' && m_bDistinguishEols)
1020 AppendStringAdv(line, nCurPos, lpspc->c_lf);
1023 AppendStringAdv(line, nCurPos, lpspc->c_eol);
1028 else if (pszChars[i] >= _T('\x00') && pszChars[i] <= _T('\x1F'))
1030 AppendEscapeAdv(line, nCurPos, pszChars[i]);
1034 nCurPos += GetCharCellCountFromChar(pszChars + i);
1035 for (; i < next; ++i)
1036 line += pszChars[i];
1042 m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
1043 for (int i1=0, next=0; i1<nLength; i1 = next)
1045 next = m_iterChar.next();
1046 nCurPos += GetCharCellCountFromChar(pszChars + i1);
1047 for (; i1 < next; ++i1)
1048 line += pszChars[i1];
1057 * @brief Draw a chunk of text (one color, one line, full or part of line)
1059 * @note In ANSI build, this routine is buggy for multibytes or double-width characters
1061 void CCrystalTextView::
1062 DrawLineHelperImpl (CDC * pdc, CPoint & ptOrigin, const CRect & rcClip,
1063 int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd, LPCTSTR pszChars, int nOffset, int nCount, int &nActualOffset)
1065 ASSERT (nCount >= 0);
1069 nActualOffset += ExpandChars (pszChars, nOffset, nCount, line, nActualOffset);
1070 const int lineLen = line.GetLength();
1071 const int nCharWidth = GetCharWidth();
1072 const int nCharWidthNarrowed = nCharWidth / 2;
1073 const int nCharWidthWidened = nCharWidth * 2 - nCharWidthNarrowed;
1074 const int nLineHeight = GetLineHeight();
1075 m_iterChar.setText(reinterpret_cast<const UChar *>((LPCTSTR)line), lineLen);
1077 // i the character index, from 0 to lineLen-1
1080 // Pass if the text begins after the right end of the clipping region
1081 if (ptOrigin.x < rcClip.right)
1083 // Because ExtTextOut is buggy when ptOrigin.x < - 4095 * charWidth
1084 // or when nCount >= 4095
1085 // and because this is not well documented,
1086 // we decide to do the left & right clipping here
1088 // Update the position after the left clipped characters
1089 // stop for i = first visible character, at least partly
1090 const int clipLeft = rcClip.left - nCharWidth * 2;
1091 for ( ; i < lineLen; i = m_iterChar.next())
1093 int pnWidthsCurrent = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1094 ptOrigin.x += pnWidthsCurrent;
1095 if (ptOrigin.x >= clipLeft)
1097 ptOrigin.x -= pnWidthsCurrent;
1104 //CSize sz = pdc->GetTextExtent(line, nCount);
1105 //ASSERT(sz.cx == m_nCharWidth * nCount);
1110 // We have to draw some characters
1114 // A raw estimate of the number of characters to display
1115 // For wide characters, nCountFit may be overvalued
1116 int nWidth = rcClip.right - ptOrigin.x;
1117 int nCount1 = lineLen - ibegin;
1118 int nCountFit = nWidth / nCharWidth + 2/* wide char */;
1119 if (nCount1 > nCountFit) {
1121 if (_ismbslead((unsigned char *)(LPCSTR)line, (unsigned char *)(LPCSTR)line + nCountFit - 1))
1124 nCount1 = nCountFit;
1127 // Table of charwidths as CCrystalEditor thinks they are
1128 // Seems that CrystalEditor's and ExtTextOut()'s charwidths aren't
1129 // same with some fonts and text is drawn only partially
1130 // if this table is not used.
1131 vector<int> nWidths(nCount1 + 2);
1132 bool bdisphex = false;
1133 for (int next = i; i < nCount1 + ibegin ; i = next)
1135 if (line[i] == '\t') // Escape sequence leadin?
1138 // Substitute a space narrowed to half the width of a character cell.
1140 nSumWidth += nWidths[i - ibegin] = nCharWidthNarrowed;
1141 // 1st hex digit has normal width.
1142 nSumWidth += nWidths[m_iterChar.next() - ibegin] = nCharWidth;
1143 // 2nd hex digit is padded by half the width of a character cell.
1144 nSumWidth += nWidths[m_iterChar.next() - ibegin] = nCharWidthWidened;
1148 nSumWidth += nWidths[i - ibegin] = GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1150 next = m_iterChar.next();
1153 if (ptOrigin.x + nSumWidth > rcClip.left)
1155 if (crText == CLR_NONE || nColorIndex & COLORINDEX_APPLYFORCE)
1156 pdc->SetTextColor(GetColor(nColorIndex));
1158 pdc->SetTextColor(crText);
1159 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1160 pdc->SetBkColor(GetColor(nBgColorIndex));
1162 pdc->SetBkColor(crBkgnd);
1164 pdc->SelectObject(GetFont(GetItalic(nColorIndex),
1165 GetBold(nColorIndex)));
1166 // we are sure to have less than 4095 characters because all the chars are visible
1168 RECT rcTextBlock = {ptOrigin.x, ptOrigin.y, ptOrigin.x + nSumWidth + 2, ptOrigin.y + nLineHeight};
1169 IntersectRect(&rcIntersect, &rcClip, &rcTextBlock);
1170 VERIFY(pdc->ExtTextOut(ptOrigin.x, ptOrigin.y, ETO_CLIPPED | ETO_OPAQUE,
1171 &rcIntersect, LPCTSTR(line) + ibegin, nCount1, &nWidths[0]));
1174 // Draw rounded rectangles around control characters
1176 pdc->IntersectClipRect(&rcClip);
1177 HDC hDC = pdc->m_hDC;
1178 HGDIOBJ hBrush = ::GetStockObject(NULL_BRUSH);
1179 hBrush = ::SelectObject(hDC, hBrush);
1180 HGDIOBJ hPen = ::CreatePen(PS_SOLID, 1, ::GetTextColor(hDC));
1181 hPen = ::SelectObject(hDC, hPen);
1183 for (int j = 0 ; j < nCount1 ; ++j)
1185 // Assume narrowed space is converted escape sequence leadin.
1186 if (line[ibegin + j] == ' ' && nWidths[j] < nCharWidth)
1188 ::RoundRect(hDC, x + 2, ptOrigin.y + 1,
1189 x + 3 * nCharWidth - 2, ptOrigin.y + nLineHeight - 1,
1190 nCharWidth / 2, nLineHeight / 2);
1194 hPen = ::SelectObject(hDC, hPen);
1195 ::DeleteObject(hPen);
1196 hBrush = ::SelectObject(hDC, hBrush);
1201 // Update the final position after the visible characters
1202 ptOrigin.x += nSumWidth;
1206 // Update the final position after the right clipped characters
1207 for ( ; i < lineLen; i = m_iterChar.next())
1209 ptOrigin.x += GetCharCellCountFromChar(static_cast<const TCHAR *>(line) + i) * nCharWidth;
1214 void CCrystalTextView::
1215 DrawLineHelper (CDC * pdc, CPoint & ptOrigin, const CRect & rcClip, int nColorIndex, int nBgColorIndex,
1216 COLORREF crText, COLORREF crBkgnd, LPCTSTR pszChars, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos)
1220 if (m_bFocused || m_bShowInactiveSelection)
1222 int nSelBegin = 0, nSelEnd = 0;
1223 if ( !m_bColumnSelection )
1225 if (m_ptDrawSelStart.y > ptTextPos.y)
1229 else if (m_ptDrawSelStart.y == ptTextPos.y)
1231 nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
1234 if (nSelBegin > nCount)
1237 if (m_ptDrawSelEnd.y > ptTextPos.y)
1241 else if (m_ptDrawSelEnd.y == ptTextPos.y)
1243 nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
1246 if (nSelEnd > nCount)
1252 int nSelLeft, nSelRight;
1253 GetColumnSelection (ptTextPos.y, nSelLeft, nSelRight);
1254 nSelBegin = nSelLeft - ptTextPos.x;
1255 nSelEnd = nSelRight - ptTextPos.x;
1256 if (nSelBegin < 0) nSelBegin = 0;
1257 if (nSelBegin > nCount) nSelBegin = nCount;
1258 if (nSelEnd < 0) nSelEnd = 0;
1259 if (nSelEnd > nCount) nSelEnd = nCount;
1262 ASSERT (nSelBegin >= 0 && nSelBegin <= nCount);
1263 ASSERT (nSelEnd >= 0 && nSelEnd <= nCount);
1264 ASSERT (nSelBegin <= nSelEnd);
1266 // Draw part of the text before selection
1269 DrawLineHelperImpl (pdc, ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, pszChars, nOffset, nSelBegin, nActualOffset);
1271 if (nSelBegin < nSelEnd)
1273 DrawLineHelperImpl (pdc, ptOrigin, rcClip,
1274 nColorIndex & ~COLORINDEX_APPLYFORCE, nBgColorIndex & ~COLORINDEX_APPLYFORCE,
1275 GetColor (COLORINDEX_SELTEXT),
1276 GetColor (COLORINDEX_SELBKGND),
1277 pszChars, nOffset + nSelBegin, nSelEnd - nSelBegin, nActualOffset);
1279 if (nSelEnd < nCount)
1281 DrawLineHelperImpl (pdc, ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, pszChars, nOffset + nSelEnd, nCount - nSelEnd, nActualOffset);
1286 DrawLineHelperImpl (pdc, ptOrigin, rcClip, nColorIndex, nBgColorIndex, crText, crBkgnd, pszChars, nOffset, nCount, nActualOffset);
1291 void CCrystalTextView::
1292 GetLineColors (int nLineIndex, COLORREF & crBkgnd,
1293 COLORREF & crText, bool & bDrawWhitespace)
1295 DWORD dwLineFlags = GetLineFlags (nLineIndex);
1296 bDrawWhitespace = true;
1297 crText = RGB (255, 255, 255);
1298 if (dwLineFlags & LF_EXECUTION)
1300 crBkgnd = RGB (0, 128, 0);
1303 if (dwLineFlags & LF_BREAKPOINT)
1305 crBkgnd = RGB (255, 0, 0);
1308 if (dwLineFlags & LF_INVALID_BREAKPOINT)
1310 crBkgnd = RGB (128, 128, 0);
1315 bDrawWhitespace = false;
1318 DWORD CCrystalTextView::
1319 GetParseCookie (int nLineIndex)
1321 const int nLineCount = GetLineCount ();
1322 if (m_ParseCookies->size() == 0)
1324 // must be initialized to invalid value (DWORD) -1
1325 m_ParseCookies->assign(nLineCount, static_cast<DWORD>(-1));
1330 if ((*m_ParseCookies)[nLineIndex] != - 1)
1331 return (*m_ParseCookies)[nLineIndex];
1334 while (L >= 0 && (*m_ParseCookies)[L] == - 1)
1339 while (L <= nLineIndex)
1343 dwCookie = (*m_ParseCookies)[L - 1];
1344 ASSERT (dwCookie != - 1);
1345 (*m_ParseCookies)[L] = ParseLine (dwCookie, GetLineChars(L), GetLineLength(L), nullptr, nBlocks);
1346 ASSERT ((*m_ParseCookies)[L] != - 1);
1350 return (*m_ParseCookies)[nLineIndex];
1353 std::vector<TEXTBLOCK> CCrystalTextView::
1354 GetAdditionalTextBlocks (int nLineIndex)
1360 void CCrystalTextView::WrapLine( int nLineIndex, int nMaxLineWidth, int *anBreaks, int &nBreaks )
1362 // There must be a parser attached to this view
1363 if( m_pParser == nullptr )
1366 m_pParser->WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1370 void CCrystalTextView::WrapLineCached(
1371 int nLineIndex, int nMaxLineWidth, int *anBreaks, int &nBreaks )
1373 if( !GetLineVisible (nLineIndex) )
1379 // If the word wrap is not active, there is no breaks in the line
1386 // word wrap is active
1387 if( nLineIndex < m_panSubLines->GetSize() && !anBreaks && (*m_panSubLines)[nLineIndex] > -1 )
1388 // return cached data
1389 nBreaks = (*m_panSubLines)[nLineIndex] - 1;
1392 // recompute line wrap
1394 WrapLine( nLineIndex, nMaxLineWidth, anBreaks, nBreaks );
1397 ASSERT( nBreaks > -1 );
1398 m_panSubLines->SetAtGrow( nLineIndex, nBreaks + 1 );
1400 // RecalcVertScrollBar();
1405 void CCrystalTextView::InvalidateLineCache( int nLineIndex1, int nLineIndex2 /*= -1*/ )
1407 // invalidate cached sub line index
1408 InvalidateSubLineIndexCache( nLineIndex1 );
1410 // invalidate cached sub line count
1412 if( nLineIndex2 == -1 && nLineIndex1 < m_panSubLines->GetSize() )
1413 for( int i = nLineIndex1; i < m_panSubLines->GetSize(); i++ )
1414 (*m_panSubLines)[i] = -1;
1417 if( nLineIndex1 > nLineIndex2 )
1419 int nStorage = nLineIndex1;
1420 nLineIndex1 = nLineIndex2;
1421 nLineIndex2 = nStorage;
1424 if( nLineIndex1 >= m_panSubLines->GetSize() )
1427 if( nLineIndex2 >= m_panSubLines->GetSize() )
1428 nLineIndex2 = (int) m_panSubLines->GetUpperBound();
1430 for( int i = nLineIndex1; i <= nLineIndex2; i++ )
1431 if( i >= 0 && i < m_panSubLines->GetSize() )
1432 (*m_panSubLines)[i] = -1;
1437 * @brief Invalidate sub line index cache from the specified index to the end of file.
1438 * @param [in] nLineIndex Index of the first line to invalidate
1440 void CCrystalTextView::InvalidateSubLineIndexCache( int nLineIndex )
1442 if (m_nLastLineIndexCalculatedSubLineIndex > nLineIndex)
1443 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex - 1;
1447 * @brief Invalidate items related screen size.
1449 void CCrystalTextView::InvalidateScreenRect(bool bInvalidateView)
1451 if (m_pCacheBitmap != nullptr)
1453 delete m_pCacheBitmap;
1454 m_pCacheBitmap = nullptr;
1456 m_nScreenChars = -1;
1457 m_nScreenLines = -1;
1458 InvalidateLineCache(0, -1);
1459 if (bInvalidateView)
1462 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
1463 RecalcVertScrollBar ();
1464 RecalcHorzScrollBar ();
1469 void CCrystalTextView::DrawScreenLine( CDC *pdc, CPoint &ptOrigin, const CRect &rcClip,
1470 const std::vector<TEXTBLOCK>& blocks, int &nActualItem,
1471 COLORREF crText, COLORREF crBkgnd, bool bDrawWhitespace,
1472 LPCTSTR pszChars, int nOffset, int nCount, int &nActualOffset, CPoint ptTextPos )
1474 CPoint originalOrigin = ptOrigin;
1475 CPoint ptOriginZeroWidthBlock;
1476 CRect frect = rcClip;
1477 const int nLineLength = GetViewableLineLength( ptTextPos.y );
1478 const int nLineHeight = GetLineHeight();
1479 int nBgColorIndexZeorWidthBlock = COLORINDEX_NONE;
1480 bool bPrevZeroWidthBlock = false;
1481 static const int ZEROWIDTHBLOCK_WIDTH = 2;
1483 frect.top = ptOrigin.y;
1484 frect.bottom = frect.top + nLineHeight;
1486 int nBlockSize = static_cast<int>(blocks.size());
1487 ASSERT( nActualItem < nBlockSize );
1489 if( nBlockSize > 0 && nActualItem < nBlockSize - 1 &&
1490 blocks[nActualItem + 1].m_nCharPos >= nOffset &&
1491 blocks[nActualItem + 1].m_nCharPos <= nOffset + nCount )
1493 ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1494 blocks[nActualItem].m_nCharPos <= nLineLength);
1497 for (I = nActualItem; I < blocks.size() - 1 &&
1498 blocks[I + 1].m_nCharPos <= nOffset + nCount; I ++)
1500 const TEXTBLOCK& blk = blocks[I];
1501 ASSERT(blk.m_nCharPos >= 0 && blk.m_nCharPos <= nLineLength);
1503 int nOffsetToUse = (nOffset > blk.m_nCharPos) ?
1504 nOffset : blk.m_nCharPos;
1505 if (blocks[I + 1].m_nCharPos - nOffsetToUse > 0)
1507 int nOldActualOffset = nActualOffset;
1508 DrawLineHelper(pdc, ptOrigin, rcClip, blk.m_nColorIndex, blk.m_nBgColorIndex, crText, crBkgnd, pszChars,
1509 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1510 blocks[I + 1].m_nCharPos - nOffsetToUse,
1511 nActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1512 if (bPrevZeroWidthBlock)
1514 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1515 DrawLineHelper(pdc, ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blk.m_nColorIndex, nBgColorIndexZeorWidthBlock, crText, crBkgnd, pszChars,
1516 (nOffset > blk.m_nCharPos)? nOffset : blk.m_nCharPos,
1517 blocks[I + 1].m_nCharPos - nOffsetToUse,
1518 nOldActualOffset, CPoint( nOffsetToUse, ptTextPos.y ));
1519 bPrevZeroWidthBlock = false;
1524 if (!bPrevZeroWidthBlock)
1526 int nBgColorIndex = blk.m_nBgColorIndex;
1527 COLORREF clrBkColor;
1528 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1529 clrBkColor = GetColor(nBgColorIndex);
1531 clrBkColor = crBkgnd;
1532 pdc->FillSolidRect(ptOrigin.x, ptOrigin.y, ZEROWIDTHBLOCK_WIDTH, GetLineHeight(), clrBkColor);
1533 ptOriginZeroWidthBlock = ptOrigin;
1534 nBgColorIndexZeorWidthBlock = blk.m_nBgColorIndex;
1535 bPrevZeroWidthBlock = true;
1538 if (ptOrigin.x > rcClip.right)
1542 nActualItem = static_cast<int>(I);
1544 ASSERT(blocks[nActualItem].m_nCharPos >= 0 &&
1545 blocks[nActualItem].m_nCharPos <= nLineLength);
1547 if (nOffset + nCount - blocks[nActualItem].m_nCharPos > 0)
1549 int nOldActualOffset = nActualOffset;
1550 DrawLineHelper(pdc, ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1551 crText, crBkgnd, pszChars, blocks[nActualItem].m_nCharPos,
1552 nOffset + nCount - blocks[nActualItem].m_nCharPos,
1553 nActualOffset, CPoint(blocks[nActualItem].m_nCharPos, ptTextPos.y));
1554 if (bPrevZeroWidthBlock)
1556 CRect rcClipZeroWidthBlock(ptOriginZeroWidthBlock.x, rcClip.top, ptOriginZeroWidthBlock.x + ZEROWIDTHBLOCK_WIDTH, rcClip.bottom);
1557 DrawLineHelper(pdc, ptOriginZeroWidthBlock, rcClipZeroWidthBlock, blocks[nActualItem].m_nColorIndex, nBgColorIndexZeorWidthBlock,
1558 crText, crBkgnd, pszChars, blocks[nActualItem].m_nCharPos,
1559 nOffset + nCount - blocks[nActualItem].m_nCharPos,
1560 nOldActualOffset, CPoint(blocks[nActualItem].m_nCharPos, ptTextPos.y));
1561 bPrevZeroWidthBlock = false;
1566 if (!bPrevZeroWidthBlock)
1568 int nBgColorIndex = blocks[nActualItem].m_nBgColorIndex;
1569 COLORREF clrBkColor;
1570 if (crBkgnd == CLR_NONE || nBgColorIndex & COLORINDEX_APPLYFORCE)
1571 clrBkColor = GetColor(nBgColorIndex);
1573 clrBkColor = crBkgnd;
1574 pdc->FillSolidRect(ptOrigin.x, ptOrigin.y, ZEROWIDTHBLOCK_WIDTH, GetLineHeight(), clrBkColor);
1575 bPrevZeroWidthBlock = true;
1582 pdc, ptOrigin, rcClip, blocks[nActualItem].m_nColorIndex, blocks[nActualItem].m_nBgColorIndex,
1583 crText, crBkgnd, pszChars, nOffset, nCount, nActualOffset, ptTextPos);
1586 // Draw space on the right of the text
1588 frect.left = ptOrigin.x + (bPrevZeroWidthBlock ? ZEROWIDTHBLOCK_WIDTH : 0);
1590 if ((m_bFocused || m_bShowInactiveSelection)
1591 && !m_bColumnSelection
1592 && IsInsideSelBlock(CPoint(nLineLength, ptTextPos.y))
1593 && (nOffset + nCount) == nLineLength )
1595 if (frect.left >= rcClip.left)
1597 const int nCharWidth = GetCharWidth();
1598 pdc->FillSolidRect(frect.left, frect.top, nCharWidth, frect.Height(),
1599 GetColor(COLORINDEX_SELBKGND));
1600 frect.left += nCharWidth;
1603 if (frect.left < rcClip.left)
1604 frect.left = rcClip.left;
1606 if (frect.right > frect.left)
1607 pdc->FillSolidRect(frect, bDrawWhitespace ?
1608 crBkgnd : GetColor(COLORINDEX_WHITESPACE));
1610 // set origin to beginning of next screen line
1611 ptOrigin.x = originalOrigin.x;
1612 ptOrigin.y+= nLineHeight;
1616 class IntArray : public CArray<int, int>
1619 explicit IntArray(int len) { SetSize(len); }
1622 std::vector<TEXTBLOCK> CCrystalTextView::
1623 MergeTextBlocks (const std::vector<TEXTBLOCK>& blocks1, const std::vector<TEXTBLOCK>& blocks2) const
1627 std::vector<TEXTBLOCK> mergedBlocks(blocks1.size() + blocks2.size());
1629 for (i = 0, j = 0, k = 0; ; k++)
1631 if (i >= blocks1.size() && j >= blocks2.size())
1635 else if ((i < blocks1.size()&& j < blocks2.size()) &&
1636 (blocks1[i].m_nCharPos == blocks2[j].m_nCharPos))
1638 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1639 if (blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1640 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1642 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1643 if (blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1644 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1646 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1650 else if (j >= blocks2.size() || (i < blocks1.size() &&
1651 blocks1[i].m_nCharPos < blocks2[j].m_nCharPos))
1653 mergedBlocks[k].m_nCharPos = blocks1[i].m_nCharPos;
1654 if (blocks2.size() == 0 || blocks2[j - 1].m_nColorIndex == COLORINDEX_NONE)
1655 mergedBlocks[k].m_nColorIndex = blocks1[i].m_nColorIndex;
1657 mergedBlocks[k].m_nColorIndex = blocks2[j - 1].m_nColorIndex;
1658 if (blocks2.size() == 0 || blocks2[j - 1].m_nBgColorIndex == COLORINDEX_NONE)
1659 mergedBlocks[k].m_nBgColorIndex = blocks1[i].m_nBgColorIndex;
1661 mergedBlocks[k].m_nBgColorIndex = blocks2[j - 1].m_nBgColorIndex;
1664 else if (i >= blocks1.size() || (j < blocks2.size() && blocks1[i].m_nCharPos > blocks2[j].m_nCharPos))
1666 mergedBlocks[k].m_nCharPos = blocks2[j].m_nCharPos;
1667 if (i > 0 && blocks2[j].m_nColorIndex == COLORINDEX_NONE)
1668 mergedBlocks[k].m_nColorIndex = blocks1[i - 1].m_nColorIndex;
1670 mergedBlocks[k].m_nColorIndex = blocks2[j].m_nColorIndex;
1671 if (i > 0 && blocks2[j].m_nBgColorIndex == COLORINDEX_NONE)
1672 mergedBlocks[k].m_nBgColorIndex = blocks1[i - 1].m_nBgColorIndex;
1674 mergedBlocks[k].m_nBgColorIndex = blocks2[j].m_nBgColorIndex;
1680 for (i = 0; i < k; ++i)
1683 (mergedBlocks[i - 1].m_nColorIndex != mergedBlocks[i].m_nColorIndex ||
1684 mergedBlocks[i - 1].m_nBgColorIndex != mergedBlocks[i].m_nBgColorIndex))
1686 mergedBlocks[j] = mergedBlocks[i];
1691 mergedBlocks.resize(j);
1692 return mergedBlocks;
1695 std::vector<TEXTBLOCK>
1696 CCrystalTextView::GetMarkerTextBlocks(int nLineIndex) const
1698 std::vector<TEXTBLOCK> allblocks;
1699 if (!m_pMarkers->GetEnabled())
1702 int nLength = GetViewableLineLength (nLineIndex);
1704 for (const auto& marker : m_pMarkers->GetMarkers())
1706 if (!marker.second.bVisible)
1709 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1710 blocks[0].m_nCharPos = 0;
1711 blocks[0].m_nColorIndex = COLORINDEX_NONE;
1712 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
1714 const TCHAR *pszChars = GetLineChars(nLineIndex);
1715 int nLineLength = GetLineLength(nLineIndex);
1716 if (pszChars != nullptr)
1718 for (const TCHAR *p = pszChars; p < pszChars + nLineLength; )
1720 RxNode *node = nullptr;
1723 size_t nPos = ::FindStringHelper(pszChars, p, marker.second.sFindWhat, marker.second.dwFlags | FIND_NO_WRAP, nMatchLen, node, &matches);
1726 if (nLineLength < static_cast<int>((p - pszChars) + nPos) + nMatchLen)
1727 nMatchLen = static_cast<int>(nLineLength - (p - pszChars));
1728 ASSERT(((p - pszChars) + nPos) < INT_MAX);
1729 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos);
1730 blocks[nBlocks].m_nBgColorIndex = marker.second.nBgColorIndex | COLORINDEX_APPLYFORCE;
1731 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1733 ASSERT(((p - pszChars) + nPos + nMatchLen) < INT_MAX);
1734 blocks[nBlocks].m_nCharPos = static_cast<int>((p - pszChars) + nPos + nMatchLen);
1735 blocks[nBlocks].m_nBgColorIndex = COLORINDEX_NONE;
1736 blocks[nBlocks].m_nColorIndex = COLORINDEX_NONE;
1738 p += nPos + (nMatchLen == 0 ? 1 : nMatchLen);
1740 blocks.resize(nBlocks);
1741 allblocks = MergeTextBlocks(allblocks, blocks);
1748 std::vector<TEXTBLOCK>
1749 CCrystalTextView::GetTextBlocks(int nLineIndex)
1751 int nLength = GetViewableLineLength (nLineIndex);
1754 DWORD dwCookie = GetParseCookie(nLineIndex - 1);
1755 std::vector<TEXTBLOCK> blocks((nLength + 1) * 3); // be aware of nLength == 0
1757 // insert at least one textblock of normal color at the beginning
1758 blocks[0].m_nCharPos = 0;
1759 blocks[0].m_nColorIndex = COLORINDEX_NORMALTEXT;
1760 blocks[0].m_nBgColorIndex = COLORINDEX_BKGND;
1762 (*m_ParseCookies)[nLineIndex] = ParseLine(dwCookie, GetLineChars(nLineIndex), GetLineLength(nLineIndex), blocks.data(), nBlocks);
1763 ASSERT((*m_ParseCookies)[nLineIndex] != -1);
1764 blocks.resize(nBlocks);
1766 return MergeTextBlocks(blocks,
1767 (m_pMarkers && m_pMarkers->GetEnabled() && m_pMarkers->GetMarkers().size() > 0) ?
1768 MergeTextBlocks(GetAdditionalTextBlocks(nLineIndex), GetMarkerTextBlocks(nLineIndex)) :
1769 GetAdditionalTextBlocks(nLineIndex));
1772 void CCrystalTextView::
1773 DrawSingleLine (CDC * pdc, const CRect & rc, int nLineIndex)
1775 const int nCharWidth = GetCharWidth();
1776 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
1778 if (nLineIndex == -1)
1780 // Draw line beyond the text
1781 pdc->FillSolidRect (rc, GetColor (COLORINDEX_WHITESPACE));
1785 // Acquire the background color for the current line
1786 bool bDrawWhitespace = false;
1787 COLORREF crBkgnd, crText;
1788 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
1790 int nLength = GetViewableLineLength (nLineIndex);
1791 LPCTSTR pszChars = GetLineChars (nLineIndex);
1793 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
1795 int nActualItem = 0;
1796 int nActualOffset = 0;
1798 IntArray anBreaks(nLength);
1801 WrapLineCached( nLineIndex, GetScreenChars(), anBreaks.GetData(), nBreaks );
1803 // Draw the line text
1804 CPoint origin (rc.left - m_nOffsetChar * nCharWidth, rc.top);
1805 if (crBkgnd != CLR_NONE)
1806 pdc->SetBkColor (crBkgnd);
1807 if (crText != CLR_NONE)
1808 pdc->SetTextColor (crText);
1812 // Draw all the screen lines of the wrapped line
1813 ASSERT( anBreaks[0] < nLength );
1815 // draw start of line to first break
1818 blocks, nActualItem,
1819 crText, crBkgnd, bDrawWhitespace,
1820 pszChars, 0, anBreaks[0], nActualOffset, CPoint( 0, nLineIndex ) );
1822 // draw from first break to last break
1824 for( i = 0; i < nBreaks - 1; i++ )
1826 ASSERT( anBreaks[i] >= 0 && anBreaks[i] < nLength );
1829 blocks, nActualItem,
1830 crText, crBkgnd, bDrawWhitespace,
1831 pszChars, anBreaks[i], anBreaks[i + 1] - anBreaks[i],
1832 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
1835 // draw from last break till end of line
1838 blocks, nActualItem,
1839 crText, crBkgnd, bDrawWhitespace,
1840 pszChars, anBreaks[i], nLength - anBreaks[i],
1841 nActualOffset, CPoint( anBreaks[i], nLineIndex ) );
1846 blocks, nActualItem,
1847 crText, crBkgnd, bDrawWhitespace,
1848 pszChars, 0, nLength, nActualOffset, CPoint(0, nLineIndex));
1850 // Draw empty sublines
1851 int nEmptySubLines = GetEmptySubLines(nLineIndex);
1852 if (nEmptySubLines > 0)
1855 frect.top = frect.bottom - nEmptySubLines * GetLineHeight();
1856 pdc->FillSolidRect(frect, crBkgnd == CLR_NONE ? GetColor(COLORINDEX_WHITESPACE) : crBkgnd);
1861 * @brief Escape special characters
1862 * @param [in] strText The text to escape
1863 * @param [in, out] bLastCharSpace Whether last char processed was white space
1864 * @param [in, out] nNonbreakChars The number of non-break characters in the text
1865 * @param [in] nScreenChars The maximum number of characters to display per line
1866 * @return The escaped text
1869 EscapeHTML (const CString & strText, bool & bLastCharSpace, int & nNonbreakChars, int nScreenChars)
1872 int len = strText.GetLength ();
1873 for (int i = 0; i < len; ++i)
1875 TCHAR ch = strText[i];
1879 strHTML += _T("&");
1880 bLastCharSpace = false;
1884 strHTML += _T("<");
1885 bLastCharSpace = false;
1889 strHTML += _T(">");
1890 bLastCharSpace = false;
1896 strHTML += _T("<wbr>");
1897 bLastCharSpace = false;
1903 strHTML += _T(" ");
1904 bLastCharSpace = false;
1909 bLastCharSpace = true;
1915 bLastCharSpace = false;
1919 if (IsDBCSLeadByte (ch))
1920 strHTML += strText[++i];
1922 if ((nNonbreakChars % nScreenChars) == nScreenChars - 1)
1924 strHTML += _T("<wbr>");
1932 // Make a CString from printf-style args (single call version of CString::Format)
1933 static CString Fmt(LPCTSTR fmt, ...)
1937 va_start(args, fmt);
1938 str.FormatV(fmt, args);
1944 * @brief Return all the styles necessary to render this view as HTML code
1945 * @return The HTML styles
1947 CString CCrystalTextView::
1950 int arColorIndices[] = {
1951 COLORINDEX_NORMALTEXT,
1954 COLORINDEX_FUNCNAME,
1957 COLORINDEX_OPERATOR,
1959 COLORINDEX_PREPROCESSOR,
1960 COLORINDEX_HIGHLIGHTTEXT1,
1961 COLORINDEX_HIGHLIGHTTEXT2,
1965 int arBgColorIndices[] = {
1967 COLORINDEX_SELBKGND,
1968 COLORINDEX_HIGHLIGHTBKGND1,
1969 COLORINDEX_HIGHLIGHTBKGND2,
1970 COLORINDEX_HIGHLIGHTBKGND3,
1971 COLORINDEX_HIGHLIGHTBKGND4,
1975 for (int f = 0; f < sizeof(arColorIndices)/sizeof(int); f++)
1977 int nColorIndex = arColorIndices[f];
1978 for (int b = 0; b < sizeof(arBgColorIndices)/sizeof(int); b++)
1980 int nBgColorIndex = arBgColorIndices[b];
1983 strStyles += Fmt (_T(".sf%db%d {"), nColorIndex, nBgColorIndex);
1984 clr = GetColor (nColorIndex);
1985 strStyles += Fmt (_T("color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
1986 clr = GetColor (nBgColorIndex);
1987 strStyles += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
1988 if (GetBold (nColorIndex))
1989 strStyles += _T("font-weight: bold; ");
1990 if (GetItalic (nColorIndex))
1991 strStyles += _T("font-style: italic; ");
1992 strStyles += _T("}\n");
1999 * @brief Return the HTML attribute associated with the specified colors
2000 * @param [in] nColorIndex Index of text color
2001 * @param [in] nBgColorIndex Index of background color
2002 * @param [in] crText Base text color
2003 * @param [in] crBkgnd Base background color
2004 * @return The HTML attribute
2006 CString CCrystalTextView::
2007 GetHTMLAttribute (int nColorIndex, int nBgColorIndex, COLORREF crText, COLORREF crBkgnd)
2012 if ((crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE)) &&
2013 (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE)))
2014 return Fmt(_T("class=\"sf%db%d\""), nColorIndex & ~COLORINDEX_APPLYFORCE, nBgColorIndex & ~COLORINDEX_APPLYFORCE);
2016 if (crText == CLR_NONE || (nColorIndex & COLORINDEX_APPLYFORCE))
2017 clr = GetColor (nColorIndex);
2020 strAttr += Fmt (_T("style=\"color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2022 if (crBkgnd == CLR_NONE || (nBgColorIndex & COLORINDEX_APPLYFORCE))
2023 clr = GetColor (nBgColorIndex);
2026 strAttr += Fmt (_T("background-color: #%02x%02x%02x; "), GetRValue (clr), GetGValue (clr), GetBValue (clr));
2028 if (GetBold (nColorIndex))
2029 strAttr += _T("font-weight: bold; ");
2030 if (GetItalic (nColorIndex))
2031 strAttr += _T("font-style: italic; ");
2033 strAttr += _T("\"");
2039 * @brief Retrieve the html version of the line
2040 * @param [in] nLineIndex Index of line in view
2041 * @param [in] pszTag The HTML tag to enclose the line
2042 * @return The html version of the line
2044 CString CCrystalTextView::
2045 GetHTMLLine (int nLineIndex, LPCTSTR pszTag)
2047 ASSERT (nLineIndex >= -1 && nLineIndex < GetLineCount ());
2049 int nLength = GetViewableLineLength (nLineIndex);
2050 LPCTSTR pszChars = GetLineChars (nLineIndex);
2052 // Acquire the background color for the current line
2053 bool bDrawWhitespace = false;
2054 COLORREF crBkgnd, crText;
2055 GetLineColors (nLineIndex, crBkgnd, crText, bDrawWhitespace);
2057 std::vector<TEXTBLOCK> blocks = GetTextBlocks(nLineIndex);
2060 CString strExpanded;
2062 int nNonbreakChars = 0;
2063 bool bLastCharSpace = false;
2064 const int nScreenChars = 40; // GetScreenChars();
2069 strHTML += GetHTMLAttribute (COLORINDEX_NORMALTEXT, COLORINDEX_BKGND, crText, crBkgnd);
2070 strHTML += _T("><code>");
2072 for (i = 0; i < blocks.size() - 1; i++)
2074 ExpandChars (pszChars, blocks[i].m_nCharPos, blocks[i + 1].m_nCharPos - blocks[i].m_nCharPos, strExpanded, 0);
2075 if (!strExpanded.IsEmpty())
2077 strHTML += _T("<span ");
2078 strHTML += GetHTMLAttribute (blocks[i].m_nColorIndex, blocks[i].m_nBgColorIndex, crText, crBkgnd);
2080 strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2081 strHTML += _T("</span>");
2084 if (blocks.size() > 0)
2086 ExpandChars (pszChars, blocks[i].m_nCharPos, nLength - blocks[i].m_nCharPos, strExpanded, 0);
2087 if (!strExpanded.IsEmpty())
2089 strHTML += _T("<span ");
2090 strHTML += GetHTMLAttribute (blocks[i].m_nColorIndex, blocks[i].m_nBgColorIndex, crText, crBkgnd);
2092 strHTML += EscapeHTML (strExpanded, bLastCharSpace, nNonbreakChars, nScreenChars);
2093 strHTML += _T("</span>");
2095 if (strExpanded.Compare (CString (' ', strExpanded.GetLength())) == 0)
2096 strHTML += _T(" ");
2098 strHTML += _T("</code></");
2105 COLORREF CCrystalTextView::
2106 GetColor (int nColorIndex)
2108 if (m_pColors != nullptr)
2110 nColorIndex &= ~COLORINDEX_APPLYFORCE;
2111 return m_pColors->GetColor(nColorIndex);
2114 return RGB(0, 0, 0);
2117 DWORD CCrystalTextView::
2118 GetLineFlags (int nLineIndex) const
2120 if (m_pTextBuffer == nullptr)
2122 return m_pTextBuffer->GetLineFlags (nLineIndex);
2126 * @brief Draw selection margin.
2127 * @param [in] pdc Pointer to draw context.
2128 * @param [in] rect The rectangle to draw.
2129 * @param [in] nLineIndex Index of line in view.
2130 * @param [in] nLineNumber Line number to display. if -1, it's not displayed.
2132 void CCrystalTextView::
2133 DrawMargin (CDC * pdc, const CRect & rect, int nLineIndex, int nLineNumber)
2135 if (!m_bSelMargin && !m_bViewLineNumbers)
2136 pdc->FillSolidRect (rect, GetColor (COLORINDEX_BKGND));
2138 pdc->FillSolidRect (rect, GetColor (COLORINDEX_SELMARGIN));
2140 if (m_bViewLineNumbers && nLineNumber > 0)
2142 TCHAR szNumbers[32];
2143 wsprintf(szNumbers, _T("%d"), nLineNumber);
2144 CFont *pOldFont = pdc->SelectObject(GetFont());
2145 COLORREF clrOldColor = pdc->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
2146 UINT uiOldAlign = pdc->SetTextAlign(TA_RIGHT);
2147 pdc->TextOut(rect.right - (pdc->IsPrinting() ? 0 : 4), rect.top, szNumbers, lstrlen(szNumbers));
2148 pdc->SetTextAlign(uiOldAlign);
2149 pdc->SelectObject(pOldFont);
2150 pdc->SetTextColor(clrOldColor);
2153 // Draw line revision mark (or background) whenever we have valid lineindex
2154 COLORREF clrRevisionMark = GetColor(COLORINDEX_WHITESPACE);
2155 if (nLineIndex >= 0 && m_pTextBuffer != nullptr)
2157 // get line revision marks color
2158 DWORD dwRevisionNumber = m_pTextBuffer->GetLineRevisionNumber(nLineIndex);
2159 if (dwRevisionNumber > 0)
2161 if (m_pTextBuffer->m_dwRevisionNumberOnSave < dwRevisionNumber)
2162 clrRevisionMark = UNSAVED_REVMARK_CLR;
2164 clrRevisionMark = SAVED_REVMARK_CLR;
2168 // draw line revision marks
2169 CRect rc(rect.right - (pdc->IsPrinting () ? 0 : MARGIN_REV_WIDTH), rect.top, rect.right, rect.bottom);
2170 pdc->FillSolidRect (rc, clrRevisionMark);
2172 if (!m_bSelMargin || pdc->IsPrinting ())
2175 int nImageIndex = -1;
2176 if (nLineIndex >= 0)
2178 DWORD dwLineFlags = GetLineFlags (nLineIndex);
2179 static const DWORD adwFlags[] =
2183 LF_COMPILATION_ERROR,
2195 LF_INVALID_BREAKPOINT
2197 for (int I = 0; I < sizeof (adwFlags) / sizeof (adwFlags[0]); I++)
2199 if ((dwLineFlags & adwFlags[I]) != 0)
2206 if (m_pIcons == nullptr)
2208 m_pIcons = new CImageList;
2209 VERIFY (m_pIcons->Create(MARGIN_ICON_WIDTH, MARGIN_ICON_HEIGHT,
2210 ILC_COLOR32 | ILC_MASK, 0, 1));
2212 bmp.LoadBitmap(IDR_MARGIN_ICONS);
2213 m_pIcons->Add(&bmp, RGB(255, 255, 255));
2215 if (nImageIndex >= 0)
2217 CPoint pt(rect.left + 2, rect.top + (GetLineHeight() - MARGIN_ICON_HEIGHT) / 2);
2218 VERIFY(m_pIcons->Draw(pdc, nImageIndex, pt, ILD_TRANSPARENT));
2219 VERIFY (m_pIcons->Draw (pdc, nImageIndex, pt, ILD_TRANSPARENT));
2222 // draw wrapped-line-icon
2223 if (nLineNumber > 0)
2226 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2227 for (int i = 0; i < nBreaks; i++)
2229 CPoint pt(rect.right - MARGIN_ICON_WIDTH, rect.top + (GetLineHeight()
2230 - MARGIN_ICON_WIDTH) / 2 + (i+1) * GetLineHeight());
2231 m_pIcons->Draw (pdc, ICON_INDEX_WRAPLINE, pt, ILD_TRANSPARENT);
2236 void CCrystalTextView::
2237 DrawBoundaryLine (CDC * pdc, int nLeft, int nRight, int y)
2239 CPen *pOldPen = (CPen *)pdc->SelectStockObject (BLACK_PEN);
2240 pdc->MoveTo (nLeft, y);
2241 pdc->LineTo (nRight, y);
2242 pdc->SelectObject (pOldPen);
2245 void CCrystalTextView::
2246 DrawLineCursor (CDC * pdc, int nLeft, int nRight, int y, int nHeight)
2249 dcMem.CreateCompatibleDC (pdc);
2251 bitmap.CreateCompatibleBitmap (pdc, nRight - nLeft, nHeight);
2252 CBitmap *pOldBitmap = dcMem.SelectObject (&bitmap);
2253 dcMem.SetBkColor(RGB(0, 255, 0));
2254 BLENDFUNCTION blend = {0};
2255 blend.BlendOp = AC_SRC_OVER;
2256 blend.SourceConstantAlpha = 24;
2257 pdc->AlphaBlend (nLeft, y, nRight - nLeft, nHeight, &dcMem, 0, 0, nRight - nLeft, nHeight, blend);
2258 dcMem.SelectObject (pOldBitmap);
2261 bool CCrystalTextView::
2262 IsInsideSelBlock (CPoint ptTextPos)
2265 ASSERT_VALIDTEXTPOS (ptTextPos);
2266 if (ptTextPos.y < m_ptDrawSelStart.y)
2268 if (ptTextPos.y > m_ptDrawSelEnd.y)
2270 if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
2272 if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
2274 if (ptTextPos.y == m_ptDrawSelEnd.y)
2275 return ptTextPos.x < m_ptDrawSelEnd.x;
2276 ASSERT (ptTextPos.y == m_ptDrawSelStart.y);
2277 return ptTextPos.x >= m_ptDrawSelStart.x;
2279 ASSERT (m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
2280 return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
2283 bool CCrystalTextView::
2284 IsInsideSelection (const CPoint & ptTextPos)
2286 PrepareSelBounds ();
2287 return IsInsideSelBlock (ptTextPos);
2291 * @brief : class the selection extremities in ascending order
2293 * @note : Updates m_ptDrawSelStart and m_ptDrawSelEnd
2294 * This function must be called before reading these values
2296 void CCrystalTextView::
2299 if (m_ptSelStart.y < m_ptSelEnd.y ||
2300 (m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
2302 m_ptDrawSelStart = m_ptSelStart;
2303 m_ptDrawSelEnd = m_ptSelEnd;
2307 m_ptDrawSelStart = m_ptSelEnd;
2308 m_ptDrawSelEnd = m_ptSelStart;
2312 void CCrystalTextView::
2315 // We use the same GDI objects for both drawing and printing.
2316 // So when printing is in progress, we do nothing in this function.
2321 GetClientRect (rcClient);
2323 if (m_pTextBuffer == nullptr)
2325 pdc->FillSolidRect(&rcClient, GetSysColor(COLOR_WINDOW));
2329 const int nLineCount = GetLineCount ();
2330 const int nLineHeight = GetLineHeight ();
2331 PrepareSelBounds ();
2333 // if the private arrays (m_ParseCookies and m_pnActualLineLength)
2334 // are defined, check they are in phase with the text buffer
2335 if (m_ParseCookies->size())
2336 ASSERT(m_ParseCookies->size() == static_cast<size_t>(nLineCount));
2337 if (m_pnActualLineLength->size())
2338 ASSERT(m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
2341 VERIFY (cacheDC.CreateCompatibleDC (pdc));
2342 if (m_pCacheBitmap == nullptr)
2344 m_pCacheBitmap = new CBitmap;
2345 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), rcClient.Height()));
2347 CBitmap *pOldBitmap = cacheDC.SelectObject (m_pCacheBitmap);
2351 rcLine.bottom = rcLine.top + nLineHeight;
2352 CRect rcCacheMargin (0, 0, GetMarginWidth (), nLineHeight);
2353 CRect rcCacheLine (GetMarginWidth (), 0, rcLine.Width (), nLineHeight);
2356 int nSubLineOffset = GetSubLineIndex( m_nTopLine ) - m_nTopSubLine;
2357 if( nSubLineOffset < 0 )
2359 rcCacheMargin.OffsetRect( 0, nSubLineOffset * nLineHeight );
2360 rcCacheLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
2363 int nCursorY = TextToClient (m_ptCursorPos).y;
2367 int nCurrentLine = m_nTopLine;
2368 while (rcLine.top < rcClient.bottom)
2372 if( nCurrentLine < nLineCount /*&& GetLineLength( nCurrentLine ) > nMaxLineChars*/ )
2373 nSubLines = GetSubLines(nCurrentLine);
2375 rcLine.bottom = rcLine.top + nSubLines * nLineHeight;
2376 rcCacheLine.bottom = rcCacheLine.top + rcLine.Height();
2377 rcCacheMargin.bottom = rcCacheMargin.top + rcLine.Height();
2379 if( rcCacheLine.top < 0 )
2380 rcLine.bottom+= rcCacheLine.top;
2382 if (pdc->RectVisible(rcLine))
2384 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
2386 DrawMargin (&cacheDC, rcCacheMargin, nCurrentLine, nCurrentLine + 1);
2387 DrawSingleLine (&cacheDC, rcCacheLine, nCurrentLine);
2388 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
2389 DrawBoundaryLine (&cacheDC, rcCacheMargin.left, rcCacheLine.right, rcCacheMargin.bottom-1);
2390 if (nCurrentLine == m_ptCursorPos.y)
2391 DrawLineCursor (&cacheDC, rcCacheMargin.left, rcCacheLine.right,
2392 nCursorY - rcLine.top + nLineHeight - 1, 1);
2396 DrawMargin (&cacheDC, rcCacheMargin, -1, -1);
2397 DrawSingleLine (&cacheDC, rcCacheLine, -1);
2400 VERIFY (pdc->BitBlt (rcLine.left, rcLine.top, rcLine.Width (),
2401 rcLine.Height (), &cacheDC, 0, 0, SRCCOPY));
2406 rcLine.top = rcLine.bottom;
2407 rcCacheLine.top = 0;
2408 rcCacheMargin.top = 0;
2410 rcLine.OffsetRect(0, nLineHeight);
2415 cacheDC.SelectObject (pOldBitmap);
2416 cacheDC.DeleteDC ();
2419 void CCrystalTextView::
2422 // m_bWordWrap = false;
2428 m_nScreenLines = -1;
2429 m_nScreenChars = -1;
2430 m_nIdealCharPos = -1;
2433 if (m_pIcons != nullptr)
2438 for (int I = 0; I < 4; I++)
2440 if (m_apFonts[I] != nullptr)
2442 delete m_apFonts[I];
2443 m_apFonts[I] = nullptr;
2446 InvalidateLineCache( 0, -1 );
2447 m_ParseCookies->clear();
2448 m_pnActualLineLength->clear();
2449 m_ptCursorPos.x = 0;
2450 m_ptCursorPos.y = 0;
2451 m_ptSelStart = m_ptSelEnd = m_ptCursorPos;
2452 if (m_bDragSelection)
2455 KillTimer (m_nDragSelTimer);
2457 m_bDragSelection = false;
2458 m_bVertScrollBarLocked = false;
2459 m_bHorzScrollBarLocked = false;
2460 if (::IsWindow (m_hWnd))
2462 m_bShowInactiveSelection = true; // FP: reverted because I like it
2463 m_bPrintHeader = false;
2464 m_bPrintFooter = true;
2466 m_bMultipleSearch = false; // More search
2470 void CCrystalTextView::
2473 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
2474 if (m_bFocused && !m_bCursorHidden &&
2475 CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x) >= m_nOffsetChar)
2477 int nCaretHeight = GetLineVisible(m_ptCursorPos.y) ? GetLineHeight () : 0;
2478 if (m_bOverrideCaret) //UPDATE
2479 CreateSolidCaret(GetCharWidth(), nCaretHeight);
2481 CreateSolidCaret (2, nCaretHeight);
2483 SetCaretPos (TextToClient (m_ptCursorPos));
2485 UpdateCompositionWindowPos(); /* IME */
2494 void CCrystalTextView::
2499 int CCrystalTextView::
2502 if (m_pTextBuffer != nullptr)
2504 return m_pTextBuffer->GetCRLFMode ();
2509 void CCrystalTextView::
2510 SetCRLFMode (CRLFSTYLE nCRLFMode)
2512 if (m_pTextBuffer != nullptr)
2514 m_pTextBuffer->SetCRLFMode (nCRLFMode);
2518 int CCrystalTextView::
2521 if (m_pTextBuffer == nullptr)
2524 return m_pTextBuffer->GetTabSize();
2528 void CCrystalTextView::
2529 SetTabSize (int nTabSize)
2531 ASSERT (nTabSize >= 0 && nTabSize <= 64);
2532 if (m_pTextBuffer == nullptr)
2535 if (m_pTextBuffer->GetTabSize() != nTabSize)
2537 m_pTextBuffer->SetTabSize( nTabSize );
2539 m_pnActualLineLength->clear();
2540 RecalcHorzScrollBar ();
2546 CFont *CCrystalTextView::
2547 GetFont (bool bItalic /*= false*/ , bool bBold /*= false*/ )
2555 if (m_apFonts[nIndex] == nullptr)
2557 m_apFonts[nIndex] = new CFont;
2558 if (!m_lfBaseFont.lfHeight)
2560 CClientDC dc (GetDesktopWindow ());
2561 m_lfBaseFont.lfHeight = -MulDiv (11, dc.GetDeviceCaps (LOGPIXELSY), 72);
2563 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
2564 m_lfBaseFont.lfItalic = (BYTE) bItalic;
2565 if (!m_apFonts[nIndex]->CreateFontIndirect (&m_lfBaseFont))
2567 delete m_apFonts[nIndex];
2568 m_apFonts[nIndex] = nullptr;
2569 return CView::GetFont ();
2572 return m_apFonts[nIndex];
2575 void CCrystalTextView::
2578 CDC *pdc = GetDC ();
2579 CFont *pOldFont = pdc->SelectObject (GetFont ());
2580 CSize szCharExt = pdc->GetTextExtent (_T ("X"));
2581 m_nLineHeight = szCharExt.cy;
2582 if (m_nLineHeight < 1)
2584 m_nCharWidth = szCharExt.cx;
2587 if (pdc->GetTextMetrics(&tm))
2588 m_nCharWidth -= tm.tmOverhang;
2590 pdc->SelectObject (GetFont (false, true));
2591 szCharExt = pdc->GetTextExtent (_T ("X"));
2592 if (m_nLineHeight < szCharExt.cy)
2593 m_nLineHeight = szCharExt.cy;
2594 pdc->SelectObject (pOldFont);
2598 int CCrystalTextView::
2601 if (m_nLineHeight == -1)
2603 return m_nLineHeight;
2606 int CCrystalTextView::GetSubLines( int nLineIndex )
2608 // get a number of lines this wrapped lines contains
2610 WrapLineCached( nLineIndex, GetScreenChars(), nullptr, nBreaks );
2612 return GetEmptySubLines(nLineIndex) + nBreaks + 1;
2615 int CCrystalTextView::GetEmptySubLines( int nLineIndex )
2620 bool CCrystalTextView::IsEmptySubLineIndex( int nSubLineIndex )
2624 GetLineBySubLine(nSubLineIndex, nLineIndex, dummy);
2625 int nSubLineIndexNextLine = GetSubLineIndex(nLineIndex) + GetSubLines(nLineIndex);
2626 if (nSubLineIndexNextLine - GetEmptySubLines(nLineIndex) <= nSubLineIndex && nSubLineIndex < nSubLineIndexNextLine)
2632 int CCrystalTextView::CharPosToPoint( int nLineIndex, int nCharPos, CPoint &charPoint )
2634 // if we do not wrap lines, y is allways 0 and x is equl to nCharPos
2637 charPoint.x = nCharPos;
2642 vector<int> anBreaks(GetLineLength (nLineIndex) + 1);
2645 WrapLineCached (nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks);
2647 int i = (nBreaks <= 0) ? -1 : nBreaks - 1;
2648 for (; i >= 0 && nCharPos < anBreaks[i]; i--)
2651 charPoint.x = (i >= 0)? nCharPos - anBreaks[i] : nCharPos;
2652 charPoint.y = i + 1;
2654 int nReturnVal = (i >= 0)? anBreaks[i] : 0;
2659 /** Does character introduce a multicharacter character? */
2660 static inline bool IsLeadByte(TCHAR ch)
2665 return _getmbcp() && IsDBCSLeadByte(ch);
2669 int CCrystalTextView::CursorPointToCharPos( int nLineIndex, const CPoint &curPoint )
2671 // calculate char pos out of point
2672 const int nLength = GetLineLength( nLineIndex );
2673 const int nScreenChars = GetScreenChars();
2674 LPCTSTR szLine = GetLineChars( nLineIndex );
2677 vector<int> anBreaks(nLength + 1);
2680 WrapLineCached( nLineIndex, nScreenChars, &anBreaks[0], nBreaks );
2682 // find char pos that matches cursor position
2686 const int nTabSize = GetTabSize();
2688 int nIndex=0, nPrevIndex = 0;
2689 m_iterChar.setText(reinterpret_cast<const UChar *>(szLine), nLength);
2690 for( nIndex = 0; nIndex < nLength; nIndex = m_iterChar.next())
2692 if( nBreaks > 0 && nIndex == anBreaks[nYPos] )
2699 if (szLine[nIndex] == _T('\t'))
2700 nOffset = nTabSize - nCurPos % nTabSize;
2702 nOffset = GetCharCellCountFromChar(szLine + nIndex);
2706 if( nXPos > curPoint.x && nYPos == curPoint.y )
2708 else if( nYPos > curPoint.y )
2710 nIndex = nPrevIndex;
2714 nPrevIndex = nIndex;
2720 void CCrystalTextView::SubLineCursorPosToTextPos( const CPoint &subLineCurPos, CPoint &textPos )
2723 int nSubLineOffset, nLine;
2725 GetLineBySubLine( subLineCurPos.y, nLine, nSubLineOffset );
2727 // compute cursor-position
2728 textPos.x = CursorPointToCharPos( nLine, CPoint( subLineCurPos.x, nSubLineOffset ) );
2733 * @brief Calculate last character position in (sub)line.
2734 * @param [in] nLineIndex Linenumber to check.
2735 * @param [in] nSublineOffset Subline index in wrapped line.
2736 * @return Position of the last character.
2738 int CCrystalTextView::SubLineEndToCharPos(int nLineIndex, int nSubLineOffset)
2740 const int nLength = GetLineLength(nLineIndex);
2742 // if word wrapping is disabled, the end is equal to the length of the line -1
2747 vector<int> anBreaks(nLength + 1);
2750 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks);
2752 // if there is no break inside the line or the given subline is the last
2753 // one in this line...
2754 if (nBreaks <= 0 || nSubLineOffset == nBreaks)
2759 // compute character position for end of subline
2760 ASSERT(nSubLineOffset >= 0 && nSubLineOffset <= nBreaks);
2762 int nReturnVal = anBreaks[nSubLineOffset] - 1;
2768 * @brief Calculate first character position in (sub)line.
2769 * @param [in] nLineIndex Linenumber to check.
2770 * @param [in] nSublineOffset Subline index in wrapped line.
2771 * @return Position of the first character.
2773 int CCrystalTextView::SubLineHomeToCharPos(int nLineIndex, int nSubLineOffset)
2775 // if word wrapping is disabled, the start is 0
2776 if (!m_bWordWrap || nSubLineOffset == 0)
2780 int nLength = GetLineLength(nLineIndex);
2781 vector<int> anBreaks(nLength + 1);
2784 WrapLineCached(nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks);
2786 // if there is no break inside the line...
2792 // compute character position for end of subline
2793 ASSERT(nSubLineOffset > 0 && nSubLineOffset <= nBreaks);
2795 int nReturnVal = anBreaks[nSubLineOffset - 1];
2801 int CCrystalTextView::
2804 if (m_nCharWidth == -1)
2806 return m_nCharWidth;
2809 int CCrystalTextView::
2810 GetMaxLineLength (int nTopLine, int nLines)
2812 int nMaxLineLength = 0;
2813 const int nLineCount = (std::min)(nTopLine + nLines, GetLineCount ());
2814 for (int I = nTopLine; I < nLineCount; I++)
2816 int nActualLength = GetLineActualLength (I);
2817 if (nMaxLineLength < nActualLength)
2818 nMaxLineLength = nActualLength;
2820 return nMaxLineLength;
2823 CCrystalTextView *CCrystalTextView::
2824 GetSiblingView (int nRow, int nCol)
2826 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
2827 if (pSplitter == nullptr)
2829 CWnd *pWnd = CWnd::FromHandlePermanent (
2830 ::GetDlgItem (pSplitter->m_hWnd, pSplitter->IdFromRowCol (nRow, nCol)));
2831 if (pWnd == nullptr || !pWnd->IsKindOf (RUNTIME_CLASS (CCrystalTextView)))
2833 return (CCrystalTextView *) pWnd;
2836 void CCrystalTextView::
2837 GoToLine (int nLine, bool bRelative)
2839 int nLines = m_pTextBuffer->GetLineCount () - 1;
2840 CPoint ptCursorPos = GetCursorPos ();
2843 nLine += ptCursorPos.y;
2855 int nChars = m_pTextBuffer->GetLineLength (nLine);
2860 if (ptCursorPos.x > nChars)
2862 ptCursorPos.x = nChars;
2864 if (ptCursorPos.x >= 0)
2866 ptCursorPos.y = nLine;
2867 ASSERT_VALIDTEXTPOS (ptCursorPos);
2868 SetAnchor (ptCursorPos);
2869 SetSelection (ptCursorPos, ptCursorPos);
2870 SetCursorPos (ptCursorPos);
2871 EnsureVisible (ptCursorPos);
2876 void CCrystalTextView::
2879 CView::OnInitialUpdate ();
2880 CString sDoc = GetDocument ()->GetPathName (), sExt = GetExt (sDoc);
2881 if (!sExt.IsEmpty())
2883 AttachToBuffer (nullptr);
2885 CSplitterWnd *pSplitter = GetParentSplitter (this, false);
2886 if (pSplitter != nullptr)
2888 // See CSplitterWnd::IdFromRowCol() implementation
2889 int nRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
2890 int nCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
2891 ASSERT (nRow >= 0 && nRow < pSplitter->GetRowCount ());
2892 ASSERT (nCol >= 0 && nCol < pSplitter->GetColumnCount ());
2896 CCrystalTextView *pSiblingView = GetSiblingView (0, nCol);
2897 if (pSiblingView != nullptr && pSiblingView != this)
2899 m_nOffsetChar = pSiblingView->m_nOffsetChar;
2900 ASSERT (m_nOffsetChar >= 0 && m_nOffsetChar <= GetMaxLineLength (m_nTopLine, GetScreenLines()));
2906 CCrystalTextView *pSiblingView = GetSiblingView (nRow, 0);
2907 if (pSiblingView != nullptr && pSiblingView != this)
2909 m_nTopLine = pSiblingView->m_nTopLine;
2910 ASSERT (m_nTopLine >= 0 && m_nTopLine < GetLineCount ());
2914 SetFont (m_LogFont);
2915 if (m_bRememberLastPos && !sDoc.IsEmpty ())
2918 CString sKey = REG_EDITPAD;
2919 sKey += _T ("\\Remembered");
2921 if (reg.Open (HKEY_CURRENT_USER, sKey, KEY_READ) &&
2922 reg.LoadBinary (sDoc, (LPBYTE) dwLastPos, sizeof (dwLastPos)))
2925 ptCursorPos.x = dwLastPos[1];
2926 ptCursorPos.y = dwLastPos[2];
2927 if (IsValidTextPosY (ptCursorPos))
2929 if (!IsValidTextPosX (ptCursorPos))
2931 ASSERT_VALIDTEXTPOS (ptCursorPos);
2932 SetCursorPos (ptCursorPos);
2933 SetSelection (ptCursorPos, ptCursorPos);
2934 SetAnchor (ptCursorPos);
2935 EnsureVisible (ptCursorPos);
2941 /////////////////////////////////////////////////////////////////////////////
2942 // CCrystalTextView printing
2944 void CCrystalTextView::
2945 OnPrepareDC (CDC * pDC, CPrintInfo * pInfo /*= nullptr*/)
2947 CView::OnPrepareDC (pDC, pInfo);
2949 if (pInfo != nullptr)
2951 pInfo->m_bContinuePrinting = true;
2952 if (m_nPrintPages != 0 && (int) pInfo->m_nCurPage > m_nPrintPages)
2953 pInfo->m_bContinuePrinting = false;
2957 BOOL CCrystalTextView::
2958 OnPreparePrinting (CPrintInfo * pInfo)
2960 return DoPreparePrinting (pInfo);
2963 void CCrystalTextView::
2964 GetPrintHeaderText (int nPageNum, CString & text)
2966 ASSERT (m_bPrintHeader);
2970 void CCrystalTextView::
2971 GetPrintFooterText (int nPageNum, CString & text)
2973 ASSERT (m_bPrintFooter);
2974 text.Format (_T ("Page %d/%d"), nPageNum, m_nPrintPages);
2977 void CCrystalTextView::
2978 PrintHeader (CDC * pdc, int nPageNum)
2980 CRect rcHeader = m_rcPrintArea;
2981 rcHeader.bottom = rcHeader.top;
2982 rcHeader.top -= (m_nPrintLineHeight + m_nPrintLineHeight / 2);
2985 GetPrintHeaderText (nPageNum, text);
2986 if (!text.IsEmpty ())
2987 pdc->DrawText (text, &rcHeader, DT_CENTER | DT_NOPREFIX | DT_TOP | DT_SINGLELINE);
2990 void CCrystalTextView::
2991 PrintFooter (CDC * pdc, int nPageNum)
2993 CRect rcFooter = m_rcPrintArea;
2994 rcFooter.top = rcFooter.bottom;
2995 rcFooter.bottom += (m_nPrintLineHeight + m_nPrintLineHeight / 2);
2998 GetPrintFooterText (nPageNum, text);
2999 if (!text.IsEmpty ())
3000 pdc->DrawText (text, &rcFooter, DT_CENTER | DT_NOPREFIX | DT_BOTTOM | DT_SINGLELINE);
3004 * @brief Retrieves the print margins
3005 * @param nLeft [out] Left margin
3006 * @param nTop [out] Top margin
3007 * @param nRight [out] right margin
3008 * @param nBottom [out] Bottom margin
3010 void CCrystalTextView::
3011 GetPrintMargins (long & nLeft, long & nTop, long & nRight, long & nBottom)
3013 nLeft = DEFAULT_PRINT_MARGIN;
3014 nTop = DEFAULT_PRINT_MARGIN;
3015 nRight = DEFAULT_PRINT_MARGIN;
3016 nBottom = DEFAULT_PRINT_MARGIN;
3018 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
3021 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
3023 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
3025 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
3027 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
3032 void CCrystalTextView::
3033 RecalcPageLayouts (CDC * pdc, CPrintInfo * pInfo)
3035 m_ptPageArea = pInfo->m_rectDraw;
3036 m_ptPageArea.NormalizeRect ();
3038 m_nPrintLineHeight = pdc->GetTextExtent (_T ("X")).cy;
3040 m_rcPrintArea = m_ptPageArea;
3041 CSize szTopLeft, szBottomRight;
3042 CWinApp *pApp = AfxGetApp ();
3043 ASSERT (pApp != nullptr);
3044 GetPrintMargins (szTopLeft.cx, szTopLeft.cy, szBottomRight.cx, szBottomRight.cy);
3045 pdc->HIMETRICtoLP (&szTopLeft);
3046 pdc->HIMETRICtoLP (&szBottomRight);
3047 m_rcPrintArea.left += szTopLeft.cx;
3048 m_rcPrintArea.right -= szBottomRight.cx;
3049 m_rcPrintArea.top += szTopLeft.cy;
3050 m_rcPrintArea.bottom -= szBottomRight.cy;
3052 m_rcPrintArea.top += m_nPrintLineHeight + m_nPrintLineHeight / 2;
3054 m_rcPrintArea.bottom -= m_nPrintLineHeight + m_nPrintLineHeight / 2;
3056 InvalidateLineCache (0, -1);
3058 m_nScreenChars = (m_rcPrintArea.Width () - GetMarginWidth (pdc)) / GetCharWidth ();
3059 m_nScreenLines = m_rcPrintArea.Height () / GetLineHeight ();
3062 void CCrystalTextView::
3063 OnBeginPrinting (CDC * pdc, CPrintInfo * pInfo)
3065 ASSERT (m_pPrintFont == nullptr);
3066 CFont *pDisplayFont = GetFont ();
3069 CDC *pDisplayDC = GetDC ();
3070 pDisplayFont->GetLogFont (&lf);
3071 lf.lfHeight = MulDiv (lf.lfHeight, pdc->GetDeviceCaps (LOGPIXELSY), pDisplayDC->GetDeviceCaps (LOGPIXELSY));
3072 lf.lfWidth = MulDiv (lf.lfWidth, pdc->GetDeviceCaps (LOGPIXELSX), pDisplayDC->GetDeviceCaps (LOGPIXELSX));
3073 ReleaseDC (pDisplayDC);
3075 m_pPrintFont = new CFont;
3076 if (!m_pPrintFont->CreateFontIndirect (&lf))
3078 delete m_pPrintFont;
3079 m_pPrintFont = nullptr;
3083 GetFont (m_lfSavedBaseFont);
3084 m_pPrintFont->GetLogFont (&lf);
3091 void CCrystalTextView::
3092 OnEndPrinting (CDC * pdc, CPrintInfo * pInfo)
3094 if (m_pPrintFont != nullptr)
3096 delete m_pPrintFont;
3097 m_pPrintFont = nullptr;
3098 SetFont(m_lfSavedBaseFont);
3101 m_nPrintLineHeight = 0;
3102 m_bPrinting = false;
3105 void CCrystalTextView::
3106 OnPrint (CDC * pdc, CPrintInfo * pInfo)
3108 pdc->SelectObject (m_pPrintFont);
3110 const COLORREF defaultLineColor = RGB(0,0,0);
3111 const COLORREF defaultBgColor = RGB(255,255,255);
3113 RecalcPageLayouts (pdc, pInfo);
3115 m_nPrintPages = (GetSubLineCount () + GetScreenLines () - 1) / GetScreenLines ();
3117 ASSERT (pInfo->m_nCurPage >= 1 && (int) pInfo->m_nCurPage <= m_nPrintPages);
3119 int nTopSubLine = (pInfo->m_nCurPage - 1) * GetScreenLines ();
3120 int nEndSubLine = nTopSubLine + GetScreenLines () - 1;
3121 if (nEndSubLine >= GetSubLineCount ())
3122 nEndSubLine = GetSubLineCount () - 1;
3124 int nTopLine, nEndLine;
3125 GetLineBySubLine (nTopSubLine, nTopLine, nSubLines);
3126 GetLineBySubLine (nEndSubLine, nEndLine, nSubLines);
3128 TRACE (_T ("Printing page %d of %d, lines %d - %d\n"),
3129 pInfo->m_nCurPage, m_nPrintPages, nTopLine, nEndLine);
3131 pdc->SetTextColor(defaultLineColor);
3132 pdc->SetBkColor(defaultBgColor);
3136 PrintHeader (pdc, pInfo->m_nCurPage);
3141 PrintFooter (pdc, pInfo->m_nCurPage);
3144 // set clipping region
3145 // see http://support.microsoft.com/kb/128334
3146 CRect rectClip = m_rcPrintArea;
3147 rectClip.right = rectClip.left + GetMarginWidth (pdc) + GetScreenChars () * GetCharWidth ();
3148 rectClip.bottom = rectClip.top + GetScreenLines () * GetLineHeight ();
3149 if (!!pdc->IsKindOf (RUNTIME_CLASS (CPreviewDC)))
3151 CPreviewDC *pPrevDC = (CPreviewDC *)pdc;
3153 pPrevDC->PrinterDPtoScreenDP (&rectClip.TopLeft ());
3154 pPrevDC->PrinterDPtoScreenDP (&rectClip.BottomRight ());
3157 ::GetViewportOrgEx (pdc->m_hDC,&ptOrg);
3161 rgn.CreateRectRgnIndirect (&rectClip);
3162 pdc->SelectClipRgn (&rgn);
3166 CRect rcLine = m_rcPrintArea;
3167 int nLineHeight = GetLineHeight ();
3168 rcLine.bottom = rcLine.top + nLineHeight;
3170 rcMargin.right = rcMargin.left + GetMarginWidth (pdc);
3171 rcLine.left = rcMargin.right;
3173 int nSubLineOffset = GetSubLineIndex (nTopLine) - nTopSubLine;
3174 if( nSubLineOffset < 0 )
3176 rcLine.OffsetRect( 0, nSubLineOffset * nLineHeight );
3179 int nLineCount = GetLineCount();
3181 for (nCurrentLine = nTopLine; nCurrentLine <= nEndLine; nCurrentLine++)
3183 rcLine.bottom = rcLine.top + GetSubLines (nCurrentLine) * nLineHeight;
3184 rcMargin.bottom = rcLine.bottom;
3186 if (nCurrentLine < nLineCount && GetLineVisible (nCurrentLine))
3188 DrawMargin (pdc, rcMargin, nCurrentLine, nCurrentLine + 1);
3189 DrawSingleLine (pdc, rcLine, nCurrentLine);
3190 if (nCurrentLine+1 < nLineCount && !GetLineVisible (nCurrentLine + 1))
3191 DrawBoundaryLine (pdc, rcMargin.left, rcLine.right, rcMargin.bottom-1);
3194 rcLine.top = rcLine.bottom;
3195 rcMargin.top = rcLine.bottom;
3198 pdc->SelectClipRgn (nullptr);
3202 /////////////////////////////////////////////////////////////////////////////
3203 // CCrystalTextView message handlers
3205 int CCrystalTextView::
3208 if (m_pTextBuffer == nullptr)
3209 return 1; // Single empty line
3211 int nLineCount = m_pTextBuffer->GetLineCount ();
3212 ASSERT (nLineCount > 0);
3217 int CCrystalTextView::GetSubLineCount()
3219 const int nLineCount = GetLineCount();
3221 // if we do not wrap words, number of sub lines is
3222 // equal to number of lines
3223 if( !m_bWordWrap && !m_bHideLines )
3226 // calculate number of sub lines
3227 if (nLineCount <= 0)
3229 return CCrystalTextView::GetSubLineIndex( nLineCount - 1 ) + GetSubLines( nLineCount - 1 );
3232 int CCrystalTextView::GetSubLineIndex( int nLineIndex )
3234 // if we do not wrap words, subline index of this line is equal to its index
3235 if( !m_bWordWrap && !m_bHideLines )
3238 // calculate subline index of the line
3239 int nSubLineCount = 0;
3240 int nLineCount = GetLineCount();
3242 if( nLineIndex >= nLineCount )
3243 nLineIndex = nLineCount - 1;
3245 // return cached subline index of the line if it is already cached.
3246 if (nLineIndex <= m_nLastLineIndexCalculatedSubLineIndex)
3247 return (*m_panSubLineIndexCache)[nLineIndex];
3249 // calculate subline index of the line and cache it.
3250 if (m_nLastLineIndexCalculatedSubLineIndex >= 0)
3251 nSubLineCount = (*m_panSubLineIndexCache)[m_nLastLineIndexCalculatedSubLineIndex];
3254 m_nLastLineIndexCalculatedSubLineIndex = 0;
3255 m_panSubLineIndexCache->SetAtGrow( 0, 0 );
3258 // TODO: Rethink this, it is very time consuming
3259 for( int i = m_nLastLineIndexCalculatedSubLineIndex; i < nLineIndex; i++ )
3261 m_panSubLineIndexCache->SetAtGrow( i, nSubLineCount);
3262 nSubLineCount+= GetSubLines( i );
3264 m_panSubLineIndexCache->SetAtGrow( nLineIndex, nSubLineCount);
3265 m_nLastLineIndexCalculatedSubLineIndex = nLineIndex;
3267 return nSubLineCount;
3270 // See comment in the header file
3271 void CCrystalTextView::GetLineBySubLine(int nSubLineIndex, int &nLine, int &nSubLine)
3273 if (GetSubLineCount() == 0)
3280 ASSERT( nSubLineIndex < GetSubLineCount() );
3282 // if we do not wrap words, nLine is equal to nSubLineIndex and nSubLine is allways 0
3283 if ( !m_bWordWrap && !m_bHideLines )
3285 nLine = nSubLineIndex;
3291 const int nLineCount = GetLineCount();
3294 int base = 0, i = 0, nSubLineIndex2 = 0;
3295 for (int lim = nLineCount; lim != 0; lim >>= 1)
3297 i = base + (lim >> 1);
3298 nSubLineIndex2 = GetSubLineIndex(i);
3299 if (nSubLineIndex >= nSubLineIndex2 && nSubLineIndex < nSubLineIndex2 + GetSubLines(i))
3301 else if (nSubLineIndex2 <= nSubLineIndex) /* key > p: move right */
3305 } /* else move left */
3308 ASSERT(i < nLineCount);
3310 nSubLine = nSubLineIndex - nSubLineIndex2;
3313 int CCrystalTextView::
3314 GetLineLength (int nLineIndex) const
3316 if (m_pTextBuffer == nullptr)
3318 return m_pTextBuffer->GetLineLength (nLineIndex);
3321 int CCrystalTextView::
3322 GetFullLineLength (int nLineIndex) const
3324 if (m_pTextBuffer == nullptr)
3326 return m_pTextBuffer->GetFullLineLength (nLineIndex);
3329 // How many bytes of line are displayed on-screen?
3330 int CCrystalTextView::
3331 GetViewableLineLength (int nLineIndex) const
3334 return GetFullLineLength(nLineIndex);
3336 return GetLineLength(nLineIndex);
3339 LPCTSTR CCrystalTextView::
3340 GetLineChars (int nLineIndex) const
3342 if (m_pTextBuffer == nullptr)
3344 return m_pTextBuffer->GetLineChars (nLineIndex);
3348 * @brief Reattach buffer after deleting/inserting ghost lines :
3350 * @note no need to reinitialize the horizontal scrollbar
3351 * no need to reset the editor options (m_bOvrMode, m_bLastReplace)
3353 void CCrystalTextView::
3354 ReAttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3356 if (m_pTextBuffer != nullptr)
3357 m_pTextBuffer->RemoveView (this);
3358 if (pBuf == nullptr)
3360 pBuf = LocateTextBuffer ();
3363 m_pTextBuffer = pBuf;
3364 if (m_pTextBuffer != nullptr)
3365 m_pTextBuffer->AddView (this);
3366 // don't reset CCrystalEditView options
3367 CCrystalTextView::ResetView ();
3369 // Init scrollbars arrows
3370 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3371 if (pVertScrollBarCtrl != nullptr)
3372 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3373 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3374 // Update vertical scrollbar only
3375 RecalcVertScrollBar ();
3379 * @brief Attach buffer (maybe for the first time)
3380 * initialize the view and initialize both scrollbars
3382 void CCrystalTextView::
3383 AttachToBuffer (CCrystalTextBuffer * pBuf /*= nullptr*/ )
3385 if (m_pTextBuffer != nullptr)
3386 m_pTextBuffer->RemoveView (this);
3387 if (pBuf == nullptr)
3389 pBuf = LocateTextBuffer ();
3392 m_pTextBuffer = pBuf;
3393 if (m_pTextBuffer != nullptr)
3394 m_pTextBuffer->AddView (this);
3398 CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl (SB_VERT);
3399 if (pVertScrollBarCtrl != nullptr)
3400 pVertScrollBarCtrl->EnableScrollBar (GetScreenLines () >= GetLineCount ()?
3401 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3402 CScrollBar *pHorzScrollBarCtrl = GetScrollBarCtrl (SB_HORZ);
3403 if (pHorzScrollBarCtrl != nullptr)
3404 pHorzScrollBarCtrl->EnableScrollBar (GetScreenChars () >= GetMaxLineLength (m_nTopLine, GetScreenLines())?
3405 ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
3407 // Update scrollbars
3408 RecalcVertScrollBar ();
3409 RecalcHorzScrollBar ();
3412 void CCrystalTextView::
3415 if (m_pTextBuffer != nullptr)
3417 m_pTextBuffer->RemoveView (this);
3418 m_pTextBuffer = nullptr;
3419 // don't reset CCrystalEditView options
3420 CCrystalTextView::ResetView ();
3424 int CCrystalTextView::
3427 if (m_nScreenLines == -1)
3430 GetClientRect (&rect);
3431 m_nScreenLines = rect.Height () / GetLineHeight ();
3433 return m_nScreenLines;
3436 bool CCrystalTextView::
3437 GetItalic (int nColorIndex)
3439 // WINMERGE - since italic text has problems,
3440 // lets disable it. E.g. "_" chars disappear and last
3441 // char may be cropped.
3444 // return nColorIndex == COLORINDEX_COMMENT;
3447 bool CCrystalTextView::
3448 GetBold (int nColorIndex)
3450 if (m_pColors != nullptr)
3452 nColorIndex &= ~COLORINDEX_APPLYFORCE;
3453 return m_pColors->GetBold(nColorIndex);
3459 int CCrystalTextView::
3462 if (m_nScreenChars == -1)
3465 GetClientRect (&rect);
3466 m_nScreenChars = (rect.Width () - GetMarginWidth ()) / GetCharWidth ();
3468 return m_nScreenChars;
3471 void CCrystalTextView::
3474 GetFont ()->GetLogFont (&m_lfBaseFont);
3475 DetachFromBuffer ();
3478 CView::OnDestroy ();
3480 for (int I = 0; I < 4; I++)
3482 if (m_apFonts[I] != nullptr)
3484 delete m_apFonts[I];
3485 m_apFonts[I] = nullptr;
3488 if (m_pCacheBitmap != nullptr)
3490 delete m_pCacheBitmap;
3491 m_pCacheBitmap = nullptr;
3495 BOOL CCrystalTextView::
3496 OnEraseBkgnd (CDC * pdc)
3498 UNREFERENCED_PARAMETER(pdc);
3502 void CCrystalTextView::
3503 OnSize (UINT nType, int cx, int cy)
3505 CView::OnSize (nType, cx, cy);
3508 // get char position of top left visible character with old cached word wrap
3510 SubLineCursorPosToTextPos( CPoint( 0, m_nTopSubLine ), topPos );
3514 // we have to recompute the line wrapping
3515 InvalidateScreenRect(false);
3517 // compute new top sub line
3519 CharPosToPoint( topPos.y, topPos.x, topSubLine );
3520 m_nTopSubLine = GetSubLineIndex(topPos.y) + topSubLine.y;
3522 ScrollToSubLine( m_nTopSubLine );
3524 // set caret to right position
3528 RecalcVertScrollBar (false, false);
3529 RecalcHorzScrollBar (false, false);
3532 void CCrystalTextView::
3533 UpdateSiblingScrollPos (bool bHorz)
3535 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
3536 if (pSplitterWnd != nullptr)
3538 // See CSplitterWnd::IdFromRowCol() implementation for details
3539 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
3540 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
3541 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
3542 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
3546 int nCols = pSplitterWnd->GetColumnCount ();
3547 for (int nCol = 0; nCol < nCols; nCol++)
3549 if (nCol != nCurrentCol) // We don't need to update ourselves
3551 CCrystalTextView *pSiblingView = GetSiblingView (nCurrentRow, nCol);
3552 if (pSiblingView != nullptr)
3553 pSiblingView->OnUpdateSibling (this, false);
3559 int nRows = pSplitterWnd->GetRowCount ();
3560 for (int nRow = 0; nRow < nRows; nRow++)
3562 if (nRow != nCurrentRow) // We don't need to update ourselves
3564 CCrystalTextView *pSiblingView = GetSiblingView (nRow, nCurrentCol);
3565 if (pSiblingView != nullptr)
3566 pSiblingView->OnUpdateSibling (this, false);
3573 void CCrystalTextView::
3574 OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
3576 if (pUpdateSource != this)
3578 ASSERT (pUpdateSource != nullptr);
3579 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
3582 ASSERT (pUpdateSource->m_nTopLine >= 0);
3583 ASSERT (pUpdateSource->m_nTopLine < GetLineCount ());
3584 if (pUpdateSource->m_nTopLine != m_nTopLine)
3586 ScrollToLine (pUpdateSource->m_nTopLine, true, false);
3592 ASSERT (pUpdateSource->m_nOffsetChar >= 0);
3593 ASSERT (pUpdateSource->m_nOffsetChar < GetMaxLineLength (m_nTopLine, GetScreenLines()));
3594 if (pUpdateSource->m_nOffsetChar != m_nOffsetChar)
3596 ScrollToChar (pUpdateSource->m_nOffsetChar, true, false);
3603 void CCrystalTextView::
3604 RecalcVertScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
3606 SCROLLINFO si = {0};
3607 si.cbSize = sizeof (si);
3611 si.nPos = m_nTopSubLine;
3615 const int nScreenLines = GetScreenLines();
3616 if( m_nTopSubLine > 0 && nScreenLines >= GetSubLineCount() )
3622 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3624 si.nMax = GetSubLineCount() - 1;
3625 si.nPage = nScreenLines;
3626 si.nPos = m_nTopSubLine;
3628 VERIFY (SetScrollInfo (SB_VERT, &si, bRedraw));
3631 void CCrystalTextView::
3632 OnVScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
3634 CView::OnVScroll (nSBCode, nPos, pScrollBar);
3636 // Note we cannot use nPos because of its 16-bit nature
3637 SCROLLINFO si = {0};
3638 si.cbSize = sizeof (si);
3639 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3640 VERIFY (GetScrollInfo (SB_VERT, &si));
3642 // Get the minimum and maximum scroll-bar positions.
3643 int nMinPos = si.nMin;
3644 int nMaxPos = si.nMax;
3646 // Get the current position of scroll box.
3647 int nCurPos = si.nPos;
3649 bool bDisableSmooth = true;
3652 case SB_TOP: // Scroll to top.
3654 bDisableSmooth = false;
3657 case SB_BOTTOM: // Scroll to bottom.
3659 bDisableSmooth = false;
3662 case SB_LINEUP: // Scroll one line up.
3663 if (nCurPos > nMinPos)
3667 case SB_LINEDOWN: // Scroll one line down.
3668 if (nCurPos < nMaxPos)
3672 case SB_PAGEUP: // Scroll one page up.
3673 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
3674 bDisableSmooth = false;
3677 case SB_PAGEDOWN: // Scroll one page down.
3678 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
3679 bDisableSmooth = false;
3682 case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position
3683 nCurPos = si.nTrackPos; // of the scroll box at the end of the drag operation.
3686 case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the
3687 nCurPos = si.nTrackPos; // position that the scroll box has been dragged to.
3690 ScrollToSubLine(nCurPos, bDisableSmooth);
3693 void CCrystalTextView::
3694 RecalcHorzScrollBar (bool bPositionOnly /*= false*/, bool bRedraw /*= true */)
3696 SCROLLINFO si = {0};
3697 si.cbSize = sizeof (si);
3699 const int nScreenChars = GetScreenChars();
3703 if (m_nOffsetChar > nScreenChars)
3709 // Disable horizontal scroll bar
3710 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3711 SetScrollInfo (SB_HORZ, &si);
3715 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
3720 si.nPos = m_nOffsetChar;
3724 if (nScreenChars >= nMaxLineLen && m_nOffsetChar > 0)
3730 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
3733 // Horiz scroll limit to longest line + one screenwidth
3734 si.nMax = nMaxLineLen + nScreenChars;
3735 si.nPage = nScreenChars;
3736 si.nPos = m_nOffsetChar;
3738 VERIFY (SetScrollInfo (SB_HORZ, &si, bRedraw));
3741 void CCrystalTextView::
3742 OnHScroll (UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
3744 // Default handler not needed
3745 //CView::OnHScroll (nSBCode, nPos, pScrollBar);
3747 // Again, we cannot use nPos because it's 16-bit
3748 SCROLLINFO si = {0};
3749 si.cbSize = sizeof (si);
3750 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3751 VERIFY (GetScrollInfo (SB_HORZ, &si));
3753 // Get the minimum and maximum scroll-bar positions.
3754 int nMinPos = si.nMin;
3755 int nMaxPos = si.nMax;
3757 // Get the current position of scroll box.
3758 int nCurPos = si.nPos;
3762 case SB_LEFT: // Scroll to far left.
3766 case SB_RIGHT: // Scroll to far right.
3770 case SB_ENDSCROLL: // End scroll.
3773 case SB_LINELEFT: // Scroll left.
3774 if (nCurPos > nMinPos)
3778 case SB_LINERIGHT: // Scroll right.
3779 if (nCurPos < nMaxPos)
3783 case SB_PAGELEFT: // Scroll one page left.
3784 nCurPos = max(nMinPos, nCurPos - (int) si.nPage + 1);
3787 case SB_PAGERIGHT: // Scroll one page right.
3788 nCurPos = min(nMaxPos, nCurPos + (int) si.nPage - 1);
3791 case SB_THUMBPOSITION: // Scroll to absolute position. The current position is
3792 nCurPos = si.nTrackPos; // specified by the nPos parameter.
3795 case SB_THUMBTRACK: // Drag scroll box to specified position. The current
3796 nCurPos = si.nTrackPos; // position is specified by the nPos parameter
3797 // The SB_THUMBTRACK scroll-bar code typically is used by applications that give
3798 // some feedback while the scroll box is being dragged.
3801 ScrollToChar (nCurPos, true);
3802 // This is needed, but why ? OnVScroll don't need to call UpdateCaret
3806 BOOL CCrystalTextView::
3807 OnSetCursor (CWnd * pWnd, UINT nHitTest, UINT message)
3809 if (nHitTest == HTCLIENT)
3812 ::GetCursorPos (&pt);
3813 ScreenToClient (&pt);
3814 if (pt.x < GetMarginWidth ())
3816 ::SetCursor (::LoadCursor (AfxGetInstanceHandle (), MAKEINTRESOURCE (IDR_MARGIN_CURSOR)));
3820 CPoint ptText = ClientToText (pt);
3821 PrepareSelBounds ();
3822 if (IsInsideSelBlock (ptText))
3824 // [JRT]: Support For Disabling Drag and Drop...
3825 if (!m_bDisableDragAndDrop) // If Drag And Drop Not Disabled
3827 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW)); // Set To Arrow Cursor
3831 ::SetCursor (::LoadCursor (nullptr, IDC_IBEAM));
3835 return CView::OnSetCursor (pWnd, nHitTest, message);
3839 * @brief Converts client area point to text position.
3840 * @param [in] point Client area point.
3841 * @return Text position (line index, char index in line).
3842 * @note For gray selection area char index is 0.
3844 CPoint CCrystalTextView::
3845 ClientToText (const CPoint & point)
3848 const int nSubLineCount = GetSubLineCount();
3849 const int nLineCount = GetLineCount();
3852 pt.y = m_nTopSubLine + point.y / GetLineHeight();
3853 if (pt.y >= nSubLineCount)
3854 pt.y = nSubLineCount - 1;
3860 int nOffsetChar = m_nOffsetChar;
3862 GetLineBySubLine( pt.y, nLine, nSubLineOffset );
3865 LPCTSTR pszLine = nullptr;
3867 vector<int> anBreaks(1);
3870 if (pt.y >= 0 && pt.y < nLineCount)
3872 nLength = GetLineLength( pt.y );
3873 anBreaks.resize(nLength + 1);
3874 pszLine = GetLineChars(pt.y);
3875 WrapLineCached( pt.y, GetScreenChars(), &anBreaks[0], nBreaks );
3877 if (nSubLineOffset > 0)
3879 if (nSubLineOffset < nBreaks)
3880 nOffsetChar = anBreaks[nSubLineOffset - 1];
3881 else if (nBreaks > 0)
3882 nOffsetChar = anBreaks[nBreaks - 1];
3884 if (nBreaks > nSubLineOffset)
3885 nLength = anBreaks[nSubLineOffset] - 1;
3889 // Char index for margin area is 0
3890 if (point.x > GetMarginWidth())
3891 nPos = nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth();
3895 int nIndex = 0, nPrevIndex = 0;
3899 const int nTabSize = GetTabSize();
3901 m_iterChar.setText(reinterpret_cast<const UChar *>(pszLine), nLength);
3902 while (nIndex < nLength)
3904 if (nBreaks && nIndex == anBreaks[i])
3911 if (pszLine[nIndex] == '\t')
3912 nOffset = nTabSize - nCurPos % nTabSize;
3914 nOffset = GetCharCellCountFromChar(pszLine + nIndex);
3918 if (n > nPos && i == nSubLineOffset)
3921 nPrevIndex = nIndex;
3923 nIndex = m_iterChar.next();
3926 ASSERT(nIndex >= 0 && nIndex <= nLength);
3932 void CCrystalTextView::
3933 AssertValidTextPos (const CPoint & point)
3935 if (GetLineCount () > 0)
3937 ASSERT (m_nTopLine >= 0 && m_nOffsetChar >= 0);
3938 ASSERT (point.y >= 0 && point.y < GetLineCount ());
3939 ASSERT (point.x >= 0 && point.x <= GetViewableLineLength (point.y));
3944 bool CCrystalTextView::
3945 IsValidTextPos (const CPoint &point)
3947 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
3948 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
3951 bool CCrystalTextView::
3952 IsValidTextPosX (const CPoint &point)
3954 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
3955 point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);
3958 bool CCrystalTextView::
3959 IsValidTextPosY (const CPoint &point)
3961 return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 &&
3962 point.y >= 0 && point.y < GetLineCount ();
3965 CPoint CCrystalTextView::
3966 TextToClient (const CPoint & point)
3968 ASSERT_VALIDTEXTPOS (point);
3969 LPCTSTR pszLine = GetLineChars (point.y);
3974 int nSubLineStart = CharPosToPoint( point.y, point.x, charPoint );
3975 charPoint.y+= GetSubLineIndex( point.y );
3977 // compute y-position
3978 pt.y = (charPoint.y - m_nTopSubLine) * GetLineHeight();
3980 // if pt.x is null, we know the result
3981 if( charPoint.x == 0 )
3983 pt.x = GetMarginWidth();
3987 // we have to calculate x-position
3990 pt.y = (point.y - m_nTopLine) * GetLineHeight();
3994 int nTabSize = GetTabSize ();
3995 m_iterChar.setText(reinterpret_cast<const UChar *>(pszLine), point.x);
3996 for (int nIndex = 0; nIndex < point.x; nIndex = m_iterChar.next())
3999 if( nIndex == nSubLineStart )
4002 if (pszLine[nIndex] == _T ('\t'))
4003 pt.x += (nTabSize - pt.x % nTabSize);
4005 pt.x += GetCharCellCountFromChar(pszLine + nIndex);
4011 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth () + GetMarginWidth ();
4015 void CCrystalTextView::
4016 InvalidateLines (int nLine1, int nLine2, bool bInvalidateMargin /*= false*/ )
4018 bInvalidateMargin = true;
4019 const int nLineHeight = GetLineHeight();
4023 GetClientRect (&rcInvalid);
4024 if (!bInvalidateMargin)
4025 rcInvalid.left += GetMarginWidth ();
4027 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight;
4029 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4032 InvalidateRect (&rcInvalid, false);
4036 if (nLine2 < nLine1)
4043 GetClientRect (&rcInvalid);
4044 if (!bInvalidateMargin)
4045 rcInvalid.left += GetMarginWidth ();
4047 rcInvalid.top = (GetSubLineIndex( nLine1 ) - m_nTopSubLine) * nLineHeight;
4048 rcInvalid.bottom = (GetSubLineIndex( nLine2 ) - m_nTopSubLine + GetSubLines( nLine2 )) * nLineHeight;
4050 rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
4051 rcInvalid.bottom = (nLine2 - m_nTopLine + 1) * GetLineHeight();
4054 InvalidateRect (&rcInvalid, false);
4058 void CCrystalTextView::
4059 SetSelection (const CPoint & ptStart, const CPoint & ptEnd, bool bUpdateView /* = true */)
4061 ASSERT_VALIDTEXTPOS (ptStart);
4062 ASSERT_VALIDTEXTPOS (ptEnd);
4063 if (m_ptSelStart == ptStart && !m_bColumnSelection)
4065 if (m_ptSelEnd != ptEnd)
4066 InvalidateLines (ptEnd.y, m_ptSelEnd.y);
4070 InvalidateLines (ptStart.y, ptEnd.y);
4071 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4073 m_ptSelStart = ptStart;
4077 void CCrystalTextView::
4078 AdjustTextPoint (CPoint & point)
4080 point.x += GetCharWidth () / 2; //todo
4084 void CCrystalTextView::
4085 OnSetFocus (CWnd * pOldWnd)
4087 CView::OnSetFocus (pOldWnd);
4090 if (m_ptSelStart != m_ptSelEnd)
4091 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4095 DWORD CCrystalTextView::
4096 ParseLine (DWORD dwCookie, const TCHAR *pszChars, int nLength, TEXTBLOCK * pBuf, int &nActualItems)
4098 return m_CurSourceDef->ParseLineX (dwCookie, pszChars, nLength, pBuf, nActualItems);
4101 int CCrystalTextView::
4102 CalculateActualOffset (int nLineIndex, int nCharIndex, bool bAccumulate)
4104 const int nLength = GetLineLength (nLineIndex);
4105 ASSERT (nCharIndex >= 0 && nCharIndex <= nLength);
4106 LPCTSTR pszChars = GetLineChars (nLineIndex);
4108 const int nTabSize = GetTabSize ();
4110 vector<int> anBreaks(nLength + 1);
4113 /*if( nLength > GetScreenChars() )*/
4114 WrapLineCached( nLineIndex, GetScreenChars(), &anBreaks[0], nBreaks );
4122 for( J = nBreaks - 1; J >= 0 && nCharIndex < anBreaks[J]; J-- );
4123 nPreBreak = (J >= 0) ? anBreaks[J] : 0;
4126 m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nCharIndex);
4128 for (I = 0; I < nCharIndex; I = m_iterChar.next())
4131 if( nPreBreak == I && nBreaks )
4132 nPreOffset = nOffset;
4134 if (pszChars[I] == _T ('\t'))
4135 nOffset += (nTabSize - nOffset % nTabSize);
4137 nOffset += GetCharCellCountFromChar(pszChars + I);
4142 if( nPreBreak == I && nBreaks > 0)
4145 return nOffset - nPreOffset;
4151 int CCrystalTextView::
4152 ApproxActualOffset (int nLineIndex, int nOffset)
4157 int nLength = GetLineLength (nLineIndex);
4158 LPCTSTR pszChars = GetLineChars (nLineIndex);
4159 int nCurrentOffset = 0;
4160 int nTabSize = GetTabSize ();
4161 m_iterChar.setText(reinterpret_cast<const UChar *>(pszChars), nLength);
4162 for (int I = 0; I < nLength; I = m_iterChar.next())
4164 if (pszChars[I] == _T ('\t'))
4165 nCurrentOffset += (nTabSize - nCurrentOffset % nTabSize);
4168 nCurrentOffset += GetCharCellCountFromChar(pszChars + I);
4170 if (nCurrentOffset >= nOffset)
4172 if (nOffset <= nCurrentOffset - nTabSize / 2)
4174 return m_iterChar.next();
4180 void CCrystalTextView::
4181 EnsureVisible (CPoint pt)
4183 // Scroll vertically
4184 int nSubLineCount = GetSubLineCount();
4185 int nNewTopSubLine = m_nTopSubLine;
4188 CharPosToPoint( pt.y, pt.x, subLinePos );
4189 subLinePos.y+= GetSubLineIndex( pt.y );
4191 if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
4192 nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
4193 if( subLinePos.y < nNewTopSubLine )
4194 nNewTopSubLine = subLinePos.y;
4196 if( nNewTopSubLine < 0 )
4198 if( nNewTopSubLine >= nSubLineCount )
4199 nNewTopSubLine = nSubLineCount - 1;
4201 if ( !m_bWordWrap && !m_bHideLines )
4203 // WINMERGE: This line fixes (cursor) slowdown after merges!
4204 // I don't know exactly why, but propably we are setting
4205 // m_nTopLine to zero in ResetView() and are not setting to
4206 // valid value again. Maybe this is a good place to set it?
4207 m_nTopLine = nNewTopSubLine;
4212 GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
4215 if( nNewTopSubLine != m_nTopSubLine )
4217 ScrollToSubLine( nNewTopSubLine );
4219 UpdateSiblingScrollPos( false );
4222 // Scroll horizontally
4223 // we do not need horizontally scrolling, if we wrap the words
4226 int nActualPos = CalculateActualOffset (pt.y, pt.x);
4227 int nNewOffset = m_nOffsetChar;
4228 const int nScreenChars = GetScreenChars ();
4230 // Keep 5 chars visible right to cursor
4231 if (nActualPos > nNewOffset + nScreenChars - 5)
4233 // Add 10 chars width space after line
4234 nNewOffset = nActualPos - nScreenChars + 10;
4236 // Keep 5 chars visible left to cursor
4237 if (nActualPos < nNewOffset + 5)
4239 // Jump by 10 char steps, so user sees previous letters too
4240 nNewOffset = nActualPos - 10;
4243 // Horiz scroll limit to longest line + one screenwidth
4244 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
4245 if (nNewOffset >= nMaxLineLen + nScreenChars)
4246 nNewOffset = nMaxLineLen + nScreenChars - 1;
4250 if (m_nOffsetChar != nNewOffset)
4252 ScrollToChar (nNewOffset);
4254 UpdateSiblingScrollPos (true);
4258 void CCrystalTextView::
4259 OnKillFocus (CWnd * pNewWnd)
4261 CView::OnKillFocus (pNewWnd);
4265 if (m_ptSelStart != m_ptSelEnd)
4266 InvalidateLines (m_ptSelStart.y, m_ptSelEnd.y);
4267 if (m_bDragSelection)
4270 KillTimer (m_nDragSelTimer);
4271 m_bDragSelection = false;
4275 void CCrystalTextView::
4278 CView::OnSysColorChange ();
4282 void CCrystalTextView::
4283 GetText (const CPoint & ptStart, const CPoint & ptEnd, CString & text, bool bExcludeInvisibleLines /*= true*/)
4285 if (m_pTextBuffer != nullptr)
4286 m_pTextBuffer->GetText (ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, text);
4291 void CCrystalTextView::
4292 GetTextInColumnSelection (CString & text, bool bExcludeInvisibleLines /*= true*/)
4294 if (m_pTextBuffer == nullptr)
4300 PrepareSelBounds ();
4302 CString sEol = m_pTextBuffer->GetStringEol (CRLF_STYLE_DOS);
4305 for (int L = m_ptDrawSelStart.y; L <= m_ptDrawSelEnd.y; L++)
4306 nBufSize += GetLineLength (L) + sEol.GetLength ();
4307 LPTSTR pszBuf = text.GetBuffer (nBufSize);
4309 for (int I = m_ptDrawSelStart.y; I <= m_ptDrawSelEnd.y; I++)
4311 if (bExcludeInvisibleLines && (GetLineFlags (I) & LF_INVISIBLE))
4313 int nSelLeft, nSelRight;
4314 GetColumnSelection (I, nSelLeft, nSelRight);
4315 memcpy (pszBuf, GetLineChars (I) + nSelLeft, sizeof (TCHAR) * (nSelRight - nSelLeft));
4316 pszBuf += (nSelRight - nSelLeft);
4317 memcpy (pszBuf, sEol, sizeof (TCHAR) * sEol.GetLength ());
4318 pszBuf += sEol.GetLength ();
4321 text.ReleaseBuffer ();
4325 void CCrystalTextView::
4326 UpdateView (CCrystalTextView * pSource, CUpdateContext * pContext,
4327 DWORD dwFlags, int nLineIndex /*= -1*/ )
4329 // SetTextType (GetExt (GetDocument ()->GetPathName ()));
4330 if (dwFlags & UPDATE_RESET)
4333 RecalcVertScrollBar ();
4334 RecalcHorzScrollBar ();
4338 int nLineCount = GetLineCount ();
4339 ASSERT (nLineCount > 0);
4340 ASSERT (nLineIndex >= -1 && nLineIndex < nLineCount);
4341 if ((dwFlags & UPDATE_SINGLELINE) != 0)
4343 ASSERT (nLineIndex != -1);
4344 // All text below this line should be reparsed
4345 const int cookiesSize = (int) m_ParseCookies->size();
4346 if (cookiesSize > 0)
4348 ASSERT (cookiesSize == nLineCount);
4349 // must be reinitialized to invalid value (DWORD) - 1
4350 for (int i = nLineIndex; i < cookiesSize; ++i)
4351 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4353 // This line'th actual length must be recalculated
4354 if (m_pnActualLineLength->size())
4356 ASSERT (m_pnActualLineLength->size() == static_cast<size_t>(nLineCount));
4357 // must be initialized to invalid code -1
4358 (*m_pnActualLineLength)[nLineIndex] = -1;
4360 InvalidateLineCache( nLineIndex, nLineIndex );
4363 // Repaint the lines
4364 InvalidateLines (nLineIndex, -1, true);
4368 if (m_bViewLineNumbers)
4369 // if enabling linenumber, we must invalidate all line-cache in visible area because selection margin width changes dynamically.
4370 nLineIndex = m_nTopLine < nLineIndex ? m_nTopLine : nLineIndex;
4372 if (nLineIndex == -1)
4373 nLineIndex = 0; // Refresh all text
4375 // All text below this line should be reparsed
4376 if (m_ParseCookies->size())
4378 size_t arrSize = m_ParseCookies->size();
4379 if (arrSize != static_cast<size_t>(nLineCount))
4381 size_t oldsize = arrSize;
4382 m_ParseCookies->resize(nLineCount);
4383 arrSize = nLineCount;
4384 // must be initialized to invalid value (DWORD) - 1
4385 for (size_t i = oldsize; i < arrSize; ++i)
4386 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4388 for (size_t i = nLineIndex; i < arrSize; ++i)
4389 (*m_ParseCookies)[i] = static_cast<DWORD>(-1);
4392 // Recalculate actual length for all lines below this
4393 if (m_pnActualLineLength->size())
4395 size_t arrsize = m_pnActualLineLength->size();
4396 if (arrsize != static_cast<size_t>(nLineCount))
4398 // Reallocate actual length array
4399 size_t oldsize = arrsize;
4400 m_pnActualLineLength->resize(nLineCount);
4401 arrsize = nLineCount;
4402 // must be initialized to invalid code -1
4403 for (size_t i = oldsize; i < arrsize; ++i)
4404 (*m_pnActualLineLength)[i] = -1;
4406 for (size_t i = nLineIndex; i < arrsize; ++i)
4407 (*m_pnActualLineLength)[i] = -1;
4410 InvalidateLineCache( nLineIndex, -1 );
4412 // Repaint the lines
4413 InvalidateLines (nLineIndex, -1, true);
4416 // All those points must be recalculated and validated
4417 if (pContext != nullptr)
4419 pContext->RecalcPoint (m_ptCursorPos);
4420 pContext->RecalcPoint (m_ptSelStart);
4421 pContext->RecalcPoint (m_ptSelEnd);
4422 pContext->RecalcPoint (m_ptAnchor);
4423 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
4424 ASSERT_VALIDTEXTPOS (m_ptSelStart);
4425 ASSERT_VALIDTEXTPOS (m_ptSelEnd);
4426 ASSERT_VALIDTEXTPOS (m_ptAnchor);
4427 if (m_bDraggingText)
4429 pContext->RecalcPoint (m_ptDraggedTextBegin);
4430 pContext->RecalcPoint (m_ptDraggedTextEnd);
4431 ASSERT_VALIDTEXTPOS (m_ptDraggedTextBegin);
4432 ASSERT_VALIDTEXTPOS (m_ptDraggedTextEnd);
4434 CPoint ptTopLine (0, m_nTopLine);
4435 pContext->RecalcPoint (ptTopLine);
4436 ASSERT_VALIDTEXTPOS (ptTopLine);
4437 m_nTopLine = ptTopLine.y;
4441 // Recalculate vertical scrollbar, if needed
4442 if ((dwFlags & UPDATE_VERTRANGE) != 0)
4444 if (!m_bVertScrollBarLocked)
4445 RecalcVertScrollBar ();
4448 // Recalculate horizontal scrollbar, if needed
4449 if ((dwFlags & UPDATE_HORZRANGE) != 0)
4451 if (!m_bHorzScrollBarLocked)
4452 RecalcHorzScrollBar ();
4456 HINSTANCE CCrystalTextView::
4457 GetResourceHandle ()
4459 #ifdef CRYSEDIT_RES_HANDLE
4460 return CRYSEDIT_RES_HANDLE;
4462 if (s_hResourceInst != nullptr)
4463 return s_hResourceInst;
4464 return AfxGetResourceHandle ();
4468 int CCrystalTextView::
4469 OnCreate (LPCREATESTRUCT lpCreateStruct)
4472 _tcscpy_s (m_lfBaseFont.lfFaceName, _T ("FixedSys"));
4473 m_lfBaseFont.lfHeight = 0;
4474 m_lfBaseFont.lfWeight = FW_NORMAL;
4475 m_lfBaseFont.lfItalic = false;
4476 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
4477 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
4478 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4479 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
4480 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
4482 if (CView::OnCreate (lpCreateStruct) == -1)
4485 ASSERT (m_hAccel == nullptr);
4486 // vvv GetResourceHandle () ???
4487 HINSTANCE hInst = AfxFindResourceHandle (MAKEINTRESOURCE(IDR_DEFAULT_ACCEL), RT_ACCELERATOR);
4488 ASSERT (hInst != nullptr);
4489 m_hAccel =::LoadAccelerators (hInst, MAKEINTRESOURCE (IDR_DEFAULT_ACCEL));
4490 ASSERT (m_hAccel != nullptr);
4494 void CCrystalTextView::
4495 SetAnchor (const CPoint & ptNewAnchor)
4497 ASSERT_VALIDTEXTPOS (ptNewAnchor);
4498 m_ptAnchor = ptNewAnchor;
4501 void CCrystalTextView::
4502 OnEditOperation (int nAction, LPCTSTR pszText, size_t cchText)
4506 BOOL CCrystalTextView::
4507 PreTranslateMessage (MSG * pMsg)
4509 if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
4511 if (m_hAccel != nullptr)
4513 if (::TranslateAccelerator (m_hWnd, m_hAccel, pMsg))
4517 else if (pMsg->message == WM_LBUTTONDBLCLK)
4518 m_dwLastDblClickTime = GetTickCount();
4519 else if (pMsg->message == WM_LBUTTONDOWN && (GetTickCount() - GetDoubleClickTime()) < m_dwLastDblClickTime)
4521 m_dwLastDblClickTime = 0;
4522 OnLButtonTrippleClk(static_cast<UINT>(pMsg->wParam), { GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam) });
4525 return CView::PreTranslateMessage (pMsg);
4528 CPoint CCrystalTextView::
4529 GetCursorPos () const
4531 return m_ptCursorPos;
4534 void CCrystalTextView::
4535 SetCursorPos (const CPoint & ptCursorPos)
4537 ASSERT_VALIDTEXTPOS (ptCursorPos);
4538 m_ptCursorPos = ptCursorPos;
4539 m_nIdealCharPos = CalculateActualOffset (m_ptCursorPos.y, m_ptCursorPos.x);
4543 void CCrystalTextView::
4544 UpdateCompositionWindowPos() /* IME */
4546 HIMC hIMC = ImmGetContext(m_hWnd);
4547 COMPOSITIONFORM compform;
4549 compform.dwStyle = CFS_FORCE_POSITION;
4550 compform.ptCurrentPos = GetCaretPos();
4551 ImmSetCompositionWindow(hIMC, &compform);
4553 ImmReleaseContext(m_hWnd, hIMC);
4556 void CCrystalTextView::
4557 UpdateCompositionWindowFont() /* IME */
4559 HIMC hIMC = ImmGetContext(m_hWnd);
4562 GetFont()->GetLogFont(&logfont);
4563 ImmSetCompositionFont(hIMC, &logfont);
4565 ImmReleaseContext(m_hWnd, hIMC);
4568 void CCrystalTextView::
4569 SetSelectionMargin (bool bSelMargin)
4571 if (m_bSelMargin != bSelMargin)
4573 m_bSelMargin = bSelMargin;
4574 if (::IsWindow (m_hWnd))
4576 InvalidateScreenRect ();
4577 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
4578 RecalcHorzScrollBar ();
4584 void CCrystalTextView::
4585 SetViewLineNumbers (bool bViewLineNumbers)
4587 if (m_bViewLineNumbers != bViewLineNumbers)
4589 m_bViewLineNumbers = bViewLineNumbers;
4590 if (::IsWindow (m_hWnd))
4592 InvalidateScreenRect ();
4593 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
4594 RecalcHorzScrollBar ();
4600 void CCrystalTextView::
4601 GetFont (LOGFONT & lf)
4606 void CCrystalTextView::
4607 SetFont (const LOGFONT & lf)
4612 for (int I = 0; I < 4; I++)
4614 if (m_apFonts[I] != nullptr)
4616 delete m_apFonts[I];
4617 m_apFonts[I] = nullptr;
4620 if (::IsWindow (m_hWnd))
4622 InvalidateScreenRect();
4623 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
4624 RecalcVertScrollBar ();
4625 RecalcHorzScrollBar ();
4633 void CCrystalTextView::
4634 OnUpdateIndicatorPosition (CCmdUI * pCmdUI)
4636 ASSERT_VALIDTEXTPOS (m_ptCursorPos);
4638 // VVV m_ptCursorPos.x + 1 ???
4639 stat.Format (_T ("Ln %d, Col %d"), m_ptCursorPos.y + 1, m_nIdealCharPos + 1);
4640 pCmdUI->SetText (stat);
4642 if( pCmdUI->m_pOther != nullptr && !!pCmdUI->m_pOther->IsKindOf( RUNTIME_CLASS(CStatusBar) ) )
4643 OnUpdateStatusMessage( (CStatusBar*)pCmdUI->m_pOther );
4647 void CCrystalTextView::
4648 OnUpdateIndicatorCRLF (CCmdUI * pCmdUI)
4650 if (m_pTextBuffer != nullptr)
4652 std::basic_string<TCHAR> eol;
4653 CRLFSTYLE crlfMode = m_pTextBuffer->GetCRLFMode ();
4656 case CRLF_STYLE_DOS:
4657 eol = LoadResString (IDS_EOL_DOS);
4658 pCmdUI->SetText (eol.c_str());
4659 pCmdUI->Enable (true);
4661 case CRLF_STYLE_UNIX:
4662 eol = LoadResString (IDS_EOL_UNIX);
4663 pCmdUI->SetText (eol.c_str());
4664 pCmdUI->Enable (true);
4666 case CRLF_STYLE_MAC:
4667 eol = LoadResString (IDS_EOL_MAC);
4668 pCmdUI->SetText (eol.c_str());
4669 pCmdUI->Enable (true);
4671 case CRLF_STYLE_MIXED:
4672 eol = LoadResString (IDS_EOL_MIXED);
4673 pCmdUI->SetText (eol.c_str());
4674 pCmdUI->Enable (true);
4677 pCmdUI->SetText (nullptr);
4678 pCmdUI->Enable (false);
4683 pCmdUI->SetText (nullptr);
4684 pCmdUI->Enable (false);
4688 void CCrystalTextView::
4689 OnToggleBookmark (UINT nCmdID)
4691 int nBookmarkID = nCmdID - ID_EDIT_TOGGLE_BOOKMARK0;
4692 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
4693 if (m_pTextBuffer != nullptr)
4695 DWORD dwFlags = GetLineFlags (m_ptCursorPos.y);
4696 DWORD dwMask = LF_BOOKMARK (nBookmarkID);
4697 m_pTextBuffer->SetLineFlag (m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0);
4701 void CCrystalTextView::
4702 OnGoBookmark (UINT nCmdID)
4704 int nBookmarkID = nCmdID - ID_EDIT_GO_BOOKMARK0;
4705 ASSERT (nBookmarkID >= 0 && nBookmarkID <= 9);
4706 if (m_pTextBuffer != nullptr)
4708 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
4711 CPoint pt (0, nLine);
4712 ASSERT_VALIDTEXTPOS (pt);
4714 SetSelection (pt, pt);
4721 void CCrystalTextView::
4724 if (m_pTextBuffer != nullptr)
4726 for (int nBookmarkID = 0; nBookmarkID <= 9; nBookmarkID++)
4728 int nLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARK (nBookmarkID));
4731 m_pTextBuffer->SetLineFlag (nLine, LF_BOOKMARK (nBookmarkID), false);
4738 void CCrystalTextView::
4741 m_bCursorHidden = false;
4745 void CCrystalTextView::
4748 m_bCursorHidden = true;
4752 DROPEFFECT CCrystalTextView::
4755 return DROPEFFECT_COPY;
4758 void CCrystalTextView::
4759 OnDropSource (DROPEFFECT de)
4761 ASSERT (de == DROPEFFECT_COPY);
4764 HGLOBAL CCrystalTextView::
4767 PrepareSelBounds ();
4768 if (m_ptDrawSelStart == m_ptDrawSelEnd)
4772 GetText (m_ptDrawSelStart, m_ptDrawSelEnd, text);
4773 int cchText = text.GetLength();
4774 SIZE_T cbData = (cchText + 1) * sizeof(TCHAR);
4775 HGLOBAL hData =::GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, cbData);
4776 if (hData == nullptr)
4779 LPTSTR pszData = (LPTSTR)::GlobalLock (hData);
4780 if (pszData == nullptr)
4782 ::GlobalFree(hData);
4785 memcpy (pszData, text, cbData);
4786 ::GlobalUnlock (hData);
4788 m_ptDraggedTextBegin = m_ptDrawSelStart;
4789 m_ptDraggedTextEnd = m_ptDrawSelEnd;
4794 FindStringHelper (LPCTSTR pszLineBegin, LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, DWORD dwFlags, int &nLen, RxNode *&rxnode, RxMatchRes *rxmatch)
4796 if (dwFlags & FIND_REGEXP)
4803 if (pszFindWhat[0] == '^' && pszLineBegin != pszFindWhere)
4805 rxnode = RxCompile (pszFindWhat, (dwFlags & FIND_MATCH_CASE) != 0 ? RX_CASE : 0);
4806 if (rxnode && RxExec (rxnode, pszFindWhere, _tcslen (pszFindWhere), pszFindWhere, rxmatch))
4808 pos = rxmatch->Open[0];
4809 ASSERT((rxmatch->Close[0] - rxmatch->Open[0]) < INT_MAX);
4810 nLen = static_cast<int>(rxmatch->Close[0] - rxmatch->Open[0]);
4816 ASSERT (pszFindWhere != nullptr);
4817 ASSERT (pszFindWhat != nullptr);
4819 int nLength = (int) _tcslen (pszFindWhat);
4820 LPCTSTR pszFindWhereOrig = pszFindWhere;
4825 if (dwFlags & FIND_MATCH_CASE)
4826 pszPos = _tcsstr(pszFindWhere, pszFindWhat);
4828 pszPos = StrStrI(pszFindWhere, pszFindWhat);
4829 if (pszPos == nullptr)
4831 if ((dwFlags & FIND_WHOLE_WORD) == 0)
4832 return nCur + (int) (pszPos - pszFindWhere);
4833 if (pszPos > pszFindWhereOrig && xisalnum (pszPos[-1]))
4835 nCur += (int) (pszPos - pszFindWhere + 1);
4836 pszFindWhere = pszPos + 1;
4839 if (xisalnum (pszPos[nLength]))
4841 nCur += (int) (pszPos - pszFindWhere + 1);
4842 pszFindWhere = pszPos + 1;
4845 return nCur + (int) (pszPos - pszFindWhere);
4848 //~ ASSERT (false); // Unreachable
4852 * @brief Select text in editor.
4853 * @param [in] ptStartPos Star position for highlight.
4854 * @param [in] nLength Count of characters to highlight.
4855 * @param [in] bCursorToLeft If true cursor is positioned to Left-end of text
4856 * selection, if false cursor is positioned to right-end.
4858 bool CCrystalTextView::
4859 HighlightText (const CPoint & ptStartPos, int nLength,
4860 bool bCursorToLeft /*= false*/)
4862 ASSERT_VALIDTEXTPOS (ptStartPos);
4863 CPoint ptEndPos = ptStartPos;
4864 int nCount = GetLineLength (ptEndPos.y) - ptEndPos.x;
4865 if (nLength <= nCount)
4867 ptEndPos.x += nLength;
4871 while (nLength > nCount)
4873 nLength -= nCount + 1;
4874 nCount = GetLineLength (++ptEndPos.y);
4876 ptEndPos.x = nLength;
4878 ASSERT_VALIDTEXTPOS (m_ptCursorPos); // Probably 'nLength' is bigger than expected...
4880 m_ptCursorPos = bCursorToLeft ? ptStartPos : ptEndPos;
4881 m_ptAnchor = bCursorToLeft ? ptEndPos : ptStartPos;
4882 SetSelection (ptStartPos, ptEndPos);
4885 // Scrolls found text to middle of screen if out-of-screen
4886 int nScreenLines = GetScreenLines();
4887 if (ptStartPos.y < m_nTopLine || ptEndPos.y > m_nTopLine + nScreenLines)
4889 if (ptStartPos.y > nScreenLines / 2)
4890 ScrollToLine(ptStartPos.y - nScreenLines / 2);
4892 ScrollToLine(ptStartPos.y);
4893 UpdateSiblingScrollPos (false);
4895 EnsureVisible (ptStartPos, ptEndPos);
4899 bool CCrystalTextView::
4900 FindText (LPCTSTR pszText, const CPoint & ptStartPos, DWORD dwFlags,
4901 bool bWrapSearch, CPoint * pptFoundPos)
4903 if (m_pMarkers != nullptr)
4905 m_pMarkers->SetMarker(_T("EDITOR_MARKER"), pszText, dwFlags, COLORINDEX_MARKERBKGND0, false);
4906 if (m_pMarkers->GetEnabled())
4907 m_pMarkers->UpdateViews();
4909 int nLineCount = GetLineCount ();
4910 return FindTextInBlock (pszText, ptStartPos, CPoint (0, 0),
4911 CPoint (GetLineLength (nLineCount - 1), nLineCount - 1),
4912 dwFlags, bWrapSearch, pptFoundPos);
4915 int HowManyStr (LPCTSTR s, LPCTSTR m)
4919 const int l = (int) _tcslen (m);
4920 while ((p = _tcsstr (p, m)) != nullptr)
4928 int HowManyStr (LPCTSTR s, TCHAR c)
4932 while ((p = _tcschr (p, c)) != nullptr)
4940 bool CCrystalTextView::
4941 FindTextInBlock (LPCTSTR pszText, const CPoint & ptStartPosition,
4942 const CPoint & ptBlockBegin, const CPoint & ptBlockEnd,
4943 DWORD dwFlags, bool bWrapSearch, CPoint * pptFoundPos)
4945 CPoint ptCurrentPos = ptStartPosition;
4947 ASSERT (pszText != nullptr && _tcslen (pszText) > 0);
4948 ASSERT_VALIDTEXTPOS (ptCurrentPos);
4949 ASSERT_VALIDTEXTPOS (ptBlockBegin);
4950 ASSERT_VALIDTEXTPOS (ptBlockEnd);
4951 ASSERT (ptBlockBegin.y < ptBlockEnd.y || ptBlockBegin.y == ptBlockEnd.y &&
4952 ptBlockBegin.x <= ptBlockEnd.x);
4953 if (ptBlockBegin == ptBlockEnd)
4955 CWaitCursor waitCursor;
4956 if (ptCurrentPos.y < ptBlockBegin.y || ptCurrentPos.y == ptBlockBegin.y &&
4957 ptCurrentPos.x < ptBlockBegin.x)
4958 ptCurrentPos = ptBlockBegin;
4960 CString what = pszText;
4962 if (dwFlags & FIND_REGEXP)
4964 nEolns = HowManyStr (what, _T("\\n"));
4970 if (dwFlags & FIND_DIRECTION_UP)
4972 // Let's check if we deal with whole text.
4973 // At this point, we cannot search *up* in selection
4974 ASSERT (ptBlockBegin.x == 0 && ptBlockBegin.y == 0);
4975 ASSERT (ptBlockEnd.x == GetLineLength (GetLineCount () - 1) &&
4976 ptBlockEnd.y == GetLineCount () - 1);
4978 // Proceed as if we have whole text search.
4981 while (ptCurrentPos.y >= 0)
4985 if (dwFlags & FIND_REGEXP)
4987 for (int i = 0; i <= nEolns && ptCurrentPos.y >= i; i++)
4990 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y - i);
4993 nLineLength = GetLineLength (ptCurrentPos.y - i);
4995 line = _T ('\n') + line;
4999 nLineLength = ptCurrentPos.x != -1 ? ptCurrentPos.x : GetLineLength (ptCurrentPos.y - i);
5001 if (nLineLength > 0)
5003 LPTSTR pszBuf = item.GetBuffer (nLineLength + 1);
5004 _tcsncpy_s (pszBuf, nLineLength+1, pszChars, nLineLength);
5005 item.ReleaseBuffer (nLineLength);
5009 nLineLength = line.GetLength ();
5010 if (ptCurrentPos.x == -1)
5015 nLineLength = GetLineLength(ptCurrentPos.y);
5016 if (ptCurrentPos.x == -1)
5018 ptCurrentPos.x = nLineLength;
5021 if( ptCurrentPos.x > nLineLength )
5022 ptCurrentPos.x = nLineLength;
5023 if (ptCurrentPos.x == -1)
5026 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y);
5027 _tcsncpy_s (line.GetBuffer(ptCurrentPos.x + 1), ptCurrentPos.x + 1, pszChars, ptCurrentPos.x);
5028 line.ReleaseBuffer (ptCurrentPos.x);
5031 ptrdiff_t nFoundPos = -1;
5032 int nMatchLen = what.GetLength();
5033 int nLineLen = line.GetLength();
5037 size_t nPosRel = ::FindStringHelper(line, static_cast<LPCTSTR>(line) + nPos, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5040 nFoundPos = nPos + nPosRel;
5041 nMatchLen = m_nLastFindWhatLen;
5042 nPos += nMatchLen == 0 ? 1 : nMatchLen;
5045 if( nFoundPos != -1 ) // Found text!
5047 ptCurrentPos.x = static_cast<int>(nFoundPos);
5048 *pptFoundPos = ptCurrentPos;
5053 if( ptCurrentPos.y >= 0 )
5054 ptCurrentPos.x = GetLineLength( ptCurrentPos.y );
5057 // Beginning of text reached
5061 // Start again from the end of text
5062 bWrapSearch = false;
5063 ptCurrentPos = CPoint (GetLineLength (GetLineCount () - 1), GetLineCount () - 1);
5070 while (ptCurrentPos.y <= ptBlockEnd.y)
5074 if (dwFlags & FIND_REGEXP)
5076 int nLines = m_pTextBuffer->GetLineCount ();
5077 for (int i = 0; i <= nEolns && ptCurrentPos.y + i < nLines; i++)
5080 LPCTSTR pszChars = GetLineChars (ptCurrentPos.y + i);
5081 nLineLength = GetLineLength (ptCurrentPos.y + i);
5086 if (nLineLength > 0)
5088 LPTSTR pszBuf = item.GetBuffer (nLineLength + 1);
5089 _tcsncpy_s (pszBuf, nLineLength + 1, pszChars, nLineLength);
5090 item.ReleaseBuffer (nLineLength);
5094 nLineLength = line.GetLength ();
5098 nLineLength = GetLineLength (ptCurrentPos.y) - ptCurrentPos.x;
5099 if (nLineLength <= 0)
5106 line = GetLineChars (ptCurrentPos.y);
5109 // Perform search in the line
5110 size_t nPos = ::FindStringHelper (line, static_cast<LPCTSTR>(line) + ptCurrentPos.x, what, dwFlags, m_nLastFindWhatLen, m_rxnode, &m_rxmatch);
5113 if (m_pszMatched != nullptr)
5115 m_pszMatched = _tcsdup (line);
5118 CString item = line.Left (static_cast<LONG>(nPos));
5119 LPCTSTR current = _tcsrchr (item, _T('\n'));
5124 nEolns = HowManyStr (item, _T('\n'));
5127 ptCurrentPos.y += nEolns;
5128 ptCurrentPos.x = static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5132 ptCurrentPos.x += static_cast<LONG>(nPos - (current - (LPCTSTR) item));
5134 if (ptCurrentPos.x < 0)
5139 ptCurrentPos.x += static_cast<LONG>(nPos);
5141 // Check of the text found is outside the block.
5142 if (ptCurrentPos.y == ptBlockEnd.y && ptCurrentPos.x >= ptBlockEnd.x)
5145 *pptFoundPos = ptCurrentPos;
5150 if (m_pszMatched != nullptr)
5152 m_pszMatched = nullptr;
5155 // Go further, text was not found
5160 // End of text reached
5164 // Start from the beginning
5165 bWrapSearch = false;
5166 ptCurrentPos = ptBlockBegin;
5170 //~ ASSERT (false); // Unreachable
5173 static DWORD ConvertSearchInfosToSearchFlags(const LastSearchInfos *lastSearch)
5175 DWORD dwSearchFlags = 0;
5176 if (lastSearch->m_bMatchCase)
5177 dwSearchFlags |= FIND_MATCH_CASE;
5178 if (lastSearch->m_bWholeWord)
5179 dwSearchFlags |= FIND_WHOLE_WORD;
5180 if (lastSearch->m_bRegExp)
5181 dwSearchFlags |= FIND_REGEXP;
5182 if (lastSearch->m_nDirection == 0)
5183 dwSearchFlags |= FIND_DIRECTION_UP;
5184 if (lastSearch->m_bNoWrap)
5185 dwSearchFlags |= FIND_NO_WRAP;
5186 if (lastSearch->m_bNoClose)
5187 dwSearchFlags |= FIND_NO_CLOSE;
5188 return dwSearchFlags;
5191 static void ConvertSearchFlagsToLastSearchInfos(LastSearchInfos *lastSearch, DWORD dwFlags)
5193 lastSearch->m_bMatchCase = (dwFlags & FIND_MATCH_CASE) != 0;
5194 lastSearch->m_bWholeWord = (dwFlags & FIND_WHOLE_WORD) != 0;
5195 lastSearch->m_bRegExp = (dwFlags & FIND_REGEXP) != 0;
5196 lastSearch->m_nDirection = (dwFlags & FIND_DIRECTION_UP) == 0;
5197 lastSearch->m_bNoWrap = (dwFlags & FIND_NO_WRAP) != 0;
5198 lastSearch->m_bNoClose = (dwFlags & FIND_NO_CLOSE) != 0;
5201 CPoint CCrystalTextView::
5202 GetSearchPos(DWORD dwSearchFlags)
5207 CPoint ptStart, ptEnd;
5208 GetSelection(ptStart, ptEnd);
5209 if( dwSearchFlags & FIND_DIRECTION_UP)
5210 ptSearchPos = ptStart;
5212 ptSearchPos = ptEnd;
5215 ptSearchPos = m_ptCursorPos;
5219 bool CCrystalTextView::
5220 FindText (const LastSearchInfos * lastSearch)
5223 DWORD dwSearchFlags = ConvertSearchInfosToSearchFlags(lastSearch);
5224 if (!FindText (lastSearch->m_sText, GetSearchPos(dwSearchFlags), dwSearchFlags, !lastSearch->m_bNoWrap,
5230 bool bCursorToLeft = (lastSearch->m_nDirection == 0);
5231 HighlightText (ptTextPos, m_nLastFindWhatLen, bCursorToLeft);
5233 // Save search parameters for 'F3' command
5234 m_bLastSearch = true;
5235 if (m_pszLastFindWhat != nullptr)
5236 free (m_pszLastFindWhat);
5237 m_pszLastFindWhat = _tcsdup (lastSearch->m_sText);
5238 m_dwLastSearchFlags = dwSearchFlags;
5240 // Save search parameters to registry
5241 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), m_dwLastSearchFlags));
5246 void CCrystalTextView::
5249 CWinApp *pApp = AfxGetApp ();
5250 ASSERT (pApp != nullptr);
5252 if (m_pFindTextDlg == nullptr)
5253 m_pFindTextDlg = new CFindTextDlg (this);
5255 LastSearchInfos * lastSearch = m_pFindTextDlg->GetLastSearchInfos();
5259 // Get the latest search parameters
5260 ConvertSearchFlagsToLastSearchInfos(lastSearch, m_dwLastSearchFlags);
5261 if (m_pszLastFindWhat != nullptr)
5262 lastSearch->m_sText = m_pszLastFindWhat;
5267 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("FindFlags"), &dwFlags))
5269 ConvertSearchFlagsToLastSearchInfos(lastSearch, dwFlags);
5271 m_pFindTextDlg->UseLastSearch ();
5273 // Take the current selection, if any
5276 CPoint ptSelStart, ptSelEnd;
5277 GetSelection (ptSelStart, ptSelEnd);
5278 if (ptSelStart.y == ptSelEnd.y)
5279 GetText (ptSelStart, ptSelEnd, m_pFindTextDlg->m_sText);
5283 CPoint ptCursorPos = GetCursorPos ();
5284 CPoint ptStart = WordToLeft (ptCursorPos);
5285 CPoint ptEnd = WordToRight (ptCursorPos);
5286 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5287 GetText (ptStart, ptEnd, m_pFindTextDlg->m_sText);
5290 // Execute Find dialog
5292 // m_bShowInactiveSelection = true; // FP: removed because I like it
5293 m_pFindTextDlg->UpdateData(FALSE);
5294 m_pFindTextDlg->ShowWindow(SW_SHOW);
5295 // m_bShowInactiveSelection = false; // FP: removed because I like it
5299 void CCrystalTextView::
5302 bool bEnable = m_bLastSearch;
5303 // Show dialog if no last find text
5304 if (m_pszLastFindWhat == nullptr || _tcslen(m_pszLastFindWhat) == 0)
5308 sText = m_pszLastFindWhat;
5311 // If last find-text exists, cut it to first line
5312 bEnable = !sText.IsEmpty ();
5315 int pos = sText.FindOneOf (_T("\r\n"));
5317 sText = sText.Left (pos);
5321 // CTRL-F3 will find selected text..
5322 bool bControlKey = (::GetAsyncKeyState(VK_CONTROL)& 0x8000) != 0;
5323 // CTRL-SHIFT-F3 will find selected text, but opposite direction
5324 bool bShiftKey = (::GetAsyncKeyState(VK_SHIFT)& 0x8000) != 0;
5329 CPoint ptSelStart, ptSelEnd;
5330 GetSelection (ptSelStart, ptSelEnd);
5331 GetText (ptSelStart, ptSelEnd, sText);
5335 CPoint ptCursorPos = GetCursorPos ();
5336 CPoint ptStart = WordToLeft (ptCursorPos);
5337 CPoint ptEnd = WordToRight (ptCursorPos);
5338 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5339 GetText (ptStart, ptEnd, sText);
5341 if (!sText.IsEmpty())
5344 free(m_pszLastFindWhat);
5345 m_pszLastFindWhat = _tcsdup (sText);
5346 m_bLastSearch = true;
5350 m_dwLastSearchFlags |= FIND_DIRECTION_UP;
5352 m_dwLastSearchFlags &= ~FIND_DIRECTION_UP;
5357 // for correct backward search we need some changes:
5358 if (! FindText(sText, GetSearchPos(m_dwLastSearchFlags), m_dwLastSearchFlags,
5359 (m_dwLastSearchFlags & FIND_NO_WRAP) == 0, &ptFoundPos))
5362 prompt.Format (LoadResString(IDS_EDIT_TEXT_NOT_FOUND).c_str(), (LPCTSTR)sText);
5363 AfxMessageBox (prompt, MB_ICONINFORMATION);
5366 HighlightText (ptFoundPos, m_nLastFindWhatLen, (m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0);
5367 m_bMultipleSearch = true; // More search
5370 OnEditFind(); // No previous find, open Find-dialog
5373 void CCrystalTextView::
5374 OnUpdateEditRepeat (CCmdUI * pCmdUI)
5376 pCmdUI->Enable (true);
5379 void CCrystalTextView::
5384 if (!RegLoadNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), &dwFlags))
5387 // Take the current selection, if any
5390 CPoint ptSelStart, ptSelEnd;
5391 GetSelection (ptSelStart, ptSelEnd);
5392 if (ptSelStart.y == ptSelEnd.y)
5393 GetText (ptSelStart, ptSelEnd, sText);
5397 CPoint ptCursorPos = GetCursorPos ();
5398 CPoint ptStart = WordToLeft (ptCursorPos);
5399 CPoint ptEnd = WordToRight (ptCursorPos);
5400 if (IsValidTextPos (ptStart) && IsValidTextPos (ptEnd) && ptStart != ptEnd)
5401 GetText (ptStart, ptEnd, sText);
5404 CTextMarkerDlg markerDlg(*m_pMarkers, sText, dwFlags);
5406 if (markerDlg.DoModal() == IDOK)
5408 // Save search parameters to registry
5409 VERIFY (RegSaveNumber (HKEY_CURRENT_USER, REG_EDITPAD, _T ("MarkerFlags"), markerDlg.GetLastSearchFlags()));
5410 m_pMarkers->SaveToRegistry();
5414 void CCrystalTextView::
5417 CWinApp *pApp = AfxGetApp ();
5418 ASSERT (pApp != nullptr);
5420 CPageSetupDialog dlg;
5422 if (!pApp->GetPrinterDeviceDefaults (&pd))
5425 dlg.m_psd.hDevMode = pd.hDevMode;
5426 dlg.m_psd.hDevNames = pd.hDevNames;
5427 dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS|PSD_MARGINS;
5428 dlg.m_psd.rtMargin.left = DEFAULT_PRINT_MARGIN;
5429 dlg.m_psd.rtMargin.right = DEFAULT_PRINT_MARGIN;
5430 dlg.m_psd.rtMargin.top = DEFAULT_PRINT_MARGIN;
5431 dlg.m_psd.rtMargin.bottom = DEFAULT_PRINT_MARGIN;
5433 if (reg.Open (HKEY_CURRENT_USER, REG_EDITPAD, KEY_READ))
5436 if (reg.LoadNumber (_T ("PageWidth"), &dwTemp))
5437 dlg.m_psd.ptPaperSize.x = dwTemp;
5438 if (reg.LoadNumber (_T ("PageHeight"), &dwTemp))
5439 dlg.m_psd.ptPaperSize.y = dwTemp;
5440 if (reg.LoadNumber (_T ("PageLeft"), &dwTemp))
5441 dlg.m_psd.rtMargin.left = dwTemp;
5442 if (reg.LoadNumber (_T ("PageRight"), &dwTemp))
5443 dlg.m_psd.rtMargin.right = dwTemp;
5444 if (reg.LoadNumber (_T ("PageTop"), &dwTemp))
5445 dlg.m_psd.rtMargin.top = dwTemp;
5446 if (reg.LoadNumber (_T ("PageBottom"), &dwTemp))
5447 dlg.m_psd.rtMargin.bottom = dwTemp;
5449 if (dlg.DoModal () == IDOK)
5452 if (reg1.Create (HKEY_CURRENT_USER, REG_EDITPAD, KEY_WRITE))
5454 VERIFY (reg1.SaveNumber (_T ("PageWidth"), dlg.m_psd.ptPaperSize.x));
5455 VERIFY (reg1.SaveNumber (_T ("PageHeight"), dlg.m_psd.ptPaperSize.y));
5456 VERIFY (reg1.SaveNumber (_T ("PageLeft"), dlg.m_psd.rtMargin.left));
5457 VERIFY (reg1.SaveNumber (_T ("PageRight"), dlg.m_psd.rtMargin.right));
5458 VERIFY (reg1.SaveNumber (_T ("PageTop"), dlg.m_psd.rtMargin.top));
5459 VERIFY (reg1.SaveNumber (_T ("PageBottom"), dlg.m_psd.rtMargin.bottom));
5461 pApp->SelectPrinter (dlg.m_psd.hDevNames, dlg.m_psd.hDevMode, false);
5466 * @brief Adds/removes bookmark on given line.
5467 * This functions adds bookmark or removes bookmark on given line.
5468 * @param [in] Index (0-based) of line to add/remove bookmark.
5470 void CCrystalTextView::ToggleBookmark(int nLine)
5472 ASSERT(nLine >= 0 && nLine < GetLineCount());
5473 if (m_pTextBuffer != nullptr)
5475 DWORD dwFlags = GetLineFlags (nLine);
5476 DWORD dwMask = LF_BOOKMARKS;
5477 m_pTextBuffer->SetLineFlag (nLine, dwMask, (dwFlags & dwMask) == 0, false);
5478 const int nBookmarkLine = m_pTextBuffer->GetLineWithFlag (LF_BOOKMARKS);
5479 if (nBookmarkLine >= 0)
5480 m_bBookmarkExist = true;
5482 m_bBookmarkExist = false;
5486 * @brief Called when Toggle Bookmark is selected from the GUI.
5488 void CCrystalTextView::
5491 ToggleBookmark(m_ptCursorPos.y);
5494 void CCrystalTextView::
5497 if (m_pTextBuffer != nullptr)
5499 int nLine = m_pTextBuffer->FindNextBookmarkLine (m_ptCursorPos.y);
5502 CPoint pt (0, nLine);
5503 ASSERT_VALIDTEXTPOS (pt);
5505 SetSelection (pt, pt);
5512 void CCrystalTextView::
5515 if (m_pTextBuffer != nullptr)
5517 int nLine = m_pTextBuffer->FindPrevBookmarkLine (m_ptCursorPos.y);
5520 CPoint pt (0, nLine);
5521 ASSERT_VALIDTEXTPOS (pt);
5523 SetSelection (pt, pt);
5530 void CCrystalTextView::
5531 OnClearAllBookmarks ()
5533 if (m_pTextBuffer != nullptr)
5535 int nLineCount = GetLineCount ();
5536 for (int I = 0; I < nLineCount; I++)
5538 if (m_pTextBuffer->GetLineFlags (I) & LF_BOOKMARKS)
5539 m_pTextBuffer->SetLineFlag (I, LF_BOOKMARKS, false);
5541 m_bBookmarkExist = false;
5545 void CCrystalTextView::
5546 OnUpdateNextBookmark (CCmdUI * pCmdUI)
5548 pCmdUI->Enable (m_bBookmarkExist);
5551 void CCrystalTextView::
5552 OnUpdatePrevBookmark (CCmdUI * pCmdUI)
5554 pCmdUI->Enable (m_bBookmarkExist);
5557 void CCrystalTextView::
5558 OnUpdateClearAllBookmarks (CCmdUI * pCmdUI)
5560 pCmdUI->Enable (m_bBookmarkExist);
5563 bool CCrystalTextView::
5569 void CCrystalTextView::
5570 SetViewTabs (bool bViewTabs)
5572 if (bViewTabs != m_bViewTabs)
5574 m_bViewTabs = bViewTabs;
5575 if (::IsWindow (m_hWnd))
5580 void CCrystalTextView::
5581 SetViewEols (bool bViewEols, bool bDistinguishEols)
5583 if (bViewEols != m_bViewEols || bDistinguishEols != m_bDistinguishEols)
5585 m_bViewEols = bViewEols;
5586 m_bDistinguishEols = bDistinguishEols;
5587 if (::IsWindow (m_hWnd))
5592 DWORD CCrystalTextView::
5598 void CCrystalTextView::
5599 SetFlags (DWORD dwFlags)
5601 if (m_dwFlags != dwFlags)
5603 m_dwFlags = dwFlags;
5604 if (::IsWindow (m_hWnd))
5609 bool CCrystalTextView::
5610 GetSelectionMargin ()
5612 return m_bSelMargin;
5615 bool CCrystalTextView::
5616 GetViewLineNumbers () const
5618 return m_bViewLineNumbers;
5622 * @brief Calculate margin area width.
5623 * This function calculates needed margin width. Needed width is (approx.)
5624 * one char-width for bookmark etc markers and rest to linenumbers (if
5625 * visible). If we have linenumbers visible we need to adjust width so that
5626 * biggest number fits.
5627 * @return Margin area width in pixels.
5629 int CCrystalTextView::
5630 GetMarginWidth (CDC *pdc /*= nullptr*/)
5632 int nMarginWidth = 0;
5634 if (m_bViewLineNumbers)
5636 const int nLines = GetLineCount();
5639 for (n = 1; n <= nLines; n *= 10)
5641 nMarginWidth += GetCharWidth () * nNumbers;
5643 nMarginWidth += 2; // Small gap when symbol part disabled
5648 if (pdc == nullptr || !pdc->IsPrinting ())
5649 nMarginWidth += MARGIN_ICON_WIDTH + 7; // Width for icon markers and some margin
5653 if (pdc == nullptr || !pdc->IsPrinting ())
5654 nMarginWidth += MARGIN_REV_WIDTH; // Space for revision marks
5657 return nMarginWidth;
5660 bool CCrystalTextView::
5664 return m_bSmoothScroll;
5667 void CCrystalTextView::SetSmoothScroll (bool bSmoothScroll)
5669 m_bSmoothScroll = bSmoothScroll;
5673 bool CCrystalTextView::
5674 GetDisableDragAndDrop ()
5677 return m_bDisableDragAndDrop;
5681 void CCrystalTextView::SetDisableDragAndDrop (bool bDDAD)
5683 m_bDisableDragAndDrop = bDDAD;
5686 void CCrystalTextView::CopyProperties (CCrystalTextView *pSource)
5688 m_nTopLine = pSource->m_nTopLine;
5689 m_nTopSubLine = pSource->m_nTopSubLine;
5690 m_bViewTabs = pSource->m_bViewTabs;
5691 m_bViewEols = pSource->m_bViewEols;
5692 m_bDistinguishEols = pSource->m_bDistinguishEols;
5693 m_bSelMargin = pSource->m_bSelMargin;
5694 m_bViewLineNumbers = pSource->m_bViewLineNumbers;
5695 m_bSmoothScroll = pSource->m_bSmoothScroll;
5696 m_bWordWrap = pSource->m_bWordWrap;
5697 m_pColors = pSource->m_pColors;
5698 m_pMarkers = pSource->m_pMarkers;
5699 m_bDisableDragAndDrop = pSource->m_bDisableDragAndDrop;
5700 SetTextType(pSource->m_CurSourceDef);
5701 SetFont (pSource->m_lfBaseFont);
5705 // Mouse wheel event. zDelta is in multiples of 120.
5706 // Divide by 40 so each click is 3 lines. I know some
5707 // drivers let you set the ammount of scroll, but I
5708 // don't know how to retrieve this or if they just
5709 // adjust the zDelta you get here.
5710 BOOL CCrystalTextView::
5711 OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
5713 SCROLLINFO si = {0};
5714 si.cbSize = sizeof (si);
5715 si.fMask = SIF_PAGE | SIF_RANGE;
5716 VERIFY (GetScrollInfo (SB_VERT, &si));
5718 int nNewTopSubLine= m_nTopSubLine - zDelta / 40;
5720 if (nNewTopSubLine < 0)
5722 if (nNewTopSubLine > (si.nMax - (signed int)si.nPage + 1))
5723 nNewTopSubLine = si.nMax - si.nPage + 1;
5725 ScrollToSubLine(nNewTopSubLine, true);
5726 UpdateSiblingScrollPos(false);
5728 return CView::OnMouseWheel (nFlags, zDelta, pt);
5731 void CCrystalTextView::
5732 OnSourceType (UINT nId)
5734 SetTextType ((CCrystalTextView::TextType) (nId - ID_SOURCE_PLAIN));
5738 void CCrystalTextView::
5739 OnUpdateSourceType (CCmdUI * pCmdUI)
5741 pCmdUI->SetRadio (m_SourceDefs + (pCmdUI->m_nID - ID_SOURCE_PLAIN) == m_CurSourceDef);
5747 static LPCTSTR braces = _T("{}()[]<>");
5748 LPCTSTR pos = _tcschr (braces, c);
5749 return pos != nullptr ? (int) (pos - braces) + 1 : 0;
5753 bracetype (LPCTSTR s)
5757 return bracetype (*s);
5760 void CCrystalTextView::
5763 CPoint ptCursorPos = GetCursorPos ();
5764 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
5765 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y), pszEnd = pszText + ptCursorPos.x;
5766 bool bAfter = false;
5768 if (ptCursorPos.x < nLength)
5770 nType = bracetype (*pszEnd);
5775 else if (!nType && ptCursorPos.x > 0)
5777 nType = bracetype (pszEnd[-1]);
5781 else if (ptCursorPos.x > 0)
5783 nType = bracetype (pszEnd[-1]);
5788 int nOther, nCount = 0, nComment = 0;
5791 nOther = ((nType - 1) ^ 1) + 1;
5797 nOther = ((nType - 1) ^ 1) + 1;
5801 LPCTSTR pszOpenComment = m_CurSourceDef->opencomment,
5802 pszCloseComment = m_CurSourceDef->closecomment,
5803 pszCommentLine = m_CurSourceDef->commentline, pszTest;
5804 int nOpenComment = (int) _tcslen (pszOpenComment),
5805 nCloseComment = (int) _tcslen (pszCloseComment),
5806 nCommentLine = (int) _tcslen (pszCommentLine);
5811 while (--pszEnd >= pszText)
5813 pszTest = pszEnd - nOpenComment + 1;
5814 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszOpenComment, nOpenComment))
5818 if (--pszEnd < pszText)
5823 pszTest = pszEnd - nCloseComment + 1;
5824 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCloseComment, nCloseComment))
5828 if (--pszEnd < pszText)
5835 pszTest = pszEnd - nCommentLine + 1;
5836 if (pszTest >= pszText && !_tcsnicmp (pszTest, pszCommentLine, nCommentLine))
5840 if (bracetype (*pszEnd) == nType)
5844 else if (bracetype (*pszEnd) == nOther)
5848 ptCursorPos.x = (LONG) (pszEnd - pszText);
5851 SetCursorPos (ptCursorPos);
5852 SetSelection (ptCursorPos, ptCursorPos);
5853 SetAnchor (ptCursorPos);
5854 EnsureVisible (ptCursorPos);
5862 ptCursorPos.x = m_pTextBuffer->GetLineLength (--ptCursorPos.y);
5863 pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
5864 pszEnd = pszText + ptCursorPos.x;
5872 LPCTSTR pszBegin = pszText;
5874 pszEnd = pszBegin + nLength;
5875 int nLines = m_pTextBuffer->GetLineCount ();
5878 while (pszText < pszEnd)
5880 pszTest = pszText + nCloseComment;
5881 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCloseComment, nCloseComment))
5885 if (pszText > pszEnd)
5890 pszTest = pszText + nOpenComment;
5891 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszOpenComment, nOpenComment))
5895 if (pszText > pszEnd)
5902 pszTest = pszText + nCommentLine;
5903 if (pszTest <= pszEnd && !_tcsnicmp (pszText, pszCommentLine, nCommentLine))
5907 if (bracetype (*pszText) == nType)
5911 else if (bracetype (*pszText) == nOther)
5915 ptCursorPos.x = (LONG) (pszText - pszBegin);
5918 SetCursorPos (ptCursorPos);
5919 SetSelection (ptCursorPos, ptCursorPos);
5920 SetAnchor (ptCursorPos);
5921 EnsureVisible (ptCursorPos);
5928 if (ptCursorPos.y < nLines)
5931 nLength = m_pTextBuffer->GetLineLength (++ptCursorPos.y);
5932 pszBegin = pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y);
5933 pszEnd = pszBegin + nLength;
5942 void CCrystalTextView::
5943 OnUpdateMatchBrace (CCmdUI * pCmdUI)
5945 CPoint ptCursorPos = GetCursorPos ();
5946 int nLength = m_pTextBuffer->GetLineLength (ptCursorPos.y);
5947 LPCTSTR pszText = m_pTextBuffer->GetLineChars (ptCursorPos.y) + ptCursorPos.x;
5948 pCmdUI->Enable (ptCursorPos.x < nLength && (bracetype (*pszText) || ptCursorPos.x > 0 && bracetype (pszText[-1])) || ptCursorPos.x > 0 && bracetype (pszText[-1]));
5951 void CCrystalTextView::
5954 CGotoDlg dlg (this);
5958 void CCrystalTextView::
5959 OnUpdateToggleSourceHeader (CCmdUI * pCmdUI)
5961 pCmdUI->Enable (m_CurSourceDef->type == SRC_C);
5964 void CCrystalTextView::
5965 OnToggleSourceHeader ()
5967 if (m_CurSourceDef->type == SRC_C)
5969 CDocument *pDoc = GetDocument ();
5970 ASSERT (pDoc != nullptr);
5971 CString sFilePath = pDoc->GetPathName (), sOriginalPath = sFilePath;
5972 if (!_tcsicmp (sFilePath.Right (2), _T (".c")))
5974 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ('h');
5976 else if (!_tcsicmp (sFilePath.Right (4), _T (".cpp")))
5978 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('h');
5980 else if (!_tcsicmp (sFilePath.Right (4), _T (".inl")))
5982 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
5983 if (!FileExist(sFilePath))
5985 sFilePath = sFilePath + _T ("pp");
5988 else if (!_tcsicmp (sFilePath.Right (4), _T (".hpp")))
5990 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
5991 if (!FileExist(sFilePath))
5993 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
5994 if (!FileExist(sFilePath))
5996 sFilePath = sFilePath + _T ("pp");
6000 else if (!_tcsicmp (sFilePath.Right (2), _T (".h")))
6002 sFilePath = sFilePath.Left (sFilePath.GetLength () - 1) + _T ("hpp");
6003 if (!FileExist(sFilePath))
6005 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ("inl");
6006 if (!FileExist(sFilePath))
6008 sFilePath = sFilePath.Left (sFilePath.GetLength () - 3) + _T ('c');
6009 if (!FileExist(sFilePath))
6011 sFilePath = sFilePath + _T ("pp");
6016 if (FileExist(sFilePath))
6018 if (!m_bSingle || !pDoc->IsModified () || pDoc->DoSave (sOriginalPath))
6020 AfxGetApp ()->OpenDocumentFile (sFilePath);
6023 m_ptCursorLast.x = m_ptCursorLast.y = 0;
6024 ASSERT_VALIDTEXTPOS (m_ptCursorLast);
6025 CPoint ptCursorPos = m_ptCursorLast;
6026 SetCursorPos (ptCursorPos);
6027 SetSelection (ptCursorPos, ptCursorPos);
6028 SetAnchor (ptCursorPos);
6029 EnsureVisible (ptCursorPos);
6037 void CCrystalTextView::
6038 OnUpdateSelMargin (CCmdUI * pCmdUI)
6040 pCmdUI->SetCheck (m_bSelMargin);
6043 void CCrystalTextView::
6046 ASSERT (m_CurSourceDef != nullptr);
6049 m_CurSourceDef->flags &= ~SRCOPT_SELMARGIN;
6050 SetSelectionMargin (false);
6054 m_CurSourceDef->flags |= SRCOPT_SELMARGIN;
6055 SetSelectionMargin (true);
6059 void CCrystalTextView::
6060 OnUpdateWordWrap (CCmdUI * pCmdUI)
6062 pCmdUI->SetCheck (m_bWordWrap);
6065 void CCrystalTextView::
6068 ASSERT (m_CurSourceDef != nullptr);
6071 m_CurSourceDef->flags &= ~SRCOPT_WORDWRAP;
6072 SetWordWrapping (false);
6076 m_CurSourceDef->flags |= SRCOPT_WORDWRAP;
6077 SetWordWrapping (true);
6081 void CCrystalTextView::
6085 RedrawWindow (nullptr, nullptr, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ERASENOW);
6088 void CCrystalTextView::
6089 OnToggleColumnSelection ()
6091 m_bColumnSelection = !m_bColumnSelection;
6096 bool CCrystalTextView::GetWordWrapping() const
6101 void CCrystalTextView::SetWordWrapping( bool bWordWrap )
6103 m_bWordWrap = bWordWrap;
6105 if( IsWindow( m_hWnd ) )
6108 InvalidateScreenRect();
6112 CCrystalParser *CCrystalTextView::SetParser( CCrystalParser *pParser )
6114 CCrystalParser *pOldParser = m_pParser;
6116 m_pParser = pParser;
6118 if( pParser != nullptr )
6119 pParser->m_pTextView = this;
6125 bool CCrystalTextView::GetEnableHideLines () const
6127 return m_bHideLines;
6130 void CCrystalTextView::SetEnableHideLines (bool bHideLines)
6132 m_bHideLines = bHideLines;
6136 * @brief Return whether a line is visible.
6138 bool CCrystalTextView::GetLineVisible (int nLineIndex) const
6140 return !m_bHideLines || !(GetLineFlags (nLineIndex) & LF_INVISIBLE);
6144 // incremental search imlementation
6145 BOOL CCrystalTextView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO *pHandlerInfo )
6147 // just look for commands
6148 if( nCode != CN_COMMAND || pExtra != nullptr )
6149 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6152 // each command that is not related to incremental search
6153 // ends the incremental search
6154 if( nID == ID_EDIT_FIND_INCREMENTAL_FORWARD ||
6155 nID == ID_EDIT_FIND_INCREMENTAL_BACKWARD ||
6156 nID == ID_EDIT_DELETE_BACK )
6157 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6159 if( nID >= ID_EDIT_FIRST && nID <= ID_EDIT_LAST )
6160 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6162 return CView::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
6165 void CCrystalTextView::OnChar( wchar_t nChar, UINT nRepCnt, UINT nFlags )
6167 CView::OnChar( nChar, nRepCnt, nFlags );
6169 // we only have to handle character-input, if we are in incremental search mode
6170 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6173 // exit incremental search, when Escape is pressed
6174 if( nChar == VK_ESCAPE )
6176 // if not end incremental search
6177 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6178 SetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6179 SetCursorPos( m_cursorPosBeforeIncrementalSearch );
6180 EnsureVisible( m_cursorPosBeforeIncrementalSearch );
6184 // exit incremental search without destroying selection
6185 if( nChar == VK_RETURN )
6187 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6191 // is the character valid for incremental search?
6192 if( !_istgraph( nChar ) && !(nChar == _T(' ')) && !(nChar == _T('\t')) )
6194 // if not end incremental search
6195 m_bIncrementalSearchForward = m_bIncrementalSearchBackward = false;
6199 // if last search was not successfull do not add a new character
6200 if( !m_bIncrementalFound )
6202 MessageBeep( MB_OK );
6206 // add character to incremental search string and search
6207 *m_pstrIncrementalSearchString += (TCHAR) nChar;
6208 OnEditFindIncremental();
6211 LRESULT CCrystalTextView::OnImeStartComposition(WPARAM wParam, LPARAM lParam) /* IME */
6213 UpdateCompositionWindowFont();
6214 UpdateCompositionWindowPos();
6216 return DefWindowProc(WM_IME_STARTCOMPOSITION, wParam, lParam);
6219 void CCrystalTextView::OnEditDeleteBack()
6221 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6224 // remove last character from search string
6225 if( m_pstrIncrementalSearchString->IsEmpty() )
6228 *m_pstrIncrementalSearchString = m_pstrIncrementalSearchString->Left( m_pstrIncrementalSearchString->GetLength() - 1 );
6229 OnEditFindIncremental();
6233 void CCrystalTextView::OnEditFindIncremental( bool bFindNextOccurence /*= false*/ )
6235 // when string is empty, then goto position where the search starts
6236 if( m_pstrIncrementalSearchString->IsEmpty() )
6238 SetSelection( m_incrementalSearchStartPos, m_incrementalSearchStartPos );
6239 SetCursorPos( m_incrementalSearchStartPos );
6240 EnsureVisible( m_incrementalSearchStartPos );
6244 // otherwise search next occurence of search string,
6245 // starting at current cursor position
6246 CPoint matchStart, matchEnd;
6248 // calculate start point for search
6249 if( bFindNextOccurence )
6251 CPoint selStart, selEnd;
6252 GetSelection( selStart, selEnd );
6253 m_incrementalSearchStartPos = (m_bIncrementalSearchBackward)? selStart : selEnd;
6256 m_bIncrementalFound = FindText(
6257 *m_pstrIncrementalSearchString,
6258 m_incrementalSearchStartPos,
6259 m_bIncrementalSearchBackward? FIND_DIRECTION_UP : 0,
6263 if( !m_bIncrementalFound )
6265 MessageBeep( MB_OK );
6269 // select found text and set cursor to end of match
6270 matchEnd = matchStart;
6271 matchEnd.x+= m_pstrIncrementalSearchString->GetLength();
6272 SetSelection( matchStart, matchEnd );
6273 SetCursorPos( matchEnd );
6274 EnsureVisible( matchEnd );
6279 void CCrystalTextView::OnEditFindIncrementalForward()
6281 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6284 if( !m_pstrIncrementalSearchString->IsEmpty() )
6285 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6286 m_pstrIncrementalSearchString->Empty();
6287 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6288 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6290 else if( m_bIncrementalSearchForward )
6292 if( m_pstrIncrementalSearchString->IsEmpty() )
6294 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6295 m_pstrIncrementalSearchStringOld->Empty();
6296 OnEditFindIncremental();
6299 OnEditFindIncremental( true );
6304 m_bIncrementalSearchForward = true;
6305 m_bIncrementalSearchBackward = false;
6306 m_bIncrementalFound = true;
6307 OnEditFindIncremental();
6310 void CCrystalTextView::OnEditFindIncrementalBackward()
6312 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6315 if( !m_pstrIncrementalSearchString->IsEmpty() )
6316 *m_pstrIncrementalSearchStringOld = *m_pstrIncrementalSearchString;
6317 m_pstrIncrementalSearchString->Empty();
6318 GetSelection( m_selStartBeforeIncrementalSearch, m_selEndBeforeIncrementalSearch );
6319 m_incrementalSearchStartPos = m_cursorPosBeforeIncrementalSearch = m_ptCursorPos;
6321 else if( m_bIncrementalSearchBackward )
6323 if( m_pstrIncrementalSearchString->IsEmpty() )
6325 *m_pstrIncrementalSearchString = *m_pstrIncrementalSearchStringOld;
6326 m_pstrIncrementalSearchStringOld->Empty();
6327 OnEditFindIncremental();
6330 OnEditFindIncremental( true );
6335 m_bIncrementalSearchForward = false;
6336 m_bIncrementalSearchBackward = true;
6337 m_bIncrementalFound = true;
6338 OnEditFindIncremental();
6341 void CCrystalTextView::OnUpdateEditFindIncrementalForward(CCmdUI* pCmdUI)
6343 if (m_pTextBuffer != nullptr)
6345 int nLines = m_pTextBuffer->GetLineCount ();
6346 int nChars = m_pTextBuffer->GetLineLength (m_ptCursorPos.y);
6347 pCmdUI->Enable(m_ptCursorPos.y < nLines - 1 || m_ptCursorPos.x < nChars);
6350 pCmdUI->Enable(false);
6353 void CCrystalTextView::OnUpdateEditFindIncrementalBackward(CCmdUI* pCmdUI)
6355 if (m_pTextBuffer != nullptr)
6357 pCmdUI->Enable(m_ptCursorPos.y > 0 || m_ptCursorPos.x > 0);
6360 pCmdUI->Enable(false);
6363 void CCrystalTextView::OnUpdateStatusMessage( CStatusBar *pStatusBar )
6365 static bool bUpdatedAtLastCall = false;
6367 ASSERT( pStatusBar != nullptr && IsWindow( pStatusBar->m_hWnd ) );
6368 if( pStatusBar == nullptr || !IsWindow( pStatusBar->m_hWnd ) )
6371 if( !m_bIncrementalSearchForward && !m_bIncrementalSearchBackward )
6373 if( bUpdatedAtLastCall )
6374 pStatusBar->SetPaneText( 0, LoadResString(AFX_IDS_IDLEMESSAGE).c_str() );
6376 bUpdatedAtLastCall = false;
6383 if( !m_bIncrementalFound )
6384 formatid = IDS_FIND_INCREMENTAL_FAILED;
6385 else if( m_bIncrementalSearchForward )
6386 formatid = IDS_FIND_INCREMENTAL_FORWARD;
6387 else if( m_bIncrementalSearchBackward )
6388 formatid = IDS_FIND_INCREMENTAL_BACKWARD;
6391 strFormat.Format( LoadResString(formatid).c_str(), (LPCTSTR)*m_pstrIncrementalSearchString );
6393 pStatusBar->SetPaneText( 0, strFormat );
6394 bUpdatedAtLastCall = false;
6398 bool CCrystalTextView::IsTextBufferInitialized () const
6400 return m_pTextBuffer && m_pTextBuffer->IsTextBufferInitialized();
6403 CString CCrystalTextView::GetTextBufferEol(int nLine) const
6405 return m_pTextBuffer->GetLineEol(nLine);
6408 void CCrystalTextView::SetMarkersContext(CCrystalTextMarkers * pMarkers)
6410 pMarkers->AddView(this);
6411 m_pMarkers = pMarkers;
6415 int CCrystalTextView::GetCharCellCountUnicodeChar(const wchar_t *pch)
6418 if (!m_bChWidthsCalculated[ch/256])
6420 if (U16_IS_SURROGATE(ch) && U16_IS_SURROGATE_LEAD(ch))
6422 return wcwidth(U16_GET_SUPPLEMENTARY(ch, pch[1]));
6426 int nWidthArray[256];
6427 wchar_t nStart = ch/256*256;
6428 wchar_t nEnd = nStart + 255;
6430 CFont *pOldFont = pdc->SelectObject(GetFont());
6431 GetCharWidth32(pdc->m_hDC, nStart, nEnd, nWidthArray);
6432 int nCharWidth = GetCharWidth();
6433 for (int i = 0; i < 256; i++)
6435 if (nCharWidth * 15 < nWidthArray[i] * 10)
6436 m_iChDoubleWidthFlags[(nStart+i)/32] |= 1 << (i % 32);
6439 wchar_t ch2 = static_cast<wchar_t>(nStart + i);
6440 if (wcwidth(ch2) > 1)
6441 m_iChDoubleWidthFlags[(nStart + i) / 32] |= 1 << (i % 32);
6444 m_bChWidthsCalculated[ch / 256] = true;
6445 pdc->SelectObject(pOldFont);
6448 if (m_iChDoubleWidthFlags[ch / 32] & (1 << (ch % 32)))
6455 /** @brief Reset computed unicode character widths. */
6456 void CCrystalTextView::ResetCharWidths ()
6459 ZeroMemory(m_bChWidthsCalculated, sizeof(m_bChWidthsCalculated));
6460 ZeroMemory(m_iChDoubleWidthFlags, sizeof(m_iChDoubleWidthFlags));
6464 // This function assumes selection is in one line
6465 void CCrystalTextView::EnsureVisible (CPoint ptStart, CPoint ptEnd)
6467 // Scroll vertically
6469 int nSubLineCount = GetSubLineCount();
6470 int nNewTopSubLine = m_nTopSubLine;
6472 CPoint subLinePosEnd;
6474 CharPosToPoint( ptStart.y, ptStart.x, subLinePos );
6475 subLinePos.y += GetSubLineIndex( ptStart.y );
6476 CharPosToPoint( ptEnd.y, ptEnd.x, subLinePosEnd );
6477 subLinePosEnd.y += GetSubLineIndex( ptEnd.y );
6479 if( subLinePos.y >= nNewTopSubLine + GetScreenLines() )
6480 nNewTopSubLine = subLinePos.y - GetScreenLines() + 1;
6481 if( subLinePos.y < nNewTopSubLine )
6482 nNewTopSubLine = subLinePos.y;
6484 if( nNewTopSubLine < 0 )
6486 if( nNewTopSubLine >= nSubLineCount )
6487 nNewTopSubLine = nSubLineCount - 1;
6489 if ( !m_bWordWrap && !m_bHideLines )
6491 // WINMERGE: This line fixes (cursor) slowdown after merges!
6492 // I don't know exactly why, but propably we are setting
6493 // m_nTopLine to zero in ResetView() and are not setting to
6494 // valid value again. Maybe this is a good place to set it?
6495 m_nTopLine = nNewTopSubLine;
6500 GetLineBySubLine(nNewTopSubLine, m_nTopLine, dummy);
6503 if( nNewTopSubLine != m_nTopSubLine )
6505 ScrollToSubLine( nNewTopSubLine );
6507 UpdateSiblingScrollPos( false );
6510 // Scroll horizontally
6512 // we do not need horizontally scrolling, if we wrap the words
6516 int nActualPos = CalculateActualOffset (ptStart.y, ptStart.x);
6517 int nActualEndPos = CalculateActualOffset (ptEnd.y, ptEnd.x);
6518 int nNewOffset = m_nOffsetChar;
6519 const int nScreenChars = GetScreenChars ();
6520 const int nBeginOffset = nActualPos - m_nOffsetChar;
6521 const int nEndOffset = nActualEndPos - m_nOffsetChar;
6522 const int nSelLen = nActualEndPos - nActualPos;
6524 // Selection fits to screen, scroll whole selection visible
6525 if (nSelLen < nScreenChars)
6527 // Begin of selection not visible
6528 if (nBeginOffset > nScreenChars)
6530 // Scroll so that there is max 5 chars margin at end
6531 if (nScreenChars - nSelLen > 5)
6532 nNewOffset = nActualPos + 5 - nScreenChars + nSelLen;
6534 nNewOffset = nActualPos - 5;
6536 else if (nBeginOffset < 0)
6538 // Scroll so that there is max 5 chars margin at begin
6539 if (nScreenChars - nSelLen >= 5)
6540 nNewOffset = nActualPos - 5;
6542 nNewOffset = nActualPos - 5 - nScreenChars + nSelLen;
6544 // End of selection not visible
6545 else if (nEndOffset > nScreenChars ||
6548 nNewOffset = nActualPos - 5;
6551 else // Selection does not fit screen so scroll to begin of selection
6553 nNewOffset = nActualPos - 5;
6556 // Horiz scroll limit to longest line + one screenwidth
6557 const int nMaxLineLen = GetMaxLineLength (m_nTopLine, GetScreenLines());
6558 if (nNewOffset >= nMaxLineLen + nScreenChars)
6559 nNewOffset = nMaxLineLen + nScreenChars - 1;
6563 if (m_nOffsetChar != nNewOffset)
6565 ScrollToChar (nNewOffset);
6567 UpdateSiblingScrollPos (true);
6571 // Analyze the first line of file to detect its type
6572 // Mainly it works for xml files
6573 bool CCrystalTextView::
6574 SetTextTypeByContent (LPCTSTR pszContent)
6576 RxNode *rxnode = nullptr;
6579 if (::FindStringHelper(pszContent, pszContent, _T("^\\s*\\<\\?xml\\s+.+?\\?\\>\\s*$"),
6580 FIND_REGEXP, nLen, rxnode, &rxmatch) == 0)
6584 return SetTextType(CCrystalTextView::SRC_XML);
6591 ////////////////////////////////////////////////////////////////////////////