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