Update Scintilla to version 3.6.5
[geany-mirror.git] / scintilla / src / EditView.cxx
blob9ca6e95adea23a4b88eba7d744eafd865345c76f
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 "Position.h"
29 #include "SplitVector.h"
30 #include "Partitioning.h"
31 #include "RunStyles.h"
32 #include "ContractionState.h"
33 #include "CellBuffer.h"
34 #include "PerLine.h"
35 #include "KeyMap.h"
36 #include "Indicator.h"
37 #include "XPM.h"
38 #include "LineMarker.h"
39 #include "Style.h"
40 #include "ViewStyle.h"
41 #include "CharClassify.h"
42 #include "Decoration.h"
43 #include "CaseFolder.h"
44 #include "Document.h"
45 #include "UniConversion.h"
46 #include "Selection.h"
47 #include "PositionCache.h"
48 #include "EditModel.h"
49 #include "MarginView.h"
50 #include "EditView.h"
52 #ifdef SCI_NAMESPACE
53 using namespace Scintilla;
54 #endif
56 static inline bool IsControlCharacter(int ch) {
57 // iscntrl returns true for lots of chars > 127 which are displayable
58 return ch >= 0 && ch < ' ';
61 PrintParameters::PrintParameters() {
62 magnification = 0;
63 colourMode = SC_PRINT_NORMAL;
64 wrapState = eWrapWord;
67 #ifdef SCI_NAMESPACE
68 namespace Scintilla {
69 #endif
71 bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) {
72 if (st.multipleStyles) {
73 for (size_t iStyle = 0; iStyle<st.length; iStyle++) {
74 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
75 return false;
77 } else {
78 if (!vs.ValidStyle(styleOffset + st.style))
79 return false;
81 return true;
84 static int WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset,
85 const char *text, const unsigned char *styles, size_t len) {
86 int width = 0;
87 size_t start = 0;
88 while (start < len) {
89 size_t style = styles[start];
90 size_t endSegment = start;
91 while ((endSegment + 1 < len) && (static_cast<size_t>(styles[endSegment + 1]) == style))
92 endSegment++;
93 FontAlias fontText = vs.styles[style + styleOffset].font;
94 width += static_cast<int>(surface->WidthText(fontText, text + start,
95 static_cast<int>(endSegment - start + 1)));
96 start = endSegment + 1;
98 return width;
101 int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) {
102 int widthMax = 0;
103 size_t start = 0;
104 while (start < st.length) {
105 size_t lenLine = st.LineLength(start);
106 int widthSubLine;
107 if (st.multipleStyles) {
108 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
109 } else {
110 FontAlias fontText = vs.styles[styleOffset + st.style].font;
111 widthSubLine = static_cast<int>(surface->WidthText(fontText,
112 st.text + start, static_cast<int>(lenLine)));
114 if (widthSubLine > widthMax)
115 widthMax = widthSubLine;
116 start += lenLine + 1;
118 return widthMax;
121 void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,
122 const char *s, int len, DrawPhase phase) {
123 FontAlias fontText = style.font;
124 if (phase & drawBack) {
125 if (phase & drawText) {
126 // Drawing both
127 surface->DrawTextNoClip(rc, fontText, ybase, s, len,
128 style.fore, style.back);
129 } else {
130 surface->FillRectangle(rc, style.back);
132 } else if (phase & drawText) {
133 surface->DrawTextTransparent(rc, fontText, ybase, s, len, style.fore);
137 void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
138 const StyledText &st, size_t start, size_t length, DrawPhase phase) {
140 if (st.multipleStyles) {
141 int x = static_cast<int>(rcText.left);
142 size_t i = 0;
143 while (i < length) {
144 size_t end = i;
145 size_t style = st.styles[i + start];
146 while (end < length - 1 && st.styles[start + end + 1] == style)
147 end++;
148 style += styleOffset;
149 FontAlias fontText = vs.styles[style].font;
150 const int width = static_cast<int>(surface->WidthText(fontText,
151 st.text + start + i, static_cast<int>(end - i + 1)));
152 PRectangle rcSegment = rcText;
153 rcSegment.left = static_cast<XYPOSITION>(x);
154 rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
155 DrawTextNoClipPhase(surface, rcSegment, vs.styles[style],
156 rcText.top + vs.maxAscent, st.text + start + i,
157 static_cast<int>(end - i + 1), phase);
158 x += width;
159 i = end + 1;
161 } else {
162 const size_t style = st.style + styleOffset;
163 DrawTextNoClipPhase(surface, rcText, vs.styles[style],
164 rcText.top + vs.maxAscent, st.text + start,
165 static_cast<int>(length), phase);
169 #ifdef SCI_NAMESPACE
171 #endif
173 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
175 EditView::EditView() {
176 ldTabstops = NULL;
177 tabWidthMinimumPixels = 2; // needed for calculating tab stops for fractional proportional fonts
178 hideSelection = false;
179 drawOverstrikeCaret = true;
180 bufferedDraw = true;
181 phasesDraw = phasesTwo;
182 lineWidthMaxSeen = 0;
183 additionalCaretsBlink = true;
184 additionalCaretsVisible = true;
185 imeCaretBlockOverride = false;
186 pixmapLine = 0;
187 pixmapIndentGuide = 0;
188 pixmapIndentGuideHighlight = 0;
189 llc.SetLevel(LineLayoutCache::llcCaret);
190 posCache.SetSize(0x400);
191 tabArrowHeight = 4;
192 customDrawTabArrow = NULL;
193 customDrawWrapMarker = NULL;
196 EditView::~EditView() {
197 delete ldTabstops;
198 ldTabstops = NULL;
201 bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) {
202 const PhasesDraw phasesDrawNew = twoPhaseDraw ? phasesTwo : phasesOne;
203 const bool redraw = phasesDraw != phasesDrawNew;
204 phasesDraw = phasesDrawNew;
205 return redraw;
208 bool EditView::SetPhasesDraw(int phases) {
209 const PhasesDraw phasesDrawNew = static_cast<PhasesDraw>(phases);
210 const bool redraw = phasesDraw != phasesDrawNew;
211 phasesDraw = phasesDrawNew;
212 return redraw;
215 bool EditView::LinesOverlap() const {
216 return phasesDraw == phasesMultiple;
219 void EditView::ClearAllTabstops() {
220 delete ldTabstops;
221 ldTabstops = 0;
224 XYPOSITION EditView::NextTabstopPos(int line, XYPOSITION x, XYPOSITION tabWidth) const {
225 int next = GetNextTabstop(line, static_cast<int>(x + tabWidthMinimumPixels));
226 if (next > 0)
227 return static_cast<XYPOSITION>(next);
228 return (static_cast<int>((x + tabWidthMinimumPixels) / tabWidth) + 1) * tabWidth;
231 bool EditView::ClearTabstops(int line) {
232 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
233 return lt && lt->ClearTabstops(line);
236 bool EditView::AddTabstop(int line, int x) {
237 if (!ldTabstops) {
238 ldTabstops = new LineTabstops();
240 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
241 return lt && lt->AddTabstop(line, x);
244 int EditView::GetNextTabstop(int line, int x) const {
245 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
246 if (lt) {
247 return lt->GetNextTabstop(line, x);
248 } else {
249 return 0;
253 void EditView::LinesAddedOrRemoved(int lineOfPos, int linesAdded) {
254 if (ldTabstops) {
255 if (linesAdded > 0) {
256 for (int line = lineOfPos; line < lineOfPos + linesAdded; line++) {
257 ldTabstops->InsertLine(line);
259 } else {
260 for (int line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {
261 ldTabstops->RemoveLine(line);
267 void EditView::DropGraphics(bool freeObjects) {
268 if (freeObjects) {
269 delete pixmapLine;
270 pixmapLine = 0;
271 delete pixmapIndentGuide;
272 pixmapIndentGuide = 0;
273 delete pixmapIndentGuideHighlight;
274 pixmapIndentGuideHighlight = 0;
275 } else {
276 if (pixmapLine)
277 pixmapLine->Release();
278 if (pixmapIndentGuide)
279 pixmapIndentGuide->Release();
280 if (pixmapIndentGuideHighlight)
281 pixmapIndentGuideHighlight->Release();
285 void EditView::AllocateGraphics(const ViewStyle &vsDraw) {
286 if (!pixmapLine)
287 pixmapLine = Surface::Allocate(vsDraw.technology);
288 if (!pixmapIndentGuide)
289 pixmapIndentGuide = Surface::Allocate(vsDraw.technology);
290 if (!pixmapIndentGuideHighlight)
291 pixmapIndentGuideHighlight = Surface::Allocate(vsDraw.technology);
294 static const char *ControlCharacterString(unsigned char ch) {
295 const char *reps[] = {
296 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
297 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
298 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
299 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
301 if (ch < ELEMENTS(reps)) {
302 return reps[ch];
303 } else {
304 return "BAD";
308 static void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
309 int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
310 int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
311 if (xhead <= rcTab.left) {
312 ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
313 xhead = static_cast<int>(rcTab.left) - 1;
315 if ((rcTab.left + 2) < (rcTab.right - 1))
316 surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
317 else
318 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
319 surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
320 surface->LineTo(xhead, ymid - ydiff);
321 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
322 surface->LineTo(xhead, ymid + ydiff);
325 void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
326 if (!pixmapIndentGuide->Initialised()) {
327 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
328 pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
329 pixmapIndentGuideHighlight->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
330 PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight);
331 pixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[STYLE_INDENTGUIDE].back);
332 pixmapIndentGuide->PenColour(vsDraw.styles[STYLE_INDENTGUIDE].fore);
333 pixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[STYLE_BRACELIGHT].back);
334 pixmapIndentGuideHighlight->PenColour(vsDraw.styles[STYLE_BRACELIGHT].fore);
335 for (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) {
336 PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
337 pixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[STYLE_INDENTGUIDE].fore);
338 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[STYLE_BRACELIGHT].fore);
343 LineLayout *EditView::RetrieveLineLayout(int lineNumber, const EditModel &model) {
344 int posLineStart = model.pdoc->LineStart(lineNumber);
345 int posLineEnd = model.pdoc->LineStart(lineNumber + 1);
346 PLATFORM_ASSERT(posLineEnd >= posLineStart);
347 int lineCaret = model.pdoc->LineFromPosition(model.sel.MainCaret());
348 return llc.Retrieve(lineNumber, lineCaret,
349 posLineEnd - posLineStart, model.pdoc->GetStyleClock(),
350 model.LinesOnScreen() + 1, model.pdoc->LinesTotal());
354 * Fill in the LineLayout data for the given line.
355 * Copy the given @a line and its styles from the document into local arrays.
356 * Also determine the x position at which each character starts.
358 void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
359 if (!ll)
360 return;
362 PLATFORM_ASSERT(line < model.pdoc->LinesTotal());
363 PLATFORM_ASSERT(ll->chars != NULL);
364 int posLineStart = model.pdoc->LineStart(line);
365 int posLineEnd = model.pdoc->LineStart(line + 1);
366 // If the line is very long, limit the treatment to a length that should fit in the viewport
367 if (posLineEnd >(posLineStart + ll->maxLineLength)) {
368 posLineEnd = posLineStart + ll->maxLineLength;
370 if (ll->validity == LineLayout::llCheckTextAndStyle) {
371 int lineLength = posLineEnd - posLineStart;
372 if (!vstyle.viewEOL) {
373 lineLength = model.pdoc->LineEnd(line) - posLineStart;
375 if (lineLength == ll->numCharsInLine) {
376 // See if chars, styles, indicators, are all the same
377 bool allSame = true;
378 // Check base line layout
379 int styleByte = 0;
380 int numCharsInLine = 0;
381 while (numCharsInLine < lineLength) {
382 int charInDoc = numCharsInLine + posLineStart;
383 char chDoc = model.pdoc->CharAt(charInDoc);
384 styleByte = model.pdoc->StyleIndexAt(charInDoc);
385 allSame = allSame &&
386 (ll->styles[numCharsInLine] == styleByte);
387 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
388 allSame = allSame &&
389 (ll->chars[numCharsInLine] == chDoc);
390 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
391 allSame = allSame &&
392 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
393 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
394 allSame = allSame &&
395 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
396 else { // Style::caseCamel
397 if ((model.pdoc->WordCharClass(ll->chars[numCharsInLine]) == CharClassify::ccWord) &&
398 ((numCharsInLine == 0) || (model.pdoc->WordCharClass(ll->chars[numCharsInLine - 1]) != CharClassify::ccWord))) {
399 allSame = allSame && (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
400 } else {
401 allSame = allSame && (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
404 numCharsInLine++;
406 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
407 if (allSame) {
408 ll->validity = LineLayout::llPositions;
409 } else {
410 ll->validity = LineLayout::llInvalid;
412 } else {
413 ll->validity = LineLayout::llInvalid;
416 if (ll->validity == LineLayout::llInvalid) {
417 ll->widthLine = LineLayout::wrapWidthInfinite;
418 ll->lines = 1;
419 if (vstyle.edgeState == EDGE_BACKGROUND) {
420 ll->edgeColumn = model.pdoc->FindColumn(line, vstyle.theEdge);
421 if (ll->edgeColumn >= posLineStart) {
422 ll->edgeColumn -= posLineStart;
424 } else {
425 ll->edgeColumn = -1;
428 // Fill base line layout
429 const int lineLength = posLineEnd - posLineStart;
430 model.pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
431 model.pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
432 int numCharsBeforeEOL = model.pdoc->LineEnd(line) - posLineStart;
433 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
434 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
435 const unsigned char styleByte = ll->styles[styleInLine];
436 ll->styles[styleInLine] = styleByte;
438 const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
439 if (vstyle.someStylesForceCase) {
440 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
441 char chDoc = ll->chars[charInLine];
442 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
443 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
444 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
445 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
446 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseCamel) {
447 if ((model.pdoc->WordCharClass(ll->chars[charInLine]) == CharClassify::ccWord) &&
448 ((charInLine == 0) || (model.pdoc->WordCharClass(ll->chars[charInLine - 1]) != CharClassify::ccWord))) {
449 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
450 } else {
451 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
456 ll->xHighlightGuide = 0;
457 // Extra element at the end of the line to hold end x position and act as
458 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
459 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
461 // Layout the line, determining the position of each character,
462 // with an extra element at the end for the end of the line.
463 ll->positions[0] = 0;
464 bool lastSegItalics = false;
466 BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs, NULL);
467 while (bfLayout.More()) {
469 const TextSegment ts = bfLayout.Next();
471 std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f);
472 if (vstyle.styles[ll->styles[ts.start]].visible) {
473 if (ts.representation) {
474 XYPOSITION representationWidth = vstyle.controlCharWidth;
475 if (ll->chars[ts.start] == '\t') {
476 // Tab is a special case of representation, taking a variable amount of space
477 const XYPOSITION x = ll->positions[ts.start];
478 representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start];
479 } else {
480 if (representationWidth <= 0.0) {
481 XYPOSITION positionsRepr[256]; // Should expand when needed
482 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
483 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
484 representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
487 for (int ii = 0; ii < ts.length; ii++)
488 ll->positions[ts.start + 1 + ii] = representationWidth;
489 } else {
490 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
491 // Over half the segments are single characters and of these about half are space characters.
492 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
493 } else {
494 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
495 ts.length, ll->positions + ts.start + 1, model.pdoc);
498 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
501 for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
502 ll->positions[posToIncrease] += ll->positions[ts.start];
506 // Small hack to make lines that end with italics not cut off the edge of the last character
507 if (lastSegItalics) {
508 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
510 ll->numCharsInLine = numCharsInLine;
511 ll->numCharsBeforeEOL = numCharsBeforeEOL;
512 ll->validity = LineLayout::llPositions;
514 // Hard to cope when too narrow, so just assume there is space
515 if (width < 20) {
516 width = 20;
518 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
519 ll->widthLine = width;
520 if (width == LineLayout::wrapWidthInfinite) {
521 ll->lines = 1;
522 } else if (width > ll->positions[ll->numCharsInLine]) {
523 // Simple common case where line does not need wrapping.
524 ll->lines = 1;
525 } else {
526 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
527 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
529 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
530 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
531 wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
532 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
533 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
535 ll->wrapIndent = wrapAddIndent;
536 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
537 for (int i = 0; i < ll->numCharsInLine; i++) {
538 if (!IsSpaceOrTab(ll->chars[i])) {
539 ll->wrapIndent += ll->positions[i]; // Add line indent
540 break;
543 // Check for text width minimum
544 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
545 ll->wrapIndent = wrapAddIndent;
546 // Check for wrapIndent minimum
547 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
548 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
549 ll->lines = 0;
550 // Calculate line start positions based upon width.
551 int lastGoodBreak = 0;
552 int lastLineStart = 0;
553 XYACCUMULATOR startOffset = 0;
554 int p = 0;
555 while (p < ll->numCharsInLine) {
556 if ((ll->positions[p + 1] - startOffset) >= width) {
557 if (lastGoodBreak == lastLineStart) {
558 // Try moving to start of last character
559 if (p > 0) {
560 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
561 - posLineStart;
563 if (lastGoodBreak == lastLineStart) {
564 // Ensure at least one character on line.
565 lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
566 - posLineStart;
569 lastLineStart = lastGoodBreak;
570 ll->lines++;
571 ll->SetLineStart(ll->lines, lastGoodBreak);
572 startOffset = ll->positions[lastGoodBreak];
573 // take into account the space for start wrap mark and indent
574 startOffset -= ll->wrapIndent;
575 p = lastGoodBreak + 1;
576 continue;
578 if (p > 0) {
579 if (vstyle.wrapState == eWrapChar) {
580 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
581 - posLineStart;
582 p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
583 continue;
584 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
585 lastGoodBreak = p;
586 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
587 lastGoodBreak = p;
590 p++;
592 ll->lines++;
594 ll->validity = LineLayout::llLines;
598 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) {
599 Point pt;
600 if (pos.Position() == INVALID_POSITION)
601 return pt;
602 const int line = model.pdoc->LineFromPosition(pos.Position());
603 const int lineVisible = model.cs.DisplayFromDoc(line);
604 //Platform::DebugPrintf("line=%d\n", line);
605 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
606 if (surface && ll) {
607 const int posLineStart = model.pdoc->LineStart(line);
608 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
609 const int posInLine = pos.Position() - posLineStart;
610 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
611 pt.y += (lineVisible - topLine) * vs.lineHeight;
612 pt.x += vs.textStart - model.xOffset;
614 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
615 return pt;
618 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
619 pt.x = pt.x - vs.textStart;
620 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
621 if (!canReturnInvalid && (visibleLine < 0))
622 visibleLine = 0;
623 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
624 if (canReturnInvalid && (lineDoc < 0))
625 return SelectionPosition(INVALID_POSITION);
626 if (lineDoc >= model.pdoc->LinesTotal())
627 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length());
628 const int posLineStart = model.pdoc->LineStart(lineDoc);
629 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
630 if (surface && ll) {
631 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
632 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
633 const int subLine = visibleLine - lineStartSet;
634 if (subLine < ll->lines) {
635 const Range rangeSubLine = ll->SubLineRange(subLine);
636 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
637 if (subLine > 0) // Wrapped
638 pt.x -= ll->wrapIndent;
639 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
640 if (positionInLine < rangeSubLine.end) {
641 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
643 if (virtualSpace) {
644 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
645 const int spaceOffset = static_cast<int>(
646 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
647 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
648 } else if (canReturnInvalid) {
649 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
650 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
652 } else {
653 return SelectionPosition(rangeSubLine.end + posLineStart);
656 if (!canReturnInvalid)
657 return SelectionPosition(ll->numCharsInLine + posLineStart);
659 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
663 * Find the document position corresponding to an x coordinate on a particular document line.
664 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
665 * This method is used for rectangular selections and does not work on wrapped lines.
667 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
668 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
669 if (surface && ll) {
670 const int posLineStart = model.pdoc->LineStart(lineDoc);
671 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
672 const Range rangeSubLine = ll->SubLineRange(0);
673 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
674 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
675 if (positionInLine < rangeSubLine.end) {
676 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
678 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
679 const int spaceOffset = static_cast<int>(
680 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
681 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
683 return SelectionPosition(0);
686 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
687 int lineDoc = model.pdoc->LineFromPosition(pos);
688 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
689 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
690 if (surface && ll) {
691 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
692 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
693 int posInLine = pos - posLineStart;
694 lineDisplay--; // To make up for first increment ahead.
695 for (int subLine = 0; subLine < ll->lines; subLine++) {
696 if (posInLine >= ll->LineStart(subLine)) {
697 lineDisplay++;
701 return lineDisplay;
704 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
705 int line = model.pdoc->LineFromPosition(pos);
706 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
707 int posRet = INVALID_POSITION;
708 if (surface && ll) {
709 unsigned int posLineStart = model.pdoc->LineStart(line);
710 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
711 int posInLine = pos - posLineStart;
712 if (posInLine <= ll->maxLineLength) {
713 for (int subLine = 0; subLine < ll->lines; subLine++) {
714 if ((posInLine >= ll->LineStart(subLine)) &&
715 (posInLine <= ll->LineStart(subLine + 1)) &&
716 (posInLine <= ll->numCharsBeforeEOL)) {
717 if (start) {
718 posRet = ll->LineStart(subLine) + posLineStart;
719 } else {
720 if (subLine == ll->lines - 1)
721 posRet = ll->numCharsBeforeEOL + posLineStart;
722 else
723 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
729 return posRet;
732 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
733 return main ?
734 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
735 vsDraw.selAdditionalBackground;
738 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
739 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
740 if (inSelection == 1) {
741 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
742 return SelectionBackground(vsDraw, true, model.primarySelection);
744 } else if (inSelection == 2) {
745 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
746 return SelectionBackground(vsDraw, false, model.primarySelection);
748 } else {
749 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
750 (i >= ll->edgeColumn) &&
751 (i < ll->numCharsBeforeEOL))
752 return vsDraw.edgecolour;
753 if (inHotspot && vsDraw.hotspotColours.back.isSet)
754 return vsDraw.hotspotColours.back;
756 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
757 return background;
758 } else {
759 return vsDraw.styles[styleMain].back;
763 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
764 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
765 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
766 surface->Copy(rcCopyArea, from,
767 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
770 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
771 if (alpha != SC_ALPHA_NOALPHA) {
772 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
776 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
777 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
778 if (rcSegment.Empty())
779 return;
780 if (fillBackground) {
781 surface->FillRectangle(rcSegment, textBack);
783 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
784 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
785 surface->InternalLeading(ctrlCharsFont));
786 PRectangle rcCChar = rcSegment;
787 rcCChar.left = rcCChar.left + 1;
788 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
789 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
790 PRectangle rcCentral = rcCChar;
791 rcCentral.top++;
792 rcCentral.bottom--;
793 surface->FillRectangle(rcCentral, textFore);
794 PRectangle rcChar = rcCChar;
795 rcChar.left++;
796 rcChar.right--;
797 surface->DrawTextClipped(rcChar, ctrlCharsFont,
798 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
799 textBack, textFore);
802 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
803 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
804 ColourOptional background) {
806 const int posLineStart = model.pdoc->LineStart(line);
807 PRectangle rcSegment = rcLine;
809 const bool lastSubLine = subLine == (ll->lines - 1);
810 XYPOSITION virtualSpace = 0;
811 if (lastSubLine) {
812 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
813 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
815 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
817 // Fill the virtual space and show selections within it
818 if (virtualSpace > 0.0f) {
819 rcSegment.left = xEol + xStart;
820 rcSegment.right = xEol + xStart + virtualSpace;
821 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
822 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
823 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
824 for (size_t r = 0; r<model.sel.Count(); r++) {
825 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
826 if (alpha == SC_ALPHA_NOALPHA) {
827 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
828 if (!portion.Empty()) {
829 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
830 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
831 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
832 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
833 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
834 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
835 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
836 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
843 int eolInSelection = 0;
844 int alpha = SC_ALPHA_NOALPHA;
845 if (!hideSelection) {
846 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
847 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
848 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
851 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
852 XYPOSITION blobsWidth = 0;
853 if (lastSubLine) {
854 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
855 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
856 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
857 blobsWidth += rcSegment.Width();
858 char hexits[4];
859 const char *ctrlChar;
860 unsigned char chEOL = ll->chars[eolPos];
861 int styleMain = ll->styles[eolPos];
862 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
863 if (UTF8IsAscii(chEOL)) {
864 ctrlChar = ControlCharacterString(chEOL);
865 } else {
866 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
867 if (repr) {
868 ctrlChar = repr->stringRep.c_str();
869 eolPos = ll->numCharsInLine;
870 } else {
871 sprintf(hexits, "x%2X", chEOL);
872 ctrlChar = hexits;
875 ColourDesired textFore = vsDraw.styles[styleMain].fore;
876 if (eolInSelection && vsDraw.selColours.fore.isSet) {
877 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
879 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
880 if (alpha == SC_ALPHA_NOALPHA) {
881 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
882 } else {
883 surface->FillRectangle(rcSegment, textBack);
885 } else {
886 surface->FillRectangle(rcSegment, textBack);
888 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
889 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
890 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
895 // Draw the eol-is-selected rectangle
896 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
897 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
899 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
900 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
901 } else {
902 if (background.isSet) {
903 surface->FillRectangle(rcSegment, background);
904 } else if (line < model.pdoc->LinesTotal() - 1) {
905 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
906 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
907 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
908 } else {
909 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
911 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
912 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
916 // Fill the remainder of the line
917 rcSegment.left = rcSegment.right;
918 if (rcSegment.left < rcLine.left)
919 rcSegment.left = rcLine.left;
920 rcSegment.right = rcLine.right;
922 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
923 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
924 } else {
925 if (background.isSet) {
926 surface->FillRectangle(rcSegment, background);
927 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
928 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
929 } else {
930 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
932 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
933 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
937 bool drawWrapMarkEnd = false;
939 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
940 if (subLine + 1 < ll->lines) {
941 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
945 if (drawWrapMarkEnd) {
946 PRectangle rcPlace = rcSegment;
948 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
949 rcPlace.left = xEol + xStart + virtualSpace;
950 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
951 } else {
952 // rcLine is clipped to text area
953 rcPlace.right = rcLine.right;
954 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
956 if (customDrawWrapMarker == NULL) {
957 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
958 } else {
959 customDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
964 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
965 const LineLayout *ll, int xStart, PRectangle rcLine, int subLine, Indicator::DrawState drawState, int value) {
966 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
967 PRectangle rcIndic(
968 ll->positions[startPos] + xStart - subLineStart,
969 rcLine.top + vsDraw.maxAscent,
970 ll->positions[endPos] + xStart - subLineStart,
971 rcLine.top + vsDraw.maxAscent + 3);
972 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, drawState, value);
975 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
976 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under, int hoverIndicatorPos) {
977 // Draw decorators
978 const int posLineStart = model.pdoc->LineStart(line);
979 const int lineStart = ll->LineStart(subLine);
980 const int posLineEnd = posLineStart + lineEnd;
982 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
983 if (under == vsDraw.indicators[deco->indicator].under) {
984 int startPos = posLineStart + lineStart;
985 if (!deco->rs.ValueAt(startPos)) {
986 startPos = deco->rs.EndRun(startPos);
988 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
989 const Range rangeRun(deco->rs.StartRun(startPos), deco->rs.EndRun(startPos));
990 const int endPos = std::min(rangeRun.end, posLineEnd);
991 const bool hover = vsDraw.indicators[deco->indicator].IsDynamic() &&
992 rangeRun.ContainsCharacter(hoverIndicatorPos);
993 const int value = deco->rs.ValueAt(startPos);
994 Indicator::DrawState drawState = hover ? Indicator::drawHover : Indicator::drawNormal;
995 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
996 surface, vsDraw, ll, xStart, rcLine, subLine, drawState, value);
997 startPos = endPos;
998 if (!deco->rs.ValueAt(startPos)) {
999 startPos = deco->rs.EndRun(startPos);
1005 // Use indicators to highlight matching braces
1006 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1007 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
1008 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
1009 if (under == vsDraw.indicators[braceIndicator].under) {
1010 Range rangeLine(posLineStart + lineStart, posLineEnd);
1011 if (rangeLine.ContainsCharacter(model.braces[0])) {
1012 int braceOffset = model.braces[0] - posLineStart;
1013 if (braceOffset < ll->numCharsInLine) {
1014 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine, Indicator::drawNormal, 1);
1017 if (rangeLine.ContainsCharacter(model.braces[1])) {
1018 int braceOffset = model.braces[1] - posLineStart;
1019 if (braceOffset < ll->numCharsInLine) {
1020 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine, Indicator::drawNormal, 1);
1027 static bool AnnotationBoxedOrIndented(int annotationVisible) {
1028 return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED;
1031 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1032 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1033 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
1034 PRectangle rcSegment = rcLine;
1035 int annotationLine = subLine - ll->lines;
1036 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1037 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1038 if (phase & drawBack) {
1039 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1041 rcSegment.left = static_cast<XYPOSITION>(xStart);
1042 if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1043 // Only care about calculating width if tracking or need to draw indented box
1044 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1045 if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1046 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1047 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1048 rcSegment.right = rcSegment.left + widthAnnotation;
1050 if (widthAnnotation > lineWidthMaxSeen)
1051 lineWidthMaxSeen = widthAnnotation;
1053 const int annotationLines = model.pdoc->AnnotationLines(line);
1054 size_t start = 0;
1055 size_t lengthAnnotation = stAnnotation.LineLength(start);
1056 int lineInAnnotation = 0;
1057 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1058 start += lengthAnnotation + 1;
1059 lengthAnnotation = stAnnotation.LineLength(start);
1060 lineInAnnotation++;
1062 PRectangle rcText = rcSegment;
1063 if ((phase & drawBack) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1064 surface->FillRectangle(rcText,
1065 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1066 rcText.left += vsDraw.spaceWidth;
1068 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1069 stAnnotation, start, lengthAnnotation, phase);
1070 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1071 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1072 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1073 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1074 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1075 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1076 if (subLine == ll->lines) {
1077 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1078 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1080 if (subLine == ll->lines + annotationLines - 1) {
1081 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1082 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1088 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1089 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1091 int lineStart = ll->LineStart(subLine);
1092 int posBefore = posCaret;
1093 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1094 int numCharsToDraw = posAfter - posCaret;
1096 // Work out where the starting and ending offsets are. We need to
1097 // see if the previous character shares horizontal space, such as a
1098 // glyph / combining character. If so we'll need to draw that too.
1099 int offsetFirstChar = offset;
1100 int offsetLastChar = offset + (posAfter - posCaret);
1101 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1102 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1103 // The char does not share horizontal space
1104 break;
1106 // Char shares horizontal space, update the numChars to draw
1107 // Update posBefore to point to the prev char
1108 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1109 numCharsToDraw = posAfter - posBefore;
1110 offsetFirstChar = offset - (posCaret - posBefore);
1113 // See if the next character shares horizontal space, if so we'll
1114 // need to draw that too.
1115 if (offsetFirstChar < 0)
1116 offsetFirstChar = 0;
1117 numCharsToDraw = offsetLastChar - offsetFirstChar;
1118 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1119 // Update posAfter to point to the 2nd next char, this is where
1120 // the next character ends, and 2nd next begins. We'll need
1121 // to compare these two
1122 posBefore = posAfter;
1123 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1124 offsetLastChar = offset + (posAfter - posCaret);
1125 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1126 // The char does not share horizontal space
1127 break;
1129 // Char shares horizontal space, update the numChars to draw
1130 numCharsToDraw = offsetLastChar - offsetFirstChar;
1133 // We now know what to draw, update the caret drawing rectangle
1134 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1135 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1137 // Adjust caret position to take into account any word wrapping symbols.
1138 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1139 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1140 rcCaret.left += wordWrapCharWidth;
1141 rcCaret.right += wordWrapCharWidth;
1144 // This character is where the caret block is, we override the colours
1145 // (inversed) for drawing the caret here.
1146 int styleMain = ll->styles[offsetFirstChar];
1147 FontAlias fontText = vsDraw.styles[styleMain].font;
1148 surface->DrawTextClipped(rcCaret, fontText,
1149 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1150 numCharsToDraw, vsDraw.styles[styleMain].back,
1151 caretColour);
1154 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1155 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1156 // When drag is active it is the only caret drawn
1157 bool drawDrag = model.posDrag.IsValid();
1158 if (hideSelection && !drawDrag)
1159 return;
1160 const int posLineStart = model.pdoc->LineStart(lineDoc);
1161 // For each selection draw
1162 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1163 const bool mainCaret = r == model.sel.Main();
1164 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1165 const int offset = posCaret.Position() - posLineStart;
1166 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1167 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1168 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1169 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1170 if (ll->wrapIndent != 0) {
1171 int lineStart = ll->LineStart(subLine);
1172 if (lineStart != 0) // Wrapped
1173 xposCaret += ll->wrapIndent;
1175 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1176 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1177 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1178 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1179 bool caretAtEOF = false;
1180 bool caretAtEOL = false;
1181 bool drawBlockCaret = false;
1182 XYPOSITION widthOverstrikeCaret;
1183 XYPOSITION caretWidthOffset = 0;
1184 PRectangle rcCaret = rcLine;
1186 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1187 caretAtEOF = true;
1188 widthOverstrikeCaret = vsDraw.aveCharWidth;
1189 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1190 caretAtEOL = true;
1191 widthOverstrikeCaret = vsDraw.aveCharWidth;
1192 } else {
1193 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1194 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1196 if (widthOverstrikeCaret < 3) // Make sure its visible
1197 widthOverstrikeCaret = 3;
1199 if (xposCaret > 0)
1200 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1201 xposCaret += xStart;
1202 if (model.posDrag.IsValid()) {
1203 /* Dragging text, use a line caret */
1204 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1205 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1206 } else if (model.inOverstrike && drawOverstrikeCaret) {
1207 /* Overstrike (insert mode), use a modified bar caret */
1208 rcCaret.top = rcCaret.bottom - 2;
1209 rcCaret.left = xposCaret + 1;
1210 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1211 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1212 /* Block caret */
1213 rcCaret.left = xposCaret;
1214 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1215 drawBlockCaret = true;
1216 rcCaret.right = xposCaret + widthOverstrikeCaret;
1217 } else {
1218 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1220 } else {
1221 /* Line caret */
1222 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1223 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1225 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1226 if (drawBlockCaret) {
1227 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1228 } else {
1229 surface->FillRectangle(rcCaret, caretColour);
1233 if (drawDrag)
1234 break;
1238 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1239 int xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker) {
1240 // default bgnd here..
1241 surface->FillRectangle(rcLine, background.isSet ? background :
1242 vsDraw.styles[STYLE_DEFAULT].back);
1244 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1246 // draw continuation rect
1247 PRectangle rcPlace = rcLine;
1249 rcPlace.left = static_cast<XYPOSITION>(xStart);
1250 rcPlace.right = rcPlace.left + ll->wrapIndent;
1252 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1253 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1254 else
1255 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1257 if (customDrawWrapMarker == NULL) {
1258 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1259 } else {
1260 customDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1265 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1266 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1267 int subLine, ColourOptional background) const {
1269 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1270 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1271 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1272 // Does not take margin into account but not significant
1273 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1275 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs, NULL);
1277 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1279 // Background drawing loop
1280 while (bfBack.More()) {
1282 const TextSegment ts = bfBack.Next();
1283 const int i = ts.end() - 1;
1284 const int iDoc = i + posLineStart;
1286 PRectangle rcSegment = rcLine;
1287 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1288 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1289 // Only try to draw if really visible - enhances performance by not calling environment to
1290 // draw strings that are completely past the right side of the window.
1291 if (!rcSegment.Empty() && rcSegment.Intersects(rcLine)) {
1292 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1293 if (rcSegment.left < rcLine.left)
1294 rcSegment.left = rcLine.left;
1295 if (rcSegment.right > rcLine.right)
1296 rcSegment.right = rcLine.right;
1298 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1299 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1300 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1301 inHotspot, ll->styles[i], i);
1302 if (ts.representation) {
1303 if (ll->chars[i] == '\t') {
1304 // Tab display
1305 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1306 textBack = vsDraw.whitespaceColours.back;
1307 } else {
1308 // Blob display
1309 inIndentation = false;
1311 surface->FillRectangle(rcSegment, textBack);
1312 } else {
1313 // Normal text display
1314 surface->FillRectangle(rcSegment, textBack);
1315 if (vsDraw.viewWhitespace != wsInvisible) {
1316 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1317 if (ll->chars[cpos + ts.start] == ' ') {
1318 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {
1319 PRectangle rcSpace(
1320 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1321 rcSegment.top,
1322 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1323 rcSegment.bottom);
1324 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1326 } else {
1327 inIndentation = false;
1332 } else if (rcSegment.left > rcLine.right) {
1333 break;
1338 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1339 Range lineRange, int xStart) {
1340 if (vsDraw.edgeState == EDGE_LINE) {
1341 PRectangle rcSegment = rcLine;
1342 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1343 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1344 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1345 rcSegment.left -= ll->wrapIndent;
1346 rcSegment.right = rcSegment.left + 1;
1347 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
1351 // Draw underline mark as part of background if not transparent
1352 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1353 int line, PRectangle rcLine) {
1354 int marks = model.pdoc->GetMark(line);
1355 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1356 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1357 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1358 PRectangle rcUnderline = rcLine;
1359 rcUnderline.top = rcUnderline.bottom - 2;
1360 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1362 marks >>= 1;
1365 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1366 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1367 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1368 const int posLineStart = model.pdoc->LineStart(line);
1369 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1370 // For each selection draw
1371 int virtualSpaces = 0;
1372 if (subLine == (ll->lines - 1)) {
1373 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1375 SelectionPosition posStart(posLineStart + lineRange.start);
1376 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1377 SelectionSegment virtualSpaceRange(posStart, posEnd);
1378 for (size_t r = 0; r < model.sel.Count(); r++) {
1379 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1380 if (alpha != SC_ALPHA_NOALPHA) {
1381 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1382 if (!portion.Empty()) {
1383 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1384 PRectangle rcSegment = rcLine;
1385 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1386 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1387 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1388 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1389 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1390 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1391 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1393 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1394 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1395 if (rcSegment.right > rcLine.left)
1396 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1403 // Draw any translucent whole line states
1404 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1405 int line, PRectangle rcLine) {
1406 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1407 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1409 const int marksOfLine = model.pdoc->GetMark(line);
1410 int marksDrawnInText = marksOfLine & vsDraw.maskDrawInText;
1411 for (int markBit = 0; (markBit < 32) && marksDrawnInText; markBit++) {
1412 if (marksDrawnInText & 1) {
1413 if (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) {
1414 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1415 } else if (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) {
1416 PRectangle rcUnderline = rcLine;
1417 rcUnderline.top = rcUnderline.bottom - 2;
1418 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1421 marksDrawnInText >>= 1;
1423 int marksDrawnInLine = marksOfLine & vsDraw.maskInLine;
1424 for (int markBit = 0; (markBit < 32) && marksDrawnInLine; markBit++) {
1425 if (marksDrawnInLine & 1) {
1426 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1428 marksDrawnInLine >>= 1;
1432 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1433 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1434 int subLine, ColourOptional background) {
1436 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1437 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1438 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1440 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1441 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1443 // Does not take margin into account but not significant
1444 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1446 // Foreground drawing loop
1447 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1448 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs, &vsDraw);
1450 while (bfFore.More()) {
1452 const TextSegment ts = bfFore.Next();
1453 const int i = ts.end() - 1;
1454 const int iDoc = i + posLineStart;
1456 PRectangle rcSegment = rcLine;
1457 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1458 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1459 // Only try to draw if really visible - enhances performance by not calling environment to
1460 // draw strings that are completely past the right side of the window.
1461 if (rcSegment.Intersects(rcLine)) {
1462 int styleMain = ll->styles[i];
1463 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1464 FontAlias textFont = vsDraw.styles[styleMain].font;
1465 //hotspot foreground
1466 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1467 if (inHotspot) {
1468 if (vsDraw.hotspotColours.fore.isSet)
1469 textFore = vsDraw.hotspotColours.fore;
1471 if (vsDraw.indicatorsSetFore > 0) {
1472 // At least one indicator sets the text colour so see if it applies to this segment
1473 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
1474 const int indicatorValue = deco->rs.ValueAt(ts.start + posLineStart);
1475 if (indicatorValue) {
1476 const Indicator &indicator = vsDraw.indicators[deco->indicator];
1477 const bool hover = indicator.IsDynamic() &&
1478 ((model.hoverIndicatorPos >= ts.start + posLineStart) &&
1479 (model.hoverIndicatorPos <= ts.end() + posLineStart));
1480 if (hover) {
1481 if (indicator.sacHover.style == INDIC_TEXTFORE) {
1482 textFore = indicator.sacHover.fore;
1484 } else {
1485 if (indicator.sacNormal.style == INDIC_TEXTFORE) {
1486 if (indicator.Flags() & SC_INDICFLAG_VALUEFORE)
1487 textFore = indicatorValue & SC_INDICVALUEMASK;
1488 else
1489 textFore = indicator.sacNormal.fore;
1495 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1496 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1497 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1499 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1500 if (ts.representation) {
1501 if (ll->chars[i] == '\t') {
1502 // Tab display
1503 if (phasesDraw == phasesOne) {
1504 if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
1505 textBack = vsDraw.whitespaceColours.back;
1506 surface->FillRectangle(rcSegment, textBack);
1508 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1509 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1510 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1511 indentCount++) {
1512 if (indentCount > 0) {
1513 int xIndent = static_cast<int>(indentCount * indentWidth);
1514 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1515 (ll->xHighlightGuide == xIndent));
1519 if (vsDraw.viewWhitespace != wsInvisible) {
1520 if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1521 if (vsDraw.whitespaceColours.fore.isSet)
1522 textFore = vsDraw.whitespaceColours.fore;
1523 surface->PenColour(textFore);
1524 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,
1525 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1526 if (customDrawTabArrow == NULL)
1527 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1528 else
1529 customDrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1532 } else {
1533 inIndentation = false;
1534 if (vsDraw.controlCharSymbol >= 32) {
1535 // Using one font for all control characters so it can be controlled independently to ensure
1536 // the box goes around the characters tightly. Seems to be no way to work out what height
1537 // is taken by an individual character - internal leading gives varying results.
1538 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1539 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1540 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1541 rcSegment.top + vsDraw.maxAscent,
1542 cc, 1, textBack, textFore);
1543 } else {
1544 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1545 textBack, textFore, phasesDraw == phasesOne);
1548 } else {
1549 // Normal text display
1550 if (vsDraw.styles[styleMain].visible) {
1551 if (phasesDraw != phasesOne) {
1552 surface->DrawTextTransparent(rcSegment, textFont,
1553 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1554 i - ts.start + 1, textFore);
1555 } else {
1556 surface->DrawTextNoClip(rcSegment, textFont,
1557 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1558 i - ts.start + 1, textFore, textBack);
1561 if (vsDraw.viewWhitespace != wsInvisible ||
1562 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1563 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1564 if (ll->chars[cpos + ts.start] == ' ') {
1565 if (vsDraw.viewWhitespace != wsInvisible) {
1566 if (vsDraw.whitespaceColours.fore.isSet)
1567 textFore = vsDraw.whitespaceColours.fore;
1568 if (vsDraw.WhiteSpaceVisible(inIndentation)) {
1569 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1570 if ((phasesDraw == phasesOne) && drawWhitespaceBackground) {
1571 textBack = vsDraw.whitespaceColours.back;
1572 PRectangle rcSpace(
1573 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1574 rcSegment.top,
1575 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1576 rcSegment.bottom);
1577 surface->FillRectangle(rcSpace, textBack);
1579 const int halfDotWidth = vsDraw.whitespaceSize / 2;
1580 PRectangle rcDot(xmid + xStart - halfDotWidth - static_cast<XYPOSITION>(subLineStart),
1581 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1582 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1583 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1584 surface->FillRectangle(rcDot, textFore);
1587 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1588 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1589 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1590 indentCount++) {
1591 if (indentCount > 0) {
1592 int xIndent = static_cast<int>(indentCount * indentWidth);
1593 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1594 (ll->xHighlightGuide == xIndent));
1598 } else {
1599 inIndentation = false;
1604 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1605 PRectangle rcUL = rcSegment;
1606 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1607 rcUL.bottom = rcUL.top + 1;
1608 if (vsDraw.hotspotColours.fore.isSet)
1609 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1610 else
1611 surface->FillRectangle(rcUL, textFore);
1612 } else if (vsDraw.styles[styleMain].underline) {
1613 PRectangle rcUL = rcSegment;
1614 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1615 rcUL.bottom = rcUL.top + 1;
1616 surface->FillRectangle(rcUL, textFore);
1618 } else if (rcSegment.left > rcLine.right) {
1619 break;
1624 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1625 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1626 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1627 && (subLine == 0)) {
1628 const int posLineStart = model.pdoc->LineStart(line);
1629 int indentSpace = model.pdoc->GetLineIndentation(line);
1630 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1632 // Find the most recent line with some text
1634 int lineLastWithText = line;
1635 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1636 lineLastWithText--;
1638 if (lineLastWithText < line) {
1639 xStartText = 100000; // Don't limit to visible indentation on empty line
1640 // This line is empty, so use indentation of last line with text
1641 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1642 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1643 if (isFoldHeader) {
1644 // Level is one more level than parent
1645 indentLastWithText += model.pdoc->IndentSize();
1647 if (vsDraw.viewIndentationGuides == ivLookForward) {
1648 // In viLookForward mode, previous line only used if it is a fold header
1649 if (isFoldHeader) {
1650 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1652 } else { // viLookBoth
1653 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1657 int lineNextWithText = line;
1658 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1659 lineNextWithText++;
1661 if (lineNextWithText > line) {
1662 xStartText = 100000; // Don't limit to visible indentation on empty line
1663 // This line is empty, so use indentation of first next line with text
1664 indentSpace = Platform::Maximum(indentSpace,
1665 model.pdoc->GetLineIndentation(lineNextWithText));
1668 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1669 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1670 if (xIndent < xStartText) {
1671 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1672 (ll->xHighlightGuide == xIndent));
1678 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1679 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1681 if (subLine >= ll->lines) {
1682 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1683 return; // No further drawing
1686 // See if something overrides the line background color.
1687 const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1689 const int posLineStart = model.pdoc->LineStart(line);
1691 const Range lineRange = ll->SubLineRange(subLine);
1692 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1694 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1695 if (phase & drawBack) {
1696 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker);
1698 xStart += static_cast<int>(ll->wrapIndent);
1701 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1702 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1703 subLine, background);
1704 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1705 xStart, subLine, subLineStart, background);
1708 if (phase & drawIndicatorsBack) {
1709 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true, model.hoverIndicatorPos);
1710 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1711 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1714 if (phase & drawText) {
1715 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1716 subLine, background);
1719 if (phase & drawIndentationGuides) {
1720 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1723 if (phase & drawIndicatorsFore) {
1724 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false, model.hoverIndicatorPos);
1727 // End of the drawing of the current line
1728 if (phasesDraw == phasesOne) {
1729 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1730 xStart, subLine, subLineStart, background);
1733 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1734 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1737 if (phase & drawLineTranslucent) {
1738 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1742 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1743 bool expanded = model.cs.GetExpanded(line);
1744 const int level = model.pdoc->GetLevel(line);
1745 const int levelNext = model.pdoc->GetLevel(line + 1);
1746 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1747 (LevelNumber(level) < LevelNumber(levelNext))) {
1748 // Paint the line above the fold
1749 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1751 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1752 PRectangle rcFoldLine = rcLine;
1753 rcFoldLine.bottom = rcFoldLine.top + 1;
1754 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1756 // Paint the line below the fold
1757 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1759 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1760 PRectangle rcFoldLine = rcLine;
1761 rcFoldLine.top = rcFoldLine.bottom - 1;
1762 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1767 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1768 PRectangle rcClient, const ViewStyle &vsDraw) {
1769 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1770 // serifs and italic stems for aliased text.
1771 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1773 // Do the painting
1774 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1776 Surface *surface = surfaceWindow;
1777 if (bufferedDraw) {
1778 surface = pixmapLine;
1779 PLATFORM_ASSERT(pixmapLine->Initialised());
1781 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1782 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1784 const Point ptOrigin = model.GetVisibleOriginInMain();
1786 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1787 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1789 SelectionPosition posCaret = model.sel.RangeMain().caret;
1790 if (model.posDrag.IsValid())
1791 posCaret = model.posDrag;
1792 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1794 PRectangle rcTextArea = rcClient;
1795 if (vsDraw.marginInside) {
1796 rcTextArea.left += vsDraw.textStart;
1797 rcTextArea.right -= vsDraw.rightMarginWidth;
1798 } else {
1799 rcTextArea = rcArea;
1802 // Remove selection margin from drawing area so text will not be drawn
1803 // on it in unbuffered mode.
1804 if (!bufferedDraw && vsDraw.marginInside) {
1805 PRectangle rcClipText = rcTextArea;
1806 rcClipText.left -= leftTextOverlap;
1807 surfaceWindow->SetClip(rcClipText);
1810 // Loop on visible lines
1811 //double durLayout = 0.0;
1812 //double durPaint = 0.0;
1813 //double durCopy = 0.0;
1814 //ElapsedTime etWhole;
1816 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1817 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1819 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1820 AutoLineLayout ll(llc, 0);
1821 std::vector<DrawPhase> phases;
1822 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1823 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1824 phases.push_back(phase);
1826 } else {
1827 phases.push_back(drawAll);
1829 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1830 int ypos = 0;
1831 if (!bufferedDraw)
1832 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1833 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1834 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1835 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1837 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1838 // Only visible lines should be handled by the code within the loop
1839 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1840 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1841 const int subLine = visibleLine - lineStartSet;
1843 // Copy this line and its styles from the document into local arrays
1844 // and determine the x position at which each character starts.
1845 //ElapsedTime et;
1846 if (lineDoc != lineDocPrevious) {
1847 ll.Set(0);
1848 ll.Set(RetrieveLineLayout(lineDoc, model));
1849 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1850 lineDocPrevious = lineDoc;
1852 //durLayout += et.Duration(true);
1854 if (ll) {
1855 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1856 ll->hotspot = model.GetHotSpotRange();
1858 PRectangle rcLine = rcTextArea;
1859 rcLine.top = static_cast<XYPOSITION>(ypos);
1860 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
1862 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
1864 // Highlight the current braces if any
1865 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
1866 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
1868 if (leftTextOverlap && bufferedDraw) {
1869 PRectangle rcSpacer = rcLine;
1870 rcSpacer.right = rcSpacer.left;
1871 rcSpacer.left -= 1;
1872 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
1875 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
1876 //durPaint += et.Duration(true);
1878 // Restore the previous styles for the brace highlights in case layout is in cache.
1879 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
1881 if (*it & drawFoldLines) {
1882 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
1885 if (*it & drawCarets) {
1886 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
1889 if (bufferedDraw) {
1890 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
1891 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
1892 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
1893 yposScreen + vsDraw.lineHeight);
1894 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
1897 lineWidthMaxSeen = Platform::Maximum(
1898 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
1899 //durCopy += et.Duration(true);
1902 if (!bufferedDraw) {
1903 ypos += vsDraw.lineHeight;
1906 yposScreen += vsDraw.lineHeight;
1907 visibleLine++;
1910 ll.Set(0);
1911 //if (durPaint < 0.00000001)
1912 // durPaint = 0.00000001;
1914 // Right column limit indicator
1915 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
1916 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
1917 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
1918 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
1919 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1920 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
1921 if (vsDraw.edgeState == EDGE_LINE) {
1922 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1923 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
1924 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1925 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour);
1928 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1930 //Platform::DebugPrintf(
1931 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1932 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1936 // Space (3 space characters) between line numbers and text when printing.
1937 #define lineNumberPrintSpace " "
1939 static ColourDesired InvertedLight(ColourDesired orig) {
1940 unsigned int r = orig.GetRed();
1941 unsigned int g = orig.GetGreen();
1942 unsigned int b = orig.GetBlue();
1943 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1944 unsigned int il = 0xff - l;
1945 if (l == 0)
1946 return ColourDesired(0xff, 0xff, 0xff);
1947 r = r * il / l;
1948 g = g * il / l;
1949 b = b * il / l;
1950 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1953 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
1954 const EditModel &model, const ViewStyle &vs) {
1955 // Can't use measurements cached for screen
1956 posCache.Clear();
1958 ViewStyle vsPrint(vs);
1959 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
1961 // Modify the view style for printing as do not normally want any of the transient features to be printed
1962 // Printing supports only the line number margin.
1963 int lineNumberIndex = -1;
1964 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1965 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
1966 lineNumberIndex = margin;
1967 } else {
1968 vsPrint.ms[margin].width = 0;
1971 vsPrint.fixedColumnWidth = 0;
1972 vsPrint.zoomLevel = printParameters.magnification;
1973 // Don't show indentation guides
1974 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1975 vsPrint.viewIndentationGuides = ivNone;
1976 // Don't show the selection when printing
1977 vsPrint.selColours.back.isSet = false;
1978 vsPrint.selColours.fore.isSet = false;
1979 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
1980 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
1981 vsPrint.whitespaceColours.back.isSet = false;
1982 vsPrint.whitespaceColours.fore.isSet = false;
1983 vsPrint.showCaretLineBackground = false;
1984 vsPrint.alwaysShowCaretLineBackground = false;
1985 // Don't highlight matching braces using indicators
1986 vsPrint.braceHighlightIndicatorSet = false;
1987 vsPrint.braceBadLightIndicatorSet = false;
1989 // Set colours for printing according to users settings
1990 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
1991 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
1992 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
1993 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
1994 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
1995 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
1996 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1997 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
1998 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1999 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
2000 if (sty <= STYLE_DEFAULT) {
2001 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2005 // White background for the line numbers
2006 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
2008 // Printing uses different margins, so reset screen margins
2009 vsPrint.leftMarginWidth = 0;
2010 vsPrint.rightMarginWidth = 0;
2012 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
2013 // Determining width must happen after fonts have been realised in Refresh
2014 int lineNumberWidth = 0;
2015 if (lineNumberIndex >= 0) {
2016 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
2017 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
2018 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
2019 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
2022 int linePrintStart = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMin));
2023 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
2024 if (linePrintLast < linePrintStart)
2025 linePrintLast = linePrintStart;
2026 int linePrintMax = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMax));
2027 if (linePrintLast > linePrintMax)
2028 linePrintLast = linePrintMax;
2029 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
2030 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
2031 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
2032 int endPosPrint = model.pdoc->Length();
2033 if (linePrintLast < model.pdoc->LinesTotal())
2034 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
2036 // Ensure we are styled to where we are formatting.
2037 model.pdoc->EnsureStyledTo(endPosPrint);
2039 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
2040 int ypos = pfr->rc.top;
2042 int lineDoc = linePrintStart;
2044 int nPrintPos = static_cast<int>(pfr->chrg.cpMin);
2045 int visibleLine = 0;
2046 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
2047 if (printParameters.wrapState == eWrapNone)
2048 widthPrint = LineLayout::wrapWidthInfinite;
2050 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2052 // When printing, the hdc and hdcTarget may be the same, so
2053 // changing the state of surfaceMeasure may change the underlying
2054 // state of surface. Therefore, any cached state is discarded before
2055 // using each surface.
2056 surfaceMeasure->FlushCachedState();
2058 // Copy this line and its styles from the document into local arrays
2059 // and determine the x position at which each character starts.
2060 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2061 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2063 ll.containsCaret = false;
2065 PRectangle rcLine = PRectangle::FromInts(
2066 pfr->rc.left,
2067 ypos,
2068 pfr->rc.right - 1,
2069 ypos + vsPrint.lineHeight);
2071 // When document line is wrapped over multiple display lines, find where
2072 // to start printing from to ensure a particular position is on the first
2073 // line of the page.
2074 if (visibleLine == 0) {
2075 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2076 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2077 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2078 visibleLine = -iwl;
2082 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2083 visibleLine = -(ll.lines - 1);
2087 if (draw && lineNumberWidth &&
2088 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2089 (visibleLine >= 0)) {
2090 char number[100];
2091 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2092 PRectangle rcNumber = rcLine;
2093 rcNumber.right = rcNumber.left + lineNumberWidth;
2094 // Right justify
2095 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2096 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2097 surface->FlushCachedState();
2098 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2099 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2100 vsPrint.styles[STYLE_LINENUMBER].fore,
2101 vsPrint.styles[STYLE_LINENUMBER].back);
2104 // Draw the line
2105 surface->FlushCachedState();
2107 for (int iwl = 0; iwl < ll.lines; iwl++) {
2108 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2109 if (visibleLine >= 0) {
2110 if (draw) {
2111 rcLine.top = static_cast<XYPOSITION>(ypos);
2112 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2113 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2115 ypos += vsPrint.lineHeight;
2117 visibleLine++;
2118 if (iwl == ll.lines - 1)
2119 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2120 else
2121 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2125 ++lineDoc;
2128 // Clear cache so measurements are not used for screen
2129 posCache.Clear();
2131 return nPrintPos;