1 // Scintilla source code edit control
2 /** @file EditView.cxx
3  ** Defines the appearance of the main text area of the editor window.
4  **/
5 // Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cassert>
11 #include <cstring>
12 #include <cstdio>
13 #include <cmath>
14 
15 #include <stdexcept>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <forward_list>
20 #include <algorithm>
21 #include <iterator>
22 #include <memory>
23 #include <chrono>
24 
25 #include "Platform.h"
26 
27 #include "ILoader.h"
28 #include "ILexer.h"
29 #include "Scintilla.h"
30 
31 #include "StringCopy.h"
32 #include "CharacterSet.h"
33 #include "CharacterCategory.h"
34 #include "Position.h"
35 #include "IntegerRectangle.h"
36 #include "UniqueString.h"
37 #include "SplitVector.h"
38 #include "Partitioning.h"
39 #include "RunStyles.h"
40 #include "ContractionState.h"
41 #include "CellBuffer.h"
42 #include "PerLine.h"
43 #include "KeyMap.h"
44 #include "Indicator.h"
45 #include "LineMarker.h"
46 #include "Style.h"
47 #include "ViewStyle.h"
48 #include "CharClassify.h"
49 #include "Decoration.h"
50 #include "CaseFolder.h"
51 #include "Document.h"
52 #include "UniConversion.h"
53 #include "Selection.h"
54 #include "PositionCache.h"
55 #include "EditModel.h"
56 #include "MarginView.h"
57 #include "EditView.h"
58 #include "ElapsedPeriod.h"
59 
60 using namespace Scintilla;
61 
IsControlCharacter(int ch)62 static inline bool IsControlCharacter(int ch) {
63 	// iscntrl returns true for lots of chars > 127 which are displayable
64 	return ch >= 0 && ch < ' ';
65 }
66 
PrintParameters()67 PrintParameters::PrintParameters() noexcept {
68 	magnification = 0;
69 	colourMode = SC_PRINT_NORMAL;
70 	wrapState = eWrapWord;
71 }
72 
73 namespace Scintilla {
74 
ValidStyledText(const ViewStyle & vs,size_t styleOffset,const StyledText & st)75 bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) {
76 	if (st.multipleStyles) {
77 		for (size_t iStyle = 0; iStyle<st.length; iStyle++) {
78 			if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
79 				return false;
80 		}
81 	} else {
82 		if (!vs.ValidStyle(styleOffset + st.style))
83 			return false;
84 	}
85 	return true;
86 }
87 
WidthStyledText(Surface * surface,const ViewStyle & vs,int styleOffset,const char * text,const unsigned char * styles,size_t len)88 static int WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset,
89 	const char *text, const unsigned char *styles, size_t len) {
90 	int width = 0;
91 	size_t start = 0;
92 	while (start < len) {
93 		const unsigned char style = styles[start];
94 		size_t endSegment = start;
95 		while ((endSegment + 1 < len) && (styles[endSegment + 1] == style))
96 			endSegment++;
97 		FontAlias fontText = vs.styles[style + styleOffset].font;
98 		width += static_cast<int>(surface->WidthText(fontText, text + start,
99 			static_cast<int>(endSegment - start + 1)));
100 		start = endSegment + 1;
101 	}
102 	return width;
103 }
104 
WidestLineWidth(Surface * surface,const ViewStyle & vs,int styleOffset,const StyledText & st)105 int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) {
106 	int widthMax = 0;
107 	size_t start = 0;
108 	while (start < st.length) {
109 		const size_t lenLine = st.LineLength(start);
110 		int widthSubLine;
111 		if (st.multipleStyles) {
112 			widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
113 		} else {
114 			FontAlias fontText = vs.styles[styleOffset + st.style].font;
115 			widthSubLine = static_cast<int>(surface->WidthText(fontText,
116 				st.text + start, static_cast<int>(lenLine)));
117 		}
118 		if (widthSubLine > widthMax)
119 			widthMax = widthSubLine;
120 		start += lenLine + 1;
121 	}
122 	return widthMax;
123 }
124 
DrawTextNoClipPhase(Surface * surface,PRectangle rc,const Style & style,XYPOSITION ybase,const char * s,int len,DrawPhase phase)125 void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,
126 	const char *s, int len, DrawPhase phase) {
127 	FontAlias fontText = style.font;
128 	if (phase & drawBack) {
129 		if (phase & drawText) {
130 			// Drawing both
131 			surface->DrawTextNoClip(rc, fontText, ybase, s, len,
132 				style.fore, style.back);
133 		} else {
134 			surface->FillRectangle(rc, style.back);
135 		}
136 	} else if (phase & drawText) {
137 		surface->DrawTextTransparent(rc, fontText, ybase, s, len, style.fore);
138 	}
139 }
140 
DrawStyledText(Surface * surface,const ViewStyle & vs,int styleOffset,PRectangle rcText,const StyledText & st,size_t start,size_t length,DrawPhase phase)141 void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
142 	const StyledText &st, size_t start, size_t length, DrawPhase phase) {
143 
144 	if (st.multipleStyles) {
145 		int x = static_cast<int>(rcText.left);
146 		size_t i = 0;
147 		while (i < length) {
148 			size_t end = i;
149 			size_t style = st.styles[i + start];
150 			while (end < length - 1 && st.styles[start + end + 1] == style)
151 				end++;
152 			style += styleOffset;
153 			FontAlias fontText = vs.styles[style].font;
154 			const int width = static_cast<int>(surface->WidthText(fontText,
155 				st.text + start + i, static_cast<int>(end - i + 1)));
156 			PRectangle rcSegment = rcText;
157 			rcSegment.left = static_cast<XYPOSITION>(x);
158 			rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
159 			DrawTextNoClipPhase(surface, rcSegment, vs.styles[style],
160 				rcText.top + vs.maxAscent, st.text + start + i,
161 				static_cast<int>(end - i + 1), phase);
162 			x += width;
163 			i = end + 1;
164 		}
165 	} else {
166 		const size_t style = st.style + styleOffset;
167 		DrawTextNoClipPhase(surface, rcText, vs.styles[style],
168 			rcText.top + vs.maxAscent, st.text + start,
169 			static_cast<int>(length), phase);
170 	}
171 }
172 
173 }
174 
175 const XYPOSITION epsilon = 0.0001f;	// A small nudge to avoid floating point precision issues
176 
EditView()177 EditView::EditView() {
178 	tabWidthMinimumPixels = 2; // needed for calculating tab stops for fractional proportional fonts
179 	hideSelection = false;
180 	drawOverstrikeCaret = true;
181 	bufferedDraw = true;
182 	phasesDraw = phasesTwo;
183 	lineWidthMaxSeen = 0;
184 	additionalCaretsBlink = true;
185 	additionalCaretsVisible = true;
186 	imeCaretBlockOverride = false;
187 	llc.SetLevel(LineLayoutCache::llcCaret);
188 	posCache.SetSize(0x400);
189 	tabArrowHeight = 4;
190 	customDrawTabArrow = nullptr;
191 	customDrawWrapMarker = nullptr;
192 }
193 
~EditView()194 EditView::~EditView() {
195 }
196 
SetTwoPhaseDraw(bool twoPhaseDraw)197 bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) noexcept {
198 	const PhasesDraw phasesDrawNew = twoPhaseDraw ? phasesTwo : phasesOne;
199 	const bool redraw = phasesDraw != phasesDrawNew;
200 	phasesDraw = phasesDrawNew;
201 	return redraw;
202 }
203 
SetPhasesDraw(int phases)204 bool EditView::SetPhasesDraw(int phases) noexcept {
205 	const PhasesDraw phasesDrawNew = static_cast<PhasesDraw>(phases);
206 	const bool redraw = phasesDraw != phasesDrawNew;
207 	phasesDraw = phasesDrawNew;
208 	return redraw;
209 }
210 
LinesOverlap() const211 bool EditView::LinesOverlap() const noexcept {
212 	return phasesDraw == phasesMultiple;
213 }
214 
ClearAllTabstops()215 void EditView::ClearAllTabstops() noexcept {
216 	ldTabstops.reset();
217 }
218 
NextTabstopPos(Sci::Line line,XYPOSITION x,XYPOSITION tabWidth) const219 XYPOSITION EditView::NextTabstopPos(Sci::Line line, XYPOSITION x, XYPOSITION tabWidth) const {
220 	const int next = GetNextTabstop(line, static_cast<int>(x + tabWidthMinimumPixels));
221 	if (next > 0)
222 		return static_cast<XYPOSITION>(next);
223 	return (static_cast<int>((x + tabWidthMinimumPixels) / tabWidth) + 1) * tabWidth;
224 }
225 
ClearTabstops(Sci::Line line)226 bool EditView::ClearTabstops(Sci::Line line) {
227 	return ldTabstops && ldTabstops->ClearTabstops(line);
228 }
229 
AddTabstop(Sci::Line line,int x)230 bool EditView::AddTabstop(Sci::Line line, int x) {
231 	if (!ldTabstops) {
232 		ldTabstops.reset(new LineTabstops());
233 	}
234 	return ldTabstops && ldTabstops->AddTabstop(line, x);
235 }
236 
GetNextTabstop(Sci::Line line,int x) const237 int EditView::GetNextTabstop(Sci::Line line, int x) const {
238 	if (ldTabstops) {
239 		return ldTabstops->GetNextTabstop(line, x);
240 	} else {
241 		return 0;
242 	}
243 }
244 
LinesAddedOrRemoved(Sci::Line lineOfPos,Sci::Line linesAdded)245 void EditView::LinesAddedOrRemoved(Sci::Line lineOfPos, Sci::Line linesAdded) {
246 	if (ldTabstops) {
247 		if (linesAdded > 0) {
248 			for (Sci::Line line = lineOfPos; line < lineOfPos + linesAdded; line++) {
249 				ldTabstops->InsertLine(line);
250 			}
251 		} else {
252 			for (Sci::Line line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {
253 				ldTabstops->RemoveLine(line);
254 			}
255 		}
256 	}
257 }
258 
DropGraphics(bool freeObjects)259 void EditView::DropGraphics(bool freeObjects) {
260 	if (freeObjects) {
261 		pixmapLine.reset();
262 		pixmapIndentGuide.reset();
263 		pixmapIndentGuideHighlight.reset();
264 	} else {
265 		if (pixmapLine)
266 			pixmapLine->Release();
267 		if (pixmapIndentGuide)
268 			pixmapIndentGuide->Release();
269 		if (pixmapIndentGuideHighlight)
270 			pixmapIndentGuideHighlight->Release();
271 	}
272 }
273 
AllocateGraphics(const ViewStyle & vsDraw)274 void EditView::AllocateGraphics(const ViewStyle &vsDraw) {
275 	if (!pixmapLine)
276 		pixmapLine.reset(Surface::Allocate(vsDraw.technology));
277 	if (!pixmapIndentGuide)
278 		pixmapIndentGuide.reset(Surface::Allocate(vsDraw.technology));
279 	if (!pixmapIndentGuideHighlight)
280 		pixmapIndentGuideHighlight.reset(Surface::Allocate(vsDraw.technology));
281 }
282 
ControlCharacterString(unsigned char ch)283 static const char *ControlCharacterString(unsigned char ch) noexcept {
284 	const char * const reps[] = {
285 		"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
286 		"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
287 		"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
288 		"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
289 	};
290 	if (ch < ELEMENTS(reps)) {
291 		return reps[ch];
292 	} else {
293 		return "BAD";
294 	}
295 }
296 
DrawTabArrow(Surface * surface,PRectangle rcTab,int ymid,const ViewStyle & vsDraw)297 static void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid, const ViewStyle &vsDraw) {
298 	const IntegerRectangle ircTab(rcTab);
299 	if ((rcTab.left + 2) < (rcTab.right - 1))
300 		surface->MoveTo(ircTab.left + 2, ymid);
301 	else
302 		surface->MoveTo(ircTab.right - 1, ymid);
303 	surface->LineTo(ircTab.right - 1, ymid);
304 
305 	// Draw the arrow head if needed
306 	if (vsDraw.tabDrawMode == tdLongArrow) {
307 		int ydiff = (ircTab.bottom - ircTab.top) / 2;
308 		int xhead = ircTab.right - 1 - ydiff;
309 		if (xhead <= rcTab.left) {
310 			ydiff -= ircTab.left - xhead - 1;
311 			xhead = ircTab.left - 1;
312 		}
313 		surface->LineTo(xhead, ymid - ydiff);
314 		surface->MoveTo(ircTab.right - 1, ymid);
315 		surface->LineTo(xhead, ymid + ydiff);
316 	}
317 }
318 
RefreshPixMaps(Surface * surfaceWindow,WindowID wid,const ViewStyle & vsDraw)319 void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
320 	if (!pixmapIndentGuide->Initialised()) {
321 		// 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
322 		pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
323 		pixmapIndentGuideHighlight->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
324 		const PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight);
325 		pixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[STYLE_INDENTGUIDE].back);
326 		pixmapIndentGuide->PenColour(vsDraw.styles[STYLE_INDENTGUIDE].fore);
327 		pixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[STYLE_BRACELIGHT].back);
328 		pixmapIndentGuideHighlight->PenColour(vsDraw.styles[STYLE_BRACELIGHT].fore);
329 		for (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) {
330 			const PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
331 			pixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[STYLE_INDENTGUIDE].fore);
332 			pixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[STYLE_BRACELIGHT].fore);
333 		}
334 	}
335 }
336 
RetrieveLineLayout(Sci::Line lineNumber,const EditModel & model)337 LineLayout *EditView::RetrieveLineLayout(Sci::Line lineNumber, const EditModel &model) {
338 	const Sci::Position posLineStart = model.pdoc->LineStart(lineNumber);
339 	const Sci::Position posLineEnd = model.pdoc->LineStart(lineNumber + 1);
340 	PLATFORM_ASSERT(posLineEnd >= posLineStart);
341 	const Sci::Line lineCaret = model.pdoc->SciLineFromPosition(model.sel.MainCaret());
342 	return llc.Retrieve(lineNumber, lineCaret,
343 		static_cast<int>(posLineEnd - posLineStart), model.pdoc->GetStyleClock(),
344 		model.LinesOnScreen() + 1, model.pdoc->LinesTotal());
345 }
346 
347 namespace {
348 
349 /**
350 * Return the chDoc argument with case transformed as indicated by the caseForce argument.
351 * chPrevious is needed for camel casing.
352 * This only affects ASCII characters and is provided for languages with case-insensitive
353 * ASCII keywords where the user wishes to view keywords in a preferred case.
354 */
CaseForce(Style::ecaseForced caseForce,char chDoc,char chPrevious)355 inline char CaseForce(Style::ecaseForced caseForce, char chDoc, char chPrevious) {
356 	switch (caseForce) {
357 	case Style::caseMixed:
358 		return chDoc;
359 	case Style::caseLower:
360 		return MakeLowerCase(chDoc);
361 	case Style::caseUpper:
362 		return MakeUpperCase(chDoc);
363 	case Style::caseCamel:
364 	default:	// default should not occur, included to avoid warnings
365 		if (IsUpperOrLowerCase(chDoc) && !IsUpperOrLowerCase(chPrevious)) {
366 			return MakeUpperCase(chDoc);
367 		} else {
368 			return MakeLowerCase(chDoc);
369 		}
370 	}
371 }
372 
373 }
374 
375 /**
376 * Fill in the LineLayout data for the given line.
377 * Copy the given @a line and its styles from the document into local arrays.
378 * Also determine the x position at which each character starts.
379 */
LayoutLine(const EditModel & model,Sci::Line line,Surface * surface,const ViewStyle & vstyle,LineLayout * ll,int width)380 void EditView::LayoutLine(const EditModel &model, Sci::Line line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
381 	if (!ll)
382 		return;
383 
384 	PLATFORM_ASSERT(line < model.pdoc->LinesTotal());
385 	PLATFORM_ASSERT(ll->chars != NULL);
386 	const Sci::Position posLineStart = model.pdoc->LineStart(line);
387 	Sci::Position posLineEnd = model.pdoc->LineStart(line + 1);
388 	// If the line is very long, limit the treatment to a length that should fit in the viewport
389 	if (posLineEnd >(posLineStart + ll->maxLineLength)) {
390 		posLineEnd = posLineStart + ll->maxLineLength;
391 	}
392 	if (ll->validity == LineLayout::llCheckTextAndStyle) {
393 		Sci::Position lineLength = posLineEnd - posLineStart;
394 		if (!vstyle.viewEOL) {
395 			lineLength = model.pdoc->LineEnd(line) - posLineStart;
396 		}
397 		if (lineLength == ll->numCharsInLine) {
398 			// See if chars, styles, indicators, are all the same
399 			bool allSame = true;
400 			// Check base line layout
401 			int styleByte = 0;
402 			int numCharsInLine = 0;
403 			char chPrevious = 0;
404 			while (numCharsInLine < lineLength) {
405 				const Sci::Position charInDoc = numCharsInLine + posLineStart;
406 				const char chDoc = model.pdoc->CharAt(charInDoc);
407 				styleByte = model.pdoc->StyleIndexAt(charInDoc);
408 				allSame = allSame &&
409 					(ll->styles[numCharsInLine] == styleByte);
410 				allSame = allSame &&
411 					(ll->chars[numCharsInLine] == CaseForce(vstyle.styles[styleByte].caseForce, chDoc, chPrevious));
412 				chPrevious = chDoc;
413 				numCharsInLine++;
414 			}
415 			allSame = allSame && (ll->styles[numCharsInLine] == styleByte);	// For eolFilled
416 			if (allSame) {
417 				ll->validity = LineLayout::llPositions;
418 			} else {
419 				ll->validity = LineLayout::llInvalid;
420 			}
421 		} else {
422 			ll->validity = LineLayout::llInvalid;
423 		}
424 	}
425 	if (ll->validity == LineLayout::llInvalid) {
426 		ll->widthLine = LineLayout::wrapWidthInfinite;
427 		ll->lines = 1;
428 		if (vstyle.edgeState == EDGE_BACKGROUND) {
429 			Sci::Position edgePosition = model.pdoc->FindColumn(line, vstyle.theEdge.column);
430 			if (edgePosition >= posLineStart) {
431 				edgePosition -= posLineStart;
432 			}
433 			ll->edgeColumn = static_cast<int>(edgePosition);
434 		} else {
435 			ll->edgeColumn = -1;
436 		}
437 
438 		// Fill base line layout
439 		const int lineLength = static_cast<int>(posLineEnd - posLineStart);
440 		model.pdoc->GetCharRange(ll->chars.get(), posLineStart, lineLength);
441 		model.pdoc->GetStyleRange(ll->styles.get(), posLineStart, lineLength);
442 		const int numCharsBeforeEOL = static_cast<int>(model.pdoc->LineEnd(line) - posLineStart);
443 		const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
444 		const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
445 		if (vstyle.someStylesForceCase) {
446 			char chPrevious = 0;
447 			for (int charInLine = 0; charInLine<lineLength; charInLine++) {
448 				const char chDoc = ll->chars[charInLine];
449 				ll->chars[charInLine] = CaseForce(vstyle.styles[ll->styles[charInLine]].caseForce, chDoc, chPrevious);
450 				chPrevious = chDoc;
451 			}
452 		}
453 		ll->xHighlightGuide = 0;
454 		// Extra element at the end of the line to hold end x position and act as
455 		ll->chars[numCharsInLine] = 0;   // Also triggers processing in the loops as this is a control character
456 		ll->styles[numCharsInLine] = styleByteLast;	// For eolFilled
457 
458 		// Layout the line, determining the position of each character,
459 		// with an extra element at the end for the end of the line.
460 		ll->positions[0] = 0;
461 		bool lastSegItalics = false;
462 
463 		BreakFinder bfLayout(ll, nullptr, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs, nullptr);
464 		while (bfLayout.More()) {
465 
466 			const TextSegment ts = bfLayout.Next();
467 
468 			std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f);
469 			if (vstyle.styles[ll->styles[ts.start]].visible) {
470 				if (ts.representation) {
471 					XYPOSITION representationWidth = vstyle.controlCharWidth;
472 					if (ll->chars[ts.start] == '\t') {
473 						// Tab is a special case of representation, taking a variable amount of space
474 						const XYPOSITION x = ll->positions[ts.start];
475 						representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start];
476 					} else {
477 						if (representationWidth <= 0.0) {
478 							XYPOSITION positionsRepr[256];	// Should expand when needed
479 							posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
480 								static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
481 							representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
482 						}
483 					}
484 					for (int ii = 0; ii < ts.length; ii++)
485 						ll->positions[ts.start + 1 + ii] = representationWidth;
486 				} else {
487 					if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
488 						// Over half the segments are single characters and of these about half are space characters.
489 						ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
490 					} else {
491 						posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], &ll->chars[ts.start],
492 							ts.length, &ll->positions[ts.start + 1], model.pdoc);
493 					}
494 				}
495 				lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
496 			}
497 
498 			for (Sci::Position posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
499 				ll->positions[posToIncrease] += ll->positions[ts.start];
500 			}
501 		}
502 
503 		// Small hack to make lines that end with italics not cut off the edge of the last character
504 		if (lastSegItalics) {
505 			ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
506 		}
507 		ll->numCharsInLine = numCharsInLine;
508 		ll->numCharsBeforeEOL = numCharsBeforeEOL;
509 		ll->validity = LineLayout::llPositions;
510 	}
511 	// Hard to cope when too narrow, so just assume there is space
512 	if (width < 20) {
513 		width = 20;
514 	}
515 	if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
516 		ll->widthLine = width;
517 		if (width == LineLayout::wrapWidthInfinite) {
518 			ll->lines = 1;
519 		} else if (width > ll->positions[ll->numCharsInLine]) {
520 			// Simple common case where line does not need wrapping.
521 			ll->lines = 1;
522 		} else {
523 			if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
524 				width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
525 			}
526 			XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
527 			switch (vstyle.wrapIndentMode) {
528 			case SC_WRAPINDENT_FIXED:
529 				wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
530 				break;
531 			case SC_WRAPINDENT_INDENT:
532 				wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
533 				break;
534 			case SC_WRAPINDENT_DEEPINDENT:
535 				wrapAddIndent = model.pdoc->IndentSize() * 2 * vstyle.spaceWidth;
536 				break;
537 			}
538 			ll->wrapIndent = wrapAddIndent;
539 			if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED) {
540 				for (int i = 0; i < ll->numCharsInLine; i++) {
541 					if (!IsSpaceOrTab(ll->chars[i])) {
542 						ll->wrapIndent += ll->positions[i]; // Add line indent
543 						break;
544 					}
545 				}
546 			}
547 			// Check for text width minimum
548 			if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
549 				ll->wrapIndent = wrapAddIndent;
550 			// Check for wrapIndent minimum
551 			if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
552 				ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
553 			ll->lines = 0;
554 			// Calculate line start positions based upon width.
555 			Sci::Position lastGoodBreak = 0;
556 			Sci::Position lastLineStart = 0;
557 			XYACCUMULATOR startOffset = 0;
558 			Sci::Position p = 0;
559 			while (p < ll->numCharsInLine) {
560 				if ((ll->positions[p + 1] - startOffset) >= width) {
561 					if (lastGoodBreak == lastLineStart) {
562 						// Try moving to start of last character
563 						if (p > 0) {
564 							lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
565 								- posLineStart;
566 						}
567 						if (lastGoodBreak == lastLineStart) {
568 							// Ensure at least one character on line.
569 							lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
570 								- posLineStart;
571 						}
572 					}
573 					lastLineStart = lastGoodBreak;
574 					ll->lines++;
575 					ll->SetLineStart(ll->lines, static_cast<int>(lastGoodBreak));
576 					startOffset = ll->positions[lastGoodBreak];
577 					// take into account the space for start wrap mark and indent
578 					startOffset -= ll->wrapIndent;
579 					p = lastGoodBreak + 1;
580 					continue;
581 				}
582 				if (p > 0) {
583 					if (vstyle.wrapState == eWrapChar) {
584 						lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
585 							- posLineStart;
586 						p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
587 						continue;
588 					} else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
589 						lastGoodBreak = p;
590 					} else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
591 						lastGoodBreak = p;
592 					}
593 				}
594 				p++;
595 			}
596 			ll->lines++;
597 		}
598 		ll->validity = LineLayout::llLines;
599 	}
600 }
601 
LocationFromPosition(Surface * surface,const EditModel & model,SelectionPosition pos,Sci::Line topLine,const ViewStyle & vs,PointEnd pe)602 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, Sci::Line topLine,
603 				     const ViewStyle &vs, PointEnd pe) {
604 	Point pt;
605 	if (pos.Position() == INVALID_POSITION)
606 		return pt;
607 	Sci::Line lineDoc = model.pdoc->SciLineFromPosition(pos.Position());
608 	Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);
609 	if ((pe & peLineEnd) && (lineDoc > 0) && (pos.Position() == posLineStart)) {
610 		// Want point at end of first line
611 		lineDoc--;
612 		posLineStart = model.pdoc->LineStart(lineDoc);
613 	}
614 	const Sci::Line lineVisible = model.pcs->DisplayFromDoc(lineDoc);
615 	AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
616 	if (surface && ll) {
617 		LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
618 		const int posInLine = static_cast<int>(pos.Position() - posLineStart);
619 		pt = ll->PointFromPosition(posInLine, vs.lineHeight, pe);
620 		pt.y += (lineVisible - topLine) * vs.lineHeight;
621 		pt.x += vs.textStart - model.xOffset;
622 	}
623 	pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
624 	return pt;
625 }
626 
RangeDisplayLine(Surface * surface,const EditModel & model,Sci::Line lineVisible,const ViewStyle & vs)627 Range EditView::RangeDisplayLine(Surface *surface, const EditModel &model, Sci::Line lineVisible, const ViewStyle &vs) {
628 	Range rangeSubLine = Range(0, 0);
629 	if (lineVisible < 0) {
630 		return rangeSubLine;
631 	}
632 	const Sci::Line lineDoc = model.pcs->DocFromDisplay(lineVisible);
633 	const Sci::Position positionLineStart = model.pdoc->LineStart(lineDoc);
634 	AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
635 	if (surface && ll) {
636 		LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
637 		const Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc);
638 		const int subLine = static_cast<int>(lineVisible - lineStartSet);
639 		if (subLine < ll->lines) {
640 			rangeSubLine = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly);
641 			if (subLine == ll->lines-1) {
642 				rangeSubLine.end = model.pdoc->LineStart(lineDoc + 1) -
643 					positionLineStart;
644 			}
645 		}
646 	}
647 	rangeSubLine.start += positionLineStart;
648 	rangeSubLine.end += positionLineStart;
649 	return rangeSubLine;
650 }
651 
SPositionFromLocation(Surface * surface,const EditModel & model,PointDocument pt,bool canReturnInvalid,bool charPosition,bool virtualSpace,const ViewStyle & vs)652 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
653 	pt.x = pt.x - vs.textStart;
654 	Sci::Line visibleLine = static_cast<int>(std::floor(pt.y / vs.lineHeight));
655 	if (!canReturnInvalid && (visibleLine < 0))
656 		visibleLine = 0;
657 	const Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);
658 	if (canReturnInvalid && (lineDoc < 0))
659 		return SelectionPosition(INVALID_POSITION);
660 	if (lineDoc >= model.pdoc->LinesTotal())
661 		return SelectionPosition(canReturnInvalid ? INVALID_POSITION :
662 			model.pdoc->Length());
663 	const Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);
664 	AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
665 	if (surface && ll) {
666 		LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
667 		const Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc);
668 		const int subLine = static_cast<int>(visibleLine - lineStartSet);
669 		if (subLine < ll->lines) {
670 			const Range rangeSubLine = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly);
671 			const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
672 			if (subLine > 0)	// Wrapped
673 				pt.x -= ll->wrapIndent;
674 			const Sci::Position positionInLine = ll->FindPositionFromX(static_cast<XYPOSITION>(pt.x + subLineStart),
675 				rangeSubLine, charPosition);
676 			if (positionInLine < rangeSubLine.end) {
677 				return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
678 			}
679 			if (virtualSpace) {
680 				const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
681 				const int spaceOffset = static_cast<int>(
682 					(pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
683 				return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
684 			} else if (canReturnInvalid) {
685 				if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
686 					return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
687 				}
688 			} else {
689 				return SelectionPosition(rangeSubLine.end + posLineStart);
690 			}
691 		}
692 		if (!canReturnInvalid)
693 			return SelectionPosition(ll->numCharsInLine + posLineStart);
694 	}
695 	return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
696 }
697 
698 /**
699 * Find the document position corresponding to an x coordinate on a particular document line.
700 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
701 * This method is used for rectangular selections and does not work on wrapped lines.
702 */
SPositionFromLineX(Surface * surface,const EditModel & model,Sci::Line lineDoc,int x,const ViewStyle & vs)703 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, Sci::Line lineDoc, int x, const ViewStyle &vs) {
704 	AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
705 	if (surface && ll) {
706 		const Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);
707 		LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
708 		const Range rangeSubLine = ll->SubLineRange(0, LineLayout::Scope::visibleOnly);
709 		const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
710 		const Sci::Position positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
711 		if (positionInLine < rangeSubLine.end) {
712 			return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
713 		}
714 		const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
715 		const int spaceOffset = static_cast<int>(
716 			(x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
717 		return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
718 	}
719 	return SelectionPosition(0);
720 }
721 
DisplayFromPosition(Surface * surface,const EditModel & model,Sci::Position pos,const ViewStyle & vs)722 Sci::Line EditView::DisplayFromPosition(Surface *surface, const EditModel &model, Sci::Position pos, const ViewStyle &vs) {
723 	const Sci::Line lineDoc = model.pdoc->SciLineFromPosition(pos);
724 	Sci::Line lineDisplay = model.pcs->DisplayFromDoc(lineDoc);
725 	AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
726 	if (surface && ll) {
727 		LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
728 		const Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);
729 		const Sci::Position posInLine = pos - posLineStart;
730 		lineDisplay--; // To make up for first increment ahead.
731 		for (int subLine = 0; subLine < ll->lines; subLine++) {
732 			if (posInLine >= ll->LineStart(subLine)) {
733 				lineDisplay++;
734 			}
735 		}
736 	}
737 	return lineDisplay;
738 }
739 
StartEndDisplayLine(Surface * surface,const EditModel & model,Sci::Position pos,bool start,const ViewStyle & vs)740 Sci::Position EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, Sci::Position pos, bool start, const ViewStyle &vs) {
741 	const Sci::Line line = model.pdoc->SciLineFromPosition(pos);
742 	AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
743 	Sci::Position posRet = INVALID_POSITION;
744 	if (surface && ll) {
745 		const Sci::Position posLineStart = model.pdoc->LineStart(line);
746 		LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
747 		const Sci::Position posInLine = pos - posLineStart;
748 		if (posInLine <= ll->maxLineLength) {
749 			for (int subLine = 0; subLine < ll->lines; subLine++) {
750 				if ((posInLine >= ll->LineStart(subLine)) &&
751 				    (posInLine <= ll->LineStart(subLine + 1)) &&
752 				    (posInLine <= ll->numCharsBeforeEOL)) {
753 					if (start) {
754 						posRet = ll->LineStart(subLine) + posLineStart;
755 					} else {
756 						if (subLine == ll->lines - 1)
757 							posRet = ll->numCharsBeforeEOL + posLineStart;
758 						else
759 							posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
760 					}
761 				}
762 			}
763 		}
764 	}
765 	return posRet;
766 }
767 
SelectionBackground(const ViewStyle & vsDraw,bool main,bool primarySelection)768 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) noexcept {
769 	return main ?
770 		(primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
771 		vsDraw.selAdditionalBackground;
772 }
773 
TextBackground(const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,ColourOptional background,int inSelection,bool inHotspot,int styleMain,Sci::Position i)774 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
775 	ColourOptional background, int inSelection, bool inHotspot, int styleMain, Sci::Position i) {
776 	if (inSelection == 1) {
777 		if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
778 			return SelectionBackground(vsDraw, true, model.primarySelection);
779 		}
780 	} else if (inSelection == 2) {
781 		if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
782 			return SelectionBackground(vsDraw, false, model.primarySelection);
783 		}
784 	} else {
785 		if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
786 			(i >= ll->edgeColumn) &&
787 			(i < ll->numCharsBeforeEOL))
788 			return vsDraw.theEdge.colour;
789 		if (inHotspot && vsDraw.hotspotColours.back.isSet)
790 			return vsDraw.hotspotColours.back;
791 	}
792 	if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
793 		return background;
794 	} else {
795 		return vsDraw.styles[styleMain].back;
796 	}
797 }
798 
DrawIndentGuide(Surface * surface,Sci::Line lineVisible,int lineHeight,XYPOSITION start,PRectangle rcSegment,bool highlight)799 void EditView::DrawIndentGuide(Surface *surface, Sci::Line lineVisible, int lineHeight, XYPOSITION start, PRectangle rcSegment, bool highlight) {
800 	const Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
801 	const PRectangle rcCopyArea(start + 1, rcSegment.top,
802 		start + 2, rcSegment.bottom);
803 	surface->Copy(rcCopyArea, from,
804 		highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
805 }
806 
SimpleAlphaRectangle(Surface * surface,PRectangle rc,ColourDesired fill,int alpha)807 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
808 	if (alpha != SC_ALPHA_NOALPHA) {
809 		surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
810 	}
811 }
812 
DrawTextBlob(Surface * surface,const ViewStyle & vsDraw,PRectangle rcSegment,const char * s,ColourDesired textBack,ColourDesired textFore,bool fillBackground)813 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
814 	const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
815 	if (rcSegment.Empty())
816 		return;
817 	if (fillBackground) {
818 		surface->FillRectangle(rcSegment, textBack);
819 	}
820 	FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
821 	const int normalCharHeight = static_cast<int>(std::ceil(vsDraw.styles[STYLE_CONTROLCHAR].capitalHeight));
822 	PRectangle rcCChar = rcSegment;
823 	rcCChar.left = rcCChar.left + 1;
824 	rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
825 	rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
826 	PRectangle rcCentral = rcCChar;
827 	rcCentral.top++;
828 	rcCentral.bottom--;
829 	surface->FillRectangle(rcCentral, textFore);
830 	PRectangle rcChar = rcCChar;
831 	rcChar.left++;
832 	rcChar.right--;
833 	surface->DrawTextClipped(rcChar, ctrlCharsFont,
834 		rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
835 		textBack, textFore);
836 }
837 
DrawFrame(Surface * surface,ColourDesired colour,int alpha,PRectangle rcFrame)838 static void DrawFrame(Surface *surface, ColourDesired colour, int alpha, PRectangle rcFrame) {
839 	if (alpha != SC_ALPHA_NOALPHA)
840 		surface->AlphaRectangle(rcFrame, 0, colour, alpha, colour, alpha, 0);
841 	else
842 		surface->FillRectangle(rcFrame, colour);
843 }
844 
DrawCaretLineFramed(Surface * surface,const ViewStyle & vsDraw,const LineLayout * ll,PRectangle rcLine,int subLine)845 static void DrawCaretLineFramed(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine, int subLine) {
846 	const int width = vsDraw.GetFrameWidth();
847 	if (subLine == 0 || ll->wrapIndent == 0 || vsDraw.caretLineAlpha != SC_ALPHA_NOALPHA) {
848 		// Left
849 		DrawFrame(surface, vsDraw.caretLineBackground, vsDraw.caretLineAlpha,
850 			PRectangle(rcLine.left, rcLine.top, rcLine.left + width, rcLine.bottom));
851 	}
852 	if (subLine == 0) {
853 		// Top
854 		DrawFrame(surface, vsDraw.caretLineBackground, vsDraw.caretLineAlpha,
855 			PRectangle(rcLine.left + width, rcLine.top, rcLine.right - width, rcLine.top + width));
856 	}
857 	if (subLine == ll->lines - 1 || vsDraw.caretLineAlpha != SC_ALPHA_NOALPHA) {
858 		// Right
859 		DrawFrame(surface, vsDraw.caretLineBackground, vsDraw.caretLineAlpha,
860 			PRectangle(rcLine.right - width, rcLine.top, rcLine.right, rcLine.bottom));
861 	}
862 	if (subLine == ll->lines - 1) {
863 		// Bottom
864 		DrawFrame(surface, vsDraw.caretLineBackground, vsDraw.caretLineAlpha,
865 			PRectangle(rcLine.left + width, rcLine.bottom - width, rcLine.right - width, rcLine.bottom));
866 	}
867 }
868 
DrawEOL(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,PRectangle rcLine,Sci::Line line,Sci::Position lineEnd,int xStart,int subLine,XYACCUMULATOR subLineStart,ColourOptional background)869 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
870 	PRectangle rcLine, Sci::Line line, Sci::Position lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
871 	ColourOptional background) {
872 
873 	const Sci::Position posLineStart = model.pdoc->LineStart(line);
874 	PRectangle rcSegment = rcLine;
875 
876 	const bool lastSubLine = subLine == (ll->lines - 1);
877 	XYPOSITION virtualSpace = 0;
878 	if (lastSubLine) {
879 		const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
880 		virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
881 	}
882 	const XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
883 
884 	// Fill the virtual space and show selections within it
885 	if (virtualSpace > 0.0f) {
886 		rcSegment.left = xEol + xStart;
887 		rcSegment.right = xEol + xStart + virtualSpace;
888 		surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
889 		if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
890 			const SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)),
891 				SelectionPosition(model.pdoc->LineEnd(line),
892 					model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
893 			for (size_t r = 0; r<model.sel.Count(); r++) {
894 				const int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
895 				if (alpha == SC_ALPHA_NOALPHA) {
896 					const SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
897 					if (!portion.Empty()) {
898 						const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
899 						rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
900 							static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
901 						rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
902 							static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
903 						rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
904 						rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
905 						surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
906 					}
907 				}
908 			}
909 		}
910 	}
911 
912 	int eolInSelection = 0;
913 	int alpha = SC_ALPHA_NOALPHA;
914 	if (!hideSelection) {
915 		const Sci::Position posAfterLineEnd = model.pdoc->LineStart(line + 1);
916 		eolInSelection = lastSubLine ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
917 		alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
918 	}
919 
920 	// Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
921 	XYPOSITION blobsWidth = 0;
922 	if (lastSubLine) {
923 		for (Sci::Position eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
924 			rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
925 			rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
926 			blobsWidth += rcSegment.Width();
927 			char hexits[4] = "";
928 			const char *ctrlChar;
929 			const unsigned char chEOL = ll->chars[eolPos];
930 			const int styleMain = ll->styles[eolPos];
931 			const ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
932 			if (UTF8IsAscii(chEOL)) {
933 				ctrlChar = ControlCharacterString(chEOL);
934 			} else {
935 				const Representation *repr = model.reprs.RepresentationFromCharacter(&ll->chars[eolPos], ll->numCharsInLine - eolPos);
936 				if (repr) {
937 					ctrlChar = repr->stringRep.c_str();
938 					eolPos = ll->numCharsInLine;
939 				} else {
940 					sprintf(hexits, "x%2X", chEOL);
941 					ctrlChar = hexits;
942 				}
943 			}
944 			ColourDesired textFore = vsDraw.styles[styleMain].fore;
945 			if (eolInSelection && vsDraw.selColours.fore.isSet) {
946 				textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
947 			}
948 			if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
949 				if (alpha == SC_ALPHA_NOALPHA) {
950 					surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
951 				} else {
952 					surface->FillRectangle(rcSegment, textBack);
953 				}
954 			} else {
955 				surface->FillRectangle(rcSegment, textBack);
956 			}
957 			DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
958 			if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
959 				SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
960 			}
961 		}
962 	}
963 
964 	// Draw the eol-is-selected rectangle
965 	rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
966 	rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
967 
968 	if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
969 		surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
970 	} else {
971 		if (background.isSet) {
972 			surface->FillRectangle(rcSegment, background);
973 		} else if (line < model.pdoc->LinesTotal() - 1) {
974 			surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
975 		} else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
976 			surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
977 		} else {
978 			surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
979 		}
980 		if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
981 			SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
982 		}
983 	}
984 
985 	rcSegment.left = rcSegment.right;
986 	if (rcSegment.left < rcLine.left)
987 		rcSegment.left = rcLine.left;
988 	rcSegment.right = rcLine.right;
989 
990 	const bool fillRemainder = !lastSubLine || !model.GetFoldDisplayText(line);
991 	if (fillRemainder) {
992 		// Fill the remainder of the line
993 		FillLineRemainder(surface, model, vsDraw, ll, line, rcSegment, subLine);
994 	}
995 
996 	bool drawWrapMarkEnd = false;
997 
998 	if (subLine + 1 < ll->lines) {
999 		if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
1000 			drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
1001 		}
1002 		if (vsDraw.IsLineFrameOpaque(model.caret.active, ll->containsCaret)) {
1003 			const int width = vsDraw.GetFrameWidth();
1004 			// Draw right of frame under marker
1005 			DrawFrame(surface, vsDraw.caretLineBackground, vsDraw.caretLineAlpha,
1006 				PRectangle(rcLine.right - width, rcLine.top, rcLine.right, rcLine.bottom));
1007 		}
1008 	}
1009 
1010 	if (drawWrapMarkEnd) {
1011 		PRectangle rcPlace = rcSegment;
1012 
1013 		if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
1014 			rcPlace.left = xEol + xStart + virtualSpace;
1015 			rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1016 		} else {
1017 			// rcLine is clipped to text area
1018 			rcPlace.right = rcLine.right;
1019 			rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1020 		}
1021 		if (!customDrawWrapMarker) {
1022 			DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
1023 		} else {
1024 			customDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
1025 		}
1026 	}
1027 }
1028 
DrawIndicator(int indicNum,Sci::Position startPos,Sci::Position endPos,Surface * surface,const ViewStyle & vsDraw,const LineLayout * ll,int xStart,PRectangle rcLine,Sci::Position secondCharacter,int subLine,Indicator::DrawState drawState,int value)1029 static void DrawIndicator(int indicNum, Sci::Position startPos, Sci::Position endPos, Surface *surface, const ViewStyle &vsDraw,
1030 	const LineLayout *ll, int xStart, PRectangle rcLine, Sci::Position secondCharacter, int subLine, Indicator::DrawState drawState, int value) {
1031 	const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
1032 	const PRectangle rcIndic(
1033 		ll->positions[startPos] + xStart - subLineStart,
1034 		rcLine.top + vsDraw.maxAscent,
1035 		ll->positions[endPos] + xStart - subLineStart,
1036 		rcLine.top + vsDraw.maxAscent + 3);
1037 	PRectangle rcFirstCharacter = rcIndic;
1038 	// Allow full descent space for character indicators
1039 	rcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent;
1040 	if (secondCharacter >= 0) {
1041 		rcFirstCharacter.right = ll->positions[secondCharacter] + xStart - subLineStart;
1042 	} else {
1043 		// Indicator continued from earlier line so make an empty box and don't draw
1044 		rcFirstCharacter.right = rcFirstCharacter.left;
1045 	}
1046 	vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, rcFirstCharacter, drawState, value);
1047 }
1048 
DrawIndicators(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,int xStart,PRectangle rcLine,int subLine,Sci::Position lineEnd,bool under,Sci::Position hoverIndicatorPos)1049 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1050 	Sci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Position lineEnd, bool under, Sci::Position hoverIndicatorPos) {
1051 	// Draw decorators
1052 	const Sci::Position posLineStart = model.pdoc->LineStart(line);
1053 	const Sci::Position lineStart = ll->LineStart(subLine);
1054 	const Sci::Position posLineEnd = posLineStart + lineEnd;
1055 
1056 	for (const IDecoration *deco : model.pdoc->decorations->View()) {
1057 		if (under == vsDraw.indicators[deco->Indicator()].under) {
1058 			Sci::Position startPos = posLineStart + lineStart;
1059 			if (!deco->ValueAt(startPos)) {
1060 				startPos = deco->EndRun(startPos);
1061 			}
1062 			while ((startPos < posLineEnd) && (deco->ValueAt(startPos))) {
1063 				const Range rangeRun(deco->StartRun(startPos), deco->EndRun(startPos));
1064 				const Sci::Position endPos = std::min(rangeRun.end, posLineEnd);
1065 				const bool hover = vsDraw.indicators[deco->Indicator()].IsDynamic() &&
1066 					rangeRun.ContainsCharacter(hoverIndicatorPos);
1067 				const int value = deco->ValueAt(startPos);
1068 				const Indicator::DrawState drawState = hover ? Indicator::drawHover : Indicator::drawNormal;
1069 				const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1);
1070 				DrawIndicator(deco->Indicator(), startPos - posLineStart, endPos - posLineStart,
1071 					surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, drawState, value);
1072 				startPos = endPos;
1073 				if (!deco->ValueAt(startPos)) {
1074 					startPos = deco->EndRun(startPos);
1075 				}
1076 			}
1077 		}
1078 	}
1079 
1080 	// Use indicators to highlight matching braces
1081 	if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1082 		(vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
1083 		const int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
1084 		if (under == vsDraw.indicators[braceIndicator].under) {
1085 			const Range rangeLine(posLineStart + lineStart, posLineEnd);
1086 			if (rangeLine.ContainsCharacter(model.braces[0])) {
1087 				const Sci::Position braceOffset = model.braces[0] - posLineStart;
1088 				if (braceOffset < ll->numCharsInLine) {
1089 					const Sci::Position secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[0] + 1, 1) - posLineStart;
1090 					DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1);
1091 				}
1092 			}
1093 			if (rangeLine.ContainsCharacter(model.braces[1])) {
1094 				const Sci::Position braceOffset = model.braces[1] - posLineStart;
1095 				if (braceOffset < ll->numCharsInLine) {
1096 					const Sci::Position secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[1] + 1, 1) - posLineStart;
1097 					DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1);
1098 				}
1099 			}
1100 		}
1101 	}
1102 }
1103 
DrawFoldDisplayText(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,int xStart,PRectangle rcLine,int subLine,XYACCUMULATOR subLineStart,DrawPhase phase)1104 void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1105 							  Sci::Line line, int xStart, PRectangle rcLine, int subLine, XYACCUMULATOR subLineStart, DrawPhase phase) {
1106 	const bool lastSubLine = subLine == (ll->lines - 1);
1107 	if (!lastSubLine)
1108 		return;
1109 
1110 	const char *text = model.GetFoldDisplayText(line);
1111 	if (!text)
1112 		return;
1113 
1114 	PRectangle rcSegment = rcLine;
1115 	const char *foldDisplayText = text;
1116 	const int lengthFoldDisplayText = static_cast<int>(strlen(foldDisplayText));
1117 	FontAlias fontText = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].font;
1118 	const int widthFoldDisplayText = static_cast<int>(surface->WidthText(fontText, foldDisplayText, lengthFoldDisplayText));
1119 
1120 	int eolInSelection = 0;
1121 	int alpha = SC_ALPHA_NOALPHA;
1122 	if (!hideSelection) {
1123 		const Sci::Position posAfterLineEnd = model.pdoc->LineStart(line + 1);
1124 		eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
1125 		alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1126 	}
1127 
1128 	const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1129 	const XYPOSITION virtualSpace = model.sel.VirtualSpaceFor(
1130 		model.pdoc->LineEnd(line)) * spaceWidth;
1131 	rcSegment.left = xStart + static_cast<XYPOSITION>(ll->positions[ll->numCharsInLine] - subLineStart) + virtualSpace + vsDraw.aveCharWidth;
1132 	rcSegment.right = rcSegment.left + static_cast<XYPOSITION>(widthFoldDisplayText);
1133 
1134 	const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1135 	ColourDesired textFore = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].fore;
1136 	if (eolInSelection && (vsDraw.selColours.fore.isSet)) {
1137 		textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1138 	}
1139 	const ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection,
1140 											false, STYLE_FOLDDISPLAYTEXT, -1);
1141 
1142 	if (model.trackLineWidth) {
1143 		if (rcSegment.right + 1> lineWidthMaxSeen) {
1144 			// Fold display text border drawn on rcSegment.right with width 1 is the last visble object of the line
1145 			lineWidthMaxSeen = static_cast<int>(rcSegment.right + 1);
1146 		}
1147 	}
1148 
1149 	if (phase & drawBack) {
1150 		surface->FillRectangle(rcSegment, textBack);
1151 
1152 		// Fill Remainder of the line
1153 		PRectangle rcRemainder = rcSegment;
1154 		rcRemainder.left = rcRemainder.right;
1155 		if (rcRemainder.left < rcLine.left)
1156 			rcRemainder.left = rcLine.left;
1157 		rcRemainder.right = rcLine.right;
1158 		FillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine);
1159 	}
1160 
1161 	if (phase & drawText) {
1162 		if (phasesDraw != phasesOne) {
1163 			surface->DrawTextTransparent(rcSegment, fontText,
1164 				rcSegment.top + vsDraw.maxAscent, foldDisplayText,
1165 				lengthFoldDisplayText, textFore);
1166 		} else {
1167 			surface->DrawTextNoClip(rcSegment, fontText,
1168 				rcSegment.top + vsDraw.maxAscent, foldDisplayText,
1169 				lengthFoldDisplayText, textFore, textBack);
1170 		}
1171 	}
1172 
1173 	if (phase & drawIndicatorsFore) {
1174 		if (model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_BOXED) {
1175 			surface->PenColour(textFore);
1176 			PRectangle rcBox = rcSegment;
1177 			rcBox.left = round(rcSegment.left);
1178 			rcBox.right = round(rcSegment.right);
1179 			const IntegerRectangle ircBox(rcBox);
1180 			surface->MoveTo(ircBox.left, ircBox.top);
1181 			surface->LineTo(ircBox.left, ircBox.bottom);
1182 			surface->MoveTo(ircBox.right, ircBox.top);
1183 			surface->LineTo(ircBox.right, ircBox.bottom);
1184 			surface->MoveTo(ircBox.left, ircBox.top);
1185 			surface->LineTo(ircBox.right, ircBox.top);
1186 			surface->MoveTo(ircBox.left, ircBox.bottom - 1);
1187 			surface->LineTo(ircBox.right, ircBox.bottom - 1);
1188 		}
1189 	}
1190 
1191 	if (phase & drawSelectionTranslucent) {
1192 		if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && alpha != SC_ALPHA_NOALPHA) {
1193 			SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
1194 		}
1195 	}
1196 }
1197 
AnnotationBoxedOrIndented(int annotationVisible)1198 static constexpr bool AnnotationBoxedOrIndented(int annotationVisible) noexcept {
1199 	return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED;
1200 }
1201 
DrawAnnotation(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,int xStart,PRectangle rcLine,int subLine,DrawPhase phase)1202 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1203 	Sci::Line line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1204 	const int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
1205 	PRectangle rcSegment = rcLine;
1206 	const int annotationLine = subLine - ll->lines;
1207 	const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1208 	if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1209 		if (phase & drawBack) {
1210 			surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1211 		}
1212 		rcSegment.left = static_cast<XYPOSITION>(xStart);
1213 		if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1214 			// Only care about calculating width if tracking or need to draw indented box
1215 			int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1216 			if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1217 				widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1218 				rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1219 				rcSegment.right = rcSegment.left + widthAnnotation;
1220 			}
1221 			if (widthAnnotation > lineWidthMaxSeen)
1222 				lineWidthMaxSeen = widthAnnotation;
1223 		}
1224 		const int annotationLines = model.pdoc->AnnotationLines(line);
1225 		size_t start = 0;
1226 		size_t lengthAnnotation = stAnnotation.LineLength(start);
1227 		int lineInAnnotation = 0;
1228 		while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1229 			start += lengthAnnotation + 1;
1230 			lengthAnnotation = stAnnotation.LineLength(start);
1231 			lineInAnnotation++;
1232 		}
1233 		PRectangle rcText = rcSegment;
1234 		if ((phase & drawBack) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1235 			surface->FillRectangle(rcText,
1236 				vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1237 			rcText.left += vsDraw.spaceWidth;
1238 		}
1239 		DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1240 			stAnnotation, start, lengthAnnotation, phase);
1241 		if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1242 			surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1243 			const IntegerRectangle ircSegment(rcSegment);
1244 			surface->MoveTo(ircSegment.left, ircSegment.top);
1245 			surface->LineTo(ircSegment.left, ircSegment.bottom);
1246 			surface->MoveTo(ircSegment.right, ircSegment.top);
1247 			surface->LineTo(ircSegment.right, ircSegment.bottom);
1248 			if (subLine == ll->lines) {
1249 				surface->MoveTo(ircSegment.left, ircSegment.top);
1250 				surface->LineTo(ircSegment.right, ircSegment.top);
1251 			}
1252 			if (subLine == ll->lines + annotationLines - 1) {
1253 				surface->MoveTo(ircSegment.left, ircSegment.bottom - 1);
1254 				surface->LineTo(ircSegment.right, ircSegment.bottom - 1);
1255 			}
1256 		}
1257 	}
1258 }
1259 
DrawBlockCaret(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int subLine,int xStart,Sci::Position offset,Sci::Position posCaret,PRectangle rcCaret,ColourDesired caretColour)1260 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1261 	int subLine, int xStart, Sci::Position offset, Sci::Position posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1262 
1263 	const Sci::Position lineStart = ll->LineStart(subLine);
1264 	Sci::Position posBefore = posCaret;
1265 	Sci::Position posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1266 	Sci::Position numCharsToDraw = posAfter - posCaret;
1267 
1268 	// Work out where the starting and ending offsets are. We need to
1269 	// see if the previous character shares horizontal space, such as a
1270 	// glyph / combining character. If so we'll need to draw that too.
1271 	Sci::Position offsetFirstChar = offset;
1272 	Sci::Position offsetLastChar = offset + (posAfter - posCaret);
1273 	while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1274 		if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1275 			// The char does not share horizontal space
1276 			break;
1277 		}
1278 		// Char shares horizontal space, update the numChars to draw
1279 		// Update posBefore to point to the prev char
1280 		posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1281 		numCharsToDraw = posAfter - posBefore;
1282 		offsetFirstChar = offset - (posCaret - posBefore);
1283 	}
1284 
1285 	// See if the next character shares horizontal space, if so we'll
1286 	// need to draw that too.
1287 	if (offsetFirstChar < 0)
1288 		offsetFirstChar = 0;
1289 	numCharsToDraw = offsetLastChar - offsetFirstChar;
1290 	while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1291 		// Update posAfter to point to the 2nd next char, this is where
1292 		// the next character ends, and 2nd next begins. We'll need
1293 		// to compare these two
1294 		posBefore = posAfter;
1295 		posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1296 		offsetLastChar = offset + (posAfter - posCaret);
1297 		if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1298 			// The char does not share horizontal space
1299 			break;
1300 		}
1301 		// Char shares horizontal space, update the numChars to draw
1302 		numCharsToDraw = offsetLastChar - offsetFirstChar;
1303 	}
1304 
1305 	// We now know what to draw, update the caret drawing rectangle
1306 	rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1307 	rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1308 
1309 	// Adjust caret position to take into account any word wrapping symbols.
1310 	if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1311 		const XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1312 		rcCaret.left += wordWrapCharWidth;
1313 		rcCaret.right += wordWrapCharWidth;
1314 	}
1315 
1316 	// This character is where the caret block is, we override the colours
1317 	// (inversed) for drawing the caret here.
1318 	const int styleMain = ll->styles[offsetFirstChar];
1319 	FontAlias fontText = vsDraw.styles[styleMain].font;
1320 	surface->DrawTextClipped(rcCaret, fontText,
1321 		rcCaret.top + vsDraw.maxAscent, &ll->chars[offsetFirstChar],
1322 		static_cast<int>(numCharsToDraw), vsDraw.styles[styleMain].back,
1323 		caretColour);
1324 }
1325 
DrawCarets(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line lineDoc,int xStart,PRectangle rcLine,int subLine) const1326 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1327 	Sci::Line lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1328 	// When drag is active it is the only caret drawn
1329 	const bool drawDrag = model.posDrag.IsValid();
1330 	if (hideSelection && !drawDrag)
1331 		return;
1332 	const Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);
1333 	// For each selection draw
1334 	for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1335 		const bool mainCaret = r == model.sel.Main();
1336 		SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1337 		if ((vsDraw.DrawCaretInsideSelection(model.inOverstrike, imeCaretBlockOverride)) &&
1338 			!drawDrag &&
1339 			posCaret > model.sel.Range(r).anchor) {
1340 			if (posCaret.VirtualSpace() > 0)
1341 				posCaret.SetVirtualSpace(posCaret.VirtualSpace() - 1);
1342 			else
1343 				posCaret.SetPosition(model.pdoc->MovePositionOutsideChar(posCaret.Position()-1, -1));
1344 		}
1345 		const int offset = static_cast<int>(posCaret.Position() - posLineStart);
1346 		const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1347 		const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1348 		if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1349 			XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1350 			if (ll->wrapIndent != 0) {
1351 				const Sci::Position lineStart = ll->LineStart(subLine);
1352 				if (lineStart != 0)	// Wrapped
1353 					xposCaret += ll->wrapIndent;
1354 			}
1355 			const bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1356 			const bool caretVisibleState = additionalCaretsVisible || mainCaret;
1357 			if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1358 				(drawDrag || (caretBlinkState && caretVisibleState))) {
1359 				bool caretAtEOF = false;
1360 				bool caretAtEOL = false;
1361 				bool drawBlockCaret = false;
1362 				XYPOSITION widthOverstrikeCaret;
1363 				XYPOSITION caretWidthOffset = 0;
1364 				PRectangle rcCaret = rcLine;
1365 
1366 				if (posCaret.Position() == model.pdoc->Length()) {   // At end of document
1367 					caretAtEOF = true;
1368 					widthOverstrikeCaret = vsDraw.aveCharWidth;
1369 				} else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) {	// At end of line
1370 					caretAtEOL = true;
1371 					widthOverstrikeCaret = vsDraw.aveCharWidth;
1372 				} else {
1373 					const int widthChar = model.pdoc->LenChar(posCaret.Position());
1374 					widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1375 				}
1376 				if (widthOverstrikeCaret < 3)	// Make sure its visible
1377 					widthOverstrikeCaret = 3;
1378 
1379 				if (xposCaret > 0)
1380 					caretWidthOffset = 0.51f;	// Move back so overlaps both character cells.
1381 				xposCaret += xStart;
1382 				const ViewStyle::CaretShape caretShape = drawDrag ? ViewStyle::CaretShape::line : vsDraw.CaretShapeForMode(model.inOverstrike);
1383 				if (drawDrag) {
1384 					/* Dragging text, use a line caret */
1385 					rcCaret.left = round(xposCaret - caretWidthOffset);
1386 					rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1387 				} else if ((caretShape == ViewStyle::CaretShape::bar) && drawOverstrikeCaret) {
1388 					/* Overstrike (insert mode), use a modified bar caret */
1389 					rcCaret.top = rcCaret.bottom - 2;
1390 					rcCaret.left = xposCaret + 1;
1391 					rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1392 				} else if ((caretShape == ViewStyle::CaretShape::block) || imeCaretBlockOverride) {
1393 					/* Block caret */
1394 					rcCaret.left = xposCaret;
1395 					if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1396 						drawBlockCaret = true;
1397 						rcCaret.right = xposCaret + widthOverstrikeCaret;
1398 					} else {
1399 						rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1400 					}
1401 				} else {
1402 					/* Line caret */
1403 					rcCaret.left = round(xposCaret - caretWidthOffset);
1404 					rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1405 				}
1406 				const ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1407 				if (drawBlockCaret) {
1408 					DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1409 				} else {
1410 					surface->FillRectangle(rcCaret, caretColour);
1411 				}
1412 			}
1413 		}
1414 		if (drawDrag)
1415 			break;
1416 	}
1417 }
1418 
DrawWrapIndentAndMarker(Surface * surface,const ViewStyle & vsDraw,const LineLayout * ll,int xStart,PRectangle rcLine,ColourOptional background,DrawWrapMarkerFn customDrawWrapMarker,bool caretActive)1419 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1420 	int xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker,
1421 	bool caretActive) {
1422 	// default bgnd here..
1423 	surface->FillRectangle(rcLine, background.isSet ? background :
1424 		vsDraw.styles[STYLE_DEFAULT].back);
1425 
1426 	if (vsDraw.IsLineFrameOpaque(caretActive, ll->containsCaret)) {
1427 		const int width = vsDraw.GetFrameWidth();
1428 		// Draw left of frame under marker
1429 		DrawFrame(surface, vsDraw.caretLineBackground, vsDraw.caretLineAlpha,
1430 			PRectangle(rcLine.left, rcLine.top, rcLine.left + width, rcLine.bottom));
1431 	}
1432 
1433 	if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1434 
1435 		// draw continuation rect
1436 		PRectangle rcPlace = rcLine;
1437 
1438 		rcPlace.left = static_cast<XYPOSITION>(xStart);
1439 		rcPlace.right = rcPlace.left + ll->wrapIndent;
1440 
1441 		if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1442 			rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1443 		else
1444 			rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1445 
1446 		if (!customDrawWrapMarker) {
1447 			DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1448 		} else {
1449 			customDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1450 		}
1451 	}
1452 }
1453 
DrawBackground(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,PRectangle rcLine,Range lineRange,Sci::Position posLineStart,int xStart,int subLine,ColourOptional background) const1454 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1455 	PRectangle rcLine, Range lineRange, Sci::Position posLineStart, int xStart,
1456 	int subLine, ColourOptional background) const {
1457 
1458 	const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1459 	bool inIndentation = subLine == 0;	// Do not handle indentation except on first subline.
1460 	const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1461 	// Does not take margin into account but not significant
1462 	const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1463 
1464 	BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs, nullptr);
1465 
1466 	const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1467 
1468 	// Background drawing loop
1469 	while (bfBack.More()) {
1470 
1471 		const TextSegment ts = bfBack.Next();
1472 		const Sci::Position i = ts.end() - 1;
1473 		const Sci::Position iDoc = i + posLineStart;
1474 
1475 		PRectangle rcSegment = rcLine;
1476 		rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1477 		rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1478 		// Only try to draw if really visible - enhances performance by not calling environment to
1479 		// draw strings that are completely past the right side of the window.
1480 		if (!rcSegment.Empty() && rcSegment.Intersects(rcLine)) {
1481 			// Clip to line rectangle, since may have a huge position which will not work with some platforms
1482 			if (rcSegment.left < rcLine.left)
1483 				rcSegment.left = rcLine.left;
1484 			if (rcSegment.right > rcLine.right)
1485 				rcSegment.right = rcLine.right;
1486 
1487 			const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1488 			const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1489 			ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1490 				inHotspot, ll->styles[i], i);
1491 			if (ts.representation) {
1492 				if (ll->chars[i] == '\t') {
1493 					// Tab display
1494 					if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1495 						textBack = vsDraw.whitespaceColours.back;
1496 				} else {
1497 					// Blob display
1498 					inIndentation = false;
1499 				}
1500 				surface->FillRectangle(rcSegment, textBack);
1501 			} else {
1502 				// Normal text display
1503 				surface->FillRectangle(rcSegment, textBack);
1504 				if (vsDraw.viewWhitespace != wsInvisible) {
1505 					for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1506 						if (ll->chars[cpos + ts.start] == ' ') {
1507 							if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {
1508 								const PRectangle rcSpace(
1509 									ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1510 									rcSegment.top,
1511 									ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1512 									rcSegment.bottom);
1513 								surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1514 							}
1515 						} else {
1516 							inIndentation = false;
1517 						}
1518 					}
1519 				}
1520 			}
1521 		} else if (rcSegment.left > rcLine.right) {
1522 			break;
1523 		}
1524 	}
1525 }
1526 
DrawEdgeLine(Surface * surface,const ViewStyle & vsDraw,const LineLayout * ll,PRectangle rcLine,Range lineRange,int xStart)1527 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1528 	Range lineRange, int xStart) {
1529 	if (vsDraw.edgeState == EDGE_LINE) {
1530 		PRectangle rcSegment = rcLine;
1531 		const int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);
1532 		rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1533 		if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1534 			rcSegment.left -= ll->wrapIndent;
1535 		rcSegment.right = rcSegment.left + 1;
1536 		surface->FillRectangle(rcSegment, vsDraw.theEdge.colour);
1537 	} else if (vsDraw.edgeState == EDGE_MULTILINE) {
1538 		for (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {
1539 			if (vsDraw.theMultiEdge[edge].column >= 0) {
1540 				PRectangle rcSegment = rcLine;
1541 				const int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);
1542 				rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1543 				if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1544 					rcSegment.left -= ll->wrapIndent;
1545 				rcSegment.right = rcSegment.left + 1;
1546 				surface->FillRectangle(rcSegment, vsDraw.theMultiEdge[edge].colour);
1547 			}
1548 		}
1549 	}
1550 }
1551 
1552 // Draw underline mark as part of background if not transparent
DrawMarkUnderline(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,Sci::Line line,PRectangle rcLine)1553 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1554 	Sci::Line line, PRectangle rcLine) {
1555 	int marks = model.pdoc->GetMark(line);
1556 	for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1557 		if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1558 			(vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1559 			PRectangle rcUnderline = rcLine;
1560 			rcUnderline.top = rcUnderline.bottom - 2;
1561 			surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1562 		}
1563 		marks >>= 1;
1564 	}
1565 }
DrawTranslucentSelection(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,PRectangle rcLine,int subLine,Range lineRange,int xStart)1566 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1567 	Sci::Line line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1568 	if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1569 		const Sci::Position posLineStart = model.pdoc->LineStart(line);
1570 		const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1571 		// For each selection draw
1572 		Sci::Position virtualSpaces = 0;
1573 		if (subLine == (ll->lines - 1)) {
1574 			virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1575 		}
1576 		const SelectionPosition posStart(posLineStart + lineRange.start);
1577 		const SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1578 		const SelectionSegment virtualSpaceRange(posStart, posEnd);
1579 		for (size_t r = 0; r < model.sel.Count(); r++) {
1580 			const int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1581 			if (alpha != SC_ALPHA_NOALPHA) {
1582 				const SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1583 				if (!portion.Empty()) {
1584 					const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1585 					PRectangle rcSegment = rcLine;
1586 					rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1587 						static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1588 					rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1589 						static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1590 					if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1591 						if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1592 							rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1593 					}
1594 					rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1595 					rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1596 					if (rcSegment.right > rcLine.left)
1597 						SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1598 				}
1599 			}
1600 		}
1601 	}
1602 }
1603 
1604 // Draw any translucent whole line states
DrawTranslucentLineState(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,PRectangle rcLine,int subLine)1605 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1606 	Sci::Line line, PRectangle rcLine, int subLine) {
1607 	if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret &&
1608 		vsDraw.caretLineAlpha != SC_ALPHA_NOALPHA) {
1609 		if (vsDraw.caretLineFrame) {
1610 			DrawCaretLineFramed(surface, vsDraw, ll, rcLine, subLine);
1611 		} else {
1612 			SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1613 		}
1614 	}
1615 	const int marksOfLine = model.pdoc->GetMark(line);
1616 	int marksDrawnInText = marksOfLine & vsDraw.maskDrawInText;
1617 	for (int markBit = 0; (markBit < 32) && marksDrawnInText; markBit++) {
1618 		if (marksDrawnInText & 1) {
1619 			if (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) {
1620 				SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1621 			} else if (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) {
1622 				PRectangle rcUnderline = rcLine;
1623 				rcUnderline.top = rcUnderline.bottom - 2;
1624 				SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1625 			}
1626 		}
1627 		marksDrawnInText >>= 1;
1628 	}
1629 	int marksDrawnInLine = marksOfLine & vsDraw.maskInLine;
1630 	for (int markBit = 0; (markBit < 32) && marksDrawnInLine; markBit++) {
1631 		if (marksDrawnInLine & 1) {
1632 			SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1633 		}
1634 		marksDrawnInLine >>= 1;
1635 	}
1636 }
1637 
DrawForeground(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line lineVisible,PRectangle rcLine,Range lineRange,Sci::Position posLineStart,int xStart,int subLine,ColourOptional background)1638 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1639 	Sci::Line lineVisible, PRectangle rcLine, Range lineRange, Sci::Position posLineStart, int xStart,
1640 	int subLine, ColourOptional background) {
1641 
1642 	const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1643 	const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1644 	bool inIndentation = subLine == 0;	// Do not handle indentation except on first subline.
1645 
1646 	const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1647 	const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1648 
1649 	// Does not take margin into account but not significant
1650 	const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1651 
1652 	// Foreground drawing loop
1653 	BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1654 		(((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs, &vsDraw);
1655 
1656 	while (bfFore.More()) {
1657 
1658 		const TextSegment ts = bfFore.Next();
1659 		const Sci::Position i = ts.end() - 1;
1660 		const Sci::Position iDoc = i + posLineStart;
1661 
1662 		PRectangle rcSegment = rcLine;
1663 		rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1664 		rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1665 		// Only try to draw if really visible - enhances performance by not calling environment to
1666 		// draw strings that are completely past the right side of the window.
1667 		if (rcSegment.Intersects(rcLine)) {
1668 			const int styleMain = ll->styles[i];
1669 			ColourDesired textFore = vsDraw.styles[styleMain].fore;
1670 			FontAlias textFont = vsDraw.styles[styleMain].font;
1671 			//hotspot foreground
1672 			const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1673 			if (inHotspot) {
1674 				if (vsDraw.hotspotColours.fore.isSet)
1675 					textFore = vsDraw.hotspotColours.fore;
1676 			}
1677 			if (vsDraw.indicatorsSetFore) {
1678 				// At least one indicator sets the text colour so see if it applies to this segment
1679 				for (const IDecoration *deco : model.pdoc->decorations->View()) {
1680 					const int indicatorValue = deco->ValueAt(ts.start + posLineStart);
1681 					if (indicatorValue) {
1682 						const Indicator &indicator = vsDraw.indicators[deco->Indicator()];
1683 						const bool hover = indicator.IsDynamic() &&
1684 							((model.hoverIndicatorPos >= ts.start + posLineStart) &&
1685 							(model.hoverIndicatorPos <= ts.end() + posLineStart));
1686 						if (hover) {
1687 							if (indicator.sacHover.style == INDIC_TEXTFORE) {
1688 								textFore = indicator.sacHover.fore;
1689 							}
1690 						} else {
1691 							if (indicator.sacNormal.style == INDIC_TEXTFORE) {
1692 								if (indicator.Flags() & SC_INDICFLAG_VALUEFORE)
1693 									textFore = ColourDesired(indicatorValue & SC_INDICVALUEMASK);
1694 								else
1695 									textFore = indicator.sacNormal.fore;
1696 							}
1697 						}
1698 					}
1699 				}
1700 			}
1701 			const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1702 			if (inSelection && (vsDraw.selColours.fore.isSet)) {
1703 				textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1704 			}
1705 			ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1706 			if (ts.representation) {
1707 				if (ll->chars[i] == '\t') {
1708 					// Tab display
1709 					if (phasesDraw == phasesOne) {
1710 						if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1711 							textBack = vsDraw.whitespaceColours.back;
1712 						surface->FillRectangle(rcSegment, textBack);
1713 					}
1714 					if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1715 						for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1716 							indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1717 							indentCount++) {
1718 							if (indentCount > 0) {
1719 								const XYPOSITION xIndent = std::floor(indentCount * indentWidth);
1720 								DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1721 									(ll->xHighlightGuide == xIndent));
1722 							}
1723 						}
1724 					}
1725 					if (vsDraw.viewWhitespace != wsInvisible) {
1726 						if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1727 							if (vsDraw.whitespaceColours.fore.isSet)
1728 								textFore = vsDraw.whitespaceColours.fore;
1729 							surface->PenColour(textFore);
1730 							const PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,
1731 								rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1732 							const int segmentTop = static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2);
1733 							if (!customDrawTabArrow)
1734 								DrawTabArrow(surface, rcTab, segmentTop, vsDraw);
1735 							else
1736 								customDrawTabArrow(surface, rcTab, segmentTop);
1737 						}
1738 					}
1739 				} else {
1740 					inIndentation = false;
1741 					if (vsDraw.controlCharSymbol >= 32) {
1742 						// Using one font for all control characters so it can be controlled independently to ensure
1743 						// the box goes around the characters tightly. Seems to be no way to work out what height
1744 						// is taken by an individual character - internal leading gives varying results.
1745 						FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1746 						const char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1747 						surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1748 							rcSegment.top + vsDraw.maxAscent,
1749 							cc, 1, textBack, textFore);
1750 					} else {
1751 						DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1752 							textBack, textFore, phasesDraw == phasesOne);
1753 					}
1754 				}
1755 			} else {
1756 				// Normal text display
1757 				if (vsDraw.styles[styleMain].visible) {
1758 					if (phasesDraw != phasesOne) {
1759 						surface->DrawTextTransparent(rcSegment, textFont,
1760 							rcSegment.top + vsDraw.maxAscent, &ll->chars[ts.start],
1761 							static_cast<int>(i - ts.start + 1), textFore);
1762 					} else {
1763 						surface->DrawTextNoClip(rcSegment, textFont,
1764 							rcSegment.top + vsDraw.maxAscent, &ll->chars[ts.start],
1765 							static_cast<int>(i - ts.start + 1), textFore, textBack);
1766 					}
1767 				}
1768 				if (vsDraw.viewWhitespace != wsInvisible ||
1769 					(inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1770 					for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1771 						if (ll->chars[cpos + ts.start] == ' ') {
1772 							if (vsDraw.viewWhitespace != wsInvisible) {
1773 								if (vsDraw.whitespaceColours.fore.isSet)
1774 									textFore = vsDraw.whitespaceColours.fore;
1775 								if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1776 									const XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1777 									if ((phasesDraw == phasesOne) && drawWhitespaceBackground) {
1778 										textBack = vsDraw.whitespaceColours.back;
1779 										const PRectangle rcSpace(
1780 											ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1781 											rcSegment.top,
1782 											ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1783 											rcSegment.bottom);
1784 										surface->FillRectangle(rcSpace, textBack);
1785 									}
1786 									const int halfDotWidth = vsDraw.whitespaceSize / 2;
1787 									PRectangle rcDot(xmid + xStart - halfDotWidth - static_cast<XYPOSITION>(subLineStart),
1788 										rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1789 									rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1790 									rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1791 									surface->FillRectangle(rcDot, textFore);
1792 								}
1793 							}
1794 							if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1795 								for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1796 									indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1797 									indentCount++) {
1798 									if (indentCount > 0) {
1799 										const XYPOSITION xIndent = std::floor(indentCount * indentWidth);
1800 										DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1801 											(ll->xHighlightGuide == xIndent));
1802 									}
1803 								}
1804 							}
1805 						} else {
1806 							inIndentation = false;
1807 						}
1808 					}
1809 				}
1810 			}
1811 			if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1812 				PRectangle rcUL = rcSegment;
1813 				rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1814 				rcUL.bottom = rcUL.top + 1;
1815 				if (vsDraw.hotspotColours.fore.isSet)
1816 					surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1817 				else
1818 					surface->FillRectangle(rcUL, textFore);
1819 			} else if (vsDraw.styles[styleMain].underline) {
1820 				PRectangle rcUL = rcSegment;
1821 				rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1822 				rcUL.bottom = rcUL.top + 1;
1823 				surface->FillRectangle(rcUL, textFore);
1824 			}
1825 		} else if (rcSegment.left > rcLine.right) {
1826 			break;
1827 		}
1828 	}
1829 }
1830 
DrawIndentGuidesOverEmpty(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,Sci::Line lineVisible,PRectangle rcLine,int xStart,int subLine)1831 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1832 	Sci::Line line, Sci::Line lineVisible, PRectangle rcLine, int xStart, int subLine) {
1833 	if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1834 		&& (subLine == 0)) {
1835 		const Sci::Position posLineStart = model.pdoc->LineStart(line);
1836 		int indentSpace = model.pdoc->GetLineIndentation(line);
1837 		int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1838 
1839 		// Find the most recent line with some text
1840 
1841 		Sci::Line lineLastWithText = line;
1842 		while (lineLastWithText > std::max(line - 20, static_cast<Sci::Line>(0)) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1843 			lineLastWithText--;
1844 		}
1845 		if (lineLastWithText < line) {
1846 			xStartText = 100000;	// Don't limit to visible indentation on empty line
1847 			// This line is empty, so use indentation of last line with text
1848 			int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1849 			const int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1850 			if (isFoldHeader) {
1851 				// Level is one more level than parent
1852 				indentLastWithText += model.pdoc->IndentSize();
1853 			}
1854 			if (vsDraw.viewIndentationGuides == ivLookForward) {
1855 				// In viLookForward mode, previous line only used if it is a fold header
1856 				if (isFoldHeader) {
1857 					indentSpace = std::max(indentSpace, indentLastWithText);
1858 				}
1859 			} else {	// viLookBoth
1860 				indentSpace = std::max(indentSpace, indentLastWithText);
1861 			}
1862 		}
1863 
1864 		Sci::Line lineNextWithText = line;
1865 		while (lineNextWithText < std::min(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1866 			lineNextWithText++;
1867 		}
1868 		if (lineNextWithText > line) {
1869 			xStartText = 100000;	// Don't limit to visible indentation on empty line
1870 			// This line is empty, so use indentation of first next line with text
1871 			indentSpace = std::max(indentSpace,
1872 				model.pdoc->GetLineIndentation(lineNextWithText));
1873 		}
1874 
1875 		for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1876 			const XYPOSITION xIndent = std::floor(indentPos * vsDraw.spaceWidth);
1877 			if (xIndent < xStartText) {
1878 				DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1879 					(ll->xHighlightGuide == xIndent));
1880 			}
1881 		}
1882 	}
1883 }
1884 
DrawLine(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,Sci::Line lineVisible,int xStart,PRectangle rcLine,int subLine,DrawPhase phase)1885 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1886 	Sci::Line line, Sci::Line lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1887 
1888 	if (subLine >= ll->lines) {
1889 		DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1890 		return; // No further drawing
1891 	}
1892 
1893 	// See if something overrides the line background colour.
1894 	const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1895 
1896 	const Sci::Position posLineStart = model.pdoc->LineStart(line);
1897 
1898 	const Range lineRange = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly);
1899 	const Range lineRangeIncludingEnd = ll->SubLineRange(subLine, LineLayout::Scope::includeEnd);
1900 	const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1901 
1902 	if ((ll->wrapIndent != 0) && (subLine > 0)) {
1903 		if (phase & drawBack) {
1904 			DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker, model.caret.active);
1905 		}
1906 		xStart += static_cast<int>(ll->wrapIndent);
1907 	}
1908 
1909 	if (phasesDraw != phasesOne) {
1910 		if (phase & drawBack) {
1911 			DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1912 				subLine, background);
1913 			DrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, drawBack);
1914 			phase = static_cast<DrawPhase>(phase & ~drawBack);	// Remove drawBack to not draw again in DrawFoldDisplayText
1915 			DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1916 				xStart, subLine, subLineStart, background);
1917 			if (vsDraw.IsLineFrameOpaque(model.caret.active, ll->containsCaret))
1918 				DrawCaretLineFramed(surface, vsDraw, ll, rcLine, subLine);
1919 		}
1920 
1921 		if (phase & drawIndicatorsBack) {
1922 			DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRangeIncludingEnd.end, true, model.hoverIndicatorPos);
1923 			DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1924 			DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1925 		}
1926 	}
1927 
1928 	if (phase & drawText) {
1929 		DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1930 			subLine, background);
1931 	}
1932 
1933 	if (phase & drawIndentationGuides) {
1934 		DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1935 	}
1936 
1937 	if (phase & drawIndicatorsFore) {
1938 		DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRangeIncludingEnd.end, false, model.hoverIndicatorPos);
1939 	}
1940 
1941 	DrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, phase);
1942 
1943 	if (phasesDraw == phasesOne) {
1944 		DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1945 			xStart, subLine, subLineStart, background);
1946 		if (vsDraw.IsLineFrameOpaque(model.caret.active, ll->containsCaret))
1947 			DrawCaretLineFramed(surface, vsDraw, ll, rcLine, subLine);
1948 		DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1949 		DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1950 	}
1951 
1952 	if (!hideSelection && (phase & drawSelectionTranslucent)) {
1953 		DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1954 	}
1955 
1956 	if (phase & drawLineTranslucent) {
1957 		DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine, subLine);
1958 	}
1959 }
1960 
DrawFoldLines(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,Sci::Line line,PRectangle rcLine)1961 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, Sci::Line line, PRectangle rcLine) {
1962 	const bool expanded = model.pcs->GetExpanded(line);
1963 	const int level = model.pdoc->GetLevel(line);
1964 	const int levelNext = model.pdoc->GetLevel(line + 1);
1965 	if ((level & SC_FOLDLEVELHEADERFLAG) &&
1966 		(LevelNumber(level) < LevelNumber(levelNext))) {
1967 		// Paint the line above the fold
1968 		if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1969 			||
1970 			(!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1971 			PRectangle rcFoldLine = rcLine;
1972 			rcFoldLine.bottom = rcFoldLine.top + 1;
1973 			surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1974 		}
1975 		// Paint the line below the fold
1976 		if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1977 			||
1978 			(!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1979 			PRectangle rcFoldLine = rcLine;
1980 			rcFoldLine.top = rcFoldLine.bottom - 1;
1981 			surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1982 		}
1983 	}
1984 }
1985 
PaintText(Surface * surfaceWindow,const EditModel & model,PRectangle rcArea,PRectangle rcClient,const ViewStyle & vsDraw)1986 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1987 	PRectangle rcClient, const ViewStyle &vsDraw) {
1988 	// Allow text at start of line to overlap 1 pixel into the margin as this displays
1989 	// serifs and italic stems for aliased text.
1990 	const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1991 
1992 	// Do the painting
1993 	if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1994 
1995 		Surface *surface = surfaceWindow;
1996 		if (bufferedDraw) {
1997 			surface = pixmapLine.get();
1998 			PLATFORM_ASSERT(pixmapLine->Initialised());
1999 		}
2000 		surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
2001 		surface->SetDBCSMode(model.pdoc->dbcsCodePage);
2002 
2003 		const Point ptOrigin = model.GetVisibleOriginInMain();
2004 
2005 		const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
2006 		const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
2007 
2008 		SelectionPosition posCaret = model.sel.RangeMain().caret;
2009 		if (model.posDrag.IsValid())
2010 			posCaret = model.posDrag;
2011 		const Sci::Line lineCaret = model.pdoc->SciLineFromPosition(posCaret.Position());
2012 
2013 		PRectangle rcTextArea = rcClient;
2014 		if (vsDraw.marginInside) {
2015 			rcTextArea.left += vsDraw.textStart;
2016 			rcTextArea.right -= vsDraw.rightMarginWidth;
2017 		} else {
2018 			rcTextArea = rcArea;
2019 		}
2020 
2021 		// Remove selection margin from drawing area so text will not be drawn
2022 		// on it in unbuffered mode.
2023 		if (!bufferedDraw && vsDraw.marginInside) {
2024 			PRectangle rcClipText = rcTextArea;
2025 			rcClipText.left -= leftTextOverlap;
2026 			surfaceWindow->SetClip(rcClipText);
2027 		}
2028 
2029 		// Loop on visible lines
2030 #if defined(TIME_PAINTING)
2031 		double durLayout = 0.0;
2032 		double durPaint = 0.0;
2033 		double durCopy = 0.0;
2034 		ElapsedPeriod epWhole;
2035 #endif
2036 		const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
2037 			(vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
2038 
2039 		Sci::Line lineDocPrevious = -1;	// Used to avoid laying out one document line multiple times
2040 		AutoLineLayout ll(llc, nullptr);
2041 		std::vector<DrawPhase> phases;
2042 		if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
2043 			for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
2044 				phases.push_back(phase);
2045 			}
2046 		} else {
2047 			phases.push_back(drawAll);
2048 		}
2049 		for (const DrawPhase &phase : phases) {
2050 			int ypos = 0;
2051 			if (!bufferedDraw)
2052 				ypos += screenLinePaintFirst * vsDraw.lineHeight;
2053 			int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
2054 			Sci::Line visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
2055 			while (visibleLine < model.pcs->LinesDisplayed() && yposScreen < rcArea.bottom) {
2056 
2057 				const Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);
2058 				// Only visible lines should be handled by the code within the loop
2059 				PLATFORM_ASSERT(model.pcs->GetVisible(lineDoc));
2060 				const Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc);
2061 				const int subLine = static_cast<int>(visibleLine - lineStartSet);
2062 
2063 				// Copy this line and its styles from the document into local arrays
2064 				// and determine the x position at which each character starts.
2065 #if defined(TIME_PAINTING)
2066 				ElapsedPeriod ep;
2067 #endif
2068 				if (lineDoc != lineDocPrevious) {
2069 					ll.Set(nullptr);
2070 					ll.Set(RetrieveLineLayout(lineDoc, model));
2071 					LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
2072 					lineDocPrevious = lineDoc;
2073 				}
2074 #if defined(TIME_PAINTING)
2075 				durLayout += ep.Duration(true);
2076 #endif
2077 				if (ll) {
2078 					ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
2079 					ll->hotspot = model.GetHotSpotRange();
2080 
2081 					PRectangle rcLine = rcTextArea;
2082 					rcLine.top = static_cast<XYPOSITION>(ypos);
2083 					rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
2084 
2085 					const Range rangeLine(model.pdoc->LineStart(lineDoc),
2086 						model.pdoc->LineStart(lineDoc + 1));
2087 
2088 					// Highlight the current braces if any
2089 					ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
2090 						static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
2091 
2092 					if (leftTextOverlap && (bufferedDraw || ((phasesDraw < phasesMultiple) && (phase & drawBack)))) {
2093 						// Clear the left margin
2094 						PRectangle rcSpacer = rcLine;
2095 						rcSpacer.right = rcSpacer.left;
2096 						rcSpacer.left -= 1;
2097 						surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
2098 					}
2099 
2100 					DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, phase);
2101 #if defined(TIME_PAINTING)
2102 					durPaint += ep.Duration(true);
2103 #endif
2104 					// Restore the previous styles for the brace highlights in case layout is in cache.
2105 					ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
2106 
2107 					if (phase & drawFoldLines) {
2108 						DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
2109 					}
2110 
2111 					if (phase & drawCarets) {
2112 						DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
2113 					}
2114 
2115 					if (bufferedDraw) {
2116 						const Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
2117 						const PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
2118 							static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
2119 							yposScreen + vsDraw.lineHeight);
2120 						surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
2121 					}
2122 
2123 					lineWidthMaxSeen = std::max(
2124 						lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
2125 #if defined(TIME_PAINTING)
2126 					durCopy += ep.Duration(true);
2127 #endif
2128 				}
2129 
2130 				if (!bufferedDraw) {
2131 					ypos += vsDraw.lineHeight;
2132 				}
2133 
2134 				yposScreen += vsDraw.lineHeight;
2135 				visibleLine++;
2136 			}
2137 		}
2138 		ll.Set(nullptr);
2139 #if defined(TIME_PAINTING)
2140 		if (durPaint < 0.00000001)
2141 			durPaint = 0.00000001;
2142 #endif
2143 		// Right column limit indicator
2144 		PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
2145 		rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
2146 		rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
2147 		rcBeyondEOF.top = static_cast<XYPOSITION>((model.pcs->LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
2148 		if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
2149 			surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
2150 			if (vsDraw.edgeState == EDGE_LINE) {
2151 				const int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);
2152 				rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
2153 				rcBeyondEOF.right = rcBeyondEOF.left + 1;
2154 				surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.theEdge.colour);
2155 			} else if (vsDraw.edgeState == EDGE_MULTILINE) {
2156 				for (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {
2157 					if (vsDraw.theMultiEdge[edge].column >= 0) {
2158 						const int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);
2159 						rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
2160 						rcBeyondEOF.right = rcBeyondEOF.left + 1;
2161 						surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.theMultiEdge[edge].colour);
2162 					}
2163 				}
2164 			}
2165 		}
2166 		//Platform::DebugPrintf("start display %d, offset = %d\n", model.pdoc->Length(), model.xOffset);
2167 #if defined(TIME_PAINTING)
2168 		Platform::DebugPrintf(
2169 		"Layout:%9.6g    Paint:%9.6g    Ratio:%9.6g   Copy:%9.6g   Total:%9.6g\n",
2170 		durLayout, durPaint, durLayout / durPaint, durCopy, epWhole.Duration());
2171 #endif
2172 	}
2173 }
2174 
FillLineRemainder(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,Sci::Line line,PRectangle rcArea,int subLine) const2175 void EditView::FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
2176 	Sci::Line line, PRectangle rcArea, int subLine) const {
2177 		int eolInSelection = 0;
2178 		int alpha = SC_ALPHA_NOALPHA;
2179 		if (!hideSelection) {
2180 			const Sci::Position posAfterLineEnd = model.pdoc->LineStart(line + 1);
2181 			eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
2182 			alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2183 		}
2184 
2185 		const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
2186 
2187 		if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2188 			surface->FillRectangle(rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
2189 		} else {
2190 			if (background.isSet) {
2191 				surface->FillRectangle(rcArea, background);
2192 			} else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
2193 				surface->FillRectangle(rcArea, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
2194 			} else {
2195 				surface->FillRectangle(rcArea, vsDraw.styles[STYLE_DEFAULT].back);
2196 			}
2197 			if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2198 				SimpleAlphaRectangle(surface, rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
2199 			}
2200 		}
2201 }
2202 
2203 // Space (3 space characters) between line numbers and text when printing.
2204 #define lineNumberPrintSpace "   "
2205 
InvertedLight(ColourDesired orig)2206 static ColourDesired InvertedLight(ColourDesired orig) noexcept {
2207 	unsigned int r = orig.GetRed();
2208 	unsigned int g = orig.GetGreen();
2209 	unsigned int b = orig.GetBlue();
2210 	const unsigned int l = (r + g + b) / 3; 	// There is a better calculation for this that matches human eye
2211 	const unsigned int il = 0xff - l;
2212 	if (l == 0)
2213 		return ColourDesired(0xff, 0xff, 0xff);
2214 	r = r * il / l;
2215 	g = g * il / l;
2216 	b = b * il / l;
2217 	return ColourDesired(std::min(r, 0xffu), std::min(g, 0xffu), std::min(b, 0xffu));
2218 }
2219 
FormatRange(bool draw,const Sci_RangeToFormat * pfr,Surface * surface,Surface * surfaceMeasure,const EditModel & model,const ViewStyle & vs)2220 Sci::Position EditView::FormatRange(bool draw, const Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
2221 	const EditModel &model, const ViewStyle &vs) {
2222 	// Can't use measurements cached for screen
2223 	posCache.Clear();
2224 
2225 	ViewStyle vsPrint(vs);
2226 	vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
2227 
2228 	// Modify the view style for printing as do not normally want any of the transient features to be printed
2229 	// Printing supports only the line number margin.
2230 	int lineNumberIndex = -1;
2231 	for (size_t margin = 0; margin < vs.ms.size(); margin++) {
2232 		if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
2233 			lineNumberIndex = static_cast<int>(margin);
2234 		} else {
2235 			vsPrint.ms[margin].width = 0;
2236 		}
2237 	}
2238 	vsPrint.fixedColumnWidth = 0;
2239 	vsPrint.zoomLevel = printParameters.magnification;
2240 	// Don't show indentation guides
2241 	// If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
2242 	vsPrint.viewIndentationGuides = ivNone;
2243 	// Don't show the selection when printing
2244 	vsPrint.selColours.back.isSet = false;
2245 	vsPrint.selColours.fore.isSet = false;
2246 	vsPrint.selAlpha = SC_ALPHA_NOALPHA;
2247 	vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
2248 	vsPrint.whitespaceColours.back.isSet = false;
2249 	vsPrint.whitespaceColours.fore.isSet = false;
2250 	vsPrint.showCaretLineBackground = false;
2251 	vsPrint.alwaysShowCaretLineBackground = false;
2252 	// Don't highlight matching braces using indicators
2253 	vsPrint.braceHighlightIndicatorSet = false;
2254 	vsPrint.braceBadLightIndicatorSet = false;
2255 
2256 	// Set colours for printing according to users settings
2257 	for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
2258 		if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
2259 			vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
2260 			vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
2261 		} else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
2262 			vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
2263 			vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2264 		} else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
2265 			vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2266 		} else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
2267 			if (sty <= STYLE_DEFAULT) {
2268 				vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2269 			}
2270 		}
2271 	}
2272 	// White background for the line numbers if SC_PRINT_SCREENCOLOURS isn't used
2273 	if (printParameters.colourMode != SC_PRINT_SCREENCOLOURS)
2274 		vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
2275 
2276 	// Printing uses different margins, so reset screen margins
2277 	vsPrint.leftMarginWidth = 0;
2278 	vsPrint.rightMarginWidth = 0;
2279 
2280 	vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
2281 	// Determining width must happen after fonts have been realised in Refresh
2282 	int lineNumberWidth = 0;
2283 	if (lineNumberIndex >= 0) {
2284 		lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
2285 			"99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
2286 		vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
2287 		vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);	// Recalculate fixedColumnWidth
2288 	}
2289 
2290 	const Sci::Line linePrintStart =
2291 		model.pdoc->SciLineFromPosition(static_cast<Sci::Position>(pfr->chrg.cpMin));
2292 	Sci::Line linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
2293 	if (linePrintLast < linePrintStart)
2294 		linePrintLast = linePrintStart;
2295 	const Sci::Line linePrintMax =
2296 		model.pdoc->SciLineFromPosition(static_cast<Sci::Position>(pfr->chrg.cpMax));
2297 	if (linePrintLast > linePrintMax)
2298 		linePrintLast = linePrintMax;
2299 	//Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
2300 	//      linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
2301 	//      surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
2302 	Sci::Position endPosPrint = model.pdoc->Length();
2303 	if (linePrintLast < model.pdoc->LinesTotal())
2304 		endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
2305 
2306 	// Ensure we are styled to where we are formatting.
2307 	model.pdoc->EnsureStyledTo(endPosPrint);
2308 
2309 	const int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
2310 	int ypos = pfr->rc.top;
2311 
2312 	Sci::Line lineDoc = linePrintStart;
2313 
2314 	Sci::Position nPrintPos = static_cast<Sci::Position>(pfr->chrg.cpMin);
2315 	int visibleLine = 0;
2316 	int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
2317 	if (printParameters.wrapState == eWrapNone)
2318 		widthPrint = LineLayout::wrapWidthInfinite;
2319 
2320 	while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2321 
2322 		// When printing, the hdc and hdcTarget may be the same, so
2323 		// changing the state of surfaceMeasure may change the underlying
2324 		// state of surface. Therefore, any cached state is discarded before
2325 		// using each surface.
2326 		surfaceMeasure->FlushCachedState();
2327 
2328 		// Copy this line and its styles from the document into local arrays
2329 		// and determine the x position at which each character starts.
2330 		LineLayout ll(static_cast<int>(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1));
2331 		LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2332 
2333 		ll.containsCaret = false;
2334 
2335 		PRectangle rcLine = PRectangle::FromInts(
2336 			pfr->rc.left,
2337 			ypos,
2338 			pfr->rc.right - 1,
2339 			ypos + vsPrint.lineHeight);
2340 
2341 		// When document line is wrapped over multiple display lines, find where
2342 		// to start printing from to ensure a particular position is on the first
2343 		// line of the page.
2344 		if (visibleLine == 0) {
2345 			const Sci::Position startWithinLine = nPrintPos -
2346 				model.pdoc->LineStart(lineDoc);
2347 			for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2348 				if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2349 					visibleLine = -iwl;
2350 				}
2351 			}
2352 
2353 			if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2354 				visibleLine = -(ll.lines - 1);
2355 			}
2356 		}
2357 
2358 		if (draw && lineNumberWidth &&
2359 			(ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2360 			(visibleLine >= 0)) {
2361 			const std::string number = std::to_string(lineDoc + 1) + lineNumberPrintSpace;
2362 			PRectangle rcNumber = rcLine;
2363 			rcNumber.right = rcNumber.left + lineNumberWidth;
2364 			// Right justify
2365 			rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2366 				vsPrint.styles[STYLE_LINENUMBER].font, number.c_str(), static_cast<int>(number.length()));
2367 			surface->FlushCachedState();
2368 			surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2369 				static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number.c_str(), static_cast<int>(number.length()),
2370 				vsPrint.styles[STYLE_LINENUMBER].fore,
2371 				vsPrint.styles[STYLE_LINENUMBER].back);
2372 		}
2373 
2374 		// Draw the line
2375 		surface->FlushCachedState();
2376 
2377 		for (int iwl = 0; iwl < ll.lines; iwl++) {
2378 			if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2379 				if (visibleLine >= 0) {
2380 					if (draw) {
2381 						rcLine.top = static_cast<XYPOSITION>(ypos);
2382 						rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2383 						DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2384 					}
2385 					ypos += vsPrint.lineHeight;
2386 				}
2387 				visibleLine++;
2388 				if (iwl == ll.lines - 1)
2389 					nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2390 				else
2391 					nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2392 			}
2393 		}
2394 
2395 		++lineDoc;
2396 	}
2397 
2398 	// Clear cache so measurements are not used for screen
2399 	posCache.Clear();
2400 
2401 	return nPrintPos;
2402 }
2403