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