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