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