Adjusted and applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / src / EditView.cxx
blob0d26631618269c011db00e671765c2323cb1a5cf
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 <string>
16 #include <vector>
17 #include <map>
18 #include <algorithm>
19 #include <memory>
21 #include "Platform.h"
23 #include "ILexer.h"
24 #include "Scintilla.h"
26 #include "StringCopy.h"
27 #include "SplitVector.h"
28 #include "Partitioning.h"
29 #include "RunStyles.h"
30 #include "ContractionState.h"
31 #include "CellBuffer.h"
32 #include "PerLine.h"
33 #include "KeyMap.h"
34 #include "Indicator.h"
35 #include "XPM.h"
36 #include "LineMarker.h"
37 #include "Style.h"
38 #include "ViewStyle.h"
39 #include "CharClassify.h"
40 #include "Decoration.h"
41 #include "CaseFolder.h"
42 #include "Document.h"
43 #include "UniConversion.h"
44 #include "Selection.h"
45 #include "PositionCache.h"
46 #include "EditModel.h"
47 #include "MarginView.h"
48 #include "EditView.h"
49 #include "Editor.h"
51 #ifdef SCI_NAMESPACE
52 using namespace Scintilla;
53 #endif
55 static inline bool IsControlCharacter(int ch) {
56 // iscntrl returns true for lots of chars > 127 which are displayable
57 return ch >= 0 && ch < ' ';
60 PrintParameters::PrintParameters() {
61 magnification = 0;
62 colourMode = SC_PRINT_NORMAL;
63 wrapState = eWrapWord;
66 #ifdef SCI_NAMESPACE
67 namespace Scintilla {
68 #endif
70 bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) {
71 if (st.multipleStyles) {
72 for (size_t iStyle = 0; iStyle<st.length; iStyle++) {
73 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
74 return false;
76 } else {
77 if (!vs.ValidStyle(styleOffset + st.style))
78 return false;
80 return true;
83 static int WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset,
84 const char *text, const unsigned char *styles, size_t len) {
85 int width = 0;
86 size_t start = 0;
87 while (start < len) {
88 size_t style = styles[start];
89 size_t endSegment = start;
90 while ((endSegment + 1 < len) && (static_cast<size_t>(styles[endSegment + 1]) == style))
91 endSegment++;
92 FontAlias fontText = vs.styles[style + styleOffset].font;
93 width += static_cast<int>(surface->WidthText(fontText, text + start,
94 static_cast<int>(endSegment - start + 1)));
95 start = endSegment + 1;
97 return width;
100 int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) {
101 int widthMax = 0;
102 size_t start = 0;
103 while (start < st.length) {
104 size_t lenLine = st.LineLength(start);
105 int widthSubLine;
106 if (st.multipleStyles) {
107 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
108 } else {
109 FontAlias fontText = vs.styles[styleOffset + st.style].font;
110 widthSubLine = static_cast<int>(surface->WidthText(fontText,
111 st.text + start, static_cast<int>(lenLine)));
113 if (widthSubLine > widthMax)
114 widthMax = widthSubLine;
115 start += lenLine + 1;
117 return widthMax;
120 void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,
121 const char *s, int len, DrawPhase phase) {
122 FontAlias fontText = style.font;
123 if (phase & drawBack) {
124 if (phase & drawText) {
125 // Drawing both
126 surface->DrawTextNoClip(rc, fontText, ybase, s, len,
127 style.fore, style.back);
128 } else {
129 surface->FillRectangle(rc, style.back);
131 } else if (phase & drawText) {
132 surface->DrawTextTransparent(rc, fontText, ybase, s, len, style.fore);
136 void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
137 const StyledText &st, size_t start, size_t length, DrawPhase phase) {
139 if (st.multipleStyles) {
140 int x = static_cast<int>(rcText.left);
141 size_t i = 0;
142 while (i < length) {
143 size_t end = i;
144 size_t style = st.styles[i + start];
145 while (end < length - 1 && st.styles[start + end + 1] == style)
146 end++;
147 style += styleOffset;
148 FontAlias fontText = vs.styles[style].font;
149 const int width = static_cast<int>(surface->WidthText(fontText,
150 st.text + start + i, static_cast<int>(end - i + 1)));
151 PRectangle rcSegment = rcText;
152 rcSegment.left = static_cast<XYPOSITION>(x);
153 rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
154 DrawTextNoClipPhase(surface, rcSegment, vs.styles[style],
155 rcText.top + vs.maxAscent, st.text + start + i,
156 static_cast<int>(end - i + 1), phase);
157 x += width;
158 i = end + 1;
160 } else {
161 const size_t style = st.style + styleOffset;
162 DrawTextNoClipPhase(surface, rcText, vs.styles[style],
163 rcText.top + vs.maxAscent, st.text + start,
164 static_cast<int>(length), phase);
168 #ifdef SCI_NAMESPACE
170 #endif
172 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
174 EditView::EditView() {
175 ldTabstops = NULL;
176 hideSelection = false;
177 drawOverstrikeCaret = true;
178 bufferedDraw = true;
179 phasesDraw = phasesTwo;
180 lineWidthMaxSeen = 0;
181 additionalCaretsBlink = true;
182 additionalCaretsVisible = true;
183 imeCaretBlockOverride = false;
184 pixmapLine = 0;
185 pixmapIndentGuide = 0;
186 pixmapIndentGuideHighlight = 0;
187 llc.SetLevel(LineLayoutCache::llcCaret);
188 posCache.SetSize(0x400);
189 editor = NULL;
192 EditView::~EditView() {
193 delete ldTabstops;
194 ldTabstops = NULL;
197 bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) {
198 const PhasesDraw phasesDrawNew = twoPhaseDraw ? phasesTwo : phasesOne;
199 const bool redraw = phasesDraw != phasesDrawNew;
200 phasesDraw = phasesDrawNew;
201 return redraw;
204 bool EditView::SetPhasesDraw(int phases) {
205 const PhasesDraw phasesDrawNew = static_cast<PhasesDraw>(phases);
206 const bool redraw = phasesDraw != phasesDrawNew;
207 phasesDraw = phasesDrawNew;
208 return redraw;
211 bool EditView::LinesOverlap() const {
212 return phasesDraw == phasesMultiple;
215 void EditView::ClearAllTabstops() {
216 delete ldTabstops;
217 ldTabstops = 0;
220 XYPOSITION EditView::NextTabstopPos(int line, XYPOSITION x, XYPOSITION tabWidth) const {
221 int next = GetNextTabstop(line, static_cast<int>(x + 2));
222 if (next > 0)
223 return static_cast<XYPOSITION>(next);
224 return (static_cast<int>((x + 2) / tabWidth) + 1) * tabWidth;
227 bool EditView::ClearTabstops(int line) {
228 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
229 return lt && lt->ClearTabstops(line);
232 bool EditView::AddTabstop(int line, int x) {
233 if (!ldTabstops) {
234 ldTabstops = new LineTabstops();
236 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
237 return lt && lt->AddTabstop(line, x);
240 int EditView::GetNextTabstop(int line, int x) const {
241 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
242 if (lt) {
243 return lt->GetNextTabstop(line, x);
244 } else {
245 return 0;
249 void EditView::LinesAddedOrRemoved(int lineOfPos, int linesAdded) {
250 if (ldTabstops) {
251 if (linesAdded > 0) {
252 for (int line = lineOfPos; line < lineOfPos + linesAdded; line++) {
253 ldTabstops->InsertLine(line);
255 } else {
256 for (int line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {
257 ldTabstops->RemoveLine(line);
263 void EditView::DropGraphics(bool freeObjects) {
264 if (freeObjects) {
265 delete pixmapLine;
266 pixmapLine = 0;
267 delete pixmapIndentGuide;
268 pixmapIndentGuide = 0;
269 delete pixmapIndentGuideHighlight;
270 pixmapIndentGuideHighlight = 0;
271 } else {
272 if (pixmapLine)
273 pixmapLine->Release();
274 if (pixmapIndentGuide)
275 pixmapIndentGuide->Release();
276 if (pixmapIndentGuideHighlight)
277 pixmapIndentGuideHighlight->Release();
281 void EditView::AllocateGraphics(const ViewStyle &vsDraw) {
282 if (!pixmapLine)
283 pixmapLine = Surface::Allocate(vsDraw.technology);
284 if (!pixmapIndentGuide)
285 pixmapIndentGuide = Surface::Allocate(vsDraw.technology);
286 if (!pixmapIndentGuideHighlight)
287 pixmapIndentGuideHighlight = Surface::Allocate(vsDraw.technology);
290 const char *ControlCharacterString(unsigned char ch) {
291 const char *reps[] = {
292 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
293 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
294 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
295 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
297 if (ch < ELEMENTS(reps)) {
298 return reps[ch];
299 } else {
300 return "BAD";
304 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
305 int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
306 int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
307 if (xhead <= rcTab.left) {
308 ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
309 xhead = static_cast<int>(rcTab.left) - 1;
311 if ((rcTab.left + 2) < (rcTab.right - 1))
312 surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
313 else
314 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
315 surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
316 surface->LineTo(xhead, ymid - ydiff);
317 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
318 surface->LineTo(xhead, ymid + ydiff);
321 void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
322 if (!pixmapIndentGuide->Initialised()) {
323 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
324 pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
325 pixmapIndentGuideHighlight->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
326 PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight);
327 pixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[STYLE_INDENTGUIDE].back);
328 pixmapIndentGuide->PenColour(vsDraw.styles[STYLE_INDENTGUIDE].fore);
329 pixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[STYLE_BRACELIGHT].back);
330 pixmapIndentGuideHighlight->PenColour(vsDraw.styles[STYLE_BRACELIGHT].fore);
331 for (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) {
332 PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
333 pixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[STYLE_INDENTGUIDE].fore);
334 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[STYLE_BRACELIGHT].fore);
339 LineLayout *EditView::RetrieveLineLayout(int lineNumber, const EditModel &model) {
340 int posLineStart = model.pdoc->LineStart(lineNumber);
341 int posLineEnd = model.pdoc->LineStart(lineNumber + 1);
342 PLATFORM_ASSERT(posLineEnd >= posLineStart);
343 int lineCaret = model.pdoc->LineFromPosition(model.sel.MainCaret());
344 return llc.Retrieve(lineNumber, lineCaret,
345 posLineEnd - posLineStart, model.pdoc->GetStyleClock(),
346 model.LinesOnScreen() + 1, model.pdoc->LinesTotal());
350 * Fill in the LineLayout data for the given line.
351 * Copy the given @a line and its styles from the document into local arrays.
352 * Also determine the x position at which each character starts.
354 void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
355 if (!ll)
356 return;
358 PLATFORM_ASSERT(line < model.pdoc->LinesTotal());
359 PLATFORM_ASSERT(ll->chars != NULL);
360 int posLineStart = model.pdoc->LineStart(line);
361 int posLineEnd = model.pdoc->LineStart(line + 1);
362 // If the line is very long, limit the treatment to a length that should fit in the viewport
363 if (posLineEnd >(posLineStart + ll->maxLineLength)) {
364 posLineEnd = posLineStart + ll->maxLineLength;
366 if (ll->validity == LineLayout::llCheckTextAndStyle) {
367 int lineLength = posLineEnd - posLineStart;
368 if (!vstyle.viewEOL) {
369 lineLength = model.pdoc->LineEnd(line) - posLineStart;
371 if (lineLength == ll->numCharsInLine) {
372 // See if chars, styles, indicators, are all the same
373 bool allSame = true;
374 // Check base line layout
375 char styleByte = 0;
376 int numCharsInLine = 0;
377 while (numCharsInLine < lineLength) {
378 int charInDoc = numCharsInLine + posLineStart;
379 char chDoc = model.pdoc->CharAt(charInDoc);
380 styleByte = model.pdoc->StyleAt(charInDoc);
381 allSame = allSame &&
382 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte));
383 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
384 allSame = allSame &&
385 (ll->chars[numCharsInLine] == chDoc);
386 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
387 allSame = allSame &&
388 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
389 else // Style::caseUpper
390 allSame = allSame &&
391 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
392 numCharsInLine++;
394 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
395 if (allSame) {
396 ll->validity = LineLayout::llPositions;
397 } else {
398 ll->validity = LineLayout::llInvalid;
400 } else {
401 ll->validity = LineLayout::llInvalid;
404 if (ll->validity == LineLayout::llInvalid) {
405 ll->widthLine = LineLayout::wrapWidthInfinite;
406 ll->lines = 1;
407 if (vstyle.edgeState == EDGE_BACKGROUND) {
408 ll->edgeColumn = model.pdoc->FindColumn(line, vstyle.theEdge);
409 if (ll->edgeColumn >= posLineStart) {
410 ll->edgeColumn -= posLineStart;
412 } else {
413 ll->edgeColumn = -1;
416 // Fill base line layout
417 const int lineLength = posLineEnd - posLineStart;
418 model.pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
419 model.pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
420 int numCharsBeforeEOL = model.pdoc->LineEnd(line) - posLineStart;
421 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
422 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
423 const unsigned char styleByte = ll->styles[styleInLine];
424 ll->styles[styleInLine] = styleByte;
426 const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
427 if (vstyle.someStylesForceCase) {
428 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
429 char chDoc = ll->chars[charInLine];
430 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
431 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
432 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
433 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
436 ll->xHighlightGuide = 0;
437 // Extra element at the end of the line to hold end x position and act as
438 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
439 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
441 // Layout the line, determining the position of each character,
442 // with an extra element at the end for the end of the line.
443 ll->positions[0] = 0;
444 bool lastSegItalics = false;
446 BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs);
447 while (bfLayout.More()) {
449 const TextSegment ts = bfLayout.Next();
451 std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f);
452 if (vstyle.styles[ll->styles[ts.start]].visible) {
453 if (ts.representation) {
454 XYPOSITION representationWidth = vstyle.controlCharWidth;
455 if (ll->chars[ts.start] == '\t') {
456 // Tab is a special case of representation, taking a variable amount of space
457 const XYPOSITION x = ll->positions[ts.start];
458 representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start];
459 } else {
460 if (representationWidth <= 0.0) {
461 XYPOSITION positionsRepr[256]; // Should expand when needed
462 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
463 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
464 representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
467 for (int ii = 0; ii < ts.length; ii++)
468 ll->positions[ts.start + 1 + ii] = representationWidth;
469 } else {
470 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
471 // Over half the segments are single characters and of these about half are space characters.
472 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
473 } else {
474 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
475 ts.length, ll->positions + ts.start + 1, model.pdoc);
478 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
481 for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
482 ll->positions[posToIncrease] += ll->positions[ts.start];
486 // Small hack to make lines that end with italics not cut off the edge of the last character
487 if (lastSegItalics) {
488 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
490 ll->numCharsInLine = numCharsInLine;
491 ll->numCharsBeforeEOL = numCharsBeforeEOL;
492 ll->validity = LineLayout::llPositions;
494 // Hard to cope when too narrow, so just assume there is space
495 if (width < 20) {
496 width = 20;
498 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
499 ll->widthLine = width;
500 if (width == LineLayout::wrapWidthInfinite) {
501 ll->lines = 1;
502 } else if (width > ll->positions[ll->numCharsInLine]) {
503 // Simple common case where line does not need wrapping.
504 ll->lines = 1;
505 } else {
506 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
507 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
509 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
510 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
511 wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
512 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
513 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
515 ll->wrapIndent = wrapAddIndent;
516 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
517 for (int i = 0; i < ll->numCharsInLine; i++) {
518 if (!IsSpaceOrTab(ll->chars[i])) {
519 ll->wrapIndent += ll->positions[i]; // Add line indent
520 break;
523 // Check for text width minimum
524 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
525 ll->wrapIndent = wrapAddIndent;
526 // Check for wrapIndent minimum
527 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
528 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
529 ll->lines = 0;
530 // Calculate line start positions based upon width.
531 int lastGoodBreak = 0;
532 int lastLineStart = 0;
533 XYACCUMULATOR startOffset = 0;
534 int p = 0;
535 while (p < ll->numCharsInLine) {
536 if ((ll->positions[p + 1] - startOffset) >= width) {
537 if (lastGoodBreak == lastLineStart) {
538 // Try moving to start of last character
539 if (p > 0) {
540 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
541 - posLineStart;
543 if (lastGoodBreak == lastLineStart) {
544 // Ensure at least one character on line.
545 lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
546 - posLineStart;
549 lastLineStart = lastGoodBreak;
550 ll->lines++;
551 ll->SetLineStart(ll->lines, lastGoodBreak);
552 startOffset = ll->positions[lastGoodBreak];
553 // take into account the space for start wrap mark and indent
554 startOffset -= ll->wrapIndent;
555 p = lastGoodBreak + 1;
556 continue;
558 if (p > 0) {
559 if (vstyle.wrapState == eWrapChar) {
560 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
561 - posLineStart;
562 p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
563 continue;
564 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
565 lastGoodBreak = p;
566 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
567 lastGoodBreak = p;
570 p++;
572 ll->lines++;
574 ll->validity = LineLayout::llLines;
578 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) {
579 Point pt;
580 if (pos.Position() == INVALID_POSITION)
581 return pt;
582 const int line = model.pdoc->LineFromPosition(pos.Position());
583 const int lineVisible = model.cs.DisplayFromDoc(line);
584 //Platform::DebugPrintf("line=%d\n", line);
585 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
586 if (surface && ll) {
587 const int posLineStart = model.pdoc->LineStart(line);
588 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
589 const int posInLine = pos.Position() - posLineStart;
590 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
591 pt.y += (lineVisible - topLine) * vs.lineHeight;
592 pt.x += vs.textStart - model.xOffset;
594 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
595 return pt;
598 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
599 pt.x = pt.x - vs.textStart;
600 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
601 if (!canReturnInvalid && (visibleLine < 0))
602 visibleLine = 0;
603 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
604 if (canReturnInvalid && (lineDoc < 0))
605 return SelectionPosition(INVALID_POSITION);
606 if (lineDoc >= model.pdoc->LinesTotal())
607 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length());
608 const int posLineStart = model.pdoc->LineStart(lineDoc);
609 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
610 if (surface && ll) {
611 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
612 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
613 const int subLine = visibleLine - lineStartSet;
614 if (subLine < ll->lines) {
615 const Range rangeSubLine = ll->SubLineRange(subLine);
616 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
617 if (subLine > 0) // Wrapped
618 pt.x -= ll->wrapIndent;
619 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
620 if (positionInLine < rangeSubLine.end) {
621 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
623 if (virtualSpace) {
624 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
625 const int spaceOffset = static_cast<int>(
626 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
627 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
628 } else if (canReturnInvalid) {
629 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
630 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
632 } else {
633 return SelectionPosition(rangeSubLine.end + posLineStart);
636 if (!canReturnInvalid)
637 return SelectionPosition(ll->numCharsInLine + posLineStart);
639 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
643 * Find the document position corresponding to an x coordinate on a particular document line.
644 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
645 * This method is used for rectangular selections and does not work on wrapped lines.
647 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
648 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
649 if (surface && ll) {
650 const int posLineStart = model.pdoc->LineStart(lineDoc);
651 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
652 const Range rangeSubLine = ll->SubLineRange(0);
653 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
654 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
655 if (positionInLine < rangeSubLine.end) {
656 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
658 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
659 const int spaceOffset = static_cast<int>(
660 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
661 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
663 return SelectionPosition(0);
666 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
667 int lineDoc = model.pdoc->LineFromPosition(pos);
668 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
669 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
670 if (surface && ll) {
671 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
672 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
673 int posInLine = pos - posLineStart;
674 lineDisplay--; // To make up for first increment ahead.
675 for (int subLine = 0; subLine < ll->lines; subLine++) {
676 if (posInLine >= ll->LineStart(subLine)) {
677 lineDisplay++;
681 return lineDisplay;
684 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
685 int line = model.pdoc->LineFromPosition(pos);
686 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
687 int posRet = INVALID_POSITION;
688 if (surface && ll) {
689 unsigned int posLineStart = model.pdoc->LineStart(line);
690 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
691 int posInLine = pos - posLineStart;
692 if (posInLine <= ll->maxLineLength) {
693 for (int subLine = 0; subLine < ll->lines; subLine++) {
694 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
695 if (start) {
696 posRet = ll->LineStart(subLine) + posLineStart;
697 } else {
698 if (subLine == ll->lines - 1)
699 posRet = ll->LineStart(subLine + 1) + posLineStart;
700 else
701 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
707 return posRet;
710 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
711 return main ?
712 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
713 vsDraw.selAdditionalBackground;
716 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
717 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
718 if (inSelection == 1) {
719 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
720 return SelectionBackground(vsDraw, true, model.primarySelection);
722 } else if (inSelection == 2) {
723 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
724 return SelectionBackground(vsDraw, false, model.primarySelection);
726 } else {
727 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
728 (i >= ll->edgeColumn) &&
729 (i < ll->numCharsBeforeEOL))
730 return vsDraw.edgecolour;
731 if (inHotspot && vsDraw.hotspotColours.back.isSet)
732 return vsDraw.hotspotColours.back;
734 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
735 return background;
736 } else {
737 return vsDraw.styles[styleMain].back;
741 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
742 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
743 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
744 surface->Copy(rcCopyArea, from,
745 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
748 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
749 if (alpha != SC_ALPHA_NOALPHA) {
750 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
754 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
755 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
756 if (fillBackground) {
757 surface->FillRectangle(rcSegment, textBack);
759 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
760 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
761 surface->InternalLeading(ctrlCharsFont));
762 PRectangle rcCChar = rcSegment;
763 rcCChar.left = rcCChar.left + 1;
764 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
765 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
766 PRectangle rcCentral = rcCChar;
767 rcCentral.top++;
768 rcCentral.bottom--;
769 surface->FillRectangle(rcCentral, textFore);
770 PRectangle rcChar = rcCChar;
771 rcChar.left++;
772 rcChar.right--;
773 surface->DrawTextClipped(rcChar, ctrlCharsFont,
774 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
775 textBack, textFore);
778 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
779 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
780 ColourOptional background) {
782 const int posLineStart = model.pdoc->LineStart(line);
783 PRectangle rcSegment = rcLine;
785 const bool lastSubLine = subLine == (ll->lines - 1);
786 XYPOSITION virtualSpace = 0;
787 if (lastSubLine) {
788 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
789 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
791 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
793 // Fill the virtual space and show selections within it
794 if (virtualSpace) {
795 rcSegment.left = xEol + xStart;
796 rcSegment.right = xEol + xStart + virtualSpace;
797 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
798 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
799 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
800 for (size_t r = 0; r<model.sel.Count(); r++) {
801 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
802 if (alpha == SC_ALPHA_NOALPHA) {
803 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
804 if (!portion.Empty()) {
805 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
806 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
807 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
808 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
809 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
810 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
811 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
812 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
819 int eolInSelection = 0;
820 int alpha = SC_ALPHA_NOALPHA;
821 if (!hideSelection) {
822 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
823 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
824 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
827 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
828 XYPOSITION blobsWidth = 0;
829 if (lastSubLine) {
830 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
831 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
832 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
833 blobsWidth += rcSegment.Width();
834 char hexits[4];
835 const char *ctrlChar;
836 unsigned char chEOL = ll->chars[eolPos];
837 int styleMain = ll->styles[eolPos];
838 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
839 if (UTF8IsAscii(chEOL)) {
840 ctrlChar = ControlCharacterString(chEOL);
841 } else {
842 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
843 if (repr) {
844 ctrlChar = repr->stringRep.c_str();
845 eolPos = ll->numCharsInLine;
846 } else {
847 sprintf(hexits, "x%2X", chEOL);
848 ctrlChar = hexits;
851 ColourDesired textFore = vsDraw.styles[styleMain].fore;
852 if (eolInSelection && vsDraw.selColours.fore.isSet) {
853 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
855 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
856 if (alpha == SC_ALPHA_NOALPHA) {
857 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
858 } else {
859 surface->FillRectangle(rcSegment, textBack);
861 } else {
862 surface->FillRectangle(rcSegment, textBack);
864 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
865 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
866 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
871 // Draw the eol-is-selected rectangle
872 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
873 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
875 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
876 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
877 } else {
878 if (background.isSet) {
879 surface->FillRectangle(rcSegment, background);
880 } else if (line < model.pdoc->LinesTotal() - 1) {
881 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
882 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
883 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
884 } else {
885 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
887 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
888 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
892 // Fill the remainder of the line
893 rcSegment.left = rcSegment.right;
894 if (rcSegment.left < rcLine.left)
895 rcSegment.left = rcLine.left;
896 rcSegment.right = rcLine.right;
898 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
899 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
900 } else {
901 if (background.isSet) {
902 surface->FillRectangle(rcSegment, background);
903 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
904 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
905 } else {
906 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
908 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
909 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
913 bool drawWrapMarkEnd = false;
915 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
916 if (subLine + 1 < ll->lines) {
917 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
921 if (drawWrapMarkEnd) {
922 PRectangle rcPlace = rcSegment;
924 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
925 rcPlace.left = xEol + xStart + virtualSpace;
926 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
927 } else {
928 // rcLine is clipped to text area
929 rcPlace.right = rcLine.right;
930 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
932 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
936 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
937 const LineLayout *ll, int xStart, PRectangle rcLine, int subLine) {
938 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
939 PRectangle rcIndic(
940 ll->positions[startPos] + xStart - subLineStart,
941 rcLine.top + vsDraw.maxAscent,
942 ll->positions[endPos] + xStart - subLineStart,
943 rcLine.top + vsDraw.maxAscent + 3);
944 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
947 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
948 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under) {
949 // Draw decorators
950 const int posLineStart = model.pdoc->LineStart(line);
951 const int lineStart = ll->LineStart(subLine);
952 const int posLineEnd = posLineStart + lineEnd;
954 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
955 if (under == vsDraw.indicators[deco->indicator].under) {
956 int startPos = posLineStart + lineStart;
957 if (!deco->rs.ValueAt(startPos)) {
958 startPos = deco->rs.EndRun(startPos);
960 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
961 int endPos = deco->rs.EndRun(startPos);
962 if (endPos > posLineEnd)
963 endPos = posLineEnd;
964 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
965 surface, vsDraw, ll, xStart, rcLine, subLine);
966 startPos = endPos;
967 if (!deco->rs.ValueAt(startPos)) {
968 startPos = deco->rs.EndRun(startPos);
974 // Use indicators to highlight matching braces
975 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
976 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
977 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
978 if (under == vsDraw.indicators[braceIndicator].under) {
979 Range rangeLine(posLineStart + lineStart, posLineEnd);
980 if (rangeLine.ContainsCharacter(model.braces[0])) {
981 int braceOffset = model.braces[0] - posLineStart;
982 if (braceOffset < ll->numCharsInLine) {
983 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
986 if (rangeLine.ContainsCharacter(model.braces[1])) {
987 int braceOffset = model.braces[1] - posLineStart;
988 if (braceOffset < ll->numCharsInLine) {
989 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
996 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
997 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
998 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
999 PRectangle rcSegment = rcLine;
1000 int annotationLine = subLine - ll->lines;
1001 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1002 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1003 if (phase & drawBack) {
1004 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1006 rcSegment.left = static_cast<XYPOSITION>(xStart);
1007 if (model.trackLineWidth || (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1008 // Only care about calculating width if tracking or need to draw box
1009 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1010 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
1011 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1013 if (widthAnnotation > lineWidthMaxSeen)
1014 lineWidthMaxSeen = widthAnnotation;
1015 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
1016 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1017 rcSegment.right = rcSegment.left + widthAnnotation;
1020 const int annotationLines = model.pdoc->AnnotationLines(line);
1021 size_t start = 0;
1022 size_t lengthAnnotation = stAnnotation.LineLength(start);
1023 int lineInAnnotation = 0;
1024 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1025 start += lengthAnnotation + 1;
1026 lengthAnnotation = stAnnotation.LineLength(start);
1027 lineInAnnotation++;
1029 PRectangle rcText = rcSegment;
1030 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1031 surface->FillRectangle(rcText,
1032 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1033 rcText.left += vsDraw.spaceWidth;
1035 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1036 stAnnotation, start, lengthAnnotation, phase);
1037 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1038 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1039 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1040 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1041 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1042 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1043 if (subLine == ll->lines) {
1044 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1045 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1047 if (subLine == ll->lines + annotationLines - 1) {
1048 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1049 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1055 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1056 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1058 int lineStart = ll->LineStart(subLine);
1059 int posBefore = posCaret;
1060 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1061 int numCharsToDraw = posAfter - posCaret;
1063 // Work out where the starting and ending offsets are. We need to
1064 // see if the previous character shares horizontal space, such as a
1065 // glyph / combining character. If so we'll need to draw that too.
1066 int offsetFirstChar = offset;
1067 int offsetLastChar = offset + (posAfter - posCaret);
1068 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1069 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1070 // The char does not share horizontal space
1071 break;
1073 // Char shares horizontal space, update the numChars to draw
1074 // Update posBefore to point to the prev char
1075 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1076 numCharsToDraw = posAfter - posBefore;
1077 offsetFirstChar = offset - (posCaret - posBefore);
1080 // See if the next character shares horizontal space, if so we'll
1081 // need to draw that too.
1082 if (offsetFirstChar < 0)
1083 offsetFirstChar = 0;
1084 numCharsToDraw = offsetLastChar - offsetFirstChar;
1085 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1086 // Update posAfter to point to the 2nd next char, this is where
1087 // the next character ends, and 2nd next begins. We'll need
1088 // to compare these two
1089 posBefore = posAfter;
1090 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1091 offsetLastChar = offset + (posAfter - posCaret);
1092 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1093 // The char does not share horizontal space
1094 break;
1096 // Char shares horizontal space, update the numChars to draw
1097 numCharsToDraw = offsetLastChar - offsetFirstChar;
1100 // We now know what to draw, update the caret drawing rectangle
1101 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1102 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1104 // Adjust caret position to take into account any word wrapping symbols.
1105 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1106 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1107 rcCaret.left += wordWrapCharWidth;
1108 rcCaret.right += wordWrapCharWidth;
1111 // This character is where the caret block is, we override the colours
1112 // (inversed) for drawing the caret here.
1113 int styleMain = ll->styles[offsetFirstChar];
1114 FontAlias fontText = vsDraw.styles[styleMain].font;
1115 surface->DrawTextClipped(rcCaret, fontText,
1116 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1117 numCharsToDraw, vsDraw.styles[styleMain].back,
1118 caretColour);
1121 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1122 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1123 // When drag is active it is the only caret drawn
1124 bool drawDrag = model.posDrag.IsValid();
1125 if (hideSelection && !drawDrag)
1126 return;
1127 const int posLineStart = model.pdoc->LineStart(lineDoc);
1128 // For each selection draw
1129 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1130 const bool mainCaret = r == model.sel.Main();
1131 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1132 const int offset = posCaret.Position() - posLineStart;
1133 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1134 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1135 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1136 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1137 if (ll->wrapIndent != 0) {
1138 int lineStart = ll->LineStart(subLine);
1139 if (lineStart != 0) // Wrapped
1140 xposCaret += ll->wrapIndent;
1142 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1143 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1144 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1145 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1146 bool caretAtEOF = false;
1147 bool caretAtEOL = false;
1148 bool drawBlockCaret = false;
1149 XYPOSITION widthOverstrikeCaret;
1150 XYPOSITION caretWidthOffset = 0;
1151 PRectangle rcCaret = rcLine;
1153 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1154 caretAtEOF = true;
1155 widthOverstrikeCaret = vsDraw.aveCharWidth;
1156 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1157 caretAtEOL = true;
1158 widthOverstrikeCaret = vsDraw.aveCharWidth;
1159 } else {
1160 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1161 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1163 if (widthOverstrikeCaret < 3) // Make sure its visible
1164 widthOverstrikeCaret = 3;
1166 if (xposCaret > 0)
1167 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1168 xposCaret += xStart;
1169 if (model.posDrag.IsValid()) {
1170 /* Dragging text, use a line caret */
1171 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1172 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1173 } else if (model.inOverstrike && drawOverstrikeCaret) {
1174 /* Overstrike (insert mode), use a modified bar caret */
1175 rcCaret.top = rcCaret.bottom - 2;
1176 rcCaret.left = xposCaret + 1;
1177 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1178 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1179 /* Block caret */
1180 rcCaret.left = xposCaret;
1181 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1182 drawBlockCaret = true;
1183 rcCaret.right = xposCaret + widthOverstrikeCaret;
1184 } else {
1185 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1187 } else {
1188 /* Line caret */
1189 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1190 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1192 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1193 if (drawBlockCaret) {
1194 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1195 } else {
1196 surface->FillRectangle(rcCaret, caretColour);
1200 if (drawDrag)
1201 break;
1205 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1206 int xStart, PRectangle rcLine, ColourOptional background) {
1207 // default bgnd here..
1208 surface->FillRectangle(rcLine, background.isSet ? background :
1209 vsDraw.styles[STYLE_DEFAULT].back);
1211 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1213 // draw continuation rect
1214 PRectangle rcPlace = rcLine;
1216 rcPlace.left = static_cast<XYPOSITION>(xStart);
1217 rcPlace.right = rcPlace.left + ll->wrapIndent;
1219 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1220 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1221 else
1222 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1224 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1228 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1229 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1230 int subLine, ColourOptional background) const {
1232 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1233 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1234 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1235 // Does not take margin into account but not significant
1236 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1238 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs);
1240 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1242 // Background drawing loop
1243 while (bfBack.More()) {
1245 const TextSegment ts = bfBack.Next();
1246 const int i = ts.end() - 1;
1247 const int iDoc = i + posLineStart;
1249 PRectangle rcSegment = rcLine;
1250 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1251 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1252 // Only try to draw if really visible - enhances performance by not calling environment to
1253 // draw strings that are completely past the right side of the window.
1254 if (rcSegment.Intersects(rcLine)) {
1255 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1256 if (rcSegment.left < rcLine.left)
1257 rcSegment.left = rcLine.left;
1258 if (rcSegment.right > rcLine.right)
1259 rcSegment.right = rcLine.right;
1261 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1262 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1263 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1264 inHotspot, ll->styles[i], i);
1265 if (ts.representation) {
1266 if (ll->chars[i] == '\t') {
1267 // Tab display
1268 if (drawWhitespaceBackground &&
1269 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1270 textBack = vsDraw.whitespaceColours.back;
1271 } else {
1272 // Blob display
1273 inIndentation = false;
1275 surface->FillRectangle(rcSegment, textBack);
1276 } else {
1277 // Normal text display
1278 surface->FillRectangle(rcSegment, textBack);
1279 if (vsDraw.viewWhitespace != wsInvisible ||
1280 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
1281 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1282 if (ll->chars[cpos + ts.start] == ' ') {
1283 if (drawWhitespaceBackground &&
1284 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1285 PRectangle rcSpace(
1286 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1287 rcSegment.top,
1288 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1289 rcSegment.bottom);
1290 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1292 } else {
1293 inIndentation = false;
1298 } else if (rcSegment.left > rcLine.right) {
1299 break;
1304 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1305 Range lineRange, int xStart) {
1306 if (vsDraw.edgeState == EDGE_LINE) {
1307 PRectangle rcSegment = rcLine;
1308 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1309 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1310 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1311 rcSegment.left -= ll->wrapIndent;
1312 rcSegment.right = rcSegment.left + 1;
1313 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
1317 // Draw underline mark as part of background if not transparent
1318 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1319 int line, PRectangle rcLine) {
1320 int marks = model.pdoc->GetMark(line);
1321 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1322 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1323 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1324 PRectangle rcUnderline = rcLine;
1325 rcUnderline.top = rcUnderline.bottom - 2;
1326 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1328 marks >>= 1;
1331 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1332 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1333 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1334 const int posLineStart = model.pdoc->LineStart(line);
1335 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1336 // For each selection draw
1337 int virtualSpaces = 0;
1338 if (subLine == (ll->lines - 1)) {
1339 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1341 SelectionPosition posStart(posLineStart + lineRange.start);
1342 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1343 SelectionSegment virtualSpaceRange(posStart, posEnd);
1344 for (size_t r = 0; r < model.sel.Count(); r++) {
1345 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1346 if (alpha != SC_ALPHA_NOALPHA) {
1347 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1348 if (!portion.Empty()) {
1349 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1350 PRectangle rcSegment = rcLine;
1351 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1352 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1353 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1354 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1355 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1356 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1357 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1359 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1360 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1361 if (rcSegment.right > rcLine.left)
1362 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1369 // Draw any translucent whole line states
1370 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1371 int line, PRectangle rcLine) {
1372 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1373 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1375 int marks = model.pdoc->GetMark(line);
1376 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1377 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
1378 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1379 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
1380 PRectangle rcUnderline = rcLine;
1381 rcUnderline.top = rcUnderline.bottom - 2;
1382 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1384 marks >>= 1;
1386 if (vsDraw.maskInLine) {
1387 int marksMasked = model.pdoc->GetMark(line) & vsDraw.maskInLine;
1388 if (marksMasked) {
1389 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
1390 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
1391 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1393 marksMasked >>= 1;
1399 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1400 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1401 int subLine, ColourOptional background) {
1403 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1404 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1405 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1407 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1408 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1410 // Does not take margin into account but not significant
1411 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1413 // Foreground drawing loop
1414 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1415 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs);
1417 while (bfFore.More()) {
1419 const TextSegment ts = bfFore.Next();
1420 const int i = ts.end() - 1;
1421 const int iDoc = i + posLineStart;
1423 PRectangle rcSegment = rcLine;
1424 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1425 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1426 // Only try to draw if really visible - enhances performance by not calling environment to
1427 // draw strings that are completely past the right side of the window.
1428 if (rcSegment.Intersects(rcLine)) {
1429 int styleMain = ll->styles[i];
1430 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1431 FontAlias textFont = vsDraw.styles[styleMain].font;
1432 //hotspot foreground
1433 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1434 if (inHotspot) {
1435 if (vsDraw.hotspotColours.fore.isSet)
1436 textFore = vsDraw.hotspotColours.fore;
1438 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1439 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1440 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1442 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1443 if (ts.representation) {
1444 if (ll->chars[i] == '\t') {
1445 // Tab display
1446 if (phasesDraw == phasesOne) {
1447 if (drawWhitespaceBackground &&
1448 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1449 textBack = vsDraw.whitespaceColours.back;
1450 surface->FillRectangle(rcSegment, textBack);
1452 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1453 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1454 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1455 indentCount++) {
1456 if (indentCount > 0) {
1457 int xIndent = static_cast<int>(indentCount * indentWidth);
1458 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1459 (ll->xHighlightGuide == xIndent));
1463 if (vsDraw.viewWhitespace != wsInvisible) {
1464 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1465 if (vsDraw.whitespaceColours.fore.isSet)
1466 textFore = vsDraw.whitespaceColours.fore;
1467 surface->PenColour(textFore);
1468 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
1469 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1470 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1473 } else {
1474 inIndentation = false;
1475 if (vsDraw.controlCharSymbol >= 32) {
1476 // Using one font for all control characters so it can be controlled independently to ensure
1477 // the box goes around the characters tightly. Seems to be no way to work out what height
1478 // is taken by an individual character - internal leading gives varying results.
1479 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1480 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1481 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1482 rcSegment.top + vsDraw.maxAscent,
1483 cc, 1, textBack, textFore);
1484 } else {
1485 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1486 textBack, textFore, phasesDraw == phasesOne);
1489 } else {
1490 // Normal text display
1491 if (vsDraw.styles[styleMain].visible) {
1492 if (phasesDraw != phasesOne) {
1493 surface->DrawTextTransparent(rcSegment, textFont,
1494 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1495 i - ts.start + 1, textFore);
1496 } else {
1497 surface->DrawTextNoClip(rcSegment, textFont,
1498 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1499 i - ts.start + 1, textFore, textBack);
1502 if (vsDraw.viewWhitespace != wsInvisible ||
1503 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1504 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1505 if (ll->chars[cpos + ts.start] == ' ') {
1506 if (vsDraw.viewWhitespace != wsInvisible) {
1507 if (vsDraw.whitespaceColours.fore.isSet)
1508 textFore = vsDraw.whitespaceColours.fore;
1509 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1510 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1511 if ((phasesDraw == phasesOne) && drawWhitespaceBackground &&
1512 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1513 textBack = vsDraw.whitespaceColours.back;
1514 PRectangle rcSpace(
1515 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1516 rcSegment.top,
1517 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1518 rcSegment.bottom);
1519 surface->FillRectangle(rcSpace, textBack);
1521 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
1522 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1523 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1524 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1525 surface->FillRectangle(rcDot, textFore);
1528 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1529 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1530 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1531 indentCount++) {
1532 if (indentCount > 0) {
1533 int xIndent = static_cast<int>(indentCount * indentWidth);
1534 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1535 (ll->xHighlightGuide == xIndent));
1539 } else {
1540 inIndentation = false;
1545 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1546 PRectangle rcUL = rcSegment;
1547 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1548 rcUL.bottom = rcUL.top + 1;
1549 if (vsDraw.hotspotColours.fore.isSet)
1550 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1551 else
1552 surface->FillRectangle(rcUL, textFore);
1553 } else if (vsDraw.styles[styleMain].underline) {
1554 PRectangle rcUL = rcSegment;
1555 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1556 rcUL.bottom = rcUL.top + 1;
1557 surface->FillRectangle(rcUL, textFore);
1559 } else if (rcSegment.left > rcLine.right) {
1560 break;
1565 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1566 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1567 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1568 && (subLine == 0)) {
1569 const int posLineStart = model.pdoc->LineStart(line);
1570 int indentSpace = model.pdoc->GetLineIndentation(line);
1571 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1573 // Find the most recent line with some text
1575 int lineLastWithText = line;
1576 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1577 lineLastWithText--;
1579 if (lineLastWithText < line) {
1580 xStartText = 100000; // Don't limit to visible indentation on empty line
1581 // This line is empty, so use indentation of last line with text
1582 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1583 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1584 if (isFoldHeader) {
1585 // Level is one more level than parent
1586 indentLastWithText += model.pdoc->IndentSize();
1588 if (vsDraw.viewIndentationGuides == ivLookForward) {
1589 // In viLookForward mode, previous line only used if it is a fold header
1590 if (isFoldHeader) {
1591 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1593 } else { // viLookBoth
1594 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1598 int lineNextWithText = line;
1599 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1600 lineNextWithText++;
1602 if (lineNextWithText > line) {
1603 xStartText = 100000; // Don't limit to visible indentation on empty line
1604 // This line is empty, so use indentation of first next line with text
1605 indentSpace = Platform::Maximum(indentSpace,
1606 model.pdoc->GetLineIndentation(lineNextWithText));
1609 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1610 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1611 if (xIndent < xStartText) {
1612 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1613 (ll->xHighlightGuide == xIndent));
1619 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1620 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1622 if (subLine >= ll->lines) {
1623 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1624 return; // No further drawing
1627 // See if something overrides the line background color.
1628 ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1629 SCNotification scn = { 0 };
1630 scn.nmhdr.code = SCN_GETBKCOLOR;
1631 scn.line = line;
1632 scn.lParam = -1;
1633 if (editor)
1634 ((Editor*)editor)->NotifyParent(&scn);
1635 if (scn.lParam != -1) {
1636 background.Set(scn.lParam);
1637 background.isSet = true;
1640 const int posLineStart = model.pdoc->LineStart(line);
1642 const Range lineRange = ll->SubLineRange(subLine);
1643 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1645 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1646 if (phase & drawBack) {
1647 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background);
1649 xStart += static_cast<int>(ll->wrapIndent);
1652 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1653 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1654 subLine, background);
1655 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1656 xStart, subLine, subLineStart, background);
1659 if (phase & drawIndicatorsBack) {
1660 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true);
1661 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1662 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1665 if (phase & drawText) {
1666 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1667 subLine, background);
1670 if (phase & drawIndentationGuides) {
1671 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1674 if (phase & drawIndicatorsFore) {
1675 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false);
1678 // End of the drawing of the current line
1679 if (phasesDraw == phasesOne) {
1680 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1681 xStart, subLine, subLineStart, background);
1684 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1685 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1688 if (phase & drawLineTranslucent) {
1689 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1693 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1694 bool expanded = model.cs.GetExpanded(line);
1695 const int level = model.pdoc->GetLevel(line);
1696 const int levelNext = model.pdoc->GetLevel(line + 1);
1697 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1698 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
1699 // Paint the line above the fold
1700 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1702 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1703 PRectangle rcFoldLine = rcLine;
1704 rcFoldLine.bottom = rcFoldLine.top + 1;
1705 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1707 // Paint the line below the fold
1708 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1710 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1711 PRectangle rcFoldLine = rcLine;
1712 rcFoldLine.top = rcFoldLine.bottom - 1;
1713 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1718 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1719 PRectangle rcClient, const ViewStyle &vsDraw) {
1720 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1721 // serifs and italic stems for aliased text.
1722 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1724 // Do the painting
1725 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1727 Surface *surface = surfaceWindow;
1728 if (bufferedDraw) {
1729 surface = pixmapLine;
1730 PLATFORM_ASSERT(pixmapLine->Initialised());
1732 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1733 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1735 const Point ptOrigin = model.GetVisibleOriginInMain();
1737 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1738 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1740 SelectionPosition posCaret = model.sel.RangeMain().caret;
1741 if (model.posDrag.IsValid())
1742 posCaret = model.posDrag;
1743 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1745 PRectangle rcTextArea = rcClient;
1746 if (vsDraw.marginInside) {
1747 rcTextArea.left += vsDraw.textStart;
1748 rcTextArea.right -= vsDraw.rightMarginWidth;
1749 } else {
1750 rcTextArea = rcArea;
1753 // Remove selection margin from drawing area so text will not be drawn
1754 // on it in unbuffered mode.
1755 if (!bufferedDraw && vsDraw.marginInside) {
1756 PRectangle rcClipText = rcTextArea;
1757 rcClipText.left -= leftTextOverlap;
1758 surfaceWindow->SetClip(rcClipText);
1761 // Loop on visible lines
1762 //double durLayout = 0.0;
1763 //double durPaint = 0.0;
1764 //double durCopy = 0.0;
1765 //ElapsedTime etWhole;
1767 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1768 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1770 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1771 AutoLineLayout ll(llc, 0);
1772 std::vector<DrawPhase> phases;
1773 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1774 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1775 phases.push_back(phase);
1777 } else {
1778 phases.push_back(drawAll);
1780 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1781 int ypos = 0;
1782 if (!bufferedDraw)
1783 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1784 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1785 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1786 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1788 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1789 // Only visible lines should be handled by the code within the loop
1790 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1791 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1792 const int subLine = visibleLine - lineStartSet;
1794 // Copy this line and its styles from the document into local arrays
1795 // and determine the x position at which each character starts.
1796 //ElapsedTime et;
1797 if (lineDoc != lineDocPrevious) {
1798 ll.Set(0);
1799 ll.Set(RetrieveLineLayout(lineDoc, model));
1800 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1801 lineDocPrevious = lineDoc;
1803 //durLayout += et.Duration(true);
1805 if (ll) {
1806 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1807 ll->hotspot = model.GetHotSpotRange();
1809 PRectangle rcLine = rcTextArea;
1810 rcLine.top = static_cast<XYPOSITION>(ypos);
1811 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
1813 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
1815 // Highlight the current braces if any
1816 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
1817 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
1819 if (leftTextOverlap && bufferedDraw) {
1820 PRectangle rcSpacer = rcLine;
1821 rcSpacer.right = rcSpacer.left;
1822 rcSpacer.left -= 1;
1823 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
1826 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
1827 //durPaint += et.Duration(true);
1829 // Restore the previous styles for the brace highlights in case layout is in cache.
1830 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
1832 if (*it & drawFoldLines) {
1833 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
1836 if (*it & drawCarets) {
1837 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
1840 if (bufferedDraw) {
1841 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
1842 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
1843 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
1844 yposScreen + vsDraw.lineHeight);
1845 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
1848 lineWidthMaxSeen = Platform::Maximum(
1849 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
1850 //durCopy += et.Duration(true);
1853 if (!bufferedDraw) {
1854 ypos += vsDraw.lineHeight;
1857 yposScreen += vsDraw.lineHeight;
1858 visibleLine++;
1861 ll.Set(0);
1862 //if (durPaint < 0.00000001)
1863 // durPaint = 0.00000001;
1865 // Right column limit indicator
1866 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
1867 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
1868 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
1869 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
1870 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1871 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
1872 if (vsDraw.edgeState == EDGE_LINE) {
1873 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1874 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
1875 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1876 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour);
1879 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1881 //Platform::DebugPrintf(
1882 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1883 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1887 // Space (3 space characters) between line numbers and text when printing.
1888 #define lineNumberPrintSpace " "
1890 ColourDesired InvertedLight(ColourDesired orig) {
1891 unsigned int r = orig.GetRed();
1892 unsigned int g = orig.GetGreen();
1893 unsigned int b = orig.GetBlue();
1894 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1895 unsigned int il = 0xff - l;
1896 if (l == 0)
1897 return ColourDesired(0xff, 0xff, 0xff);
1898 r = r * il / l;
1899 g = g * il / l;
1900 b = b * il / l;
1901 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1904 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
1905 const EditModel &model, const ViewStyle &vs) {
1906 // Can't use measurements cached for screen
1907 posCache.Clear();
1909 ViewStyle vsPrint(vs);
1910 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
1912 // Modify the view style for printing as do not normally want any of the transient features to be printed
1913 // Printing supports only the line number margin.
1914 int lineNumberIndex = -1;
1915 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1916 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
1917 lineNumberIndex = margin;
1918 } else {
1919 vsPrint.ms[margin].width = 0;
1922 vsPrint.fixedColumnWidth = 0;
1923 vsPrint.zoomLevel = printParameters.magnification;
1924 // Don't show indentation guides
1925 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1926 vsPrint.viewIndentationGuides = ivNone;
1927 // Don't show the selection when printing
1928 vsPrint.selColours.back.isSet = false;
1929 vsPrint.selColours.fore.isSet = false;
1930 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
1931 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
1932 vsPrint.whitespaceColours.back.isSet = false;
1933 vsPrint.whitespaceColours.fore.isSet = false;
1934 vsPrint.showCaretLineBackground = false;
1935 vsPrint.alwaysShowCaretLineBackground = false;
1936 // Don't highlight matching braces using indicators
1937 vsPrint.braceHighlightIndicatorSet = false;
1938 vsPrint.braceBadLightIndicatorSet = false;
1940 // Set colours for printing according to users settings
1941 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
1942 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
1943 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
1944 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
1945 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
1946 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
1947 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1948 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
1949 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1950 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
1951 if (sty <= STYLE_DEFAULT) {
1952 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1956 // White background for the line numbers
1957 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
1959 // Printing uses different margins, so reset screen margins
1960 vsPrint.leftMarginWidth = 0;
1961 vsPrint.rightMarginWidth = 0;
1963 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
1964 // Determining width must happen after fonts have been realised in Refresh
1965 int lineNumberWidth = 0;
1966 if (lineNumberIndex >= 0) {
1967 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
1968 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
1969 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
1970 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
1973 int linePrintStart = model.pdoc->LineFromPosition(pfr->chrg.cpMin);
1974 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
1975 if (linePrintLast < linePrintStart)
1976 linePrintLast = linePrintStart;
1977 int linePrintMax = model.pdoc->LineFromPosition(pfr->chrg.cpMax);
1978 if (linePrintLast > linePrintMax)
1979 linePrintLast = linePrintMax;
1980 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
1981 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
1982 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
1983 int endPosPrint = model.pdoc->Length();
1984 if (linePrintLast < model.pdoc->LinesTotal())
1985 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
1987 // Ensure we are styled to where we are formatting.
1988 model.pdoc->EnsureStyledTo(endPosPrint);
1990 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
1991 int ypos = pfr->rc.top;
1993 int lineDoc = linePrintStart;
1995 int nPrintPos = pfr->chrg.cpMin;
1996 int visibleLine = 0;
1997 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
1998 if (printParameters.wrapState == eWrapNone)
1999 widthPrint = LineLayout::wrapWidthInfinite;
2001 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2003 // When printing, the hdc and hdcTarget may be the same, so
2004 // changing the state of surfaceMeasure may change the underlying
2005 // state of surface. Therefore, any cached state is discarded before
2006 // using each surface.
2007 surfaceMeasure->FlushCachedState();
2009 // Copy this line and its styles from the document into local arrays
2010 // and determine the x position at which each character starts.
2011 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2012 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2014 ll.containsCaret = false;
2016 PRectangle rcLine = PRectangle::FromInts(
2017 pfr->rc.left,
2018 ypos,
2019 pfr->rc.right - 1,
2020 ypos + vsPrint.lineHeight);
2022 // When document line is wrapped over multiple display lines, find where
2023 // to start printing from to ensure a particular position is on the first
2024 // line of the page.
2025 if (visibleLine == 0) {
2026 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2027 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2028 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2029 visibleLine = -iwl;
2033 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2034 visibleLine = -(ll.lines - 1);
2038 if (draw && lineNumberWidth &&
2039 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2040 (visibleLine >= 0)) {
2041 char number[100];
2042 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2043 PRectangle rcNumber = rcLine;
2044 rcNumber.right = rcNumber.left + lineNumberWidth;
2045 // Right justify
2046 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2047 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2048 surface->FlushCachedState();
2049 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2050 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2051 vsPrint.styles[STYLE_LINENUMBER].fore,
2052 vsPrint.styles[STYLE_LINENUMBER].back);
2055 // Draw the line
2056 surface->FlushCachedState();
2058 for (int iwl = 0; iwl < ll.lines; iwl++) {
2059 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2060 if (visibleLine >= 0) {
2061 if (draw) {
2062 rcLine.top = static_cast<XYPOSITION>(ypos);
2063 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2064 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2066 ypos += vsPrint.lineHeight;
2068 visibleLine++;
2069 if (iwl == ll.lines - 1)
2070 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2071 else
2072 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2076 ++lineDoc;
2079 // Clear cache so measurements are not used for screen
2080 posCache.Clear();
2082 return nPrintPos;