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