Update Scintilla to version 3.7.1
[geany-mirror.git] / scintilla / src / EditView.cxx
blob8ffc1bcff9d86fcdb2c0dd297a159b1af5ef3c9d
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, Point 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(pt.x + subLineStart, rangeSubLine, charPosition);
675 if (positionInLine < rangeSubLine.end) {
676 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
678 if (virtualSpace) {
679 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
680 const int spaceOffset = static_cast<int>(
681 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
682 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
683 } else if (canReturnInvalid) {
684 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
685 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
687 } else {
688 return SelectionPosition(rangeSubLine.end + posLineStart);
691 if (!canReturnInvalid)
692 return SelectionPosition(ll->numCharsInLine + posLineStart);
694 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
698 * Find the document position corresponding to an x coordinate on a particular document line.
699 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
700 * This method is used for rectangular selections and does not work on wrapped lines.
702 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
703 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
704 if (surface && ll) {
705 const int posLineStart = model.pdoc->LineStart(lineDoc);
706 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
707 const Range rangeSubLine = ll->SubLineRange(0);
708 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
709 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
710 if (positionInLine < rangeSubLine.end) {
711 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
713 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
714 const int spaceOffset = static_cast<int>(
715 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
716 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
718 return SelectionPosition(0);
721 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
722 int lineDoc = model.pdoc->LineFromPosition(pos);
723 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
724 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
725 if (surface && ll) {
726 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
727 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
728 int posInLine = pos - posLineStart;
729 lineDisplay--; // To make up for first increment ahead.
730 for (int subLine = 0; subLine < ll->lines; subLine++) {
731 if (posInLine >= ll->LineStart(subLine)) {
732 lineDisplay++;
736 return lineDisplay;
739 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
740 int line = model.pdoc->LineFromPosition(pos);
741 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
742 int posRet = INVALID_POSITION;
743 if (surface && ll) {
744 unsigned int posLineStart = model.pdoc->LineStart(line);
745 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
746 int posInLine = pos - posLineStart;
747 if (posInLine <= ll->maxLineLength) {
748 for (int subLine = 0; subLine < ll->lines; subLine++) {
749 if ((posInLine >= ll->LineStart(subLine)) &&
750 (posInLine <= ll->LineStart(subLine + 1)) &&
751 (posInLine <= ll->numCharsBeforeEOL)) {
752 if (start) {
753 posRet = ll->LineStart(subLine) + posLineStart;
754 } else {
755 if (subLine == ll->lines - 1)
756 posRet = ll->numCharsBeforeEOL + posLineStart;
757 else
758 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
764 return posRet;
767 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
768 return main ?
769 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
770 vsDraw.selAdditionalBackground;
773 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
774 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
775 if (inSelection == 1) {
776 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
777 return SelectionBackground(vsDraw, true, model.primarySelection);
779 } else if (inSelection == 2) {
780 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
781 return SelectionBackground(vsDraw, false, model.primarySelection);
783 } else {
784 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
785 (i >= ll->edgeColumn) &&
786 (i < ll->numCharsBeforeEOL))
787 return vsDraw.theEdge.colour;
788 if (inHotspot && vsDraw.hotspotColours.back.isSet)
789 return vsDraw.hotspotColours.back;
791 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
792 return background;
793 } else {
794 return vsDraw.styles[styleMain].back;
798 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
799 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
800 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
801 surface->Copy(rcCopyArea, from,
802 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
805 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
806 if (alpha != SC_ALPHA_NOALPHA) {
807 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
811 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
812 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
813 if (rcSegment.Empty())
814 return;
815 if (fillBackground) {
816 surface->FillRectangle(rcSegment, textBack);
818 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
819 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
820 surface->InternalLeading(ctrlCharsFont));
821 PRectangle rcCChar = rcSegment;
822 rcCChar.left = rcCChar.left + 1;
823 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
824 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
825 PRectangle rcCentral = rcCChar;
826 rcCentral.top++;
827 rcCentral.bottom--;
828 surface->FillRectangle(rcCentral, textFore);
829 PRectangle rcChar = rcCChar;
830 rcChar.left++;
831 rcChar.right--;
832 surface->DrawTextClipped(rcChar, ctrlCharsFont,
833 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
834 textBack, textFore);
837 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
838 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
839 ColourOptional background) {
841 const int posLineStart = model.pdoc->LineStart(line);
842 PRectangle rcSegment = rcLine;
844 const bool lastSubLine = subLine == (ll->lines - 1);
845 XYPOSITION virtualSpace = 0;
846 if (lastSubLine) {
847 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
848 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
850 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
852 // Fill the virtual space and show selections within it
853 if (virtualSpace > 0.0f) {
854 rcSegment.left = xEol + xStart;
855 rcSegment.right = xEol + xStart + virtualSpace;
856 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
857 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
858 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
859 for (size_t r = 0; r<model.sel.Count(); r++) {
860 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
861 if (alpha == SC_ALPHA_NOALPHA) {
862 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
863 if (!portion.Empty()) {
864 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
865 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
866 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
867 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
868 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
869 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
870 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
871 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
878 int eolInSelection = 0;
879 int alpha = SC_ALPHA_NOALPHA;
880 if (!hideSelection) {
881 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
882 eolInSelection = (lastSubLine == true) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
883 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
886 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
887 XYPOSITION blobsWidth = 0;
888 if (lastSubLine) {
889 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
890 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
891 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
892 blobsWidth += rcSegment.Width();
893 char hexits[4];
894 const char *ctrlChar;
895 unsigned char chEOL = ll->chars[eolPos];
896 int styleMain = ll->styles[eolPos];
897 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
898 if (UTF8IsAscii(chEOL)) {
899 ctrlChar = ControlCharacterString(chEOL);
900 } else {
901 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
902 if (repr) {
903 ctrlChar = repr->stringRep.c_str();
904 eolPos = ll->numCharsInLine;
905 } else {
906 sprintf(hexits, "x%2X", chEOL);
907 ctrlChar = hexits;
910 ColourDesired textFore = vsDraw.styles[styleMain].fore;
911 if (eolInSelection && vsDraw.selColours.fore.isSet) {
912 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
914 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
915 if (alpha == SC_ALPHA_NOALPHA) {
916 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
917 } else {
918 surface->FillRectangle(rcSegment, textBack);
920 } else {
921 surface->FillRectangle(rcSegment, textBack);
923 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
924 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
925 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
930 // Draw the eol-is-selected rectangle
931 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
932 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
934 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
935 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
936 } else {
937 if (background.isSet) {
938 surface->FillRectangle(rcSegment, background);
939 } else if (line < model.pdoc->LinesTotal() - 1) {
940 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
941 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
942 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
943 } else {
944 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
946 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
947 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
951 rcSegment.left = rcSegment.right;
952 if (rcSegment.left < rcLine.left)
953 rcSegment.left = rcLine.left;
954 rcSegment.right = rcLine.right;
956 bool fillRemainder = !lastSubLine || model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_HIDDEN || !model.cs.GetFoldDisplayTextShown(line);
957 if (fillRemainder) {
958 // Fill the remainder of the line
959 FillLineRemainder(surface, model, vsDraw, ll, line, rcSegment, subLine);
962 bool drawWrapMarkEnd = false;
964 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
965 if (subLine + 1 < ll->lines) {
966 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
970 if (drawWrapMarkEnd) {
971 PRectangle rcPlace = rcSegment;
973 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
974 rcPlace.left = xEol + xStart + virtualSpace;
975 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
976 } else {
977 // rcLine is clipped to text area
978 rcPlace.right = rcLine.right;
979 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
981 if (customDrawWrapMarker == NULL) {
982 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
983 } else {
984 customDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
989 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
990 const LineLayout *ll, int xStart, PRectangle rcLine, int secondCharacter, int subLine, Indicator::DrawState drawState, int value) {
991 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
992 PRectangle rcIndic(
993 ll->positions[startPos] + xStart - subLineStart,
994 rcLine.top + vsDraw.maxAscent,
995 ll->positions[endPos] + xStart - subLineStart,
996 rcLine.top + vsDraw.maxAscent + 3);
997 PRectangle rcFirstCharacter = rcIndic;
998 // Allow full descent space for character indicators
999 rcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent;
1000 if (secondCharacter >= 0) {
1001 rcFirstCharacter.right = ll->positions[secondCharacter] + xStart - subLineStart;
1002 } else {
1003 // Indicator continued from earlier line so make an empty box and don't draw
1004 rcFirstCharacter.right = rcFirstCharacter.left;
1006 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, rcFirstCharacter, drawState, value);
1009 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1010 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under, int hoverIndicatorPos) {
1011 // Draw decorators
1012 const int posLineStart = model.pdoc->LineStart(line);
1013 const int lineStart = ll->LineStart(subLine);
1014 const int posLineEnd = posLineStart + lineEnd;
1016 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
1017 if (under == vsDraw.indicators[deco->indicator].under) {
1018 int startPos = posLineStart + lineStart;
1019 if (!deco->rs.ValueAt(startPos)) {
1020 startPos = deco->rs.EndRun(startPos);
1022 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
1023 const Range rangeRun(deco->rs.StartRun(startPos), deco->rs.EndRun(startPos));
1024 const int endPos = std::min(rangeRun.end, posLineEnd);
1025 const bool hover = vsDraw.indicators[deco->indicator].IsDynamic() &&
1026 rangeRun.ContainsCharacter(hoverIndicatorPos);
1027 const int value = deco->rs.ValueAt(startPos);
1028 Indicator::DrawState drawState = hover ? Indicator::drawHover : Indicator::drawNormal;
1029 const int posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1);
1030 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
1031 surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, drawState, value);
1032 startPos = endPos;
1033 if (!deco->rs.ValueAt(startPos)) {
1034 startPos = deco->rs.EndRun(startPos);
1040 // Use indicators to highlight matching braces
1041 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1042 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
1043 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
1044 if (under == vsDraw.indicators[braceIndicator].under) {
1045 Range rangeLine(posLineStart + lineStart, posLineEnd);
1046 if (rangeLine.ContainsCharacter(model.braces[0])) {
1047 int braceOffset = model.braces[0] - posLineStart;
1048 if (braceOffset < ll->numCharsInLine) {
1049 const int secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[0] + 1, 1) - posLineStart;
1050 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1);
1053 if (rangeLine.ContainsCharacter(model.braces[1])) {
1054 int braceOffset = model.braces[1] - posLineStart;
1055 if (braceOffset < ll->numCharsInLine) {
1056 const int secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[1] + 1, 1) - posLineStart;
1057 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1);
1064 void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1065 int line, int xStart, PRectangle rcLine, int subLine, XYACCUMULATOR subLineStart, DrawPhase phase) {
1066 const bool lastSubLine = subLine == (ll->lines - 1);
1067 if (!lastSubLine)
1068 return;
1070 if ((model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_HIDDEN) || !model.cs.GetFoldDisplayTextShown(line))
1071 return;
1073 PRectangle rcSegment = rcLine;
1074 const char *foldDisplayText = model.cs.GetFoldDisplayText(line);
1075 const int lengthFoldDisplayText = static_cast<int>(strlen(foldDisplayText));
1076 FontAlias fontText = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].font;
1077 const int widthFoldDisplayText = static_cast<int>(surface->WidthText(fontText, foldDisplayText, lengthFoldDisplayText));
1079 int eolInSelection = 0;
1080 int alpha = SC_ALPHA_NOALPHA;
1081 if (!hideSelection) {
1082 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
1083 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
1084 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1087 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1088 XYPOSITION virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
1089 rcSegment.left = xStart + static_cast<XYPOSITION>(ll->positions[ll->numCharsInLine] - subLineStart) + spaceWidth + virtualSpace;
1090 rcSegment.right = rcSegment.left + static_cast<XYPOSITION>(widthFoldDisplayText);
1092 ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1093 FontAlias textFont = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].font;
1094 ColourDesired textFore = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].fore;
1095 if (eolInSelection && (vsDraw.selColours.fore.isSet)) {
1096 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1098 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection,
1099 false, STYLE_FOLDDISPLAYTEXT, -1);
1101 if (model.trackLineWidth) {
1102 if (rcSegment.right + 1> lineWidthMaxSeen) {
1103 // Fold display text border drawn on rcSegment.right with width 1 is the last visble object of the line
1104 lineWidthMaxSeen = static_cast<int>(rcSegment.right + 1);
1108 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1109 surface->FillRectangle(rcSegment, textBack);
1111 // Fill Remainder of the line
1112 PRectangle rcRemainder = rcSegment;
1113 rcRemainder.left = rcRemainder.right + 1;
1114 if (rcRemainder.left < rcLine.left)
1115 rcRemainder.left = rcLine.left;
1116 rcRemainder.right = rcLine.right;
1117 FillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine);
1120 if (phase & drawText) {
1121 if (phasesDraw != phasesOne) {
1122 surface->DrawTextTransparent(rcSegment, textFont,
1123 rcSegment.top + vsDraw.maxAscent, foldDisplayText,
1124 lengthFoldDisplayText, textFore);
1125 } else {
1126 surface->DrawTextNoClip(rcSegment, textFont,
1127 rcSegment.top + vsDraw.maxAscent, foldDisplayText,
1128 lengthFoldDisplayText, textFore, textBack);
1132 if (phase & drawIndicatorsFore) {
1133 if (model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_BOXED) {
1134 surface->PenColour(textFore);
1135 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1136 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1137 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1138 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1139 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1140 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1141 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1142 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1146 if (phase & drawSelectionTranslucent) {
1147 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && alpha != SC_ALPHA_NOALPHA) {
1148 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
1153 static bool AnnotationBoxedOrIndented(int annotationVisible) {
1154 return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED;
1157 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1158 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1159 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
1160 PRectangle rcSegment = rcLine;
1161 int annotationLine = subLine - ll->lines;
1162 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1163 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1164 if (phase & drawBack) {
1165 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1167 rcSegment.left = static_cast<XYPOSITION>(xStart);
1168 if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1169 // Only care about calculating width if tracking or need to draw indented box
1170 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1171 if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1172 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1173 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1174 rcSegment.right = rcSegment.left + widthAnnotation;
1176 if (widthAnnotation > lineWidthMaxSeen)
1177 lineWidthMaxSeen = widthAnnotation;
1179 const int annotationLines = model.pdoc->AnnotationLines(line);
1180 size_t start = 0;
1181 size_t lengthAnnotation = stAnnotation.LineLength(start);
1182 int lineInAnnotation = 0;
1183 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1184 start += lengthAnnotation + 1;
1185 lengthAnnotation = stAnnotation.LineLength(start);
1186 lineInAnnotation++;
1188 PRectangle rcText = rcSegment;
1189 if ((phase & drawBack) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1190 surface->FillRectangle(rcText,
1191 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1192 rcText.left += vsDraw.spaceWidth;
1194 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1195 stAnnotation, start, lengthAnnotation, phase);
1196 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1197 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1198 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1199 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1200 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1201 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1202 if (subLine == ll->lines) {
1203 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1204 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1206 if (subLine == ll->lines + annotationLines - 1) {
1207 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1208 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1214 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1215 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1217 int lineStart = ll->LineStart(subLine);
1218 int posBefore = posCaret;
1219 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1220 int numCharsToDraw = posAfter - posCaret;
1222 // Work out where the starting and ending offsets are. We need to
1223 // see if the previous character shares horizontal space, such as a
1224 // glyph / combining character. If so we'll need to draw that too.
1225 int offsetFirstChar = offset;
1226 int offsetLastChar = offset + (posAfter - posCaret);
1227 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1228 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1229 // The char does not share horizontal space
1230 break;
1232 // Char shares horizontal space, update the numChars to draw
1233 // Update posBefore to point to the prev char
1234 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1235 numCharsToDraw = posAfter - posBefore;
1236 offsetFirstChar = offset - (posCaret - posBefore);
1239 // See if the next character shares horizontal space, if so we'll
1240 // need to draw that too.
1241 if (offsetFirstChar < 0)
1242 offsetFirstChar = 0;
1243 numCharsToDraw = offsetLastChar - offsetFirstChar;
1244 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1245 // Update posAfter to point to the 2nd next char, this is where
1246 // the next character ends, and 2nd next begins. We'll need
1247 // to compare these two
1248 posBefore = posAfter;
1249 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1250 offsetLastChar = offset + (posAfter - posCaret);
1251 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1252 // The char does not share horizontal space
1253 break;
1255 // Char shares horizontal space, update the numChars to draw
1256 numCharsToDraw = offsetLastChar - offsetFirstChar;
1259 // We now know what to draw, update the caret drawing rectangle
1260 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1261 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1263 // Adjust caret position to take into account any word wrapping symbols.
1264 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1265 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1266 rcCaret.left += wordWrapCharWidth;
1267 rcCaret.right += wordWrapCharWidth;
1270 // This character is where the caret block is, we override the colours
1271 // (inversed) for drawing the caret here.
1272 int styleMain = ll->styles[offsetFirstChar];
1273 FontAlias fontText = vsDraw.styles[styleMain].font;
1274 surface->DrawTextClipped(rcCaret, fontText,
1275 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1276 numCharsToDraw, vsDraw.styles[styleMain].back,
1277 caretColour);
1280 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1281 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1282 // When drag is active it is the only caret drawn
1283 bool drawDrag = model.posDrag.IsValid();
1284 if (hideSelection && !drawDrag)
1285 return;
1286 const int posLineStart = model.pdoc->LineStart(lineDoc);
1287 // For each selection draw
1288 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1289 const bool mainCaret = r == model.sel.Main();
1290 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1291 const int offset = posCaret.Position() - posLineStart;
1292 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1293 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1294 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1295 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1296 if (ll->wrapIndent != 0) {
1297 int lineStart = ll->LineStart(subLine);
1298 if (lineStart != 0) // Wrapped
1299 xposCaret += ll->wrapIndent;
1301 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1302 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1303 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1304 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1305 bool caretAtEOF = false;
1306 bool caretAtEOL = false;
1307 bool drawBlockCaret = false;
1308 XYPOSITION widthOverstrikeCaret;
1309 XYPOSITION caretWidthOffset = 0;
1310 PRectangle rcCaret = rcLine;
1312 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1313 caretAtEOF = true;
1314 widthOverstrikeCaret = vsDraw.aveCharWidth;
1315 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1316 caretAtEOL = true;
1317 widthOverstrikeCaret = vsDraw.aveCharWidth;
1318 } else {
1319 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1320 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1322 if (widthOverstrikeCaret < 3) // Make sure its visible
1323 widthOverstrikeCaret = 3;
1325 if (xposCaret > 0)
1326 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1327 xposCaret += xStart;
1328 if (model.posDrag.IsValid()) {
1329 /* Dragging text, use a line caret */
1330 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1331 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1332 } else if (model.inOverstrike && drawOverstrikeCaret) {
1333 /* Overstrike (insert mode), use a modified bar caret */
1334 rcCaret.top = rcCaret.bottom - 2;
1335 rcCaret.left = xposCaret + 1;
1336 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1337 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1338 /* Block caret */
1339 rcCaret.left = xposCaret;
1340 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1341 drawBlockCaret = true;
1342 rcCaret.right = xposCaret + widthOverstrikeCaret;
1343 } else {
1344 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1346 } else {
1347 /* Line caret */
1348 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1349 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1351 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1352 if (drawBlockCaret) {
1353 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1354 } else {
1355 surface->FillRectangle(rcCaret, caretColour);
1359 if (drawDrag)
1360 break;
1364 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1365 int xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker) {
1366 // default bgnd here..
1367 surface->FillRectangle(rcLine, background.isSet ? background :
1368 vsDraw.styles[STYLE_DEFAULT].back);
1370 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1372 // draw continuation rect
1373 PRectangle rcPlace = rcLine;
1375 rcPlace.left = static_cast<XYPOSITION>(xStart);
1376 rcPlace.right = rcPlace.left + ll->wrapIndent;
1378 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1379 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1380 else
1381 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1383 if (customDrawWrapMarker == NULL) {
1384 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1385 } else {
1386 customDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1391 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1392 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1393 int subLine, ColourOptional background) const {
1395 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1396 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1397 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1398 // Does not take margin into account but not significant
1399 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1401 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs, NULL);
1403 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1405 // Background drawing loop
1406 while (bfBack.More()) {
1408 const TextSegment ts = bfBack.Next();
1409 const int i = ts.end() - 1;
1410 const int iDoc = i + posLineStart;
1412 PRectangle rcSegment = rcLine;
1413 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1414 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1415 // Only try to draw if really visible - enhances performance by not calling environment to
1416 // draw strings that are completely past the right side of the window.
1417 if (!rcSegment.Empty() && rcSegment.Intersects(rcLine)) {
1418 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1419 if (rcSegment.left < rcLine.left)
1420 rcSegment.left = rcLine.left;
1421 if (rcSegment.right > rcLine.right)
1422 rcSegment.right = rcLine.right;
1424 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1425 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1426 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1427 inHotspot, ll->styles[i], i);
1428 if (ts.representation) {
1429 if (ll->chars[i] == '\t') {
1430 // Tab display
1431 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1432 textBack = vsDraw.whitespaceColours.back;
1433 } else {
1434 // Blob display
1435 inIndentation = false;
1437 surface->FillRectangle(rcSegment, textBack);
1438 } else {
1439 // Normal text display
1440 surface->FillRectangle(rcSegment, textBack);
1441 if (vsDraw.viewWhitespace != wsInvisible) {
1442 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1443 if (ll->chars[cpos + ts.start] == ' ') {
1444 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {
1445 PRectangle rcSpace(
1446 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1447 rcSegment.top,
1448 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1449 rcSegment.bottom);
1450 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1452 } else {
1453 inIndentation = false;
1458 } else if (rcSegment.left > rcLine.right) {
1459 break;
1464 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1465 Range lineRange, int xStart) {
1466 if (vsDraw.edgeState == EDGE_LINE) {
1467 PRectangle rcSegment = rcLine;
1468 int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);
1469 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1470 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1471 rcSegment.left -= ll->wrapIndent;
1472 rcSegment.right = rcSegment.left + 1;
1473 surface->FillRectangle(rcSegment, vsDraw.theEdge.colour);
1474 } else if (vsDraw.edgeState == EDGE_MULTILINE) {
1475 for (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {
1476 if (vsDraw.theMultiEdge[edge].column >= 0) {
1477 PRectangle rcSegment = rcLine;
1478 int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);
1479 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1480 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1481 rcSegment.left -= ll->wrapIndent;
1482 rcSegment.right = rcSegment.left + 1;
1483 surface->FillRectangle(rcSegment, vsDraw.theMultiEdge[edge].colour);
1489 // Draw underline mark as part of background if not transparent
1490 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1491 int line, PRectangle rcLine) {
1492 int marks = model.pdoc->GetMark(line);
1493 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1494 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1495 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1496 PRectangle rcUnderline = rcLine;
1497 rcUnderline.top = rcUnderline.bottom - 2;
1498 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1500 marks >>= 1;
1503 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1504 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1505 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1506 const int posLineStart = model.pdoc->LineStart(line);
1507 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1508 // For each selection draw
1509 int virtualSpaces = 0;
1510 if (subLine == (ll->lines - 1)) {
1511 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1513 SelectionPosition posStart(posLineStart + lineRange.start);
1514 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1515 SelectionSegment virtualSpaceRange(posStart, posEnd);
1516 for (size_t r = 0; r < model.sel.Count(); r++) {
1517 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1518 if (alpha != SC_ALPHA_NOALPHA) {
1519 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1520 if (!portion.Empty()) {
1521 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1522 PRectangle rcSegment = rcLine;
1523 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1524 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1525 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1526 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1527 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1528 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1529 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1531 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1532 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1533 if (rcSegment.right > rcLine.left)
1534 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1541 // Draw any translucent whole line states
1542 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1543 int line, PRectangle rcLine) {
1544 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1545 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1547 const int marksOfLine = model.pdoc->GetMark(line);
1548 int marksDrawnInText = marksOfLine & vsDraw.maskDrawInText;
1549 for (int markBit = 0; (markBit < 32) && marksDrawnInText; markBit++) {
1550 if (marksDrawnInText & 1) {
1551 if (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) {
1552 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1553 } else if (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) {
1554 PRectangle rcUnderline = rcLine;
1555 rcUnderline.top = rcUnderline.bottom - 2;
1556 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1559 marksDrawnInText >>= 1;
1561 int marksDrawnInLine = marksOfLine & vsDraw.maskInLine;
1562 for (int markBit = 0; (markBit < 32) && marksDrawnInLine; markBit++) {
1563 if (marksDrawnInLine & 1) {
1564 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1566 marksDrawnInLine >>= 1;
1570 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1571 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1572 int subLine, ColourOptional background) {
1574 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1575 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1576 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1578 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1579 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1581 // Does not take margin into account but not significant
1582 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1584 // Foreground drawing loop
1585 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1586 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs, &vsDraw);
1588 while (bfFore.More()) {
1590 const TextSegment ts = bfFore.Next();
1591 const int i = ts.end() - 1;
1592 const int iDoc = i + posLineStart;
1594 PRectangle rcSegment = rcLine;
1595 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1596 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1597 // Only try to draw if really visible - enhances performance by not calling environment to
1598 // draw strings that are completely past the right side of the window.
1599 if (rcSegment.Intersects(rcLine)) {
1600 int styleMain = ll->styles[i];
1601 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1602 FontAlias textFont = vsDraw.styles[styleMain].font;
1603 //hotspot foreground
1604 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1605 if (inHotspot) {
1606 if (vsDraw.hotspotColours.fore.isSet)
1607 textFore = vsDraw.hotspotColours.fore;
1609 if (vsDraw.indicatorsSetFore > 0) {
1610 // At least one indicator sets the text colour so see if it applies to this segment
1611 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
1612 const int indicatorValue = deco->rs.ValueAt(ts.start + posLineStart);
1613 if (indicatorValue) {
1614 const Indicator &indicator = vsDraw.indicators[deco->indicator];
1615 const bool hover = indicator.IsDynamic() &&
1616 ((model.hoverIndicatorPos >= ts.start + posLineStart) &&
1617 (model.hoverIndicatorPos <= ts.end() + posLineStart));
1618 if (hover) {
1619 if (indicator.sacHover.style == INDIC_TEXTFORE) {
1620 textFore = indicator.sacHover.fore;
1622 } else {
1623 if (indicator.sacNormal.style == INDIC_TEXTFORE) {
1624 if (indicator.Flags() & SC_INDICFLAG_VALUEFORE)
1625 textFore = indicatorValue & SC_INDICVALUEMASK;
1626 else
1627 textFore = indicator.sacNormal.fore;
1633 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1634 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1635 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1637 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1638 if (ts.representation) {
1639 if (ll->chars[i] == '\t') {
1640 // Tab display
1641 if (phasesDraw == phasesOne) {
1642 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1643 textBack = vsDraw.whitespaceColours.back;
1644 surface->FillRectangle(rcSegment, textBack);
1646 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1647 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1648 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1649 indentCount++) {
1650 if (indentCount > 0) {
1651 int xIndent = static_cast<int>(indentCount * indentWidth);
1652 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1653 (ll->xHighlightGuide == xIndent));
1657 if (vsDraw.viewWhitespace != wsInvisible) {
1658 if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1659 if (vsDraw.whitespaceColours.fore.isSet)
1660 textFore = vsDraw.whitespaceColours.fore;
1661 surface->PenColour(textFore);
1662 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,
1663 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1664 if (customDrawTabArrow == NULL)
1665 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2), vsDraw);
1666 else
1667 customDrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1670 } else {
1671 inIndentation = false;
1672 if (vsDraw.controlCharSymbol >= 32) {
1673 // Using one font for all control characters so it can be controlled independently to ensure
1674 // the box goes around the characters tightly. Seems to be no way to work out what height
1675 // is taken by an individual character - internal leading gives varying results.
1676 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1677 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1678 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1679 rcSegment.top + vsDraw.maxAscent,
1680 cc, 1, textBack, textFore);
1681 } else {
1682 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1683 textBack, textFore, phasesDraw == phasesOne);
1686 } else {
1687 // Normal text display
1688 if (vsDraw.styles[styleMain].visible) {
1689 if (phasesDraw != phasesOne) {
1690 surface->DrawTextTransparent(rcSegment, textFont,
1691 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1692 i - ts.start + 1, textFore);
1693 } else {
1694 surface->DrawTextNoClip(rcSegment, textFont,
1695 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1696 i - ts.start + 1, textFore, textBack);
1699 if (vsDraw.viewWhitespace != wsInvisible ||
1700 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1701 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1702 if (ll->chars[cpos + ts.start] == ' ') {
1703 if (vsDraw.viewWhitespace != wsInvisible) {
1704 if (vsDraw.whitespaceColours.fore.isSet)
1705 textFore = vsDraw.whitespaceColours.fore;
1706 if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1707 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1708 if ((phasesDraw == phasesOne) && drawWhitespaceBackground) {
1709 textBack = vsDraw.whitespaceColours.back;
1710 PRectangle rcSpace(
1711 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1712 rcSegment.top,
1713 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1714 rcSegment.bottom);
1715 surface->FillRectangle(rcSpace, textBack);
1717 const int halfDotWidth = vsDraw.whitespaceSize / 2;
1718 PRectangle rcDot(xmid + xStart - halfDotWidth - static_cast<XYPOSITION>(subLineStart),
1719 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1720 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1721 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1722 surface->FillRectangle(rcDot, textFore);
1725 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1726 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1727 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1728 indentCount++) {
1729 if (indentCount > 0) {
1730 int xIndent = static_cast<int>(indentCount * indentWidth);
1731 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1732 (ll->xHighlightGuide == xIndent));
1736 } else {
1737 inIndentation = false;
1742 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1743 PRectangle rcUL = rcSegment;
1744 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1745 rcUL.bottom = rcUL.top + 1;
1746 if (vsDraw.hotspotColours.fore.isSet)
1747 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1748 else
1749 surface->FillRectangle(rcUL, textFore);
1750 } else if (vsDraw.styles[styleMain].underline) {
1751 PRectangle rcUL = rcSegment;
1752 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1753 rcUL.bottom = rcUL.top + 1;
1754 surface->FillRectangle(rcUL, textFore);
1756 } else if (rcSegment.left > rcLine.right) {
1757 break;
1762 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1763 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1764 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1765 && (subLine == 0)) {
1766 const int posLineStart = model.pdoc->LineStart(line);
1767 int indentSpace = model.pdoc->GetLineIndentation(line);
1768 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1770 // Find the most recent line with some text
1772 int lineLastWithText = line;
1773 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1774 lineLastWithText--;
1776 if (lineLastWithText < line) {
1777 xStartText = 100000; // Don't limit to visible indentation on empty line
1778 // This line is empty, so use indentation of last line with text
1779 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1780 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1781 if (isFoldHeader) {
1782 // Level is one more level than parent
1783 indentLastWithText += model.pdoc->IndentSize();
1785 if (vsDraw.viewIndentationGuides == ivLookForward) {
1786 // In viLookForward mode, previous line only used if it is a fold header
1787 if (isFoldHeader) {
1788 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1790 } else { // viLookBoth
1791 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1795 int lineNextWithText = line;
1796 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1797 lineNextWithText++;
1799 if (lineNextWithText > line) {
1800 xStartText = 100000; // Don't limit to visible indentation on empty line
1801 // This line is empty, so use indentation of first next line with text
1802 indentSpace = Platform::Maximum(indentSpace,
1803 model.pdoc->GetLineIndentation(lineNextWithText));
1806 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1807 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1808 if (xIndent < xStartText) {
1809 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1810 (ll->xHighlightGuide == xIndent));
1816 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1817 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1819 if (subLine >= ll->lines) {
1820 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1821 return; // No further drawing
1824 // See if something overrides the line background color.
1825 const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1827 const int posLineStart = model.pdoc->LineStart(line);
1829 const Range lineRange = ll->SubLineRange(subLine);
1830 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1832 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1833 if (phase & drawBack) {
1834 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker);
1836 xStart += static_cast<int>(ll->wrapIndent);
1839 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1840 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1841 subLine, background);
1842 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1843 xStart, subLine, subLineStart, background);
1846 if (phase & drawIndicatorsBack) {
1847 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true, model.hoverIndicatorPos);
1848 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1849 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1852 if (phase & drawText) {
1853 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1854 subLine, background);
1857 if (phase & drawIndentationGuides) {
1858 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1861 if (phase & drawIndicatorsFore) {
1862 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false, model.hoverIndicatorPos);
1865 // End of the drawing of the current line
1866 if (phasesDraw == phasesOne) {
1867 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1868 xStart, subLine, subLineStart, background);
1871 DrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, phase);
1873 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1874 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1877 if (phase & drawLineTranslucent) {
1878 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1882 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1883 bool expanded = model.cs.GetExpanded(line);
1884 const int level = model.pdoc->GetLevel(line);
1885 const int levelNext = model.pdoc->GetLevel(line + 1);
1886 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1887 (LevelNumber(level) < LevelNumber(levelNext))) {
1888 // Paint the line above the fold
1889 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1891 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1892 PRectangle rcFoldLine = rcLine;
1893 rcFoldLine.bottom = rcFoldLine.top + 1;
1894 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1896 // Paint the line below the fold
1897 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1899 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1900 PRectangle rcFoldLine = rcLine;
1901 rcFoldLine.top = rcFoldLine.bottom - 1;
1902 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1907 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1908 PRectangle rcClient, const ViewStyle &vsDraw) {
1909 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1910 // serifs and italic stems for aliased text.
1911 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1913 // Do the painting
1914 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1916 Surface *surface = surfaceWindow;
1917 if (bufferedDraw) {
1918 surface = pixmapLine;
1919 PLATFORM_ASSERT(pixmapLine->Initialised());
1921 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1922 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1924 const Point ptOrigin = model.GetVisibleOriginInMain();
1926 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1927 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1929 SelectionPosition posCaret = model.sel.RangeMain().caret;
1930 if (model.posDrag.IsValid())
1931 posCaret = model.posDrag;
1932 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1934 PRectangle rcTextArea = rcClient;
1935 if (vsDraw.marginInside) {
1936 rcTextArea.left += vsDraw.textStart;
1937 rcTextArea.right -= vsDraw.rightMarginWidth;
1938 } else {
1939 rcTextArea = rcArea;
1942 // Remove selection margin from drawing area so text will not be drawn
1943 // on it in unbuffered mode.
1944 if (!bufferedDraw && vsDraw.marginInside) {
1945 PRectangle rcClipText = rcTextArea;
1946 rcClipText.left -= leftTextOverlap;
1947 surfaceWindow->SetClip(rcClipText);
1950 // Loop on visible lines
1951 //double durLayout = 0.0;
1952 //double durPaint = 0.0;
1953 //double durCopy = 0.0;
1954 //ElapsedTime etWhole;
1956 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1957 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1959 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1960 AutoLineLayout ll(llc, 0);
1961 std::vector<DrawPhase> phases;
1962 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1963 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1964 phases.push_back(phase);
1966 } else {
1967 phases.push_back(drawAll);
1969 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1970 int ypos = 0;
1971 if (!bufferedDraw)
1972 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1973 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1974 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1975 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1977 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1978 // Only visible lines should be handled by the code within the loop
1979 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1980 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1981 const int subLine = visibleLine - lineStartSet;
1983 // Copy this line and its styles from the document into local arrays
1984 // and determine the x position at which each character starts.
1985 //ElapsedTime et;
1986 if (lineDoc != lineDocPrevious) {
1987 ll.Set(0);
1988 ll.Set(RetrieveLineLayout(lineDoc, model));
1989 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1990 lineDocPrevious = lineDoc;
1992 //durLayout += et.Duration(true);
1994 if (ll) {
1995 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1996 ll->hotspot = model.GetHotSpotRange();
1998 PRectangle rcLine = rcTextArea;
1999 rcLine.top = static_cast<XYPOSITION>(ypos);
2000 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
2002 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
2004 // Highlight the current braces if any
2005 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
2006 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
2008 if (leftTextOverlap && (bufferedDraw || ((phasesDraw < phasesMultiple) && (*it & drawBack)))) {
2009 // Clear the left margin
2010 PRectangle rcSpacer = rcLine;
2011 rcSpacer.right = rcSpacer.left;
2012 rcSpacer.left -= 1;
2013 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
2016 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
2017 //durPaint += et.Duration(true);
2019 // Restore the previous styles for the brace highlights in case layout is in cache.
2020 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
2022 if (*it & drawFoldLines) {
2023 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
2026 if (*it & drawCarets) {
2027 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
2030 if (bufferedDraw) {
2031 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
2032 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
2033 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
2034 yposScreen + vsDraw.lineHeight);
2035 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
2038 lineWidthMaxSeen = Platform::Maximum(
2039 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
2040 //durCopy += et.Duration(true);
2043 if (!bufferedDraw) {
2044 ypos += vsDraw.lineHeight;
2047 yposScreen += vsDraw.lineHeight;
2048 visibleLine++;
2051 ll.Set(0);
2052 //if (durPaint < 0.00000001)
2053 // durPaint = 0.00000001;
2055 // Right column limit indicator
2056 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
2057 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
2058 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
2059 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
2060 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
2061 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
2062 if (vsDraw.edgeState == EDGE_LINE) {
2063 int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);
2064 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
2065 rcBeyondEOF.right = rcBeyondEOF.left + 1;
2066 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.theEdge.colour);
2067 } else if (vsDraw.edgeState == EDGE_MULTILINE) {
2068 for (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {
2069 if (vsDraw.theMultiEdge[edge].column >= 0) {
2070 int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);
2071 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
2072 rcBeyondEOF.right = rcBeyondEOF.left + 1;
2073 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.theMultiEdge[edge].colour);
2078 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
2080 //Platform::DebugPrintf(
2081 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
2082 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
2086 void EditView::FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
2087 int line, PRectangle rcArea, int subLine) {
2088 int eolInSelection = 0;
2089 int alpha = SC_ALPHA_NOALPHA;
2090 if (!hideSelection) {
2091 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
2092 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
2093 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2096 ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
2098 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2099 surface->FillRectangle(rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
2100 } else {
2101 if (background.isSet) {
2102 surface->FillRectangle(rcArea, background);
2103 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
2104 surface->FillRectangle(rcArea, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
2105 } else {
2106 surface->FillRectangle(rcArea, vsDraw.styles[STYLE_DEFAULT].back);
2108 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2109 SimpleAlphaRectangle(surface, rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
2114 // Space (3 space characters) between line numbers and text when printing.
2115 #define lineNumberPrintSpace " "
2117 static ColourDesired InvertedLight(ColourDesired orig) {
2118 unsigned int r = orig.GetRed();
2119 unsigned int g = orig.GetGreen();
2120 unsigned int b = orig.GetBlue();
2121 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
2122 unsigned int il = 0xff - l;
2123 if (l == 0)
2124 return ColourDesired(0xff, 0xff, 0xff);
2125 r = r * il / l;
2126 g = g * il / l;
2127 b = b * il / l;
2128 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
2131 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
2132 const EditModel &model, const ViewStyle &vs) {
2133 // Can't use measurements cached for screen
2134 posCache.Clear();
2136 ViewStyle vsPrint(vs);
2137 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
2139 // Modify the view style for printing as do not normally want any of the transient features to be printed
2140 // Printing supports only the line number margin.
2141 int lineNumberIndex = -1;
2142 for (size_t margin = 0; margin < vs.ms.size(); margin++) {
2143 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
2144 lineNumberIndex = static_cast<int>(margin);
2145 } else {
2146 vsPrint.ms[margin].width = 0;
2149 vsPrint.fixedColumnWidth = 0;
2150 vsPrint.zoomLevel = printParameters.magnification;
2151 // Don't show indentation guides
2152 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
2153 vsPrint.viewIndentationGuides = ivNone;
2154 // Don't show the selection when printing
2155 vsPrint.selColours.back.isSet = false;
2156 vsPrint.selColours.fore.isSet = false;
2157 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
2158 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
2159 vsPrint.whitespaceColours.back.isSet = false;
2160 vsPrint.whitespaceColours.fore.isSet = false;
2161 vsPrint.showCaretLineBackground = false;
2162 vsPrint.alwaysShowCaretLineBackground = false;
2163 // Don't highlight matching braces using indicators
2164 vsPrint.braceHighlightIndicatorSet = false;
2165 vsPrint.braceBadLightIndicatorSet = false;
2167 // Set colours for printing according to users settings
2168 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
2169 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
2170 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
2171 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
2172 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
2173 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
2174 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2175 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
2176 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2177 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
2178 if (sty <= STYLE_DEFAULT) {
2179 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2183 // White background for the line numbers
2184 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
2186 // Printing uses different margins, so reset screen margins
2187 vsPrint.leftMarginWidth = 0;
2188 vsPrint.rightMarginWidth = 0;
2190 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
2191 // Determining width must happen after fonts have been realised in Refresh
2192 int lineNumberWidth = 0;
2193 if (lineNumberIndex >= 0) {
2194 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
2195 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
2196 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
2197 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
2200 int linePrintStart = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMin));
2201 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
2202 if (linePrintLast < linePrintStart)
2203 linePrintLast = linePrintStart;
2204 int linePrintMax = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMax));
2205 if (linePrintLast > linePrintMax)
2206 linePrintLast = linePrintMax;
2207 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
2208 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
2209 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
2210 int endPosPrint = model.pdoc->Length();
2211 if (linePrintLast < model.pdoc->LinesTotal())
2212 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
2214 // Ensure we are styled to where we are formatting.
2215 model.pdoc->EnsureStyledTo(endPosPrint);
2217 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
2218 int ypos = pfr->rc.top;
2220 int lineDoc = linePrintStart;
2222 int nPrintPos = static_cast<int>(pfr->chrg.cpMin);
2223 int visibleLine = 0;
2224 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
2225 if (printParameters.wrapState == eWrapNone)
2226 widthPrint = LineLayout::wrapWidthInfinite;
2228 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2230 // When printing, the hdc and hdcTarget may be the same, so
2231 // changing the state of surfaceMeasure may change the underlying
2232 // state of surface. Therefore, any cached state is discarded before
2233 // using each surface.
2234 surfaceMeasure->FlushCachedState();
2236 // Copy this line and its styles from the document into local arrays
2237 // and determine the x position at which each character starts.
2238 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2239 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2241 ll.containsCaret = false;
2243 PRectangle rcLine = PRectangle::FromInts(
2244 pfr->rc.left,
2245 ypos,
2246 pfr->rc.right - 1,
2247 ypos + vsPrint.lineHeight);
2249 // When document line is wrapped over multiple display lines, find where
2250 // to start printing from to ensure a particular position is on the first
2251 // line of the page.
2252 if (visibleLine == 0) {
2253 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2254 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2255 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2256 visibleLine = -iwl;
2260 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2261 visibleLine = -(ll.lines - 1);
2265 if (draw && lineNumberWidth &&
2266 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2267 (visibleLine >= 0)) {
2268 char number[100];
2269 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2270 PRectangle rcNumber = rcLine;
2271 rcNumber.right = rcNumber.left + lineNumberWidth;
2272 // Right justify
2273 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2274 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2275 surface->FlushCachedState();
2276 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2277 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2278 vsPrint.styles[STYLE_LINENUMBER].fore,
2279 vsPrint.styles[STYLE_LINENUMBER].back);
2282 // Draw the line
2283 surface->FlushCachedState();
2285 for (int iwl = 0; iwl < ll.lines; iwl++) {
2286 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2287 if (visibleLine >= 0) {
2288 if (draw) {
2289 rcLine.top = static_cast<XYPOSITION>(ypos);
2290 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2291 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2293 ypos += vsPrint.lineHeight;
2295 visibleLine++;
2296 if (iwl == ll.lines - 1)
2297 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2298 else
2299 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2303 ++lineDoc;
2306 // Clear cache so measurements are not used for screen
2307 posCache.Clear();
2309 return nPrintPos;