Disable the "Blend Alpha" menu item if not in overlap mode
[TortoiseGit.git] / ext / scintilla / src / EditView.cxx
blob7216ed512d53820812a98ac9a26f8323b598140d
1 // Scintilla source code edit control
2 /** @file Editor.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 "SplitVector.h"
29 #include "Partitioning.h"
30 #include "RunStyles.h"
31 #include "ContractionState.h"
32 #include "CellBuffer.h"
33 #include "PerLine.h"
34 #include "KeyMap.h"
35 #include "Indicator.h"
36 #include "XPM.h"
37 #include "LineMarker.h"
38 #include "Style.h"
39 #include "ViewStyle.h"
40 #include "CharClassify.h"
41 #include "Decoration.h"
42 #include "CaseFolder.h"
43 #include "Document.h"
44 #include "UniConversion.h"
45 #include "Selection.h"
46 #include "PositionCache.h"
47 #include "EditModel.h"
48 #include "MarginView.h"
49 #include "EditView.h"
50 #include "Editor.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 hideSelection = false;
178 drawOverstrikeCaret = true;
179 bufferedDraw = true;
180 phasesDraw = phasesTwo;
181 lineWidthMaxSeen = 0;
182 additionalCaretsBlink = true;
183 additionalCaretsVisible = true;
184 imeCaretBlockOverride = false;
185 pixmapLine = 0;
186 pixmapIndentGuide = 0;
187 pixmapIndentGuideHighlight = 0;
188 llc.SetLevel(LineLayoutCache::llcCaret);
189 posCache.SetSize(0x400);
190 tabArrowHeight = 4;
191 customDrawTabArrow = NULL;
192 customDrawWrapMarker = NULL;
193 editor = 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 + 2));
226 if (next > 0)
227 return static_cast<XYPOSITION>(next);
228 return (static_cast<int>((x + 2) / 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 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 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 char 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->StyleAt(charInDoc);
385 allSame = allSame &&
386 (ll->styles[numCharsInLine] == static_cast<unsigned char>(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 // Style::caseUpper
394 allSame = allSame &&
395 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
396 numCharsInLine++;
398 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
399 if (allSame) {
400 ll->validity = LineLayout::llPositions;
401 } else {
402 ll->validity = LineLayout::llInvalid;
404 } else {
405 ll->validity = LineLayout::llInvalid;
408 if (ll->validity == LineLayout::llInvalid) {
409 ll->widthLine = LineLayout::wrapWidthInfinite;
410 ll->lines = 1;
411 if (vstyle.edgeState == EDGE_BACKGROUND) {
412 ll->edgeColumn = model.pdoc->FindColumn(line, vstyle.theEdge);
413 if (ll->edgeColumn >= posLineStart) {
414 ll->edgeColumn -= posLineStart;
416 } else {
417 ll->edgeColumn = -1;
420 // Fill base line layout
421 const int lineLength = posLineEnd - posLineStart;
422 model.pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
423 model.pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
424 int numCharsBeforeEOL = model.pdoc->LineEnd(line) - posLineStart;
425 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
426 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
427 const unsigned char styleByte = ll->styles[styleInLine];
428 ll->styles[styleInLine] = styleByte;
430 const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
431 if (vstyle.someStylesForceCase) {
432 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
433 char chDoc = ll->chars[charInLine];
434 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
435 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
436 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
437 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
440 ll->xHighlightGuide = 0;
441 // Extra element at the end of the line to hold end x position and act as
442 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
443 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
445 // Layout the line, determining the position of each character,
446 // with an extra element at the end for the end of the line.
447 ll->positions[0] = 0;
448 bool lastSegItalics = false;
450 BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs, NULL);
451 while (bfLayout.More()) {
453 const TextSegment ts = bfLayout.Next();
455 std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f);
456 if (vstyle.styles[ll->styles[ts.start]].visible) {
457 if (ts.representation) {
458 XYPOSITION representationWidth = vstyle.controlCharWidth;
459 if (ll->chars[ts.start] == '\t') {
460 // Tab is a special case of representation, taking a variable amount of space
461 const XYPOSITION x = ll->positions[ts.start];
462 representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start];
463 } else {
464 if (representationWidth <= 0.0) {
465 XYPOSITION positionsRepr[256]; // Should expand when needed
466 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
467 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
468 representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
471 for (int ii = 0; ii < ts.length; ii++)
472 ll->positions[ts.start + 1 + ii] = representationWidth;
473 } else {
474 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
475 // Over half the segments are single characters and of these about half are space characters.
476 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
477 } else {
478 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
479 ts.length, ll->positions + ts.start + 1, model.pdoc);
482 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
485 for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
486 ll->positions[posToIncrease] += ll->positions[ts.start];
490 // Small hack to make lines that end with italics not cut off the edge of the last character
491 if (lastSegItalics) {
492 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
494 ll->numCharsInLine = numCharsInLine;
495 ll->numCharsBeforeEOL = numCharsBeforeEOL;
496 ll->validity = LineLayout::llPositions;
498 // Hard to cope when too narrow, so just assume there is space
499 if (width < 20) {
500 width = 20;
502 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
503 ll->widthLine = width;
504 if (width == LineLayout::wrapWidthInfinite) {
505 ll->lines = 1;
506 } else if (width > ll->positions[ll->numCharsInLine]) {
507 // Simple common case where line does not need wrapping.
508 ll->lines = 1;
509 } else {
510 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
511 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
513 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
514 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
515 wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
516 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
517 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
519 ll->wrapIndent = wrapAddIndent;
520 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
521 for (int i = 0; i < ll->numCharsInLine; i++) {
522 if (!IsSpaceOrTab(ll->chars[i])) {
523 ll->wrapIndent += ll->positions[i]; // Add line indent
524 break;
527 // Check for text width minimum
528 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
529 ll->wrapIndent = wrapAddIndent;
530 // Check for wrapIndent minimum
531 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
532 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
533 ll->lines = 0;
534 // Calculate line start positions based upon width.
535 int lastGoodBreak = 0;
536 int lastLineStart = 0;
537 XYACCUMULATOR startOffset = 0;
538 int p = 0;
539 while (p < ll->numCharsInLine) {
540 if ((ll->positions[p + 1] - startOffset) >= width) {
541 if (lastGoodBreak == lastLineStart) {
542 // Try moving to start of last character
543 if (p > 0) {
544 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
545 - posLineStart;
547 if (lastGoodBreak == lastLineStart) {
548 // Ensure at least one character on line.
549 lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
550 - posLineStart;
553 lastLineStart = lastGoodBreak;
554 ll->lines++;
555 ll->SetLineStart(ll->lines, lastGoodBreak);
556 startOffset = ll->positions[lastGoodBreak];
557 // take into account the space for start wrap mark and indent
558 startOffset -= ll->wrapIndent;
559 p = lastGoodBreak + 1;
560 continue;
562 if (p > 0) {
563 if (vstyle.wrapState == eWrapChar) {
564 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
565 - posLineStart;
566 p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
567 continue;
568 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
569 lastGoodBreak = p;
570 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
571 lastGoodBreak = p;
574 p++;
576 ll->lines++;
578 ll->validity = LineLayout::llLines;
582 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) {
583 Point pt;
584 if (pos.Position() == INVALID_POSITION)
585 return pt;
586 const int line = model.pdoc->LineFromPosition(pos.Position());
587 const int lineVisible = model.cs.DisplayFromDoc(line);
588 //Platform::DebugPrintf("line=%d\n", line);
589 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
590 if (surface && ll) {
591 const int posLineStart = model.pdoc->LineStart(line);
592 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
593 const int posInLine = pos.Position() - posLineStart;
594 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
595 pt.y += (lineVisible - topLine) * vs.lineHeight;
596 pt.x += vs.textStart - model.xOffset;
598 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
599 return pt;
602 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
603 pt.x = pt.x - vs.textStart;
604 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
605 if (!canReturnInvalid && (visibleLine < 0))
606 visibleLine = 0;
607 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
608 if (canReturnInvalid && (lineDoc < 0))
609 return SelectionPosition(INVALID_POSITION);
610 if (lineDoc >= model.pdoc->LinesTotal())
611 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length());
612 const int posLineStart = model.pdoc->LineStart(lineDoc);
613 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
614 if (surface && ll) {
615 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
616 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
617 const int subLine = visibleLine - lineStartSet;
618 if (subLine < ll->lines) {
619 const Range rangeSubLine = ll->SubLineRange(subLine);
620 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
621 if (subLine > 0) // Wrapped
622 pt.x -= ll->wrapIndent;
623 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
624 if (positionInLine < rangeSubLine.end) {
625 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
627 if (virtualSpace) {
628 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
629 const int spaceOffset = static_cast<int>(
630 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
631 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
632 } else if (canReturnInvalid) {
633 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
634 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
636 } else {
637 return SelectionPosition(rangeSubLine.end + posLineStart);
640 if (!canReturnInvalid)
641 return SelectionPosition(ll->numCharsInLine + posLineStart);
643 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
647 * Find the document position corresponding to an x coordinate on a particular document line.
648 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
649 * This method is used for rectangular selections and does not work on wrapped lines.
651 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
652 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
653 if (surface && ll) {
654 const int posLineStart = model.pdoc->LineStart(lineDoc);
655 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
656 const Range rangeSubLine = ll->SubLineRange(0);
657 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
658 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
659 if (positionInLine < rangeSubLine.end) {
660 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
662 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
663 const int spaceOffset = static_cast<int>(
664 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
665 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
667 return SelectionPosition(0);
670 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
671 int lineDoc = model.pdoc->LineFromPosition(pos);
672 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
673 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
674 if (surface && ll) {
675 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
676 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
677 int posInLine = pos - posLineStart;
678 lineDisplay--; // To make up for first increment ahead.
679 for (int subLine = 0; subLine < ll->lines; subLine++) {
680 if (posInLine >= ll->LineStart(subLine)) {
681 lineDisplay++;
685 return lineDisplay;
688 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
689 int line = model.pdoc->LineFromPosition(pos);
690 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
691 int posRet = INVALID_POSITION;
692 if (surface && ll) {
693 unsigned int posLineStart = model.pdoc->LineStart(line);
694 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
695 int posInLine = pos - posLineStart;
696 if (posInLine <= ll->maxLineLength) {
697 for (int subLine = 0; subLine < ll->lines; subLine++) {
698 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
699 if (start) {
700 posRet = ll->LineStart(subLine) + posLineStart;
701 } else {
702 if (subLine == ll->lines - 1)
703 posRet = ll->LineStart(subLine + 1) + posLineStart;
704 else
705 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
711 return posRet;
714 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
715 return main ?
716 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
717 vsDraw.selAdditionalBackground;
720 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
721 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
722 if (inSelection == 1) {
723 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
724 return SelectionBackground(vsDraw, true, model.primarySelection);
726 } else if (inSelection == 2) {
727 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
728 return SelectionBackground(vsDraw, false, model.primarySelection);
730 } else {
731 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
732 (i >= ll->edgeColumn) &&
733 (i < ll->numCharsBeforeEOL))
734 return vsDraw.edgecolour;
735 if (inHotspot && vsDraw.hotspotColours.back.isSet)
736 return vsDraw.hotspotColours.back;
738 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
739 return background;
740 } else {
741 return vsDraw.styles[styleMain].back;
745 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
746 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
747 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
748 surface->Copy(rcCopyArea, from,
749 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
752 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
753 if (alpha != SC_ALPHA_NOALPHA) {
754 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
758 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
759 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
760 if (fillBackground) {
761 surface->FillRectangle(rcSegment, textBack);
763 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
764 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
765 surface->InternalLeading(ctrlCharsFont));
766 PRectangle rcCChar = rcSegment;
767 rcCChar.left = rcCChar.left + 1;
768 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
769 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
770 PRectangle rcCentral = rcCChar;
771 rcCentral.top++;
772 rcCentral.bottom--;
773 surface->FillRectangle(rcCentral, textFore);
774 PRectangle rcChar = rcCChar;
775 rcChar.left++;
776 rcChar.right--;
777 surface->DrawTextClipped(rcChar, ctrlCharsFont,
778 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
779 textBack, textFore);
782 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
783 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
784 ColourOptional background) {
786 const int posLineStart = model.pdoc->LineStart(line);
787 PRectangle rcSegment = rcLine;
789 const bool lastSubLine = subLine == (ll->lines - 1);
790 XYPOSITION virtualSpace = 0;
791 if (lastSubLine) {
792 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
793 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
795 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
797 // Fill the virtual space and show selections within it
798 if (virtualSpace > 0.0f) {
799 rcSegment.left = xEol + xStart;
800 rcSegment.right = xEol + xStart + virtualSpace;
801 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
802 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
803 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
804 for (size_t r = 0; r<model.sel.Count(); r++) {
805 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
806 if (alpha == SC_ALPHA_NOALPHA) {
807 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
808 if (!portion.Empty()) {
809 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
810 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
811 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
812 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
813 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
814 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
815 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
816 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
823 int eolInSelection = 0;
824 int alpha = SC_ALPHA_NOALPHA;
825 if (!hideSelection) {
826 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
827 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
828 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
831 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
832 XYPOSITION blobsWidth = 0;
833 if (lastSubLine) {
834 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
835 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
836 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
837 blobsWidth += rcSegment.Width();
838 char hexits[4];
839 const char *ctrlChar;
840 unsigned char chEOL = ll->chars[eolPos];
841 int styleMain = ll->styles[eolPos];
842 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
843 if (UTF8IsAscii(chEOL)) {
844 ctrlChar = ControlCharacterString(chEOL);
845 } else {
846 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
847 if (repr) {
848 ctrlChar = repr->stringRep.c_str();
849 eolPos = ll->numCharsInLine;
850 } else {
851 sprintf(hexits, "x%2X", chEOL);
852 ctrlChar = hexits;
855 ColourDesired textFore = vsDraw.styles[styleMain].fore;
856 if (eolInSelection && vsDraw.selColours.fore.isSet) {
857 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
859 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
860 if (alpha == SC_ALPHA_NOALPHA) {
861 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
862 } else {
863 surface->FillRectangle(rcSegment, textBack);
865 } else {
866 surface->FillRectangle(rcSegment, textBack);
868 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
869 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
870 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
875 // Draw the eol-is-selected rectangle
876 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
877 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
879 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
880 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
881 } else {
882 if (background.isSet) {
883 surface->FillRectangle(rcSegment, background);
884 } else if (line < model.pdoc->LinesTotal() - 1) {
885 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
886 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
887 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
888 } else {
889 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
891 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
892 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
896 // Fill the remainder of the line
897 rcSegment.left = rcSegment.right;
898 if (rcSegment.left < rcLine.left)
899 rcSegment.left = rcLine.left;
900 rcSegment.right = rcLine.right;
902 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
903 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
904 } else {
905 if (background.isSet) {
906 surface->FillRectangle(rcSegment, background);
907 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
908 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
909 } else {
910 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
912 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
913 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
917 bool drawWrapMarkEnd = false;
919 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
920 if (subLine + 1 < ll->lines) {
921 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
925 if (drawWrapMarkEnd) {
926 PRectangle rcPlace = rcSegment;
928 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
929 rcPlace.left = xEol + xStart + virtualSpace;
930 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
931 } else {
932 // rcLine is clipped to text area
933 rcPlace.right = rcLine.right;
934 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
936 if (customDrawWrapMarker == NULL) {
937 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
938 } else {
939 customDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
944 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
945 const LineLayout *ll, int xStart, PRectangle rcLine, int subLine, Indicator::DrawState drawState, int value) {
946 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
947 PRectangle rcIndic(
948 ll->positions[startPos] + xStart - subLineStart,
949 rcLine.top + vsDraw.maxAscent,
950 ll->positions[endPos] + xStart - subLineStart,
951 rcLine.top + vsDraw.maxAscent + 3);
952 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, drawState, value);
955 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
956 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under, int hoverIndicatorPos) {
957 // Draw decorators
958 const int posLineStart = model.pdoc->LineStart(line);
959 const int lineStart = ll->LineStart(subLine);
960 const int posLineEnd = posLineStart + lineEnd;
962 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
963 if (under == vsDraw.indicators[deco->indicator].under) {
964 int startPos = posLineStart + lineStart;
965 if (!deco->rs.ValueAt(startPos)) {
966 startPos = deco->rs.EndRun(startPos);
968 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
969 int endPos = deco->rs.EndRun(startPos);
970 if (endPos > posLineEnd)
971 endPos = posLineEnd;
972 const bool hover = vsDraw.indicators[deco->indicator].IsDynamic() &&
973 ((hoverIndicatorPos >= startPos) && (hoverIndicatorPos <= endPos));
974 const int value = deco->rs.ValueAt(startPos);
975 Indicator::DrawState drawState = hover ? Indicator::drawHover : Indicator::drawNormal;
976 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
977 surface, vsDraw, ll, xStart, rcLine, subLine, drawState, value);
978 startPos = endPos;
979 if (!deco->rs.ValueAt(startPos)) {
980 startPos = deco->rs.EndRun(startPos);
986 // Use indicators to highlight matching braces
987 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
988 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
989 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
990 if (under == vsDraw.indicators[braceIndicator].under) {
991 Range rangeLine(posLineStart + lineStart, posLineEnd);
992 if (rangeLine.ContainsCharacter(model.braces[0])) {
993 int braceOffset = model.braces[0] - posLineStart;
994 if (braceOffset < ll->numCharsInLine) {
995 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine, Indicator::drawNormal, 1);
998 if (rangeLine.ContainsCharacter(model.braces[1])) {
999 int braceOffset = model.braces[1] - posLineStart;
1000 if (braceOffset < ll->numCharsInLine) {
1001 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine, Indicator::drawNormal, 1);
1008 static bool AnnotationBoxedOrIndented(int annotationVisible) {
1009 return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED;
1012 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1013 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1014 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
1015 PRectangle rcSegment = rcLine;
1016 int annotationLine = subLine - ll->lines;
1017 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1018 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1019 if (phase & drawBack) {
1020 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1022 rcSegment.left = static_cast<XYPOSITION>(xStart);
1023 if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1024 // Only care about calculating width if tracking or need to draw indented box
1025 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1026 if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1027 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1028 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1029 rcSegment.right = rcSegment.left + widthAnnotation;
1031 if (widthAnnotation > lineWidthMaxSeen)
1032 lineWidthMaxSeen = widthAnnotation;
1034 const int annotationLines = model.pdoc->AnnotationLines(line);
1035 size_t start = 0;
1036 size_t lengthAnnotation = stAnnotation.LineLength(start);
1037 int lineInAnnotation = 0;
1038 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1039 start += lengthAnnotation + 1;
1040 lengthAnnotation = stAnnotation.LineLength(start);
1041 lineInAnnotation++;
1043 PRectangle rcText = rcSegment;
1044 if ((phase & drawBack) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1045 surface->FillRectangle(rcText,
1046 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1047 rcText.left += vsDraw.spaceWidth;
1049 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1050 stAnnotation, start, lengthAnnotation, phase);
1051 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1052 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1053 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1054 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1055 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1056 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1057 if (subLine == ll->lines) {
1058 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1059 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1061 if (subLine == ll->lines + annotationLines - 1) {
1062 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1063 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1069 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1070 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1072 int lineStart = ll->LineStart(subLine);
1073 int posBefore = posCaret;
1074 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1075 int numCharsToDraw = posAfter - posCaret;
1077 // Work out where the starting and ending offsets are. We need to
1078 // see if the previous character shares horizontal space, such as a
1079 // glyph / combining character. If so we'll need to draw that too.
1080 int offsetFirstChar = offset;
1081 int offsetLastChar = offset + (posAfter - posCaret);
1082 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1083 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1084 // The char does not share horizontal space
1085 break;
1087 // Char shares horizontal space, update the numChars to draw
1088 // Update posBefore to point to the prev char
1089 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1090 numCharsToDraw = posAfter - posBefore;
1091 offsetFirstChar = offset - (posCaret - posBefore);
1094 // See if the next character shares horizontal space, if so we'll
1095 // need to draw that too.
1096 if (offsetFirstChar < 0)
1097 offsetFirstChar = 0;
1098 numCharsToDraw = offsetLastChar - offsetFirstChar;
1099 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1100 // Update posAfter to point to the 2nd next char, this is where
1101 // the next character ends, and 2nd next begins. We'll need
1102 // to compare these two
1103 posBefore = posAfter;
1104 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1105 offsetLastChar = offset + (posAfter - posCaret);
1106 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1107 // The char does not share horizontal space
1108 break;
1110 // Char shares horizontal space, update the numChars to draw
1111 numCharsToDraw = offsetLastChar - offsetFirstChar;
1114 // We now know what to draw, update the caret drawing rectangle
1115 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1116 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1118 // Adjust caret position to take into account any word wrapping symbols.
1119 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1120 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1121 rcCaret.left += wordWrapCharWidth;
1122 rcCaret.right += wordWrapCharWidth;
1125 // This character is where the caret block is, we override the colours
1126 // (inversed) for drawing the caret here.
1127 int styleMain = ll->styles[offsetFirstChar];
1128 FontAlias fontText = vsDraw.styles[styleMain].font;
1129 surface->DrawTextClipped(rcCaret, fontText,
1130 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1131 numCharsToDraw, vsDraw.styles[styleMain].back,
1132 caretColour);
1135 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1136 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1137 // When drag is active it is the only caret drawn
1138 bool drawDrag = model.posDrag.IsValid();
1139 if (hideSelection && !drawDrag)
1140 return;
1141 const int posLineStart = model.pdoc->LineStart(lineDoc);
1142 // For each selection draw
1143 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1144 const bool mainCaret = r == model.sel.Main();
1145 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1146 const int offset = posCaret.Position() - posLineStart;
1147 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1148 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1149 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1150 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1151 if (ll->wrapIndent != 0) {
1152 int lineStart = ll->LineStart(subLine);
1153 if (lineStart != 0) // Wrapped
1154 xposCaret += ll->wrapIndent;
1156 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1157 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1158 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1159 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1160 bool caretAtEOF = false;
1161 bool caretAtEOL = false;
1162 bool drawBlockCaret = false;
1163 XYPOSITION widthOverstrikeCaret;
1164 XYPOSITION caretWidthOffset = 0;
1165 PRectangle rcCaret = rcLine;
1167 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1168 caretAtEOF = true;
1169 widthOverstrikeCaret = vsDraw.aveCharWidth;
1170 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1171 caretAtEOL = true;
1172 widthOverstrikeCaret = vsDraw.aveCharWidth;
1173 } else {
1174 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1175 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1177 if (widthOverstrikeCaret < 3) // Make sure its visible
1178 widthOverstrikeCaret = 3;
1180 if (xposCaret > 0)
1181 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1182 xposCaret += xStart;
1183 if (model.posDrag.IsValid()) {
1184 /* Dragging text, use a line caret */
1185 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1186 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1187 } else if (model.inOverstrike && drawOverstrikeCaret) {
1188 /* Overstrike (insert mode), use a modified bar caret */
1189 rcCaret.top = rcCaret.bottom - 2;
1190 rcCaret.left = xposCaret + 1;
1191 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1192 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1193 /* Block caret */
1194 rcCaret.left = xposCaret;
1195 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1196 drawBlockCaret = true;
1197 rcCaret.right = xposCaret + widthOverstrikeCaret;
1198 } else {
1199 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1201 } else {
1202 /* Line caret */
1203 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1204 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1206 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1207 if (drawBlockCaret) {
1208 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1209 } else {
1210 surface->FillRectangle(rcCaret, caretColour);
1214 if (drawDrag)
1215 break;
1219 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1220 int xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker) {
1221 // default bgnd here..
1222 surface->FillRectangle(rcLine, background.isSet ? background :
1223 vsDraw.styles[STYLE_DEFAULT].back);
1225 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1227 // draw continuation rect
1228 PRectangle rcPlace = rcLine;
1230 rcPlace.left = static_cast<XYPOSITION>(xStart);
1231 rcPlace.right = rcPlace.left + ll->wrapIndent;
1233 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1234 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1235 else
1236 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1238 if (customDrawWrapMarker == NULL) {
1239 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1240 } else {
1241 customDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1246 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1247 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1248 int subLine, ColourOptional background) const {
1250 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1251 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1252 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1253 // Does not take margin into account but not significant
1254 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1256 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs, NULL);
1258 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1260 // Background drawing loop
1261 while (bfBack.More()) {
1263 const TextSegment ts = bfBack.Next();
1264 const int i = ts.end() - 1;
1265 const int iDoc = i + posLineStart;
1267 PRectangle rcSegment = rcLine;
1268 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1269 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1270 // Only try to draw if really visible - enhances performance by not calling environment to
1271 // draw strings that are completely past the right side of the window.
1272 if (rcSegment.Intersects(rcLine)) {
1273 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1274 if (rcSegment.left < rcLine.left)
1275 rcSegment.left = rcLine.left;
1276 if (rcSegment.right > rcLine.right)
1277 rcSegment.right = rcLine.right;
1279 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1280 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1281 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1282 inHotspot, ll->styles[i], i);
1283 if (ts.representation) {
1284 if (ll->chars[i] == '\t') {
1285 // Tab display
1286 if (drawWhitespaceBackground &&
1287 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1288 textBack = vsDraw.whitespaceColours.back;
1289 } else {
1290 // Blob display
1291 inIndentation = false;
1293 surface->FillRectangle(rcSegment, textBack);
1294 } else {
1295 // Normal text display
1296 surface->FillRectangle(rcSegment, textBack);
1297 if (vsDraw.viewWhitespace != wsInvisible ||
1298 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
1299 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1300 if (ll->chars[cpos + ts.start] == ' ') {
1301 if (drawWhitespaceBackground &&
1302 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1303 PRectangle rcSpace(
1304 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1305 rcSegment.top,
1306 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1307 rcSegment.bottom);
1308 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1310 } else {
1311 inIndentation = false;
1316 } else if (rcSegment.left > rcLine.right) {
1317 break;
1322 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1323 Range lineRange, int xStart) {
1324 if (vsDraw.edgeState == EDGE_LINE) {
1325 PRectangle rcSegment = rcLine;
1326 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1327 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1328 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1329 rcSegment.left -= ll->wrapIndent;
1330 rcSegment.right = rcSegment.left + 1;
1331 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
1335 // Draw underline mark as part of background if not transparent
1336 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1337 int line, PRectangle rcLine) {
1338 int marks = model.pdoc->GetMark(line);
1339 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1340 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1341 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1342 PRectangle rcUnderline = rcLine;
1343 rcUnderline.top = rcUnderline.bottom - 2;
1344 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1346 marks >>= 1;
1349 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1350 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1351 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1352 const int posLineStart = model.pdoc->LineStart(line);
1353 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1354 // For each selection draw
1355 int virtualSpaces = 0;
1356 if (subLine == (ll->lines - 1)) {
1357 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1359 SelectionPosition posStart(posLineStart + lineRange.start);
1360 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1361 SelectionSegment virtualSpaceRange(posStart, posEnd);
1362 for (size_t r = 0; r < model.sel.Count(); r++) {
1363 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1364 if (alpha != SC_ALPHA_NOALPHA) {
1365 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1366 if (!portion.Empty()) {
1367 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1368 PRectangle rcSegment = rcLine;
1369 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1370 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1371 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1372 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1373 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1374 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1375 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1377 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1378 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1379 if (rcSegment.right > rcLine.left)
1380 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1387 // Draw any translucent whole line states
1388 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1389 int line, PRectangle rcLine) {
1390 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1391 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1393 int marks = model.pdoc->GetMark(line);
1394 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1395 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
1396 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1397 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
1398 PRectangle rcUnderline = rcLine;
1399 rcUnderline.top = rcUnderline.bottom - 2;
1400 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1402 marks >>= 1;
1404 if (vsDraw.maskInLine) {
1405 int marksMasked = model.pdoc->GetMark(line) & vsDraw.maskInLine;
1406 if (marksMasked) {
1407 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
1408 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
1409 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1411 marksMasked >>= 1;
1417 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1418 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1419 int subLine, ColourOptional background) {
1421 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1422 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1423 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1425 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1426 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1428 // Does not take margin into account but not significant
1429 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1431 // Foreground drawing loop
1432 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1433 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs, &vsDraw);
1435 while (bfFore.More()) {
1437 const TextSegment ts = bfFore.Next();
1438 const int i = ts.end() - 1;
1439 const int iDoc = i + posLineStart;
1441 PRectangle rcSegment = rcLine;
1442 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1443 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1444 // Only try to draw if really visible - enhances performance by not calling environment to
1445 // draw strings that are completely past the right side of the window.
1446 if (rcSegment.Intersects(rcLine)) {
1447 int styleMain = ll->styles[i];
1448 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1449 FontAlias textFont = vsDraw.styles[styleMain].font;
1450 //hotspot foreground
1451 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1452 if (inHotspot) {
1453 if (vsDraw.hotspotColours.fore.isSet)
1454 textFore = vsDraw.hotspotColours.fore;
1456 if (vsDraw.indicatorsSetFore > 0) {
1457 // At least one indicator sets the text colour so see if it applies to this segment
1458 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
1459 const int indicatorValue = deco->rs.ValueAt(ts.start + posLineStart);
1460 if (indicatorValue) {
1461 const Indicator &indicator = vsDraw.indicators[deco->indicator];
1462 const bool hover = indicator.IsDynamic() &&
1463 ((model.hoverIndicatorPos >= ts.start + posLineStart) &&
1464 (model.hoverIndicatorPos <= ts.end() + posLineStart));
1465 if (hover) {
1466 if (indicator.sacHover.style == INDIC_TEXTFORE) {
1467 textFore = indicator.sacHover.fore;
1469 } else {
1470 if (indicator.sacNormal.style == INDIC_TEXTFORE) {
1471 if (indicator.Flags() & SC_INDICFLAG_VALUEFORE)
1472 textFore = indicatorValue & SC_INDICVALUEMASK;
1473 else
1474 textFore = indicator.sacNormal.fore;
1480 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1481 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1482 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1484 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1485 if (ts.representation) {
1486 if (ll->chars[i] == '\t') {
1487 // Tab display
1488 if (phasesDraw == phasesOne) {
1489 if (drawWhitespaceBackground &&
1490 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1491 textBack = vsDraw.whitespaceColours.back;
1492 surface->FillRectangle(rcSegment, textBack);
1494 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1495 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1496 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1497 indentCount++) {
1498 if (indentCount > 0) {
1499 int xIndent = static_cast<int>(indentCount * indentWidth);
1500 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1501 (ll->xHighlightGuide == xIndent));
1505 if (vsDraw.viewWhitespace != wsInvisible) {
1506 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1507 if (vsDraw.whitespaceColours.fore.isSet)
1508 textFore = vsDraw.whitespaceColours.fore;
1509 surface->PenColour(textFore);
1510 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,
1511 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1512 if (customDrawTabArrow == NULL)
1513 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1514 else
1515 customDrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1518 } else {
1519 inIndentation = false;
1520 if (vsDraw.controlCharSymbol >= 32) {
1521 // Using one font for all control characters so it can be controlled independently to ensure
1522 // the box goes around the characters tightly. Seems to be no way to work out what height
1523 // is taken by an individual character - internal leading gives varying results.
1524 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1525 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1526 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1527 rcSegment.top + vsDraw.maxAscent,
1528 cc, 1, textBack, textFore);
1529 } else {
1530 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1531 textBack, textFore, phasesDraw == phasesOne);
1534 } else {
1535 // Normal text display
1536 if (vsDraw.styles[styleMain].visible) {
1537 if (phasesDraw != phasesOne) {
1538 surface->DrawTextTransparent(rcSegment, textFont,
1539 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1540 i - ts.start + 1, textFore);
1541 } else {
1542 surface->DrawTextNoClip(rcSegment, textFont,
1543 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1544 i - ts.start + 1, textFore, textBack);
1547 if (vsDraw.viewWhitespace != wsInvisible ||
1548 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1549 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1550 if (ll->chars[cpos + ts.start] == ' ') {
1551 if (vsDraw.viewWhitespace != wsInvisible) {
1552 if (vsDraw.whitespaceColours.fore.isSet)
1553 textFore = vsDraw.whitespaceColours.fore;
1554 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1555 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1556 if ((phasesDraw == phasesOne) && drawWhitespaceBackground &&
1557 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1558 textBack = vsDraw.whitespaceColours.back;
1559 PRectangle rcSpace(
1560 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1561 rcSegment.top,
1562 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1563 rcSegment.bottom);
1564 surface->FillRectangle(rcSpace, textBack);
1566 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
1567 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1568 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1569 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1570 surface->FillRectangle(rcDot, textFore);
1573 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1574 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1575 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1576 indentCount++) {
1577 if (indentCount > 0) {
1578 int xIndent = static_cast<int>(indentCount * indentWidth);
1579 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1580 (ll->xHighlightGuide == xIndent));
1584 } else {
1585 inIndentation = false;
1590 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1591 PRectangle rcUL = rcSegment;
1592 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1593 rcUL.bottom = rcUL.top + 1;
1594 if (vsDraw.hotspotColours.fore.isSet)
1595 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1596 else
1597 surface->FillRectangle(rcUL, textFore);
1598 } else if (vsDraw.styles[styleMain].underline) {
1599 PRectangle rcUL = rcSegment;
1600 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1601 rcUL.bottom = rcUL.top + 1;
1602 surface->FillRectangle(rcUL, textFore);
1604 } else if (rcSegment.left > rcLine.right) {
1605 break;
1610 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1611 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1612 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1613 && (subLine == 0)) {
1614 const int posLineStart = model.pdoc->LineStart(line);
1615 int indentSpace = model.pdoc->GetLineIndentation(line);
1616 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1618 // Find the most recent line with some text
1620 int lineLastWithText = line;
1621 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1622 lineLastWithText--;
1624 if (lineLastWithText < line) {
1625 xStartText = 100000; // Don't limit to visible indentation on empty line
1626 // This line is empty, so use indentation of last line with text
1627 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1628 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1629 if (isFoldHeader) {
1630 // Level is one more level than parent
1631 indentLastWithText += model.pdoc->IndentSize();
1633 if (vsDraw.viewIndentationGuides == ivLookForward) {
1634 // In viLookForward mode, previous line only used if it is a fold header
1635 if (isFoldHeader) {
1636 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1638 } else { // viLookBoth
1639 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1643 int lineNextWithText = line;
1644 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1645 lineNextWithText++;
1647 if (lineNextWithText > line) {
1648 xStartText = 100000; // Don't limit to visible indentation on empty line
1649 // This line is empty, so use indentation of first next line with text
1650 indentSpace = Platform::Maximum(indentSpace,
1651 model.pdoc->GetLineIndentation(lineNextWithText));
1654 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1655 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1656 if (xIndent < xStartText) {
1657 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1658 (ll->xHighlightGuide == xIndent));
1664 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1665 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1667 if (subLine >= ll->lines) {
1668 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1669 return; // No further drawing
1672 // See if something overrides the line background color.
1673 ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1674 SCNotification scn = { 0 };
1675 scn.nmhdr.code = SCN_GETBKCOLOR;
1676 scn.line = line;
1677 scn.lParam = -1;
1678 if (editor)
1679 ((Editor*)editor)->NotifyParent(&scn);
1680 if (scn.lParam != -1) {
1681 background.Set(scn.lParam);
1682 background.isSet = true;
1685 const int posLineStart = model.pdoc->LineStart(line);
1687 const Range lineRange = ll->SubLineRange(subLine);
1688 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1690 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1691 if (phase & drawBack) {
1692 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker);
1694 xStart += static_cast<int>(ll->wrapIndent);
1697 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1698 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1699 subLine, background);
1700 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1701 xStart, subLine, subLineStart, background);
1704 if (phase & drawIndicatorsBack) {
1705 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true, model.hoverIndicatorPos);
1706 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1707 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1710 if (phase & drawText) {
1711 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1712 subLine, background);
1715 if (phase & drawIndentationGuides) {
1716 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1719 if (phase & drawIndicatorsFore) {
1720 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false, model.hoverIndicatorPos);
1723 // End of the drawing of the current line
1724 if (phasesDraw == phasesOne) {
1725 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1726 xStart, subLine, subLineStart, background);
1729 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1730 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1733 if (phase & drawLineTranslucent) {
1734 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1738 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1739 bool expanded = model.cs.GetExpanded(line);
1740 const int level = model.pdoc->GetLevel(line);
1741 const int levelNext = model.pdoc->GetLevel(line + 1);
1742 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1743 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
1744 // Paint the line above the fold
1745 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1747 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1748 PRectangle rcFoldLine = rcLine;
1749 rcFoldLine.bottom = rcFoldLine.top + 1;
1750 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1752 // Paint the line below the fold
1753 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1755 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1756 PRectangle rcFoldLine = rcLine;
1757 rcFoldLine.top = rcFoldLine.bottom - 1;
1758 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1763 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1764 PRectangle rcClient, const ViewStyle &vsDraw) {
1765 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1766 // serifs and italic stems for aliased text.
1767 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1769 // Do the painting
1770 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1772 Surface *surface = surfaceWindow;
1773 if (bufferedDraw) {
1774 surface = pixmapLine;
1775 PLATFORM_ASSERT(pixmapLine->Initialised());
1777 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1778 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1780 const Point ptOrigin = model.GetVisibleOriginInMain();
1782 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1783 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1785 SelectionPosition posCaret = model.sel.RangeMain().caret;
1786 if (model.posDrag.IsValid())
1787 posCaret = model.posDrag;
1788 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1790 PRectangle rcTextArea = rcClient;
1791 if (vsDraw.marginInside) {
1792 rcTextArea.left += vsDraw.textStart;
1793 rcTextArea.right -= vsDraw.rightMarginWidth;
1794 } else {
1795 rcTextArea = rcArea;
1798 // Remove selection margin from drawing area so text will not be drawn
1799 // on it in unbuffered mode.
1800 if (!bufferedDraw && vsDraw.marginInside) {
1801 PRectangle rcClipText = rcTextArea;
1802 rcClipText.left -= leftTextOverlap;
1803 surfaceWindow->SetClip(rcClipText);
1806 // Loop on visible lines
1807 //double durLayout = 0.0;
1808 //double durPaint = 0.0;
1809 //double durCopy = 0.0;
1810 //ElapsedTime etWhole;
1812 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1813 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1815 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1816 AutoLineLayout ll(llc, 0);
1817 std::vector<DrawPhase> phases;
1818 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1819 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1820 phases.push_back(phase);
1822 } else {
1823 phases.push_back(drawAll);
1825 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1826 int ypos = 0;
1827 if (!bufferedDraw)
1828 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1829 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1830 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1831 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1833 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1834 // Only visible lines should be handled by the code within the loop
1835 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1836 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1837 const int subLine = visibleLine - lineStartSet;
1839 // Copy this line and its styles from the document into local arrays
1840 // and determine the x position at which each character starts.
1841 //ElapsedTime et;
1842 if (lineDoc != lineDocPrevious) {
1843 ll.Set(0);
1844 ll.Set(RetrieveLineLayout(lineDoc, model));
1845 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1846 lineDocPrevious = lineDoc;
1848 //durLayout += et.Duration(true);
1850 if (ll) {
1851 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1852 ll->hotspot = model.GetHotSpotRange();
1854 PRectangle rcLine = rcTextArea;
1855 rcLine.top = static_cast<XYPOSITION>(ypos);
1856 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
1858 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
1860 // Highlight the current braces if any
1861 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
1862 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
1864 if (leftTextOverlap && bufferedDraw) {
1865 PRectangle rcSpacer = rcLine;
1866 rcSpacer.right = rcSpacer.left;
1867 rcSpacer.left -= 1;
1868 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
1871 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
1872 //durPaint += et.Duration(true);
1874 // Restore the previous styles for the brace highlights in case layout is in cache.
1875 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
1877 if (*it & drawFoldLines) {
1878 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
1881 if (*it & drawCarets) {
1882 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
1885 if (bufferedDraw) {
1886 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
1887 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
1888 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
1889 yposScreen + vsDraw.lineHeight);
1890 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
1893 lineWidthMaxSeen = Platform::Maximum(
1894 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
1895 //durCopy += et.Duration(true);
1898 if (!bufferedDraw) {
1899 ypos += vsDraw.lineHeight;
1902 yposScreen += vsDraw.lineHeight;
1903 visibleLine++;
1906 ll.Set(0);
1907 //if (durPaint < 0.00000001)
1908 // durPaint = 0.00000001;
1910 // Right column limit indicator
1911 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
1912 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
1913 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
1914 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
1915 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1916 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
1917 if (vsDraw.edgeState == EDGE_LINE) {
1918 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1919 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
1920 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1921 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour);
1924 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1926 //Platform::DebugPrintf(
1927 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1928 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1932 // Space (3 space characters) between line numbers and text when printing.
1933 #define lineNumberPrintSpace " "
1935 ColourDesired InvertedLight(ColourDesired orig) {
1936 unsigned int r = orig.GetRed();
1937 unsigned int g = orig.GetGreen();
1938 unsigned int b = orig.GetBlue();
1939 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1940 unsigned int il = 0xff - l;
1941 if (l == 0)
1942 return ColourDesired(0xff, 0xff, 0xff);
1943 r = r * il / l;
1944 g = g * il / l;
1945 b = b * il / l;
1946 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1949 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
1950 const EditModel &model, const ViewStyle &vs) {
1951 // Can't use measurements cached for screen
1952 posCache.Clear();
1954 ViewStyle vsPrint(vs);
1955 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
1957 // Modify the view style for printing as do not normally want any of the transient features to be printed
1958 // Printing supports only the line number margin.
1959 int lineNumberIndex = -1;
1960 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1961 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
1962 lineNumberIndex = margin;
1963 } else {
1964 vsPrint.ms[margin].width = 0;
1967 vsPrint.fixedColumnWidth = 0;
1968 vsPrint.zoomLevel = printParameters.magnification;
1969 // Don't show indentation guides
1970 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1971 vsPrint.viewIndentationGuides = ivNone;
1972 // Don't show the selection when printing
1973 vsPrint.selColours.back.isSet = false;
1974 vsPrint.selColours.fore.isSet = false;
1975 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
1976 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
1977 vsPrint.whitespaceColours.back.isSet = false;
1978 vsPrint.whitespaceColours.fore.isSet = false;
1979 vsPrint.showCaretLineBackground = false;
1980 vsPrint.alwaysShowCaretLineBackground = false;
1981 // Don't highlight matching braces using indicators
1982 vsPrint.braceHighlightIndicatorSet = false;
1983 vsPrint.braceBadLightIndicatorSet = false;
1985 // Set colours for printing according to users settings
1986 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
1987 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
1988 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
1989 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
1990 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
1991 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
1992 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1993 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
1994 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1995 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
1996 if (sty <= STYLE_DEFAULT) {
1997 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
2001 // White background for the line numbers
2002 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
2004 // Printing uses different margins, so reset screen margins
2005 vsPrint.leftMarginWidth = 0;
2006 vsPrint.rightMarginWidth = 0;
2008 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
2009 // Determining width must happen after fonts have been realised in Refresh
2010 int lineNumberWidth = 0;
2011 if (lineNumberIndex >= 0) {
2012 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
2013 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
2014 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
2015 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
2018 int linePrintStart = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMin));
2019 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
2020 if (linePrintLast < linePrintStart)
2021 linePrintLast = linePrintStart;
2022 int linePrintMax = model.pdoc->LineFromPosition(static_cast<int>(pfr->chrg.cpMax));
2023 if (linePrintLast > linePrintMax)
2024 linePrintLast = linePrintMax;
2025 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
2026 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
2027 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
2028 int endPosPrint = model.pdoc->Length();
2029 if (linePrintLast < model.pdoc->LinesTotal())
2030 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
2032 // Ensure we are styled to where we are formatting.
2033 model.pdoc->EnsureStyledTo(endPosPrint);
2035 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
2036 int ypos = pfr->rc.top;
2038 int lineDoc = linePrintStart;
2040 int nPrintPos = static_cast<int>(pfr->chrg.cpMin);
2041 int visibleLine = 0;
2042 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
2043 if (printParameters.wrapState == eWrapNone)
2044 widthPrint = LineLayout::wrapWidthInfinite;
2046 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2048 // When printing, the hdc and hdcTarget may be the same, so
2049 // changing the state of surfaceMeasure may change the underlying
2050 // state of surface. Therefore, any cached state is discarded before
2051 // using each surface.
2052 surfaceMeasure->FlushCachedState();
2054 // Copy this line and its styles from the document into local arrays
2055 // and determine the x position at which each character starts.
2056 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2057 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2059 ll.containsCaret = false;
2061 PRectangle rcLine = PRectangle::FromInts(
2062 pfr->rc.left,
2063 ypos,
2064 pfr->rc.right - 1,
2065 ypos + vsPrint.lineHeight);
2067 // When document line is wrapped over multiple display lines, find where
2068 // to start printing from to ensure a particular position is on the first
2069 // line of the page.
2070 if (visibleLine == 0) {
2071 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2072 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2073 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2074 visibleLine = -iwl;
2078 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2079 visibleLine = -(ll.lines - 1);
2083 if (draw && lineNumberWidth &&
2084 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2085 (visibleLine >= 0)) {
2086 char number[100];
2087 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2088 PRectangle rcNumber = rcLine;
2089 rcNumber.right = rcNumber.left + lineNumberWidth;
2090 // Right justify
2091 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2092 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2093 surface->FlushCachedState();
2094 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2095 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2096 vsPrint.styles[STYLE_LINENUMBER].fore,
2097 vsPrint.styles[STYLE_LINENUMBER].back);
2100 // Draw the line
2101 surface->FlushCachedState();
2103 for (int iwl = 0; iwl < ll.lines; iwl++) {
2104 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2105 if (visibleLine >= 0) {
2106 if (draw) {
2107 rcLine.top = static_cast<XYPOSITION>(ypos);
2108 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2109 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2111 ypos += vsPrint.lineHeight;
2113 visibleLine++;
2114 if (iwl == ll.lines - 1)
2115 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2116 else
2117 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2121 ++lineDoc;
2124 // Clear cache so measurements are not used for screen
2125 posCache.Clear();
2127 return nPrintPos;