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