Adjusted and applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / src / EditView.cxx
blob50d2488eaac7b3cb64bfa5919012077707737b91
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);
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) {
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) {
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);
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) {
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 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
973 surface, vsDraw, ll, xStart, rcLine, subLine);
974 startPos = endPos;
975 if (!deco->rs.ValueAt(startPos)) {
976 startPos = deco->rs.EndRun(startPos);
982 // Use indicators to highlight matching braces
983 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
984 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
985 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
986 if (under == vsDraw.indicators[braceIndicator].under) {
987 Range rangeLine(posLineStart + lineStart, posLineEnd);
988 if (rangeLine.ContainsCharacter(model.braces[0])) {
989 int braceOffset = model.braces[0] - posLineStart;
990 if (braceOffset < ll->numCharsInLine) {
991 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
994 if (rangeLine.ContainsCharacter(model.braces[1])) {
995 int braceOffset = model.braces[1] - posLineStart;
996 if (braceOffset < ll->numCharsInLine) {
997 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
1004 static bool AnnotationBoxedOrIndented(int annotationVisible) {
1005 return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED;
1008 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1009 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1010 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
1011 PRectangle rcSegment = rcLine;
1012 int annotationLine = subLine - ll->lines;
1013 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1014 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1015 if (phase & drawBack) {
1016 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1018 rcSegment.left = static_cast<XYPOSITION>(xStart);
1019 if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1020 // Only care about calculating width if tracking or need to draw indented box
1021 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1022 if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1023 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1024 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1025 rcSegment.right = rcSegment.left + widthAnnotation;
1027 if (widthAnnotation > lineWidthMaxSeen)
1028 lineWidthMaxSeen = widthAnnotation;
1030 const int annotationLines = model.pdoc->AnnotationLines(line);
1031 size_t start = 0;
1032 size_t lengthAnnotation = stAnnotation.LineLength(start);
1033 int lineInAnnotation = 0;
1034 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1035 start += lengthAnnotation + 1;
1036 lengthAnnotation = stAnnotation.LineLength(start);
1037 lineInAnnotation++;
1039 PRectangle rcText = rcSegment;
1040 if ((phase & drawBack) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1041 surface->FillRectangle(rcText,
1042 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1043 rcText.left += vsDraw.spaceWidth;
1045 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1046 stAnnotation, start, lengthAnnotation, phase);
1047 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1048 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1049 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1050 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1051 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1052 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1053 if (subLine == ll->lines) {
1054 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1055 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1057 if (subLine == ll->lines + annotationLines - 1) {
1058 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1059 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1065 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1066 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1068 int lineStart = ll->LineStart(subLine);
1069 int posBefore = posCaret;
1070 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1071 int numCharsToDraw = posAfter - posCaret;
1073 // Work out where the starting and ending offsets are. We need to
1074 // see if the previous character shares horizontal space, such as a
1075 // glyph / combining character. If so we'll need to draw that too.
1076 int offsetFirstChar = offset;
1077 int offsetLastChar = offset + (posAfter - posCaret);
1078 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1079 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1080 // The char does not share horizontal space
1081 break;
1083 // Char shares horizontal space, update the numChars to draw
1084 // Update posBefore to point to the prev char
1085 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1086 numCharsToDraw = posAfter - posBefore;
1087 offsetFirstChar = offset - (posCaret - posBefore);
1090 // See if the next character shares horizontal space, if so we'll
1091 // need to draw that too.
1092 if (offsetFirstChar < 0)
1093 offsetFirstChar = 0;
1094 numCharsToDraw = offsetLastChar - offsetFirstChar;
1095 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1096 // Update posAfter to point to the 2nd next char, this is where
1097 // the next character ends, and 2nd next begins. We'll need
1098 // to compare these two
1099 posBefore = posAfter;
1100 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1101 offsetLastChar = offset + (posAfter - posCaret);
1102 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1103 // The char does not share horizontal space
1104 break;
1106 // Char shares horizontal space, update the numChars to draw
1107 numCharsToDraw = offsetLastChar - offsetFirstChar;
1110 // We now know what to draw, update the caret drawing rectangle
1111 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1112 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1114 // Adjust caret position to take into account any word wrapping symbols.
1115 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1116 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1117 rcCaret.left += wordWrapCharWidth;
1118 rcCaret.right += wordWrapCharWidth;
1121 // This character is where the caret block is, we override the colours
1122 // (inversed) for drawing the caret here.
1123 int styleMain = ll->styles[offsetFirstChar];
1124 FontAlias fontText = vsDraw.styles[styleMain].font;
1125 surface->DrawTextClipped(rcCaret, fontText,
1126 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1127 numCharsToDraw, vsDraw.styles[styleMain].back,
1128 caretColour);
1131 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1132 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1133 // When drag is active it is the only caret drawn
1134 bool drawDrag = model.posDrag.IsValid();
1135 if (hideSelection && !drawDrag)
1136 return;
1137 const int posLineStart = model.pdoc->LineStart(lineDoc);
1138 // For each selection draw
1139 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1140 const bool mainCaret = r == model.sel.Main();
1141 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1142 const int offset = posCaret.Position() - posLineStart;
1143 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1144 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1145 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1146 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1147 if (ll->wrapIndent != 0) {
1148 int lineStart = ll->LineStart(subLine);
1149 if (lineStart != 0) // Wrapped
1150 xposCaret += ll->wrapIndent;
1152 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1153 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1154 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1155 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1156 bool caretAtEOF = false;
1157 bool caretAtEOL = false;
1158 bool drawBlockCaret = false;
1159 XYPOSITION widthOverstrikeCaret;
1160 XYPOSITION caretWidthOffset = 0;
1161 PRectangle rcCaret = rcLine;
1163 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1164 caretAtEOF = true;
1165 widthOverstrikeCaret = vsDraw.aveCharWidth;
1166 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1167 caretAtEOL = true;
1168 widthOverstrikeCaret = vsDraw.aveCharWidth;
1169 } else {
1170 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1171 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1173 if (widthOverstrikeCaret < 3) // Make sure its visible
1174 widthOverstrikeCaret = 3;
1176 if (xposCaret > 0)
1177 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1178 xposCaret += xStart;
1179 if (model.posDrag.IsValid()) {
1180 /* Dragging text, use a line caret */
1181 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1182 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1183 } else if (model.inOverstrike && drawOverstrikeCaret) {
1184 /* Overstrike (insert mode), use a modified bar caret */
1185 rcCaret.top = rcCaret.bottom - 2;
1186 rcCaret.left = xposCaret + 1;
1187 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1188 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1189 /* Block caret */
1190 rcCaret.left = xposCaret;
1191 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1192 drawBlockCaret = true;
1193 rcCaret.right = xposCaret + widthOverstrikeCaret;
1194 } else {
1195 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1197 } else {
1198 /* Line caret */
1199 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1200 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1202 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1203 if (drawBlockCaret) {
1204 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1205 } else {
1206 surface->FillRectangle(rcCaret, caretColour);
1210 if (drawDrag)
1211 break;
1215 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1216 int xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker) {
1217 // default bgnd here..
1218 surface->FillRectangle(rcLine, background.isSet ? background :
1219 vsDraw.styles[STYLE_DEFAULT].back);
1221 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1223 // draw continuation rect
1224 PRectangle rcPlace = rcLine;
1226 rcPlace.left = static_cast<XYPOSITION>(xStart);
1227 rcPlace.right = rcPlace.left + ll->wrapIndent;
1229 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1230 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1231 else
1232 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1234 if (customDrawWrapMarker == NULL) {
1235 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1236 } else {
1237 customDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1242 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1243 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1244 int subLine, ColourOptional background) const {
1246 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1247 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1248 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1249 // Does not take margin into account but not significant
1250 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1252 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs);
1254 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1256 // Background drawing loop
1257 while (bfBack.More()) {
1259 const TextSegment ts = bfBack.Next();
1260 const int i = ts.end() - 1;
1261 const int iDoc = i + posLineStart;
1263 PRectangle rcSegment = rcLine;
1264 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1265 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1266 // Only try to draw if really visible - enhances performance by not calling environment to
1267 // draw strings that are completely past the right side of the window.
1268 if (rcSegment.Intersects(rcLine)) {
1269 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1270 if (rcSegment.left < rcLine.left)
1271 rcSegment.left = rcLine.left;
1272 if (rcSegment.right > rcLine.right)
1273 rcSegment.right = rcLine.right;
1275 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1276 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1277 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1278 inHotspot, ll->styles[i], i);
1279 if (ts.representation) {
1280 if (ll->chars[i] == '\t') {
1281 // Tab display
1282 if (drawWhitespaceBackground &&
1283 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1284 textBack = vsDraw.whitespaceColours.back;
1285 } else {
1286 // Blob display
1287 inIndentation = false;
1289 surface->FillRectangle(rcSegment, textBack);
1290 } else {
1291 // Normal text display
1292 surface->FillRectangle(rcSegment, textBack);
1293 if (vsDraw.viewWhitespace != wsInvisible ||
1294 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
1295 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1296 if (ll->chars[cpos + ts.start] == ' ') {
1297 if (drawWhitespaceBackground &&
1298 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1299 PRectangle rcSpace(
1300 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1301 rcSegment.top,
1302 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1303 rcSegment.bottom);
1304 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1306 } else {
1307 inIndentation = false;
1312 } else if (rcSegment.left > rcLine.right) {
1313 break;
1318 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1319 Range lineRange, int xStart) {
1320 if (vsDraw.edgeState == EDGE_LINE) {
1321 PRectangle rcSegment = rcLine;
1322 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1323 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1324 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1325 rcSegment.left -= ll->wrapIndent;
1326 rcSegment.right = rcSegment.left + 1;
1327 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
1331 // Draw underline mark as part of background if not transparent
1332 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1333 int line, PRectangle rcLine) {
1334 int marks = model.pdoc->GetMark(line);
1335 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1336 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1337 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1338 PRectangle rcUnderline = rcLine;
1339 rcUnderline.top = rcUnderline.bottom - 2;
1340 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1342 marks >>= 1;
1345 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1346 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1347 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1348 const int posLineStart = model.pdoc->LineStart(line);
1349 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1350 // For each selection draw
1351 int virtualSpaces = 0;
1352 if (subLine == (ll->lines - 1)) {
1353 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1355 SelectionPosition posStart(posLineStart + lineRange.start);
1356 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1357 SelectionSegment virtualSpaceRange(posStart, posEnd);
1358 for (size_t r = 0; r < model.sel.Count(); r++) {
1359 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1360 if (alpha != SC_ALPHA_NOALPHA) {
1361 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1362 if (!portion.Empty()) {
1363 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1364 PRectangle rcSegment = rcLine;
1365 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1366 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1367 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1368 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1369 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1370 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1371 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1373 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1374 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1375 if (rcSegment.right > rcLine.left)
1376 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1383 // Draw any translucent whole line states
1384 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1385 int line, PRectangle rcLine) {
1386 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1387 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1389 int marks = model.pdoc->GetMark(line);
1390 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1391 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
1392 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1393 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
1394 PRectangle rcUnderline = rcLine;
1395 rcUnderline.top = rcUnderline.bottom - 2;
1396 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1398 marks >>= 1;
1400 if (vsDraw.maskInLine) {
1401 int marksMasked = model.pdoc->GetMark(line) & vsDraw.maskInLine;
1402 if (marksMasked) {
1403 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
1404 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
1405 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1407 marksMasked >>= 1;
1413 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1414 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1415 int subLine, ColourOptional background) {
1417 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1418 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1419 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1421 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1422 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1424 // Does not take margin into account but not significant
1425 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1427 // Foreground drawing loop
1428 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1429 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs);
1431 while (bfFore.More()) {
1433 const TextSegment ts = bfFore.Next();
1434 const int i = ts.end() - 1;
1435 const int iDoc = i + posLineStart;
1437 PRectangle rcSegment = rcLine;
1438 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1439 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1440 // Only try to draw if really visible - enhances performance by not calling environment to
1441 // draw strings that are completely past the right side of the window.
1442 if (rcSegment.Intersects(rcLine)) {
1443 int styleMain = ll->styles[i];
1444 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1445 FontAlias textFont = vsDraw.styles[styleMain].font;
1446 //hotspot foreground
1447 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1448 if (inHotspot) {
1449 if (vsDraw.hotspotColours.fore.isSet)
1450 textFore = vsDraw.hotspotColours.fore;
1452 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1453 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1454 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1456 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1457 if (ts.representation) {
1458 if (ll->chars[i] == '\t') {
1459 // Tab display
1460 if (phasesDraw == phasesOne) {
1461 if (drawWhitespaceBackground &&
1462 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1463 textBack = vsDraw.whitespaceColours.back;
1464 surface->FillRectangle(rcSegment, textBack);
1466 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1467 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1468 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1469 indentCount++) {
1470 if (indentCount > 0) {
1471 int xIndent = static_cast<int>(indentCount * indentWidth);
1472 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1473 (ll->xHighlightGuide == xIndent));
1477 if (vsDraw.viewWhitespace != wsInvisible) {
1478 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1479 if (vsDraw.whitespaceColours.fore.isSet)
1480 textFore = vsDraw.whitespaceColours.fore;
1481 surface->PenColour(textFore);
1482 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,
1483 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1484 if (customDrawTabArrow == NULL)
1485 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1486 else
1487 customDrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1490 } else {
1491 inIndentation = false;
1492 if (vsDraw.controlCharSymbol >= 32) {
1493 // Using one font for all control characters so it can be controlled independently to ensure
1494 // the box goes around the characters tightly. Seems to be no way to work out what height
1495 // is taken by an individual character - internal leading gives varying results.
1496 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1497 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1498 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1499 rcSegment.top + vsDraw.maxAscent,
1500 cc, 1, textBack, textFore);
1501 } else {
1502 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1503 textBack, textFore, phasesDraw == phasesOne);
1506 } else {
1507 // Normal text display
1508 if (vsDraw.styles[styleMain].visible) {
1509 if (phasesDraw != phasesOne) {
1510 surface->DrawTextTransparent(rcSegment, textFont,
1511 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1512 i - ts.start + 1, textFore);
1513 } else {
1514 surface->DrawTextNoClip(rcSegment, textFont,
1515 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1516 i - ts.start + 1, textFore, textBack);
1519 if (vsDraw.viewWhitespace != wsInvisible ||
1520 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1521 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1522 if (ll->chars[cpos + ts.start] == ' ') {
1523 if (vsDraw.viewWhitespace != wsInvisible) {
1524 if (vsDraw.whitespaceColours.fore.isSet)
1525 textFore = vsDraw.whitespaceColours.fore;
1526 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1527 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1528 if ((phasesDraw == phasesOne) && drawWhitespaceBackground &&
1529 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1530 textBack = vsDraw.whitespaceColours.back;
1531 PRectangle rcSpace(
1532 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1533 rcSegment.top,
1534 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1535 rcSegment.bottom);
1536 surface->FillRectangle(rcSpace, textBack);
1538 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
1539 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1540 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1541 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1542 surface->FillRectangle(rcDot, textFore);
1545 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1546 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1547 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1548 indentCount++) {
1549 if (indentCount > 0) {
1550 int xIndent = static_cast<int>(indentCount * indentWidth);
1551 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1552 (ll->xHighlightGuide == xIndent));
1556 } else {
1557 inIndentation = false;
1562 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1563 PRectangle rcUL = rcSegment;
1564 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1565 rcUL.bottom = rcUL.top + 1;
1566 if (vsDraw.hotspotColours.fore.isSet)
1567 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1568 else
1569 surface->FillRectangle(rcUL, textFore);
1570 } else if (vsDraw.styles[styleMain].underline) {
1571 PRectangle rcUL = rcSegment;
1572 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1573 rcUL.bottom = rcUL.top + 1;
1574 surface->FillRectangle(rcUL, textFore);
1576 } else if (rcSegment.left > rcLine.right) {
1577 break;
1582 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1583 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1584 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1585 && (subLine == 0)) {
1586 const int posLineStart = model.pdoc->LineStart(line);
1587 int indentSpace = model.pdoc->GetLineIndentation(line);
1588 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1590 // Find the most recent line with some text
1592 int lineLastWithText = line;
1593 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1594 lineLastWithText--;
1596 if (lineLastWithText < line) {
1597 xStartText = 100000; // Don't limit to visible indentation on empty line
1598 // This line is empty, so use indentation of last line with text
1599 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1600 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1601 if (isFoldHeader) {
1602 // Level is one more level than parent
1603 indentLastWithText += model.pdoc->IndentSize();
1605 if (vsDraw.viewIndentationGuides == ivLookForward) {
1606 // In viLookForward mode, previous line only used if it is a fold header
1607 if (isFoldHeader) {
1608 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1610 } else { // viLookBoth
1611 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1615 int lineNextWithText = line;
1616 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1617 lineNextWithText++;
1619 if (lineNextWithText > line) {
1620 xStartText = 100000; // Don't limit to visible indentation on empty line
1621 // This line is empty, so use indentation of first next line with text
1622 indentSpace = Platform::Maximum(indentSpace,
1623 model.pdoc->GetLineIndentation(lineNextWithText));
1626 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1627 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1628 if (xIndent < xStartText) {
1629 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1630 (ll->xHighlightGuide == xIndent));
1636 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1637 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1639 if (subLine >= ll->lines) {
1640 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1641 return; // No further drawing
1644 // See if something overrides the line background color.
1645 ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1646 SCNotification scn = { 0 };
1647 scn.nmhdr.code = SCN_GETBKCOLOR;
1648 scn.line = line;
1649 scn.lParam = -1;
1650 if (editor)
1651 ((Editor*)editor)->NotifyParent(&scn);
1652 if (scn.lParam != -1) {
1653 background.Set(scn.lParam);
1654 background.isSet = true;
1657 const int posLineStart = model.pdoc->LineStart(line);
1659 const Range lineRange = ll->SubLineRange(subLine);
1660 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1662 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1663 if (phase & drawBack) {
1664 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker);
1666 xStart += static_cast<int>(ll->wrapIndent);
1669 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1670 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1671 subLine, background);
1672 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1673 xStart, subLine, subLineStart, background);
1676 if (phase & drawIndicatorsBack) {
1677 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true);
1678 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1679 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1682 if (phase & drawText) {
1683 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1684 subLine, background);
1687 if (phase & drawIndentationGuides) {
1688 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1691 if (phase & drawIndicatorsFore) {
1692 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false);
1695 // End of the drawing of the current line
1696 if (phasesDraw == phasesOne) {
1697 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1698 xStart, subLine, subLineStart, background);
1701 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1702 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1705 if (phase & drawLineTranslucent) {
1706 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1710 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1711 bool expanded = model.cs.GetExpanded(line);
1712 const int level = model.pdoc->GetLevel(line);
1713 const int levelNext = model.pdoc->GetLevel(line + 1);
1714 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1715 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
1716 // Paint the line above the fold
1717 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1719 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1720 PRectangle rcFoldLine = rcLine;
1721 rcFoldLine.bottom = rcFoldLine.top + 1;
1722 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1724 // Paint the line below the fold
1725 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1727 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1728 PRectangle rcFoldLine = rcLine;
1729 rcFoldLine.top = rcFoldLine.bottom - 1;
1730 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1735 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1736 PRectangle rcClient, const ViewStyle &vsDraw) {
1737 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1738 // serifs and italic stems for aliased text.
1739 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1741 // Do the painting
1742 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1744 Surface *surface = surfaceWindow;
1745 if (bufferedDraw) {
1746 surface = pixmapLine;
1747 PLATFORM_ASSERT(pixmapLine->Initialised());
1749 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1750 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1752 const Point ptOrigin = model.GetVisibleOriginInMain();
1754 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1755 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1757 SelectionPosition posCaret = model.sel.RangeMain().caret;
1758 if (model.posDrag.IsValid())
1759 posCaret = model.posDrag;
1760 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1762 PRectangle rcTextArea = rcClient;
1763 if (vsDraw.marginInside) {
1764 rcTextArea.left += vsDraw.textStart;
1765 rcTextArea.right -= vsDraw.rightMarginWidth;
1766 } else {
1767 rcTextArea = rcArea;
1770 // Remove selection margin from drawing area so text will not be drawn
1771 // on it in unbuffered mode.
1772 if (!bufferedDraw && vsDraw.marginInside) {
1773 PRectangle rcClipText = rcTextArea;
1774 rcClipText.left -= leftTextOverlap;
1775 surfaceWindow->SetClip(rcClipText);
1778 // Loop on visible lines
1779 //double durLayout = 0.0;
1780 //double durPaint = 0.0;
1781 //double durCopy = 0.0;
1782 //ElapsedTime etWhole;
1784 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1785 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1787 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1788 AutoLineLayout ll(llc, 0);
1789 std::vector<DrawPhase> phases;
1790 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1791 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1792 phases.push_back(phase);
1794 } else {
1795 phases.push_back(drawAll);
1797 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1798 int ypos = 0;
1799 if (!bufferedDraw)
1800 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1801 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1802 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1803 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1805 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1806 // Only visible lines should be handled by the code within the loop
1807 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1808 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1809 const int subLine = visibleLine - lineStartSet;
1811 // Copy this line and its styles from the document into local arrays
1812 // and determine the x position at which each character starts.
1813 //ElapsedTime et;
1814 if (lineDoc != lineDocPrevious) {
1815 ll.Set(0);
1816 ll.Set(RetrieveLineLayout(lineDoc, model));
1817 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1818 lineDocPrevious = lineDoc;
1820 //durLayout += et.Duration(true);
1822 if (ll) {
1823 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1824 ll->hotspot = model.GetHotSpotRange();
1826 PRectangle rcLine = rcTextArea;
1827 rcLine.top = static_cast<XYPOSITION>(ypos);
1828 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
1830 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
1832 // Highlight the current braces if any
1833 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
1834 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
1836 if (leftTextOverlap && bufferedDraw) {
1837 PRectangle rcSpacer = rcLine;
1838 rcSpacer.right = rcSpacer.left;
1839 rcSpacer.left -= 1;
1840 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
1843 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
1844 //durPaint += et.Duration(true);
1846 // Restore the previous styles for the brace highlights in case layout is in cache.
1847 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
1849 if (*it & drawFoldLines) {
1850 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
1853 if (*it & drawCarets) {
1854 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
1857 if (bufferedDraw) {
1858 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
1859 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
1860 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
1861 yposScreen + vsDraw.lineHeight);
1862 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
1865 lineWidthMaxSeen = Platform::Maximum(
1866 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
1867 //durCopy += et.Duration(true);
1870 if (!bufferedDraw) {
1871 ypos += vsDraw.lineHeight;
1874 yposScreen += vsDraw.lineHeight;
1875 visibleLine++;
1878 ll.Set(0);
1879 //if (durPaint < 0.00000001)
1880 // durPaint = 0.00000001;
1882 // Right column limit indicator
1883 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
1884 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
1885 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
1886 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
1887 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1888 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
1889 if (vsDraw.edgeState == EDGE_LINE) {
1890 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1891 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
1892 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1893 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour);
1896 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1898 //Platform::DebugPrintf(
1899 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1900 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1904 // Space (3 space characters) between line numbers and text when printing.
1905 #define lineNumberPrintSpace " "
1907 ColourDesired InvertedLight(ColourDesired orig) {
1908 unsigned int r = orig.GetRed();
1909 unsigned int g = orig.GetGreen();
1910 unsigned int b = orig.GetBlue();
1911 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1912 unsigned int il = 0xff - l;
1913 if (l == 0)
1914 return ColourDesired(0xff, 0xff, 0xff);
1915 r = r * il / l;
1916 g = g * il / l;
1917 b = b * il / l;
1918 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1921 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
1922 const EditModel &model, const ViewStyle &vs) {
1923 // Can't use measurements cached for screen
1924 posCache.Clear();
1926 ViewStyle vsPrint(vs);
1927 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
1929 // Modify the view style for printing as do not normally want any of the transient features to be printed
1930 // Printing supports only the line number margin.
1931 int lineNumberIndex = -1;
1932 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1933 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
1934 lineNumberIndex = margin;
1935 } else {
1936 vsPrint.ms[margin].width = 0;
1939 vsPrint.fixedColumnWidth = 0;
1940 vsPrint.zoomLevel = printParameters.magnification;
1941 // Don't show indentation guides
1942 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1943 vsPrint.viewIndentationGuides = ivNone;
1944 // Don't show the selection when printing
1945 vsPrint.selColours.back.isSet = false;
1946 vsPrint.selColours.fore.isSet = false;
1947 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
1948 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
1949 vsPrint.whitespaceColours.back.isSet = false;
1950 vsPrint.whitespaceColours.fore.isSet = false;
1951 vsPrint.showCaretLineBackground = false;
1952 vsPrint.alwaysShowCaretLineBackground = false;
1953 // Don't highlight matching braces using indicators
1954 vsPrint.braceHighlightIndicatorSet = false;
1955 vsPrint.braceBadLightIndicatorSet = false;
1957 // Set colours for printing according to users settings
1958 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
1959 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
1960 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
1961 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
1962 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
1963 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
1964 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1965 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
1966 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1967 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
1968 if (sty <= STYLE_DEFAULT) {
1969 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1973 // White background for the line numbers
1974 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
1976 // Printing uses different margins, so reset screen margins
1977 vsPrint.leftMarginWidth = 0;
1978 vsPrint.rightMarginWidth = 0;
1980 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
1981 // Determining width must happen after fonts have been realised in Refresh
1982 int lineNumberWidth = 0;
1983 if (lineNumberIndex >= 0) {
1984 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
1985 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
1986 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
1987 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
1990 int linePrintStart = model.pdoc->LineFromPosition(pfr->chrg.cpMin);
1991 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
1992 if (linePrintLast < linePrintStart)
1993 linePrintLast = linePrintStart;
1994 int linePrintMax = model.pdoc->LineFromPosition(pfr->chrg.cpMax);
1995 if (linePrintLast > linePrintMax)
1996 linePrintLast = linePrintMax;
1997 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
1998 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
1999 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
2000 int endPosPrint = model.pdoc->Length();
2001 if (linePrintLast < model.pdoc->LinesTotal())
2002 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
2004 // Ensure we are styled to where we are formatting.
2005 model.pdoc->EnsureStyledTo(endPosPrint);
2007 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
2008 int ypos = pfr->rc.top;
2010 int lineDoc = linePrintStart;
2012 int nPrintPos = pfr->chrg.cpMin;
2013 int visibleLine = 0;
2014 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
2015 if (printParameters.wrapState == eWrapNone)
2016 widthPrint = LineLayout::wrapWidthInfinite;
2018 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2020 // When printing, the hdc and hdcTarget may be the same, so
2021 // changing the state of surfaceMeasure may change the underlying
2022 // state of surface. Therefore, any cached state is discarded before
2023 // using each surface.
2024 surfaceMeasure->FlushCachedState();
2026 // Copy this line and its styles from the document into local arrays
2027 // and determine the x position at which each character starts.
2028 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2029 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2031 ll.containsCaret = false;
2033 PRectangle rcLine = PRectangle::FromInts(
2034 pfr->rc.left,
2035 ypos,
2036 pfr->rc.right - 1,
2037 ypos + vsPrint.lineHeight);
2039 // When document line is wrapped over multiple display lines, find where
2040 // to start printing from to ensure a particular position is on the first
2041 // line of the page.
2042 if (visibleLine == 0) {
2043 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2044 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2045 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2046 visibleLine = -iwl;
2050 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2051 visibleLine = -(ll.lines - 1);
2055 if (draw && lineNumberWidth &&
2056 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2057 (visibleLine >= 0)) {
2058 char number[100];
2059 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2060 PRectangle rcNumber = rcLine;
2061 rcNumber.right = rcNumber.left + lineNumberWidth;
2062 // Right justify
2063 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2064 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2065 surface->FlushCachedState();
2066 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2067 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2068 vsPrint.styles[STYLE_LINENUMBER].fore,
2069 vsPrint.styles[STYLE_LINENUMBER].back);
2072 // Draw the line
2073 surface->FlushCachedState();
2075 for (int iwl = 0; iwl < ll.lines; iwl++) {
2076 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2077 if (visibleLine >= 0) {
2078 if (draw) {
2079 rcLine.top = static_cast<XYPOSITION>(ypos);
2080 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2081 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2083 ypos += vsPrint.lineHeight;
2085 visibleLine++;
2086 if (iwl == ll.lines - 1)
2087 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2088 else
2089 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2093 ++lineDoc;
2096 // Clear cache so measurements are not used for screen
2097 posCache.Clear();
2099 return nPrintPos;