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