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