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