Update Scintilla to 3.7.2
[TortoiseGit.git] / ext / scintilla / src / EditView.cxx
blobfbe02e00ab9863bf4f8e93c555910b2e74a9ece9
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"
53 #ifdef SCI_NAMESPACE
54 using namespace Scintilla;
55 #endif
57 static inline bool IsControlCharacter(int ch) {
58 // iscntrl returns true for lots of chars > 127 which are displayable
59 return ch >= 0 && ch < ' ';
62 PrintParameters::PrintParameters() {
63 magnification = 0;
64 colourMode = SC_PRINT_NORMAL;
65 wrapState = eWrapWord;
68 #ifdef SCI_NAMESPACE
69 namespace Scintilla {
70 #endif
72 bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) {
73 if (st.multipleStyles) {
74 for (size_t iStyle = 0; iStyle<st.length; iStyle++) {
75 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
76 return false;
78 } else {
79 if (!vs.ValidStyle(styleOffset + st.style))
80 return false;
82 return true;
85 static int WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset,
86 const char *text, const unsigned char *styles, size_t len) {
87 int width = 0;
88 size_t start = 0;
89 while (start < len) {
90 size_t style = styles[start];
91 size_t endSegment = start;
92 while ((endSegment + 1 < len) && (static_cast<size_t>(styles[endSegment + 1]) == style))
93 endSegment++;
94 FontAlias fontText = vs.styles[style + styleOffset].font;
95 width += static_cast<int>(surface->WidthText(fontText, text + start,
96 static_cast<int>(endSegment - start + 1)));
97 start = endSegment + 1;
99 return width;
102 int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) {
103 int widthMax = 0;
104 size_t start = 0;
105 while (start < st.length) {
106 size_t lenLine = st.LineLength(start);
107 int widthSubLine;
108 if (st.multipleStyles) {
109 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
110 } else {
111 FontAlias fontText = vs.styles[styleOffset + st.style].font;
112 widthSubLine = static_cast<int>(surface->WidthText(fontText,
113 st.text + start, static_cast<int>(lenLine)));
115 if (widthSubLine > widthMax)
116 widthMax = widthSubLine;
117 start += lenLine + 1;
119 return widthMax;
122 void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,
123 const char *s, int len, DrawPhase phase) {
124 FontAlias fontText = style.font;
125 if (phase & drawBack) {
126 if (phase & drawText) {
127 // Drawing both
128 surface->DrawTextNoClip(rc, fontText, ybase, s, len,
129 style.fore, style.back);
130 } else {
131 surface->FillRectangle(rc, style.back);
133 } else if (phase & drawText) {
134 surface->DrawTextTransparent(rc, fontText, ybase, s, len, style.fore);
138 void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
139 const StyledText &st, size_t start, size_t length, DrawPhase phase) {
141 if (st.multipleStyles) {
142 int x = static_cast<int>(rcText.left);
143 size_t i = 0;
144 while (i < length) {
145 size_t end = i;
146 size_t style = st.styles[i + start];
147 while (end < length - 1 && st.styles[start + end + 1] == style)
148 end++;
149 style += styleOffset;
150 FontAlias fontText = vs.styles[style].font;
151 const int width = static_cast<int>(surface->WidthText(fontText,
152 st.text + start + i, static_cast<int>(end - i + 1)));
153 PRectangle rcSegment = rcText;
154 rcSegment.left = static_cast<XYPOSITION>(x);
155 rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
156 DrawTextNoClipPhase(surface, rcSegment, vs.styles[style],
157 rcText.top + vs.maxAscent, st.text + start + i,
158 static_cast<int>(end - i + 1), phase);
159 x += width;
160 i = end + 1;
162 } else {
163 const size_t style = st.style + styleOffset;
164 DrawTextNoClipPhase(surface, rcText, vs.styles[style],
165 rcText.top + vs.maxAscent, st.text + start,
166 static_cast<int>(length), phase);
170 #ifdef SCI_NAMESPACE
172 #endif
174 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
176 EditView::EditView() {
177 ldTabstops = NULL;
178 tabWidthMinimumPixels = 2; // needed for calculating tab stops for fractional proportional fonts
179 hideSelection = false;
180 drawOverstrikeCaret = true;
181 bufferedDraw = true;
182 phasesDraw = phasesTwo;
183 lineWidthMaxSeen = 0;
184 additionalCaretsBlink = true;
185 additionalCaretsVisible = true;
186 imeCaretBlockOverride = false;
187 pixmapLine = 0;
188 pixmapIndentGuide = 0;
189 pixmapIndentGuideHighlight = 0;
190 llc.SetLevel(LineLayoutCache::llcCaret);
191 posCache.SetSize(0x400);
192 tabArrowHeight = 4;
193 customDrawTabArrow = NULL;
194 customDrawWrapMarker = NULL;
197 EditView::~EditView() {
198 delete ldTabstops;
199 ldTabstops = NULL;
202 bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) {
203 const PhasesDraw phasesDrawNew = twoPhaseDraw ? phasesTwo : phasesOne;
204 const bool redraw = phasesDraw != phasesDrawNew;
205 phasesDraw = phasesDrawNew;
206 return redraw;
209 bool EditView::SetPhasesDraw(int phases) {
210 const PhasesDraw phasesDrawNew = static_cast<PhasesDraw>(phases);
211 const bool redraw = phasesDraw != phasesDrawNew;
212 phasesDraw = phasesDrawNew;
213 return redraw;
216 bool EditView::LinesOverlap() const {
217 return phasesDraw == phasesMultiple;
220 void EditView::ClearAllTabstops() {
221 delete ldTabstops;
222 ldTabstops = 0;
225 XYPOSITION EditView::NextTabstopPos(int line, XYPOSITION x, XYPOSITION tabWidth) const {
226 int next = GetNextTabstop(line, static_cast<int>(x + tabWidthMinimumPixels));
227 if (next > 0)
228 return static_cast<XYPOSITION>(next);
229 return (static_cast<int>((x + tabWidthMinimumPixels) / tabWidth) + 1) * tabWidth;
232 bool EditView::ClearTabstops(int line) {
233 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
234 return lt && lt->ClearTabstops(line);
237 bool EditView::AddTabstop(int line, int x) {
238 if (!ldTabstops) {
239 ldTabstops = new LineTabstops();
241 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
242 return lt && lt->AddTabstop(line, x);
245 int EditView::GetNextTabstop(int line, int x) const {
246 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
247 if (lt) {
248 return lt->GetNextTabstop(line, x);
249 } else {
250 return 0;
254 void EditView::LinesAddedOrRemoved(int lineOfPos, int linesAdded) {
255 if (ldTabstops) {
256 if (linesAdded > 0) {
257 for (int line = lineOfPos; line < lineOfPos + linesAdded; line++) {
258 ldTabstops->InsertLine(line);
260 } else {
261 for (int line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {
262 ldTabstops->RemoveLine(line);
268 void EditView::DropGraphics(bool freeObjects) {
269 if (freeObjects) {
270 delete pixmapLine;
271 pixmapLine = 0;
272 delete pixmapIndentGuide;
273 pixmapIndentGuide = 0;
274 delete pixmapIndentGuideHighlight;
275 pixmapIndentGuideHighlight = 0;
276 } else {
277 if (pixmapLine)
278 pixmapLine->Release();
279 if (pixmapIndentGuide)
280 pixmapIndentGuide->Release();
281 if (pixmapIndentGuideHighlight)
282 pixmapIndentGuideHighlight->Release();
286 void EditView::AllocateGraphics(const ViewStyle &vsDraw) {
287 if (!pixmapLine)
288 pixmapLine = Surface::Allocate(vsDraw.technology);
289 if (!pixmapIndentGuide)
290 pixmapIndentGuide = Surface::Allocate(vsDraw.technology);
291 if (!pixmapIndentGuideHighlight)
292 pixmapIndentGuideHighlight = Surface::Allocate(vsDraw.technology);
295 static const char *ControlCharacterString(unsigned char ch) {
296 const char *reps[] = {
297 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
298 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
299 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
300 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
302 if (ch < ELEMENTS(reps)) {
303 return reps[ch];
304 } else {
305 return "BAD";
309 static void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid, const ViewStyle &vsDraw) {
310 if ((rcTab.left + 2) < (rcTab.right - 1))
311 surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
312 else
313 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
314 surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
316 // Draw the arrow head if needed
317 if (vsDraw.tabDrawMode == tdLongArrow) {
318 int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
319 int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
320 if (xhead <= rcTab.left) {
321 ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
322 xhead = static_cast<int>(rcTab.left) - 1;
324 surface->LineTo(xhead, ymid - ydiff);
325 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
326 surface->LineTo(xhead, ymid + ydiff);
330 void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
331 if (!pixmapIndentGuide->Initialised()) {
332 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
333 pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
334 pixmapIndentGuideHighlight->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
335 PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight);
336 pixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[STYLE_INDENTGUIDE].back);
337 pixmapIndentGuide->PenColour(vsDraw.styles[STYLE_INDENTGUIDE].fore);
338 pixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[STYLE_BRACELIGHT].back);
339 pixmapIndentGuideHighlight->PenColour(vsDraw.styles[STYLE_BRACELIGHT].fore);
340 for (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) {
341 PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
342 pixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[STYLE_INDENTGUIDE].fore);
343 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[STYLE_BRACELIGHT].fore);
348 LineLayout *EditView::RetrieveLineLayout(int lineNumber, const EditModel &model) {
349 int posLineStart = model.pdoc->LineStart(lineNumber);
350 int posLineEnd = model.pdoc->LineStart(lineNumber + 1);
351 PLATFORM_ASSERT(posLineEnd >= posLineStart);
352 int lineCaret = model.pdoc->LineFromPosition(model.sel.MainCaret());
353 return llc.Retrieve(lineNumber, lineCaret,
354 posLineEnd - posLineStart, model.pdoc->GetStyleClock(),
355 model.LinesOnScreen() + 1, model.pdoc->LinesTotal());
359 * Fill in the LineLayout data for the given line.
360 * Copy the given @a line and its styles from the document into local arrays.
361 * Also determine the x position at which each character starts.
363 void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
364 if (!ll)
365 return;
367 PLATFORM_ASSERT(line < model.pdoc->LinesTotal());
368 PLATFORM_ASSERT(ll->chars != NULL);
369 int posLineStart = model.pdoc->LineStart(line);
370 int posLineEnd = model.pdoc->LineStart(line + 1);
371 // If the line is very long, limit the treatment to a length that should fit in the viewport
372 if (posLineEnd >(posLineStart + ll->maxLineLength)) {
373 posLineEnd = posLineStart + ll->maxLineLength;
375 if (ll->validity == LineLayout::llCheckTextAndStyle) {
376 int lineLength = posLineEnd - posLineStart;
377 if (!vstyle.viewEOL) {
378 lineLength = model.pdoc->LineEnd(line) - posLineStart;
380 if (lineLength == ll->numCharsInLine) {
381 // See if chars, styles, indicators, are all the same
382 bool allSame = true;
383 // Check base line layout
384 int styleByte = 0;
385 int numCharsInLine = 0;
386 while (numCharsInLine < lineLength) {
387 int charInDoc = numCharsInLine + posLineStart;
388 char chDoc = model.pdoc->CharAt(charInDoc);
389 styleByte = model.pdoc->StyleIndexAt(charInDoc);
390 allSame = allSame &&
391 (ll->styles[numCharsInLine] == styleByte);
392 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
393 allSame = allSame &&
394 (ll->chars[numCharsInLine] == chDoc);
395 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
396 allSame = allSame &&
397 (ll->chars[numCharsInLine] == MakeLowerCase(chDoc));
398 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
399 allSame = allSame &&
400 (ll->chars[numCharsInLine] == MakeUpperCase(chDoc));
401 else { // Style::caseCamel
402 if ((model.pdoc->IsASCIIWordByte(ll->chars[numCharsInLine])) &&
403 ((numCharsInLine == 0) || (!model.pdoc->IsASCIIWordByte(ll->chars[numCharsInLine - 1])))) {
404 allSame = allSame && (ll->chars[numCharsInLine] == MakeUpperCase(chDoc));
405 } else {
406 allSame = allSame && (ll->chars[numCharsInLine] == MakeLowerCase(chDoc));
409 numCharsInLine++;
411 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
412 if (allSame) {
413 ll->validity = LineLayout::llPositions;
414 } else {
415 ll->validity = LineLayout::llInvalid;
417 } else {
418 ll->validity = LineLayout::llInvalid;
421 if (ll->validity == LineLayout::llInvalid) {
422 ll->widthLine = LineLayout::wrapWidthInfinite;
423 ll->lines = 1;
424 if (vstyle.edgeState == EDGE_BACKGROUND) {
425 ll->edgeColumn = model.pdoc->FindColumn(line, vstyle.theEdge.column);
426 if (ll->edgeColumn >= posLineStart) {
427 ll->edgeColumn -= posLineStart;
429 } else {
430 ll->edgeColumn = -1;
433 // Fill base line layout
434 const int lineLength = posLineEnd - posLineStart;
435 model.pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
436 model.pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
437 int numCharsBeforeEOL = model.pdoc->LineEnd(line) - posLineStart;
438 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
439 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
440 const unsigned char styleByte = ll->styles[styleInLine];
441 ll->styles[styleInLine] = styleByte;
443 const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
444 if (vstyle.someStylesForceCase) {
445 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
446 char chDoc = ll->chars[charInLine];
447 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
448 ll->chars[charInLine] = static_cast<char>(MakeUpperCase(chDoc));
449 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
450 ll->chars[charInLine] = static_cast<char>(MakeLowerCase(chDoc));
451 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseCamel) {
452 if ((model.pdoc->IsASCIIWordByte(ll->chars[charInLine])) &&
453 ((charInLine == 0) || (!model.pdoc->IsASCIIWordByte(ll->chars[charInLine - 1])))) {
454 ll->chars[charInLine] = static_cast<char>(MakeUpperCase(chDoc));
455 } else {
456 ll->chars[charInLine] = static_cast<char>(MakeLowerCase(chDoc));
461 ll->xHighlightGuide = 0;
462 // Extra element at the end of the line to hold end x position and act as
463 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
464 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
466 // Layout the line, determining the position of each character,
467 // with an extra element at the end for the end of the line.
468 ll->positions[0] = 0;
469 bool lastSegItalics = false;
471 BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs, NULL);
472 while (bfLayout.More()) {
474 const TextSegment ts = bfLayout.Next();
476 std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f);
477 if (vstyle.styles[ll->styles[ts.start]].visible) {
478 if (ts.representation) {
479 XYPOSITION representationWidth = vstyle.controlCharWidth;
480 if (ll->chars[ts.start] == '\t') {
481 // Tab is a special case of representation, taking a variable amount of space
482 const XYPOSITION x = ll->positions[ts.start];
483 representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start];
484 } else {
485 if (representationWidth <= 0.0) {
486 XYPOSITION positionsRepr[256]; // Should expand when needed
487 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
488 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
489 representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
492 for (int ii = 0; ii < ts.length; ii++)
493 ll->positions[ts.start + 1 + ii] = representationWidth;
494 } else {
495 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
496 // Over half the segments are single characters and of these about half are space characters.
497 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
498 } else {
499 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
500 ts.length, ll->positions + ts.start + 1, model.pdoc);
503 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
506 for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
507 ll->positions[posToIncrease] += ll->positions[ts.start];
511 // Small hack to make lines that end with italics not cut off the edge of the last character
512 if (lastSegItalics) {
513 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
515 ll->numCharsInLine = numCharsInLine;
516 ll->numCharsBeforeEOL = numCharsBeforeEOL;
517 ll->validity = LineLayout::llPositions;
519 // Hard to cope when too narrow, so just assume there is space
520 if (width < 20) {
521 width = 20;
523 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
524 ll->widthLine = width;
525 if (width == LineLayout::wrapWidthInfinite) {
526 ll->lines = 1;
527 } else if (width > ll->positions[ll->numCharsInLine]) {
528 // Simple common case where line does not need wrapping.
529 ll->lines = 1;
530 } else {
531 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
532 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
534 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
535 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
536 wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
537 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
538 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
540 ll->wrapIndent = wrapAddIndent;
541 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
542 for (int i = 0; i < ll->numCharsInLine; i++) {
543 if (!IsSpaceOrTab(ll->chars[i])) {
544 ll->wrapIndent += ll->positions[i]; // Add line indent
545 break;
548 // Check for text width minimum
549 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
550 ll->wrapIndent = wrapAddIndent;
551 // Check for wrapIndent minimum
552 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
553 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
554 ll->lines = 0;
555 // Calculate line start positions based upon width.
556 int lastGoodBreak = 0;
557 int lastLineStart = 0;
558 XYACCUMULATOR startOffset = 0;
559 int p = 0;
560 while (p < ll->numCharsInLine) {
561 if ((ll->positions[p + 1] - startOffset) >= width) {
562 if (lastGoodBreak == lastLineStart) {
563 // Try moving to start of last character
564 if (p > 0) {
565 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
566 - posLineStart;
568 if (lastGoodBreak == lastLineStart) {
569 // Ensure at least one character on line.
570 lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
571 - posLineStart;
574 lastLineStart = lastGoodBreak;
575 ll->lines++;
576 ll->SetLineStart(ll->lines, lastGoodBreak);
577 startOffset = ll->positions[lastGoodBreak];
578 // take into account the space for start wrap mark and indent
579 startOffset -= ll->wrapIndent;
580 p = lastGoodBreak + 1;
581 continue;
583 if (p > 0) {
584 if (vstyle.wrapState == eWrapChar) {
585 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
586 - posLineStart;
587 p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
588 continue;
589 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
590 lastGoodBreak = p;
591 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
592 lastGoodBreak = p;
595 p++;
597 ll->lines++;
599 ll->validity = LineLayout::llLines;
603 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine,
604 const ViewStyle &vs, PointEnd pe) {
605 Point pt;
606 if (pos.Position() == INVALID_POSITION)
607 return pt;
608 int lineDoc = model.pdoc->LineFromPosition(pos.Position());
609 int posLineStart = model.pdoc->LineStart(lineDoc);
610 if ((pe & peLineEnd) && (lineDoc > 0) && (pos.Position() == posLineStart)) {
611 // Want point at end of first line
612 lineDoc--;
613 posLineStart = model.pdoc->LineStart(lineDoc);
615 const int lineVisible = model.cs.DisplayFromDoc(lineDoc);
616 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
617 if (surface && ll) {
618 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
619 const int posInLine = pos.Position() - posLineStart;
620 pt = ll->PointFromPosition(posInLine, vs.lineHeight, pe);
621 pt.y += (lineVisible - topLine) * vs.lineHeight;
622 pt.x += vs.textStart - model.xOffset;
624 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
625 return pt;
628 Range EditView::RangeDisplayLine(Surface *surface, const EditModel &model, int lineVisible, const ViewStyle &vs) {
629 Range rangeSubLine = Range(0,0);
630 if (lineVisible < 0) {
631 return rangeSubLine;
633 const int lineDoc = model.cs.DocFromDisplay(lineVisible);
634 const int positionLineStart = model.pdoc->LineStart(lineDoc);
635 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
636 if (surface && ll) {
637 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
638 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
639 const int subLine = lineVisible - lineStartSet;
640 if (subLine < ll->lines) {
641 rangeSubLine = ll->SubLineRange(subLine);
642 if (subLine == ll->lines-1) {
643 rangeSubLine.end = model.pdoc->LineStart(lineDoc + 1) -
644 positionLineStart;
648 rangeSubLine.start += positionLineStart;
649 rangeSubLine.end += positionLineStart;
650 return rangeSubLine;
653 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
654 pt.x = pt.x - vs.textStart;
655 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
656 if (!canReturnInvalid && (visibleLine < 0))
657 visibleLine = 0;
658 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
659 if (canReturnInvalid && (lineDoc < 0))
660 return SelectionPosition(INVALID_POSITION);
661 if (lineDoc >= model.pdoc->LinesTotal())
662 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length());
663 const int posLineStart = model.pdoc->LineStart(lineDoc);
664 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
665 if (surface && ll) {
666 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
667 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
668 const int subLine = visibleLine - lineStartSet;
669 if (subLine < ll->lines) {
670 const Range rangeSubLine = ll->SubLineRange(subLine);
671 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
672 if (subLine > 0) // Wrapped
673 pt.x -= ll->wrapIndent;
674 const int positionInLine = ll->FindPositionFromX(static_cast<XYPOSITION>(pt.x + subLineStart),
675 rangeSubLine, charPosition);
676 if (positionInLine < rangeSubLine.end) {
677 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
679 if (virtualSpace) {
680 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
681 const int spaceOffset = static_cast<int>(
682 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
683 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
684 } else if (canReturnInvalid) {
685 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
686 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
688 } else {
689 return SelectionPosition(rangeSubLine.end + posLineStart);
692 if (!canReturnInvalid)
693 return SelectionPosition(ll->numCharsInLine + posLineStart);
695 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
699 * Find the document position corresponding to an x coordinate on a particular document line.
700 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
701 * This method is used for rectangular selections and does not work on wrapped lines.
703 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
704 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
705 if (surface && ll) {
706 const int posLineStart = model.pdoc->LineStart(lineDoc);
707 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
708 const Range rangeSubLine = ll->SubLineRange(0);
709 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
710 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
711 if (positionInLine < rangeSubLine.end) {
712 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
714 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
715 const int spaceOffset = static_cast<int>(
716 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
717 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
719 return SelectionPosition(0);
722 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
723 int lineDoc = model.pdoc->LineFromPosition(pos);
724 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
725 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
726 if (surface && ll) {
727 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
728 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
729 int posInLine = pos - posLineStart;
730 lineDisplay--; // To make up for first increment ahead.
731 for (int subLine = 0; subLine < ll->lines; subLine++) {
732 if (posInLine >= ll->LineStart(subLine)) {
733 lineDisplay++;
737 return lineDisplay;
740 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
741 int line = model.pdoc->LineFromPosition(pos);
742 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
743 int posRet = INVALID_POSITION;
744 if (surface && ll) {
745 unsigned int posLineStart = model.pdoc->LineStart(line);
746 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
747 int posInLine = pos - posLineStart;
748 if (posInLine <= ll->maxLineLength) {
749 for (int subLine = 0; subLine < ll->lines; subLine++) {
750 if ((posInLine >= ll->LineStart(subLine)) &&
751 (posInLine <= ll->LineStart(subLine + 1)) &&
752 (posInLine <= ll->numCharsBeforeEOL)) {
753 if (start) {
754 posRet = ll->LineStart(subLine) + posLineStart;
755 } else {
756 if (subLine == ll->lines - 1)
757 posRet = ll->numCharsBeforeEOL + posLineStart;
758 else
759 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
765 return posRet;
768 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
769 return main ?
770 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
771 vsDraw.selAdditionalBackground;
774 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
775 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
776 if (inSelection == 1) {
777 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
778 return SelectionBackground(vsDraw, true, model.primarySelection);
780 } else if (inSelection == 2) {
781 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
782 return SelectionBackground(vsDraw, false, model.primarySelection);
784 } else {
785 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
786 (i >= ll->edgeColumn) &&
787 (i < ll->numCharsBeforeEOL))
788 return vsDraw.theEdge.colour;
789 if (inHotspot && vsDraw.hotspotColours.back.isSet)
790 return vsDraw.hotspotColours.back;
792 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
793 return background;
794 } else {
795 return vsDraw.styles[styleMain].back;
799 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
800 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
801 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
802 surface->Copy(rcCopyArea, from,
803 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
806 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
807 if (alpha != SC_ALPHA_NOALPHA) {
808 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
812 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
813 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
814 if (rcSegment.Empty())
815 return;
816 if (fillBackground) {
817 surface->FillRectangle(rcSegment, textBack);
819 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
820 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
821 surface->InternalLeading(ctrlCharsFont));
822 PRectangle rcCChar = rcSegment;
823 rcCChar.left = rcCChar.left + 1;
824 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
825 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
826 PRectangle rcCentral = rcCChar;
827 rcCentral.top++;
828 rcCentral.bottom--;
829 surface->FillRectangle(rcCentral, textFore);
830 PRectangle rcChar = rcCChar;
831 rcChar.left++;
832 rcChar.right--;
833 surface->DrawTextClipped(rcChar, ctrlCharsFont,
834 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
835 textBack, textFore);
838 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
839 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
840 ColourOptional background) {
842 const int posLineStart = model.pdoc->LineStart(line);
843 PRectangle rcSegment = rcLine;
845 const bool lastSubLine = subLine == (ll->lines - 1);
846 XYPOSITION virtualSpace = 0;
847 if (lastSubLine) {
848 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
849 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
851 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
853 // Fill the virtual space and show selections within it
854 if (virtualSpace > 0.0f) {
855 rcSegment.left = xEol + xStart;
856 rcSegment.right = xEol + xStart + virtualSpace;
857 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
858 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
859 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
860 for (size_t r = 0; r<model.sel.Count(); r++) {
861 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
862 if (alpha == SC_ALPHA_NOALPHA) {
863 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
864 if (!portion.Empty()) {
865 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
866 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
867 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
868 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
869 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
870 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
871 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
872 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
879 int eolInSelection = 0;
880 int alpha = SC_ALPHA_NOALPHA;
881 if (!hideSelection) {
882 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
883 eolInSelection = (lastSubLine == true) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
884 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
887 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
888 XYPOSITION blobsWidth = 0;
889 if (lastSubLine) {
890 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
891 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
892 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
893 blobsWidth += rcSegment.Width();
894 char hexits[4];
895 const char *ctrlChar;
896 unsigned char chEOL = ll->chars[eolPos];
897 int styleMain = ll->styles[eolPos];
898 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
899 if (UTF8IsAscii(chEOL)) {
900 ctrlChar = ControlCharacterString(chEOL);
901 } else {
902 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
903 if (repr) {
904 ctrlChar = repr->stringRep.c_str();
905 eolPos = ll->numCharsInLine;
906 } else {
907 sprintf(hexits, "x%2X", chEOL);
908 ctrlChar = hexits;
911 ColourDesired textFore = vsDraw.styles[styleMain].fore;
912 if (eolInSelection && vsDraw.selColours.fore.isSet) {
913 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
915 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
916 if (alpha == SC_ALPHA_NOALPHA) {
917 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
918 } else {
919 surface->FillRectangle(rcSegment, textBack);
921 } else {
922 surface->FillRectangle(rcSegment, textBack);
924 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
925 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
926 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
931 // Draw the eol-is-selected rectangle
932 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
933 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
935 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
936 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
937 } else {
938 if (background.isSet) {
939 surface->FillRectangle(rcSegment, background);
940 } else if (line < model.pdoc->LinesTotal() - 1) {
941 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
942 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
943 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
944 } else {
945 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
947 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
948 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
952 rcSegment.left = rcSegment.right;
953 if (rcSegment.left < rcLine.left)
954 rcSegment.left = rcLine.left;
955 rcSegment.right = rcLine.right;
957 bool fillRemainder = !lastSubLine || model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_HIDDEN || !model.cs.GetFoldDisplayTextShown(line);
958 if (fillRemainder) {
959 // Fill the remainder of the line
960 FillLineRemainder(surface, model, vsDraw, ll, line, rcSegment, subLine);
963 bool drawWrapMarkEnd = false;
965 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
966 if (subLine + 1 < ll->lines) {
967 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
971 if (drawWrapMarkEnd) {
972 PRectangle rcPlace = rcSegment;
974 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
975 rcPlace.left = xEol + xStart + virtualSpace;
976 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
977 } else {
978 // rcLine is clipped to text area
979 rcPlace.right = rcLine.right;
980 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
982 if (customDrawWrapMarker == NULL) {
983 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
984 } else {
985 customDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
990 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
991 const LineLayout *ll, int xStart, PRectangle rcLine, int secondCharacter, int subLine, Indicator::DrawState drawState, int value) {
992 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
993 PRectangle rcIndic(
994 ll->positions[startPos] + xStart - subLineStart,
995 rcLine.top + vsDraw.maxAscent,
996 ll->positions[endPos] + xStart - subLineStart,
997 rcLine.top + vsDraw.maxAscent + 3);
998 PRectangle rcFirstCharacter = rcIndic;
999 // Allow full descent space for character indicators
1000 rcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent;
1001 if (secondCharacter >= 0) {
1002 rcFirstCharacter.right = ll->positions[secondCharacter] + xStart - subLineStart;
1003 } else {
1004 // Indicator continued from earlier line so make an empty box and don't draw
1005 rcFirstCharacter.right = rcFirstCharacter.left;
1007 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, rcFirstCharacter, drawState, value);
1010 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1011 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under, int hoverIndicatorPos) {
1012 // Draw decorators
1013 const int posLineStart = model.pdoc->LineStart(line);
1014 const int lineStart = ll->LineStart(subLine);
1015 const int posLineEnd = posLineStart + lineEnd;
1017 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
1018 if (under == vsDraw.indicators[deco->indicator].under) {
1019 int startPos = posLineStart + lineStart;
1020 if (!deco->rs.ValueAt(startPos)) {
1021 startPos = deco->rs.EndRun(startPos);
1023 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
1024 const Range rangeRun(deco->rs.StartRun(startPos), deco->rs.EndRun(startPos));
1025 const int endPos = std::min(rangeRun.end, posLineEnd);
1026 const bool hover = vsDraw.indicators[deco->indicator].IsDynamic() &&
1027 rangeRun.ContainsCharacter(hoverIndicatorPos);
1028 const int value = deco->rs.ValueAt(startPos);
1029 Indicator::DrawState drawState = hover ? Indicator::drawHover : Indicator::drawNormal;
1030 const int posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1);
1031 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
1032 surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, drawState, value);
1033 startPos = endPos;
1034 if (!deco->rs.ValueAt(startPos)) {
1035 startPos = deco->rs.EndRun(startPos);
1041 // Use indicators to highlight matching braces
1042 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1043 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
1044 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
1045 if (under == vsDraw.indicators[braceIndicator].under) {
1046 Range rangeLine(posLineStart + lineStart, posLineEnd);
1047 if (rangeLine.ContainsCharacter(model.braces[0])) {
1048 int braceOffset = model.braces[0] - posLineStart;
1049 if (braceOffset < ll->numCharsInLine) {
1050 const int secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[0] + 1, 1) - posLineStart;
1051 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1);
1054 if (rangeLine.ContainsCharacter(model.braces[1])) {
1055 int braceOffset = model.braces[1] - posLineStart;
1056 if (braceOffset < ll->numCharsInLine) {
1057 const int secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[1] + 1, 1) - posLineStart;
1058 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1);
1065 void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1066 int line, int xStart, PRectangle rcLine, int subLine, XYACCUMULATOR subLineStart, DrawPhase phase) {
1067 const bool lastSubLine = subLine == (ll->lines - 1);
1068 if (!lastSubLine)
1069 return;
1071 if ((model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_HIDDEN) || !model.cs.GetFoldDisplayTextShown(line))
1072 return;
1074 PRectangle rcSegment = rcLine;
1075 const char *foldDisplayText = model.cs.GetFoldDisplayText(line);
1076 const int lengthFoldDisplayText = static_cast<int>(strlen(foldDisplayText));
1077 FontAlias fontText = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].font;
1078 const int widthFoldDisplayText = static_cast<int>(surface->WidthText(fontText, foldDisplayText, lengthFoldDisplayText));
1080 int eolInSelection = 0;
1081 int alpha = SC_ALPHA_NOALPHA;
1082 if (!hideSelection) {
1083 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
1084 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
1085 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1088 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1089 XYPOSITION virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
1090 rcSegment.left = xStart + static_cast<XYPOSITION>(ll->positions[ll->numCharsInLine] - subLineStart) + spaceWidth + virtualSpace;
1091 rcSegment.right = rcSegment.left + static_cast<XYPOSITION>(widthFoldDisplayText);
1093 ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1094 FontAlias textFont = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].font;
1095 ColourDesired textFore = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].fore;
1096 if (eolInSelection && (vsDraw.selColours.fore.isSet)) {
1097 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1099 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection,
1100 false, STYLE_FOLDDISPLAYTEXT, -1);
1102 if (model.trackLineWidth) {
1103 if (rcSegment.right + 1> lineWidthMaxSeen) {
1104 // Fold display text border drawn on rcSegment.right with width 1 is the last visble object of the line
1105 lineWidthMaxSeen = static_cast<int>(rcSegment.right + 1);
1109 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1110 surface->FillRectangle(rcSegment, textBack);
1112 // Fill Remainder of the line
1113 PRectangle rcRemainder = rcSegment;
1114 rcRemainder.left = rcRemainder.right + 1;
1115 if (rcRemainder.left < rcLine.left)
1116 rcRemainder.left = rcLine.left;
1117 rcRemainder.right = rcLine.right;
1118 FillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine);
1121 if (phase & drawText) {
1122 if (phasesDraw != phasesOne) {
1123 surface->DrawTextTransparent(rcSegment, textFont,
1124 rcSegment.top + vsDraw.maxAscent, foldDisplayText,
1125 lengthFoldDisplayText, textFore);
1126 } else {
1127 surface->DrawTextNoClip(rcSegment, textFont,
1128 rcSegment.top + vsDraw.maxAscent, foldDisplayText,
1129 lengthFoldDisplayText, textFore, textBack);
1133 if (phase & drawIndicatorsFore) {
1134 if (model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_BOXED) {
1135 surface->PenColour(textFore);
1136 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1137 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1138 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1139 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1140 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1141 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1142 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1143 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1147 if (phase & drawSelectionTranslucent) {
1148 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && alpha != SC_ALPHA_NOALPHA) {
1149 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
1154 static bool AnnotationBoxedOrIndented(int annotationVisible) {
1155 return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED;
1158 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1159 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1160 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
1161 PRectangle rcSegment = rcLine;
1162 int annotationLine = subLine - ll->lines;
1163 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1164 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1165 if (phase & drawBack) {
1166 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1168 rcSegment.left = static_cast<XYPOSITION>(xStart);
1169 if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1170 // Only care about calculating width if tracking or need to draw indented box
1171 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1172 if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1173 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1174 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1175 rcSegment.right = rcSegment.left + widthAnnotation;
1177 if (widthAnnotation > lineWidthMaxSeen)
1178 lineWidthMaxSeen = widthAnnotation;
1180 const int annotationLines = model.pdoc->AnnotationLines(line);
1181 size_t start = 0;
1182 size_t lengthAnnotation = stAnnotation.LineLength(start);
1183 int lineInAnnotation = 0;
1184 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1185 start += lengthAnnotation + 1;
1186 lengthAnnotation = stAnnotation.LineLength(start);
1187 lineInAnnotation++;
1189 PRectangle rcText = rcSegment;
1190 if ((phase & drawBack) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1191 surface->FillRectangle(rcText,
1192 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1193 rcText.left += vsDraw.spaceWidth;
1195 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1196 stAnnotation, start, lengthAnnotation, phase);
1197 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1198 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1199 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1200 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1201 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1202 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1203 if (subLine == ll->lines) {
1204 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1205 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1207 if (subLine == ll->lines + annotationLines - 1) {
1208 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1209 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1215 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1216 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1218 int lineStart = ll->LineStart(subLine);
1219 int posBefore = posCaret;
1220 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1221 int numCharsToDraw = posAfter - posCaret;
1223 // Work out where the starting and ending offsets are. We need to
1224 // see if the previous character shares horizontal space, such as a
1225 // glyph / combining character. If so we'll need to draw that too.
1226 int offsetFirstChar = offset;
1227 int offsetLastChar = offset + (posAfter - posCaret);
1228 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1229 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1230 // The char does not share horizontal space
1231 break;
1233 // Char shares horizontal space, update the numChars to draw
1234 // Update posBefore to point to the prev char
1235 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1236 numCharsToDraw = posAfter - posBefore;
1237 offsetFirstChar = offset - (posCaret - posBefore);
1240 // See if the next character shares horizontal space, if so we'll
1241 // need to draw that too.
1242 if (offsetFirstChar < 0)
1243 offsetFirstChar = 0;
1244 numCharsToDraw = offsetLastChar - offsetFirstChar;
1245 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1246 // Update posAfter to point to the 2nd next char, this is where
1247 // the next character ends, and 2nd next begins. We'll need
1248 // to compare these two
1249 posBefore = posAfter;
1250 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1251 offsetLastChar = offset + (posAfter - posCaret);
1252 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1253 // The char does not share horizontal space
1254 break;
1256 // Char shares horizontal space, update the numChars to draw
1257 numCharsToDraw = offsetLastChar - offsetFirstChar;
1260 // We now know what to draw, update the caret drawing rectangle
1261 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1262 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1264 // Adjust caret position to take into account any word wrapping symbols.
1265 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1266 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1267 rcCaret.left += wordWrapCharWidth;
1268 rcCaret.right += wordWrapCharWidth;
1271 // This character is where the caret block is, we override the colours
1272 // (inversed) for drawing the caret here.
1273 int styleMain = ll->styles[offsetFirstChar];
1274 FontAlias fontText = vsDraw.styles[styleMain].font;
1275 surface->DrawTextClipped(rcCaret, fontText,
1276 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1277 numCharsToDraw, vsDraw.styles[styleMain].back,
1278 caretColour);
1281 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1282 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1283 // When drag is active it is the only caret drawn
1284 bool drawDrag = model.posDrag.IsValid();
1285 if (hideSelection && !drawDrag)
1286 return;
1287 const int posLineStart = model.pdoc->LineStart(lineDoc);
1288 // For each selection draw
1289 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1290 const bool mainCaret = r == model.sel.Main();
1291 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1292 const int offset = posCaret.Position() - posLineStart;
1293 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1294 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1295 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1296 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1297 if (ll->wrapIndent != 0) {
1298 int lineStart = ll->LineStart(subLine);
1299 if (lineStart != 0) // Wrapped
1300 xposCaret += ll->wrapIndent;
1302 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1303 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1304 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1305 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1306 bool caretAtEOF = false;
1307 bool caretAtEOL = false;
1308 bool drawBlockCaret = false;
1309 XYPOSITION widthOverstrikeCaret;
1310 XYPOSITION caretWidthOffset = 0;
1311 PRectangle rcCaret = rcLine;
1313 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1314 caretAtEOF = true;
1315 widthOverstrikeCaret = vsDraw.aveCharWidth;
1316 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1317 caretAtEOL = true;
1318 widthOverstrikeCaret = vsDraw.aveCharWidth;
1319 } else {
1320 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1321 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1323 if (widthOverstrikeCaret < 3) // Make sure its visible
1324 widthOverstrikeCaret = 3;
1326 if (xposCaret > 0)
1327 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1328 xposCaret += xStart;
1329 if (model.posDrag.IsValid()) {
1330 /* Dragging text, use a line caret */
1331 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1332 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1333 } else if (model.inOverstrike && drawOverstrikeCaret) {
1334 /* Overstrike (insert mode), use a modified bar caret */
1335 rcCaret.top = rcCaret.bottom - 2;
1336 rcCaret.left = xposCaret + 1;
1337 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1338 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1339 /* Block caret */
1340 rcCaret.left = xposCaret;
1341 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1342 drawBlockCaret = true;
1343 rcCaret.right = xposCaret + widthOverstrikeCaret;
1344 } else {
1345 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1347 } else {
1348 /* Line caret */
1349 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1350 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1352 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1353 if (drawBlockCaret) {
1354 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1355 } else {
1356 surface->FillRectangle(rcCaret, caretColour);
1360 if (drawDrag)
1361 break;
1365 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1366 int xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker) {
1367 // default bgnd here..
1368 surface->FillRectangle(rcLine, background.isSet ? background :
1369 vsDraw.styles[STYLE_DEFAULT].back);
1371 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1373 // draw continuation rect
1374 PRectangle rcPlace = rcLine;
1376 rcPlace.left = static_cast<XYPOSITION>(xStart);
1377 rcPlace.right = rcPlace.left + ll->wrapIndent;
1379 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1380 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1381 else
1382 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1384 if (customDrawWrapMarker == NULL) {
1385 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1386 } else {
1387 customDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1392 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1393 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1394 int subLine, ColourOptional background) const {
1396 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1397 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1398 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1399 // Does not take margin into account but not significant
1400 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1402 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs, NULL);
1404 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1406 // Background drawing loop
1407 while (bfBack.More()) {
1409 const TextSegment ts = bfBack.Next();
1410 const int i = ts.end() - 1;
1411 const int iDoc = i + posLineStart;
1413 PRectangle rcSegment = rcLine;
1414 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1415 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1416 // Only try to draw if really visible - enhances performance by not calling environment to
1417 // draw strings that are completely past the right side of the window.
1418 if (!rcSegment.Empty() && rcSegment.Intersects(rcLine)) {
1419 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1420 if (rcSegment.left < rcLine.left)
1421 rcSegment.left = rcLine.left;
1422 if (rcSegment.right > rcLine.right)
1423 rcSegment.right = rcLine.right;
1425 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1426 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1427 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1428 inHotspot, ll->styles[i], i);
1429 if (ts.representation) {
1430 if (ll->chars[i] == '\t') {
1431 // Tab display
1432 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1433 textBack = vsDraw.whitespaceColours.back;
1434 } else {
1435 // Blob display
1436 inIndentation = false;
1438 surface->FillRectangle(rcSegment, textBack);
1439 } else {
1440 // Normal text display
1441 surface->FillRectangle(rcSegment, textBack);
1442 if (vsDraw.viewWhitespace != wsInvisible) {
1443 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1444 if (ll->chars[cpos + ts.start] == ' ') {
1445 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {
1446 PRectangle rcSpace(
1447 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1448 rcSegment.top,
1449 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1450 rcSegment.bottom);
1451 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1453 } else {
1454 inIndentation = false;
1459 } else if (rcSegment.left > rcLine.right) {
1460 break;
1465 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1466 Range lineRange, int xStart) {
1467 if (vsDraw.edgeState == EDGE_LINE) {
1468 PRectangle rcSegment = rcLine;
1469 int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);
1470 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1471 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1472 rcSegment.left -= ll->wrapIndent;
1473 rcSegment.right = rcSegment.left + 1;
1474 surface->FillRectangle(rcSegment, vsDraw.theEdge.colour);
1475 } else if (vsDraw.edgeState == EDGE_MULTILINE) {
1476 for (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {
1477 if (vsDraw.theMultiEdge[edge].column >= 0) {
1478 PRectangle rcSegment = rcLine;
1479 int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);
1480 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1481 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1482 rcSegment.left -= ll->wrapIndent;
1483 rcSegment.right = rcSegment.left + 1;
1484 surface->FillRectangle(rcSegment, vsDraw.theMultiEdge[edge].colour);
1490 // Draw underline mark as part of background if not transparent
1491 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1492 int line, PRectangle rcLine) {
1493 int marks = model.pdoc->GetMark(line);
1494 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1495 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1496 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1497 PRectangle rcUnderline = rcLine;
1498 rcUnderline.top = rcUnderline.bottom - 2;
1499 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1501 marks >>= 1;
1504 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1505 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1506 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1507 const int posLineStart = model.pdoc->LineStart(line);
1508 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1509 // For each selection draw
1510 int virtualSpaces = 0;
1511 if (subLine == (ll->lines - 1)) {
1512 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1514 SelectionPosition posStart(posLineStart + lineRange.start);
1515 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1516 SelectionSegment virtualSpaceRange(posStart, posEnd);
1517 for (size_t r = 0; r < model.sel.Count(); r++) {
1518 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1519 if (alpha != SC_ALPHA_NOALPHA) {
1520 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1521 if (!portion.Empty()) {
1522 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1523 PRectangle rcSegment = rcLine;
1524 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1525 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1526 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1527 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1528 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1529 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1530 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1532 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1533 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1534 if (rcSegment.right > rcLine.left)
1535 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1542 // Draw any translucent whole line states
1543 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1544 int line, PRectangle rcLine) {
1545 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1546 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1548 const int marksOfLine = model.pdoc->GetMark(line);
1549 int marksDrawnInText = marksOfLine & vsDraw.maskDrawInText;
1550 for (int markBit = 0; (markBit < 32) && marksDrawnInText; markBit++) {
1551 if (marksDrawnInText & 1) {
1552 if (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) {
1553 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1554 } else if (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) {
1555 PRectangle rcUnderline = rcLine;
1556 rcUnderline.top = rcUnderline.bottom - 2;
1557 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1560 marksDrawnInText >>= 1;
1562 int marksDrawnInLine = marksOfLine & vsDraw.maskInLine;
1563 for (int markBit = 0; (markBit < 32) && marksDrawnInLine; markBit++) {
1564 if (marksDrawnInLine & 1) {
1565 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1567 marksDrawnInLine >>= 1;
1571 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1572 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1573 int subLine, ColourOptional background) {
1575 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1576 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1577 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1579 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1580 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1582 // Does not take margin into account but not significant
1583 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1585 // Foreground drawing loop
1586 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1587 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs, &vsDraw);
1589 while (bfFore.More()) {
1591 const TextSegment ts = bfFore.Next();
1592 const int i = ts.end() - 1;
1593 const int iDoc = i + posLineStart;
1595 PRectangle rcSegment = rcLine;
1596 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1597 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1598 // Only try to draw if really visible - enhances performance by not calling environment to
1599 // draw strings that are completely past the right side of the window.
1600 if (rcSegment.Intersects(rcLine)) {
1601 int styleMain = ll->styles[i];
1602 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1603 FontAlias textFont = vsDraw.styles[styleMain].font;
1604 //hotspot foreground
1605 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1606 if (inHotspot) {
1607 if (vsDraw.hotspotColours.fore.isSet)
1608 textFore = vsDraw.hotspotColours.fore;
1610 if (vsDraw.indicatorsSetFore > 0) {
1611 // At least one indicator sets the text colour so see if it applies to this segment
1612 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
1613 const int indicatorValue = deco->rs.ValueAt(ts.start + posLineStart);
1614 if (indicatorValue) {
1615 const Indicator &indicator = vsDraw.indicators[deco->indicator];
1616 const bool hover = indicator.IsDynamic() &&
1617 ((model.hoverIndicatorPos >= ts.start + posLineStart) &&
1618 (model.hoverIndicatorPos <= ts.end() + posLineStart));
1619 if (hover) {
1620 if (indicator.sacHover.style == INDIC_TEXTFORE) {
1621 textFore = indicator.sacHover.fore;
1623 } else {
1624 if (indicator.sacNormal.style == INDIC_TEXTFORE) {
1625 if (indicator.Flags() & SC_INDICFLAG_VALUEFORE)
1626 textFore = indicatorValue & SC_INDICVALUEMASK;
1627 else
1628 textFore = indicator.sacNormal.fore;
1634 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1635 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1636 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1638 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1639 if (ts.representation) {
1640 if (ll->chars[i] == '\t') {
1641 // Tab display
1642 if (phasesDraw == phasesOne) {
1643 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1644 textBack = vsDraw.whitespaceColours.back;
1645 surface->FillRectangle(rcSegment, textBack);
1647 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1648 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1649 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1650 indentCount++) {
1651 if (indentCount > 0) {
1652 int xIndent = static_cast<int>(indentCount * indentWidth);
1653 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1654 (ll->xHighlightGuide == xIndent));
1658 if (vsDraw.viewWhitespace != wsInvisible) {
1659 if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1660 if (vsDraw.whitespaceColours.fore.isSet)
1661 textFore = vsDraw.whitespaceColours.fore;
1662 surface->PenColour(textFore);
1663 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,
1664 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1665 if (customDrawTabArrow == NULL)
1666 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2), vsDraw);
1667 else
1668 customDrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1671 } else {
1672 inIndentation = false;
1673 if (vsDraw.controlCharSymbol >= 32) {
1674 // Using one font for all control characters so it can be controlled independently to ensure
1675 // the box goes around the characters tightly. Seems to be no way to work out what height
1676 // is taken by an individual character - internal leading gives varying results.
1677 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1678 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1679 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1680 rcSegment.top + vsDraw.maxAscent,
1681 cc, 1, textBack, textFore);
1682 } else {
1683 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1684 textBack, textFore, phasesDraw == phasesOne);
1687 } else {
1688 // Normal text display
1689 if (vsDraw.styles[styleMain].visible) {
1690 if (phasesDraw != phasesOne) {
1691 surface->DrawTextTransparent(rcSegment, textFont,
1692 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1693 i - ts.start + 1, textFore);
1694 } else {
1695 surface->DrawTextNoClip(rcSegment, textFont,
1696 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1697 i - ts.start + 1, textFore, textBack);
1700 if (vsDraw.viewWhitespace != wsInvisible ||
1701 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1702 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1703 if (ll->chars[cpos + ts.start] == ' ') {
1704 if (vsDraw.viewWhitespace != wsInvisible) {
1705 if (vsDraw.whitespaceColours.fore.isSet)
1706 textFore = vsDraw.whitespaceColours.fore;
1707 if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1708 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1709 if ((phasesDraw == phasesOne) && drawWhitespaceBackground) {
1710 textBack = vsDraw.whitespaceColours.back;
1711 PRectangle rcSpace(
1712 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1713 rcSegment.top,
1714 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1715 rcSegment.bottom);
1716 surface->FillRectangle(rcSpace, textBack);
1718 const int halfDotWidth = vsDraw.whitespaceSize / 2;
1719 PRectangle rcDot(xmid + xStart - halfDotWidth - static_cast<XYPOSITION>(subLineStart),
1720 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1721 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1722 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1723 surface->FillRectangle(rcDot, textFore);
1726 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1727 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1728 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1729 indentCount++) {
1730 if (indentCount > 0) {
1731 int xIndent = static_cast<int>(indentCount * indentWidth);
1732 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1733 (ll->xHighlightGuide == xIndent));
1737 } else {
1738 inIndentation = false;
1743 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1744 PRectangle rcUL = rcSegment;
1745 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1746 rcUL.bottom = rcUL.top + 1;
1747 if (vsDraw.hotspotColours.fore.isSet)
1748 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1749 else
1750 surface->FillRectangle(rcUL, textFore);
1751 } else if (vsDraw.styles[styleMain].underline) {
1752 PRectangle rcUL = rcSegment;
1753 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1754 rcUL.bottom = rcUL.top + 1;
1755 surface->FillRectangle(rcUL, textFore);
1757 } else if (rcSegment.left > rcLine.right) {
1758 break;
1763 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1764 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1765 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1766 && (subLine == 0)) {
1767 const int posLineStart = model.pdoc->LineStart(line);
1768 int indentSpace = model.pdoc->GetLineIndentation(line);
1769 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1771 // Find the most recent line with some text
1773 int lineLastWithText = line;
1774 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1775 lineLastWithText--;
1777 if (lineLastWithText < line) {
1778 xStartText = 100000; // Don't limit to visible indentation on empty line
1779 // This line is empty, so use indentation of last line with text
1780 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1781 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1782 if (isFoldHeader) {
1783 // Level is one more level than parent
1784 indentLastWithText += model.pdoc->IndentSize();
1786 if (vsDraw.viewIndentationGuides == ivLookForward) {
1787 // In viLookForward mode, previous line only used if it is a fold header
1788 if (isFoldHeader) {
1789 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1791 } else { // viLookBoth
1792 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1796 int lineNextWithText = line;
1797 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1798 lineNextWithText++;
1800 if (lineNextWithText > line) {
1801 xStartText = 100000; // Don't limit to visible indentation on empty line
1802 // This line is empty, so use indentation of first next line with text
1803 indentSpace = Platform::Maximum(indentSpace,
1804 model.pdoc->GetLineIndentation(lineNextWithText));
1807 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1808 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1809 if (xIndent < xStartText) {
1810 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1811 (ll->xHighlightGuide == xIndent));
1817 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1818 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1820 if (subLine >= ll->lines) {
1821 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1822 return; // No further drawing
1825 // See if something overrides the line background color.
1826 const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1828 const int posLineStart = model.pdoc->LineStart(line);
1830 const Range lineRange = ll->SubLineRange(subLine);
1831 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1833 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1834 if (phase & drawBack) {
1835 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker);
1837 xStart += static_cast<int>(ll->wrapIndent);
1840 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1841 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1842 subLine, background);
1843 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1844 xStart, subLine, subLineStart, background);
1847 if (phase & drawIndicatorsBack) {
1848 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true, model.hoverIndicatorPos);
1849 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1850 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1853 if (phase & drawText) {
1854 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1855 subLine, background);
1858 if (phase & drawIndentationGuides) {
1859 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1862 if (phase & drawIndicatorsFore) {
1863 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false, model.hoverIndicatorPos);
1866 // End of the drawing of the current line
1867 if (phasesDraw == phasesOne) {
1868 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1869 xStart, subLine, subLineStart, background);
1872 DrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, phase);
1874 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1875 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1878 if (phase & drawLineTranslucent) {
1879 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1883 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1884 bool expanded = model.cs.GetExpanded(line);
1885 const int level = model.pdoc->GetLevel(line);
1886 const int levelNext = model.pdoc->GetLevel(line + 1);
1887 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1888 (LevelNumber(level) < LevelNumber(levelNext))) {
1889 // Paint the line above the fold
1890 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1892 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1893 PRectangle rcFoldLine = rcLine;
1894 rcFoldLine.bottom = rcFoldLine.top + 1;
1895 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1897 // Paint the line below the fold
1898 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1900 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1901 PRectangle rcFoldLine = rcLine;
1902 rcFoldLine.top = rcFoldLine.bottom - 1;
1903 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1908 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1909 PRectangle rcClient, const ViewStyle &vsDraw) {
1910 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1911 // serifs and italic stems for aliased text.
1912 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1914 // Do the painting
1915 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1917 Surface *surface = surfaceWindow;
1918 if (bufferedDraw) {
1919 surface = pixmapLine;
1920 PLATFORM_ASSERT(pixmapLine->Initialised());
1922 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1923 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1925 const Point ptOrigin = model.GetVisibleOriginInMain();
1927 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1928 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1930 SelectionPosition posCaret = model.sel.RangeMain().caret;
1931 if (model.posDrag.IsValid())
1932 posCaret = model.posDrag;
1933 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1935 PRectangle rcTextArea = rcClient;
1936 if (vsDraw.marginInside) {
1937 rcTextArea.left += vsDraw.textStart;
1938 rcTextArea.right -= vsDraw.rightMarginWidth;
1939 } else {
1940 rcTextArea = rcArea;
1943 // Remove selection margin from drawing area so text will not be drawn
1944 // on it in unbuffered mode.
1945 if (!bufferedDraw && vsDraw.marginInside) {
1946 PRectangle rcClipText = rcTextArea;
1947 rcClipText.left -= leftTextOverlap;
1948 surfaceWindow->SetClip(rcClipText);
1951 // Loop on visible lines
1952 //double durLayout = 0.0;
1953 //double durPaint = 0.0;
1954 //double durCopy = 0.0;
1955 //ElapsedTime etWhole;
1957 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1958 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1960 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1961 AutoLineLayout ll(llc, 0);
1962 std::vector<DrawPhase> phases;
1963 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1964 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1965 phases.push_back(phase);
1967 } else {
1968 phases.push_back(drawAll);
1970 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1971 int ypos = 0;
1972 if (!bufferedDraw)
1973 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1974 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1975 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1976 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1978 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1979 // Only visible lines should be handled by the code within the loop
1980 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1981 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1982 const int subLine = visibleLine - lineStartSet;
1984 // Copy this line and its styles from the document into local arrays
1985 // and determine the x position at which each character starts.
1986 //ElapsedTime et;
1987 if (lineDoc != lineDocPrevious) {
1988 ll.Set(0);
1989 ll.Set(RetrieveLineLayout(lineDoc, model));
1990 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1991 lineDocPrevious = lineDoc;
1993 //durLayout += et.Duration(true);
1995 if (ll) {
1996 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1997 ll->hotspot = model.GetHotSpotRange();
1999 PRectangle rcLine = rcTextArea;
2000 rcLine.top = static_cast<XYPOSITION>(ypos);
2001 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
2003 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
2005 // Highlight the current braces if any
2006 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
2007 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
2009 if (leftTextOverlap && (bufferedDraw || ((phasesDraw < phasesMultiple) && (*it & drawBack)))) {
2010 // Clear the left margin
2011 PRectangle rcSpacer = rcLine;
2012 rcSpacer.right = rcSpacer.left;
2013 rcSpacer.left -= 1;
2014 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
2017 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
2018 //durPaint += et.Duration(true);
2020 // Restore the previous styles for the brace highlights in case layout is in cache.
2021 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
2023 if (*it & drawFoldLines) {
2024 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
2027 if (*it & drawCarets) {
2028 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
2031 if (bufferedDraw) {
2032 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
2033 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
2034 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
2035 yposScreen + vsDraw.lineHeight);
2036 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
2039 lineWidthMaxSeen = Platform::Maximum(
2040 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
2041 //durCopy += et.Duration(true);
2044 if (!bufferedDraw) {
2045 ypos += vsDraw.lineHeight;
2048 yposScreen += vsDraw.lineHeight;
2049 visibleLine++;
2052 ll.Set(0);
2053 //if (durPaint < 0.00000001)
2054 // durPaint = 0.00000001;
2056 // Right column limit indicator
2057 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
2058 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
2059 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
2060 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
2061 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
2062 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
2063 if (vsDraw.edgeState == EDGE_LINE) {
2064 int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);
2065 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
2066 rcBeyondEOF.right = rcBeyondEOF.left + 1;
2067 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.theEdge.colour);
2068 } else if (vsDraw.edgeState == EDGE_MULTILINE) {
2069 for (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {
2070 if (vsDraw.theMultiEdge[edge].column >= 0) {
2071 int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);
2072 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
2073 rcBeyondEOF.right = rcBeyondEOF.left + 1;
2074 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.theMultiEdge[edge].colour);
2079 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
2081 //Platform::DebugPrintf(
2082 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
2083 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
2087 void EditView::FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
2088 int line, PRectangle rcArea, int subLine) {
2089 int eolInSelection = 0;
2090 int alpha = SC_ALPHA_NOALPHA;
2091 if (!hideSelection) {
2092 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
2093 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
2094 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2097 ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
2099 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2100 surface->FillRectangle(rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
2101 } else {
2102 if (background.isSet) {
2103 surface->FillRectangle(rcArea, background);
2104 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
2105 surface->FillRectangle(rcArea, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
2106 } else {
2107 surface->FillRectangle(rcArea, vsDraw.styles[STYLE_DEFAULT].back);
2109 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2110 SimpleAlphaRectangle(surface, rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
2115 // Space (3 space characters) between line numbers and text when printing.
2116 #define lineNumberPrintSpace " "
2118 static ColourDesired InvertedLight(ColourDesired orig) {
2119 unsigned int r = orig.GetRed();
2120 unsigned int g = orig.GetGreen();
2121 unsigned int b = orig.GetBlue();
2122 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
2123 unsigned int il = 0xff - l;
2124 if (l == 0)
2125 return ColourDesired(0xff, 0xff, 0xff);
2126 r = r * il / l;
2127 g = g * il / l;
2128 b = b * il / l;
2129 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
2132 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
2133 const EditModel &model, const ViewStyle &vs) {
2134 // Can't use measurements cached for screen
2135 posCache.Clear();
2137 ViewStyle vsPrint(vs);
2138 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
2140 // Modify the view style for printing as do not normally want any of the transient features to be printed
2141 // Printing supports only the line number margin.
2142 int lineNumberIndex = -1;
2143 for (size_t margin = 0; margin < vs.ms.size(); margin++) {
2144 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
2145 lineNumberIndex = static_cast<int>(margin);
2146 } else {
2147 vsPrint.ms[margin].width = 0;
2150 vsPrint.fixedColumnWidth = 0;
2151 vsPrint.zoomLevel = printParameters.magnification;
2152 // Don't show indentation guides
2153 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
2154 vsPrint.viewIndentationGuides = ivNone;
2155 // Don't show the selection when printing
2156 vsPrint.selColours.back.isSet = false;
2157 vsPrint.selColours.fore.isSet = false;
2158 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
2159 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
2160 vsPrint.whitespaceColours.back.isSet = false;
2161 vsPrint.whitespaceColours.fore.isSet = false;
2162 vsPrint.showCaretLineBackground = false;
2163 vsPrint.alwaysShowCaretLineBackground = false;
2164 // Don't highlight matching braces using indicators
2165 vsPrint.braceHighlightIndicatorSet = false;
2166 vsPrint.braceBadLightIndicatorSet = false;
2168 // Set colours for printing according to users settings
2169 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
2170 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
2171 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
2172 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
2173 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
2174 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
2175 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2176 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
2177 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2178 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
2179 if (sty <= STYLE_DEFAULT) {
2180 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2184 // White background for the line numbers
2185 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
2187 // Printing uses different margins, so reset screen margins
2188 vsPrint.leftMarginWidth = 0;
2189 vsPrint.rightMarginWidth = 0;
2191 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
2192 // Determining width must happen after fonts have been realised in Refresh
2193 int lineNumberWidth = 0;
2194 if (lineNumberIndex >= 0) {
2195 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
2196 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
2197 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
2198 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
2201 int linePrintStart = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMin));
2202 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
2203 if (linePrintLast < linePrintStart)
2204 linePrintLast = linePrintStart;
2205 int linePrintMax = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMax));
2206 if (linePrintLast > linePrintMax)
2207 linePrintLast = linePrintMax;
2208 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
2209 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
2210 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
2211 int endPosPrint = model.pdoc->Length();
2212 if (linePrintLast < model.pdoc->LinesTotal())
2213 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
2215 // Ensure we are styled to where we are formatting.
2216 model.pdoc->EnsureStyledTo(endPosPrint);
2218 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
2219 int ypos = pfr->rc.top;
2221 int lineDoc = linePrintStart;
2223 int nPrintPos = static_cast<int>(pfr->chrg.cpMin);
2224 int visibleLine = 0;
2225 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
2226 if (printParameters.wrapState == eWrapNone)
2227 widthPrint = LineLayout::wrapWidthInfinite;
2229 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2231 // When printing, the hdc and hdcTarget may be the same, so
2232 // changing the state of surfaceMeasure may change the underlying
2233 // state of surface. Therefore, any cached state is discarded before
2234 // using each surface.
2235 surfaceMeasure->FlushCachedState();
2237 // Copy this line and its styles from the document into local arrays
2238 // and determine the x position at which each character starts.
2239 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2240 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2242 ll.containsCaret = false;
2244 PRectangle rcLine = PRectangle::FromInts(
2245 pfr->rc.left,
2246 ypos,
2247 pfr->rc.right - 1,
2248 ypos + vsPrint.lineHeight);
2250 // When document line is wrapped over multiple display lines, find where
2251 // to start printing from to ensure a particular position is on the first
2252 // line of the page.
2253 if (visibleLine == 0) {
2254 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2255 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2256 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2257 visibleLine = -iwl;
2261 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2262 visibleLine = -(ll.lines - 1);
2266 if (draw && lineNumberWidth &&
2267 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2268 (visibleLine >= 0)) {
2269 char number[100];
2270 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2271 PRectangle rcNumber = rcLine;
2272 rcNumber.right = rcNumber.left + lineNumberWidth;
2273 // Right justify
2274 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2275 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2276 surface->FlushCachedState();
2277 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2278 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2279 vsPrint.styles[STYLE_LINENUMBER].fore,
2280 vsPrint.styles[STYLE_LINENUMBER].back);
2283 // Draw the line
2284 surface->FlushCachedState();
2286 for (int iwl = 0; iwl < ll.lines; iwl++) {
2287 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2288 if (visibleLine >= 0) {
2289 if (draw) {
2290 rcLine.top = static_cast<XYPOSITION>(ypos);
2291 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2292 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2294 ypos += vsPrint.lineHeight;
2296 visibleLine++;
2297 if (iwl == ll.lines - 1)
2298 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2299 else
2300 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2304 ++lineDoc;
2307 // Clear cache so measurements are not used for screen
2308 posCache.Clear();
2310 return nPrintPos;