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