Update Scintilla to version 3.5.3
[TortoiseGit.git] / ext / scintilla / src / EditView.cxx
blob11c7fed336da0e7f8c9a22a043d011aed87f8282
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"
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 tabArrowHeight = 4;
190 customDrawTabArrow = NULL;
191 customDrawWrapMarker = NULL;
194 EditView::~EditView() {
195 delete ldTabstops;
196 ldTabstops = NULL;
199 bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) {
200 const PhasesDraw phasesDrawNew = twoPhaseDraw ? phasesTwo : phasesOne;
201 const bool redraw = phasesDraw != phasesDrawNew;
202 phasesDraw = phasesDrawNew;
203 return redraw;
206 bool EditView::SetPhasesDraw(int phases) {
207 const PhasesDraw phasesDrawNew = static_cast<PhasesDraw>(phases);
208 const bool redraw = phasesDraw != phasesDrawNew;
209 phasesDraw = phasesDrawNew;
210 return redraw;
213 bool EditView::LinesOverlap() const {
214 return phasesDraw == phasesMultiple;
217 void EditView::ClearAllTabstops() {
218 delete ldTabstops;
219 ldTabstops = 0;
222 XYPOSITION EditView::NextTabstopPos(int line, XYPOSITION x, XYPOSITION tabWidth) const {
223 int next = GetNextTabstop(line, static_cast<int>(x + 2));
224 if (next > 0)
225 return static_cast<XYPOSITION>(next);
226 return (static_cast<int>((x + 2) / tabWidth) + 1) * tabWidth;
229 bool EditView::ClearTabstops(int line) {
230 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
231 return lt && lt->ClearTabstops(line);
234 bool EditView::AddTabstop(int line, int x) {
235 if (!ldTabstops) {
236 ldTabstops = new LineTabstops();
238 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
239 return lt && lt->AddTabstop(line, x);
242 int EditView::GetNextTabstop(int line, int x) const {
243 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
244 if (lt) {
245 return lt->GetNextTabstop(line, x);
246 } else {
247 return 0;
251 void EditView::LinesAddedOrRemoved(int lineOfPos, int linesAdded) {
252 if (ldTabstops) {
253 if (linesAdded > 0) {
254 for (int line = lineOfPos; line < lineOfPos + linesAdded; line++) {
255 ldTabstops->InsertLine(line);
257 } else {
258 for (int line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {
259 ldTabstops->RemoveLine(line);
265 void EditView::DropGraphics(bool freeObjects) {
266 if (freeObjects) {
267 delete pixmapLine;
268 pixmapLine = 0;
269 delete pixmapIndentGuide;
270 pixmapIndentGuide = 0;
271 delete pixmapIndentGuideHighlight;
272 pixmapIndentGuideHighlight = 0;
273 } else {
274 if (pixmapLine)
275 pixmapLine->Release();
276 if (pixmapIndentGuide)
277 pixmapIndentGuide->Release();
278 if (pixmapIndentGuideHighlight)
279 pixmapIndentGuideHighlight->Release();
283 void EditView::AllocateGraphics(const ViewStyle &vsDraw) {
284 if (!pixmapLine)
285 pixmapLine = Surface::Allocate(vsDraw.technology);
286 if (!pixmapIndentGuide)
287 pixmapIndentGuide = Surface::Allocate(vsDraw.technology);
288 if (!pixmapIndentGuideHighlight)
289 pixmapIndentGuideHighlight = Surface::Allocate(vsDraw.technology);
292 const char *ControlCharacterString(unsigned char ch) {
293 const char *reps[] = {
294 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
295 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
296 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
297 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
299 if (ch < ELEMENTS(reps)) {
300 return reps[ch];
301 } else {
302 return "BAD";
306 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
307 int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
308 int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
309 if (xhead <= rcTab.left) {
310 ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
311 xhead = static_cast<int>(rcTab.left) - 1;
313 if ((rcTab.left + 2) < (rcTab.right - 1))
314 surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
315 else
316 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
317 surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
318 surface->LineTo(xhead, ymid - ydiff);
319 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
320 surface->LineTo(xhead, ymid + ydiff);
323 void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
324 if (!pixmapIndentGuide->Initialised()) {
325 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
326 pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
327 pixmapIndentGuideHighlight->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
328 PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight);
329 pixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[STYLE_INDENTGUIDE].back);
330 pixmapIndentGuide->PenColour(vsDraw.styles[STYLE_INDENTGUIDE].fore);
331 pixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[STYLE_BRACELIGHT].back);
332 pixmapIndentGuideHighlight->PenColour(vsDraw.styles[STYLE_BRACELIGHT].fore);
333 for (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) {
334 PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
335 pixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[STYLE_INDENTGUIDE].fore);
336 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[STYLE_BRACELIGHT].fore);
341 LineLayout *EditView::RetrieveLineLayout(int lineNumber, const EditModel &model) {
342 int posLineStart = model.pdoc->LineStart(lineNumber);
343 int posLineEnd = model.pdoc->LineStart(lineNumber + 1);
344 PLATFORM_ASSERT(posLineEnd >= posLineStart);
345 int lineCaret = model.pdoc->LineFromPosition(model.sel.MainCaret());
346 return llc.Retrieve(lineNumber, lineCaret,
347 posLineEnd - posLineStart, model.pdoc->GetStyleClock(),
348 model.LinesOnScreen() + 1, model.pdoc->LinesTotal());
352 * Fill in the LineLayout data for the given line.
353 * Copy the given @a line and its styles from the document into local arrays.
354 * Also determine the x position at which each character starts.
356 void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
357 if (!ll)
358 return;
360 PLATFORM_ASSERT(line < model.pdoc->LinesTotal());
361 PLATFORM_ASSERT(ll->chars != NULL);
362 int posLineStart = model.pdoc->LineStart(line);
363 int posLineEnd = model.pdoc->LineStart(line + 1);
364 // If the line is very long, limit the treatment to a length that should fit in the viewport
365 if (posLineEnd >(posLineStart + ll->maxLineLength)) {
366 posLineEnd = posLineStart + ll->maxLineLength;
368 if (ll->validity == LineLayout::llCheckTextAndStyle) {
369 int lineLength = posLineEnd - posLineStart;
370 if (!vstyle.viewEOL) {
371 lineLength = model.pdoc->LineEnd(line) - posLineStart;
373 if (lineLength == ll->numCharsInLine) {
374 // See if chars, styles, indicators, are all the same
375 bool allSame = true;
376 // Check base line layout
377 char styleByte = 0;
378 int numCharsInLine = 0;
379 while (numCharsInLine < lineLength) {
380 int charInDoc = numCharsInLine + posLineStart;
381 char chDoc = model.pdoc->CharAt(charInDoc);
382 styleByte = model.pdoc->StyleAt(charInDoc);
383 allSame = allSame &&
384 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte));
385 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
386 allSame = allSame &&
387 (ll->chars[numCharsInLine] == chDoc);
388 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
389 allSame = allSame &&
390 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
391 else // Style::caseUpper
392 allSame = allSame &&
393 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
394 numCharsInLine++;
396 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
397 if (allSame) {
398 ll->validity = LineLayout::llPositions;
399 } else {
400 ll->validity = LineLayout::llInvalid;
402 } else {
403 ll->validity = LineLayout::llInvalid;
406 if (ll->validity == LineLayout::llInvalid) {
407 ll->widthLine = LineLayout::wrapWidthInfinite;
408 ll->lines = 1;
409 if (vstyle.edgeState == EDGE_BACKGROUND) {
410 ll->edgeColumn = model.pdoc->FindColumn(line, vstyle.theEdge);
411 if (ll->edgeColumn >= posLineStart) {
412 ll->edgeColumn -= posLineStart;
414 } else {
415 ll->edgeColumn = -1;
418 // Fill base line layout
419 const int lineLength = posLineEnd - posLineStart;
420 model.pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
421 model.pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
422 int numCharsBeforeEOL = model.pdoc->LineEnd(line) - posLineStart;
423 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
424 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
425 const unsigned char styleByte = ll->styles[styleInLine];
426 ll->styles[styleInLine] = styleByte;
428 const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
429 if (vstyle.someStylesForceCase) {
430 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
431 char chDoc = ll->chars[charInLine];
432 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
433 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
434 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
435 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
438 ll->xHighlightGuide = 0;
439 // Extra element at the end of the line to hold end x position and act as
440 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
441 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
443 // Layout the line, determining the position of each character,
444 // with an extra element at the end for the end of the line.
445 ll->positions[0] = 0;
446 bool lastSegItalics = false;
448 BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs);
449 while (bfLayout.More()) {
451 const TextSegment ts = bfLayout.Next();
453 std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f);
454 if (vstyle.styles[ll->styles[ts.start]].visible) {
455 if (ts.representation) {
456 XYPOSITION representationWidth = vstyle.controlCharWidth;
457 if (ll->chars[ts.start] == '\t') {
458 // Tab is a special case of representation, taking a variable amount of space
459 const XYPOSITION x = ll->positions[ts.start];
460 representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start];
461 } else {
462 if (representationWidth <= 0.0) {
463 XYPOSITION positionsRepr[256]; // Should expand when needed
464 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
465 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
466 representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
469 for (int ii = 0; ii < ts.length; ii++)
470 ll->positions[ts.start + 1 + ii] = representationWidth;
471 } else {
472 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
473 // Over half the segments are single characters and of these about half are space characters.
474 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
475 } else {
476 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
477 ts.length, ll->positions + ts.start + 1, model.pdoc);
480 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
483 for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
484 ll->positions[posToIncrease] += ll->positions[ts.start];
488 // Small hack to make lines that end with italics not cut off the edge of the last character
489 if (lastSegItalics) {
490 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
492 ll->numCharsInLine = numCharsInLine;
493 ll->numCharsBeforeEOL = numCharsBeforeEOL;
494 ll->validity = LineLayout::llPositions;
496 // Hard to cope when too narrow, so just assume there is space
497 if (width < 20) {
498 width = 20;
500 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
501 ll->widthLine = width;
502 if (width == LineLayout::wrapWidthInfinite) {
503 ll->lines = 1;
504 } else if (width > ll->positions[ll->numCharsInLine]) {
505 // Simple common case where line does not need wrapping.
506 ll->lines = 1;
507 } else {
508 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
509 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
511 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
512 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
513 wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
514 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
515 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
517 ll->wrapIndent = wrapAddIndent;
518 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
519 for (int i = 0; i < ll->numCharsInLine; i++) {
520 if (!IsSpaceOrTab(ll->chars[i])) {
521 ll->wrapIndent += ll->positions[i]; // Add line indent
522 break;
525 // Check for text width minimum
526 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
527 ll->wrapIndent = wrapAddIndent;
528 // Check for wrapIndent minimum
529 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
530 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
531 ll->lines = 0;
532 // Calculate line start positions based upon width.
533 int lastGoodBreak = 0;
534 int lastLineStart = 0;
535 XYACCUMULATOR startOffset = 0;
536 int p = 0;
537 while (p < ll->numCharsInLine) {
538 if ((ll->positions[p + 1] - startOffset) >= width) {
539 if (lastGoodBreak == lastLineStart) {
540 // Try moving to start of last character
541 if (p > 0) {
542 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
543 - posLineStart;
545 if (lastGoodBreak == lastLineStart) {
546 // Ensure at least one character on line.
547 lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
548 - posLineStart;
551 lastLineStart = lastGoodBreak;
552 ll->lines++;
553 ll->SetLineStart(ll->lines, lastGoodBreak);
554 startOffset = ll->positions[lastGoodBreak];
555 // take into account the space for start wrap mark and indent
556 startOffset -= ll->wrapIndent;
557 p = lastGoodBreak + 1;
558 continue;
560 if (p > 0) {
561 if (vstyle.wrapState == eWrapChar) {
562 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
563 - posLineStart;
564 p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
565 continue;
566 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
567 lastGoodBreak = p;
568 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
569 lastGoodBreak = p;
572 p++;
574 ll->lines++;
576 ll->validity = LineLayout::llLines;
580 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) {
581 Point pt;
582 if (pos.Position() == INVALID_POSITION)
583 return pt;
584 const int line = model.pdoc->LineFromPosition(pos.Position());
585 const int lineVisible = model.cs.DisplayFromDoc(line);
586 //Platform::DebugPrintf("line=%d\n", line);
587 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
588 if (surface && ll) {
589 const int posLineStart = model.pdoc->LineStart(line);
590 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
591 const int posInLine = pos.Position() - posLineStart;
592 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
593 pt.y += (lineVisible - topLine) * vs.lineHeight;
594 pt.x += vs.textStart - model.xOffset;
596 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
597 return pt;
600 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
601 pt.x = pt.x - vs.textStart;
602 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
603 if (!canReturnInvalid && (visibleLine < 0))
604 visibleLine = 0;
605 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
606 if (canReturnInvalid && (lineDoc < 0))
607 return SelectionPosition(INVALID_POSITION);
608 if (lineDoc >= model.pdoc->LinesTotal())
609 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length());
610 const int posLineStart = model.pdoc->LineStart(lineDoc);
611 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
612 if (surface && ll) {
613 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
614 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
615 const int subLine = visibleLine - lineStartSet;
616 if (subLine < ll->lines) {
617 const Range rangeSubLine = ll->SubLineRange(subLine);
618 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
619 if (subLine > 0) // Wrapped
620 pt.x -= ll->wrapIndent;
621 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
622 if (positionInLine < rangeSubLine.end) {
623 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
625 if (virtualSpace) {
626 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
627 const int spaceOffset = static_cast<int>(
628 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
629 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
630 } else if (canReturnInvalid) {
631 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
632 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
634 } else {
635 return SelectionPosition(rangeSubLine.end + posLineStart);
638 if (!canReturnInvalid)
639 return SelectionPosition(ll->numCharsInLine + posLineStart);
641 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
645 * Find the document position corresponding to an x coordinate on a particular document line.
646 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
647 * This method is used for rectangular selections and does not work on wrapped lines.
649 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
650 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
651 if (surface && ll) {
652 const int posLineStart = model.pdoc->LineStart(lineDoc);
653 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
654 const Range rangeSubLine = ll->SubLineRange(0);
655 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
656 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
657 if (positionInLine < rangeSubLine.end) {
658 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
660 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
661 const int spaceOffset = static_cast<int>(
662 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
663 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
665 return SelectionPosition(0);
668 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
669 int lineDoc = model.pdoc->LineFromPosition(pos);
670 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
671 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
672 if (surface && ll) {
673 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
674 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
675 int posInLine = pos - posLineStart;
676 lineDisplay--; // To make up for first increment ahead.
677 for (int subLine = 0; subLine < ll->lines; subLine++) {
678 if (posInLine >= ll->LineStart(subLine)) {
679 lineDisplay++;
683 return lineDisplay;
686 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
687 int line = model.pdoc->LineFromPosition(pos);
688 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
689 int posRet = INVALID_POSITION;
690 if (surface && ll) {
691 unsigned int posLineStart = model.pdoc->LineStart(line);
692 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
693 int posInLine = pos - posLineStart;
694 if (posInLine <= ll->maxLineLength) {
695 for (int subLine = 0; subLine < ll->lines; subLine++) {
696 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
697 if (start) {
698 posRet = ll->LineStart(subLine) + posLineStart;
699 } else {
700 if (subLine == ll->lines - 1)
701 posRet = ll->LineStart(subLine + 1) + posLineStart;
702 else
703 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
709 return posRet;
712 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
713 return main ?
714 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
715 vsDraw.selAdditionalBackground;
718 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
719 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
720 if (inSelection == 1) {
721 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
722 return SelectionBackground(vsDraw, true, model.primarySelection);
724 } else if (inSelection == 2) {
725 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
726 return SelectionBackground(vsDraw, false, model.primarySelection);
728 } else {
729 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
730 (i >= ll->edgeColumn) &&
731 (i < ll->numCharsBeforeEOL))
732 return vsDraw.edgecolour;
733 if (inHotspot && vsDraw.hotspotColours.back.isSet)
734 return vsDraw.hotspotColours.back;
736 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
737 return background;
738 } else {
739 return vsDraw.styles[styleMain].back;
743 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
744 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
745 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
746 surface->Copy(rcCopyArea, from,
747 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
750 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
751 if (alpha != SC_ALPHA_NOALPHA) {
752 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
756 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
757 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
758 if (fillBackground) {
759 surface->FillRectangle(rcSegment, textBack);
761 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
762 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
763 surface->InternalLeading(ctrlCharsFont));
764 PRectangle rcCChar = rcSegment;
765 rcCChar.left = rcCChar.left + 1;
766 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
767 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
768 PRectangle rcCentral = rcCChar;
769 rcCentral.top++;
770 rcCentral.bottom--;
771 surface->FillRectangle(rcCentral, textFore);
772 PRectangle rcChar = rcCChar;
773 rcChar.left++;
774 rcChar.right--;
775 surface->DrawTextClipped(rcChar, ctrlCharsFont,
776 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
777 textBack, textFore);
780 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
781 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
782 ColourOptional background) {
784 const int posLineStart = model.pdoc->LineStart(line);
785 PRectangle rcSegment = rcLine;
787 const bool lastSubLine = subLine == (ll->lines - 1);
788 XYPOSITION virtualSpace = 0;
789 if (lastSubLine) {
790 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
791 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
793 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
795 // Fill the virtual space and show selections within it
796 if (virtualSpace) {
797 rcSegment.left = xEol + xStart;
798 rcSegment.right = xEol + xStart + virtualSpace;
799 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
800 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
801 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
802 for (size_t r = 0; r<model.sel.Count(); r++) {
803 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
804 if (alpha == SC_ALPHA_NOALPHA) {
805 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
806 if (!portion.Empty()) {
807 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
808 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
809 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
810 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
811 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
812 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
813 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
814 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
821 int eolInSelection = 0;
822 int alpha = SC_ALPHA_NOALPHA;
823 if (!hideSelection) {
824 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
825 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
826 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
829 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
830 XYPOSITION blobsWidth = 0;
831 if (lastSubLine) {
832 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
833 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
834 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
835 blobsWidth += rcSegment.Width();
836 char hexits[4];
837 const char *ctrlChar;
838 unsigned char chEOL = ll->chars[eolPos];
839 int styleMain = ll->styles[eolPos];
840 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
841 if (UTF8IsAscii(chEOL)) {
842 ctrlChar = ControlCharacterString(chEOL);
843 } else {
844 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
845 if (repr) {
846 ctrlChar = repr->stringRep.c_str();
847 eolPos = ll->numCharsInLine;
848 } else {
849 sprintf(hexits, "x%2X", chEOL);
850 ctrlChar = hexits;
853 ColourDesired textFore = vsDraw.styles[styleMain].fore;
854 if (eolInSelection && vsDraw.selColours.fore.isSet) {
855 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
857 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
858 if (alpha == SC_ALPHA_NOALPHA) {
859 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
860 } else {
861 surface->FillRectangle(rcSegment, textBack);
863 } else {
864 surface->FillRectangle(rcSegment, textBack);
866 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
867 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
868 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
873 // Draw the eol-is-selected rectangle
874 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
875 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
877 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
878 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
879 } else {
880 if (background.isSet) {
881 surface->FillRectangle(rcSegment, background);
882 } else if (line < model.pdoc->LinesTotal() - 1) {
883 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
884 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
885 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
886 } else {
887 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
889 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
890 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
894 // Fill the remainder of the line
895 rcSegment.left = rcSegment.right;
896 if (rcSegment.left < rcLine.left)
897 rcSegment.left = rcLine.left;
898 rcSegment.right = rcLine.right;
900 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
901 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
902 } else {
903 if (background.isSet) {
904 surface->FillRectangle(rcSegment, background);
905 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
906 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
907 } else {
908 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
910 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
911 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
915 bool drawWrapMarkEnd = false;
917 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
918 if (subLine + 1 < ll->lines) {
919 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
923 if (drawWrapMarkEnd) {
924 PRectangle rcPlace = rcSegment;
926 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
927 rcPlace.left = xEol + xStart + virtualSpace;
928 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
929 } else {
930 // rcLine is clipped to text area
931 rcPlace.right = rcLine.right;
932 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
934 if (customDrawWrapMarker == NULL) {
935 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
936 } else {
937 customDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
942 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
943 const LineLayout *ll, int xStart, PRectangle rcLine, int subLine) {
944 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
945 PRectangle rcIndic(
946 ll->positions[startPos] + xStart - subLineStart,
947 rcLine.top + vsDraw.maxAscent,
948 ll->positions[endPos] + xStart - subLineStart,
949 rcLine.top + vsDraw.maxAscent + 3);
950 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
953 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
954 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under) {
955 // Draw decorators
956 const int posLineStart = model.pdoc->LineStart(line);
957 const int lineStart = ll->LineStart(subLine);
958 const int posLineEnd = posLineStart + lineEnd;
960 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
961 if (under == vsDraw.indicators[deco->indicator].under) {
962 int startPos = posLineStart + lineStart;
963 if (!deco->rs.ValueAt(startPos)) {
964 startPos = deco->rs.EndRun(startPos);
966 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
967 int endPos = deco->rs.EndRun(startPos);
968 if (endPos > posLineEnd)
969 endPos = posLineEnd;
970 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
971 surface, vsDraw, ll, xStart, rcLine, subLine);
972 startPos = endPos;
973 if (!deco->rs.ValueAt(startPos)) {
974 startPos = deco->rs.EndRun(startPos);
980 // Use indicators to highlight matching braces
981 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
982 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
983 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
984 if (under == vsDraw.indicators[braceIndicator].under) {
985 Range rangeLine(posLineStart + lineStart, posLineEnd);
986 if (rangeLine.ContainsCharacter(model.braces[0])) {
987 int braceOffset = model.braces[0] - posLineStart;
988 if (braceOffset < ll->numCharsInLine) {
989 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
992 if (rangeLine.ContainsCharacter(model.braces[1])) {
993 int braceOffset = model.braces[1] - posLineStart;
994 if (braceOffset < ll->numCharsInLine) {
995 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
1002 static bool AnnotationBoxedOrIndented(int annotationVisible) {
1003 return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED;
1006 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1007 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1008 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
1009 PRectangle rcSegment = rcLine;
1010 int annotationLine = subLine - ll->lines;
1011 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1012 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1013 if (phase & drawBack) {
1014 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1016 rcSegment.left = static_cast<XYPOSITION>(xStart);
1017 if (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1018 // Only care about calculating width if tracking or need to draw indented box
1019 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1020 if (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1021 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1022 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1023 rcSegment.right = rcSegment.left + widthAnnotation;
1025 if (widthAnnotation > lineWidthMaxSeen)
1026 lineWidthMaxSeen = widthAnnotation;
1028 const int annotationLines = model.pdoc->AnnotationLines(line);
1029 size_t start = 0;
1030 size_t lengthAnnotation = stAnnotation.LineLength(start);
1031 int lineInAnnotation = 0;
1032 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1033 start += lengthAnnotation + 1;
1034 lengthAnnotation = stAnnotation.LineLength(start);
1035 lineInAnnotation++;
1037 PRectangle rcText = rcSegment;
1038 if ((phase & drawBack) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {
1039 surface->FillRectangle(rcText,
1040 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1041 rcText.left += vsDraw.spaceWidth;
1043 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1044 stAnnotation, start, lengthAnnotation, phase);
1045 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1046 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1047 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1048 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1049 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1050 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1051 if (subLine == ll->lines) {
1052 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1053 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1055 if (subLine == ll->lines + annotationLines - 1) {
1056 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1057 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1063 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1064 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1066 int lineStart = ll->LineStart(subLine);
1067 int posBefore = posCaret;
1068 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1069 int numCharsToDraw = posAfter - posCaret;
1071 // Work out where the starting and ending offsets are. We need to
1072 // see if the previous character shares horizontal space, such as a
1073 // glyph / combining character. If so we'll need to draw that too.
1074 int offsetFirstChar = offset;
1075 int offsetLastChar = offset + (posAfter - posCaret);
1076 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1077 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1078 // The char does not share horizontal space
1079 break;
1081 // Char shares horizontal space, update the numChars to draw
1082 // Update posBefore to point to the prev char
1083 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1084 numCharsToDraw = posAfter - posBefore;
1085 offsetFirstChar = offset - (posCaret - posBefore);
1088 // See if the next character shares horizontal space, if so we'll
1089 // need to draw that too.
1090 if (offsetFirstChar < 0)
1091 offsetFirstChar = 0;
1092 numCharsToDraw = offsetLastChar - offsetFirstChar;
1093 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1094 // Update posAfter to point to the 2nd next char, this is where
1095 // the next character ends, and 2nd next begins. We'll need
1096 // to compare these two
1097 posBefore = posAfter;
1098 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1099 offsetLastChar = offset + (posAfter - posCaret);
1100 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1101 // The char does not share horizontal space
1102 break;
1104 // Char shares horizontal space, update the numChars to draw
1105 numCharsToDraw = offsetLastChar - offsetFirstChar;
1108 // We now know what to draw, update the caret drawing rectangle
1109 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1110 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1112 // Adjust caret position to take into account any word wrapping symbols.
1113 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1114 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1115 rcCaret.left += wordWrapCharWidth;
1116 rcCaret.right += wordWrapCharWidth;
1119 // This character is where the caret block is, we override the colours
1120 // (inversed) for drawing the caret here.
1121 int styleMain = ll->styles[offsetFirstChar];
1122 FontAlias fontText = vsDraw.styles[styleMain].font;
1123 surface->DrawTextClipped(rcCaret, fontText,
1124 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1125 numCharsToDraw, vsDraw.styles[styleMain].back,
1126 caretColour);
1129 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1130 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1131 // When drag is active it is the only caret drawn
1132 bool drawDrag = model.posDrag.IsValid();
1133 if (hideSelection && !drawDrag)
1134 return;
1135 const int posLineStart = model.pdoc->LineStart(lineDoc);
1136 // For each selection draw
1137 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1138 const bool mainCaret = r == model.sel.Main();
1139 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1140 const int offset = posCaret.Position() - posLineStart;
1141 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1142 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1143 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1144 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1145 if (ll->wrapIndent != 0) {
1146 int lineStart = ll->LineStart(subLine);
1147 if (lineStart != 0) // Wrapped
1148 xposCaret += ll->wrapIndent;
1150 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1151 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1152 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1153 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1154 bool caretAtEOF = false;
1155 bool caretAtEOL = false;
1156 bool drawBlockCaret = false;
1157 XYPOSITION widthOverstrikeCaret;
1158 XYPOSITION caretWidthOffset = 0;
1159 PRectangle rcCaret = rcLine;
1161 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1162 caretAtEOF = true;
1163 widthOverstrikeCaret = vsDraw.aveCharWidth;
1164 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1165 caretAtEOL = true;
1166 widthOverstrikeCaret = vsDraw.aveCharWidth;
1167 } else {
1168 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1169 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1171 if (widthOverstrikeCaret < 3) // Make sure its visible
1172 widthOverstrikeCaret = 3;
1174 if (xposCaret > 0)
1175 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1176 xposCaret += xStart;
1177 if (model.posDrag.IsValid()) {
1178 /* Dragging text, use a line caret */
1179 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1180 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1181 } else if (model.inOverstrike && drawOverstrikeCaret) {
1182 /* Overstrike (insert mode), use a modified bar caret */
1183 rcCaret.top = rcCaret.bottom - 2;
1184 rcCaret.left = xposCaret + 1;
1185 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1186 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1187 /* Block caret */
1188 rcCaret.left = xposCaret;
1189 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1190 drawBlockCaret = true;
1191 rcCaret.right = xposCaret + widthOverstrikeCaret;
1192 } else {
1193 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1195 } else {
1196 /* Line caret */
1197 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1198 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1200 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1201 if (drawBlockCaret) {
1202 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1203 } else {
1204 surface->FillRectangle(rcCaret, caretColour);
1208 if (drawDrag)
1209 break;
1213 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1214 int xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker) {
1215 // default bgnd here..
1216 surface->FillRectangle(rcLine, background.isSet ? background :
1217 vsDraw.styles[STYLE_DEFAULT].back);
1219 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1221 // draw continuation rect
1222 PRectangle rcPlace = rcLine;
1224 rcPlace.left = static_cast<XYPOSITION>(xStart);
1225 rcPlace.right = rcPlace.left + ll->wrapIndent;
1227 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1228 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1229 else
1230 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1232 if (customDrawWrapMarker == NULL) {
1233 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1234 } else {
1235 customDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1240 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1241 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1242 int subLine, ColourOptional background) const {
1244 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1245 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1246 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1247 // Does not take margin into account but not significant
1248 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1250 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs);
1252 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1254 // Background drawing loop
1255 while (bfBack.More()) {
1257 const TextSegment ts = bfBack.Next();
1258 const int i = ts.end() - 1;
1259 const int iDoc = i + posLineStart;
1261 PRectangle rcSegment = rcLine;
1262 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1263 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1264 // Only try to draw if really visible - enhances performance by not calling environment to
1265 // draw strings that are completely past the right side of the window.
1266 if (rcSegment.Intersects(rcLine)) {
1267 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1268 if (rcSegment.left < rcLine.left)
1269 rcSegment.left = rcLine.left;
1270 if (rcSegment.right > rcLine.right)
1271 rcSegment.right = rcLine.right;
1273 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1274 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1275 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1276 inHotspot, ll->styles[i], i);
1277 if (ts.representation) {
1278 if (ll->chars[i] == '\t') {
1279 // Tab display
1280 if (drawWhitespaceBackground &&
1281 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1282 textBack = vsDraw.whitespaceColours.back;
1283 } else {
1284 // Blob display
1285 inIndentation = false;
1287 surface->FillRectangle(rcSegment, textBack);
1288 } else {
1289 // Normal text display
1290 surface->FillRectangle(rcSegment, textBack);
1291 if (vsDraw.viewWhitespace != wsInvisible ||
1292 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
1293 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1294 if (ll->chars[cpos + ts.start] == ' ') {
1295 if (drawWhitespaceBackground &&
1296 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1297 PRectangle rcSpace(
1298 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1299 rcSegment.top,
1300 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1301 rcSegment.bottom);
1302 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1304 } else {
1305 inIndentation = false;
1310 } else if (rcSegment.left > rcLine.right) {
1311 break;
1316 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1317 Range lineRange, int xStart) {
1318 if (vsDraw.edgeState == EDGE_LINE) {
1319 PRectangle rcSegment = rcLine;
1320 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1321 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1322 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1323 rcSegment.left -= ll->wrapIndent;
1324 rcSegment.right = rcSegment.left + 1;
1325 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
1329 // Draw underline mark as part of background if not transparent
1330 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1331 int line, PRectangle rcLine) {
1332 int marks = model.pdoc->GetMark(line);
1333 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1334 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1335 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1336 PRectangle rcUnderline = rcLine;
1337 rcUnderline.top = rcUnderline.bottom - 2;
1338 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1340 marks >>= 1;
1343 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1344 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1345 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1346 const int posLineStart = model.pdoc->LineStart(line);
1347 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1348 // For each selection draw
1349 int virtualSpaces = 0;
1350 if (subLine == (ll->lines - 1)) {
1351 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1353 SelectionPosition posStart(posLineStart + lineRange.start);
1354 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1355 SelectionSegment virtualSpaceRange(posStart, posEnd);
1356 for (size_t r = 0; r < model.sel.Count(); r++) {
1357 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1358 if (alpha != SC_ALPHA_NOALPHA) {
1359 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1360 if (!portion.Empty()) {
1361 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1362 PRectangle rcSegment = rcLine;
1363 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1364 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1365 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1366 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1367 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1368 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1369 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1371 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1372 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1373 if (rcSegment.right > rcLine.left)
1374 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1381 // Draw any translucent whole line states
1382 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1383 int line, PRectangle rcLine) {
1384 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1385 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1387 int marks = model.pdoc->GetMark(line);
1388 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1389 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
1390 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1391 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
1392 PRectangle rcUnderline = rcLine;
1393 rcUnderline.top = rcUnderline.bottom - 2;
1394 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1396 marks >>= 1;
1398 if (vsDraw.maskInLine) {
1399 int marksMasked = model.pdoc->GetMark(line) & vsDraw.maskInLine;
1400 if (marksMasked) {
1401 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
1402 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
1403 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1405 marksMasked >>= 1;
1411 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1412 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1413 int subLine, ColourOptional background) {
1415 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1416 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1417 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1419 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1420 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1422 // Does not take margin into account but not significant
1423 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1425 // Foreground drawing loop
1426 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1427 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs);
1429 while (bfFore.More()) {
1431 const TextSegment ts = bfFore.Next();
1432 const int i = ts.end() - 1;
1433 const int iDoc = i + posLineStart;
1435 PRectangle rcSegment = rcLine;
1436 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1437 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1438 // Only try to draw if really visible - enhances performance by not calling environment to
1439 // draw strings that are completely past the right side of the window.
1440 if (rcSegment.Intersects(rcLine)) {
1441 int styleMain = ll->styles[i];
1442 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1443 FontAlias textFont = vsDraw.styles[styleMain].font;
1444 //hotspot foreground
1445 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1446 if (inHotspot) {
1447 if (vsDraw.hotspotColours.fore.isSet)
1448 textFore = vsDraw.hotspotColours.fore;
1450 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1451 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1452 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1454 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1455 if (ts.representation) {
1456 if (ll->chars[i] == '\t') {
1457 // Tab display
1458 if (phasesDraw == phasesOne) {
1459 if (drawWhitespaceBackground &&
1460 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1461 textBack = vsDraw.whitespaceColours.back;
1462 surface->FillRectangle(rcSegment, textBack);
1464 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1465 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1466 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1467 indentCount++) {
1468 if (indentCount > 0) {
1469 int xIndent = static_cast<int>(indentCount * indentWidth);
1470 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1471 (ll->xHighlightGuide == xIndent));
1475 if (vsDraw.viewWhitespace != wsInvisible) {
1476 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1477 if (vsDraw.whitespaceColours.fore.isSet)
1478 textFore = vsDraw.whitespaceColours.fore;
1479 surface->PenColour(textFore);
1480 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,
1481 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1482 if (customDrawTabArrow == NULL)
1483 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1484 else
1485 customDrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1488 } else {
1489 inIndentation = false;
1490 if (vsDraw.controlCharSymbol >= 32) {
1491 // Using one font for all control characters so it can be controlled independently to ensure
1492 // the box goes around the characters tightly. Seems to be no way to work out what height
1493 // is taken by an individual character - internal leading gives varying results.
1494 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1495 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1496 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1497 rcSegment.top + vsDraw.maxAscent,
1498 cc, 1, textBack, textFore);
1499 } else {
1500 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1501 textBack, textFore, phasesDraw == phasesOne);
1504 } else {
1505 // Normal text display
1506 if (vsDraw.styles[styleMain].visible) {
1507 if (phasesDraw != phasesOne) {
1508 surface->DrawTextTransparent(rcSegment, textFont,
1509 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1510 i - ts.start + 1, textFore);
1511 } else {
1512 surface->DrawTextNoClip(rcSegment, textFont,
1513 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1514 i - ts.start + 1, textFore, textBack);
1517 if (vsDraw.viewWhitespace != wsInvisible ||
1518 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1519 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1520 if (ll->chars[cpos + ts.start] == ' ') {
1521 if (vsDraw.viewWhitespace != wsInvisible) {
1522 if (vsDraw.whitespaceColours.fore.isSet)
1523 textFore = vsDraw.whitespaceColours.fore;
1524 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1525 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1526 if ((phasesDraw == phasesOne) && drawWhitespaceBackground &&
1527 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1528 textBack = vsDraw.whitespaceColours.back;
1529 PRectangle rcSpace(
1530 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1531 rcSegment.top,
1532 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1533 rcSegment.bottom);
1534 surface->FillRectangle(rcSpace, textBack);
1536 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
1537 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1538 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1539 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1540 surface->FillRectangle(rcDot, textFore);
1543 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1544 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1545 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1546 indentCount++) {
1547 if (indentCount > 0) {
1548 int xIndent = static_cast<int>(indentCount * indentWidth);
1549 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1550 (ll->xHighlightGuide == xIndent));
1554 } else {
1555 inIndentation = false;
1560 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1561 PRectangle rcUL = rcSegment;
1562 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1563 rcUL.bottom = rcUL.top + 1;
1564 if (vsDraw.hotspotColours.fore.isSet)
1565 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1566 else
1567 surface->FillRectangle(rcUL, textFore);
1568 } else if (vsDraw.styles[styleMain].underline) {
1569 PRectangle rcUL = rcSegment;
1570 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1571 rcUL.bottom = rcUL.top + 1;
1572 surface->FillRectangle(rcUL, textFore);
1574 } else if (rcSegment.left > rcLine.right) {
1575 break;
1580 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1581 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1582 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1583 && (subLine == 0)) {
1584 const int posLineStart = model.pdoc->LineStart(line);
1585 int indentSpace = model.pdoc->GetLineIndentation(line);
1586 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1588 // Find the most recent line with some text
1590 int lineLastWithText = line;
1591 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1592 lineLastWithText--;
1594 if (lineLastWithText < line) {
1595 xStartText = 100000; // Don't limit to visible indentation on empty line
1596 // This line is empty, so use indentation of last line with text
1597 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1598 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1599 if (isFoldHeader) {
1600 // Level is one more level than parent
1601 indentLastWithText += model.pdoc->IndentSize();
1603 if (vsDraw.viewIndentationGuides == ivLookForward) {
1604 // In viLookForward mode, previous line only used if it is a fold header
1605 if (isFoldHeader) {
1606 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1608 } else { // viLookBoth
1609 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1613 int lineNextWithText = line;
1614 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1615 lineNextWithText++;
1617 if (lineNextWithText > line) {
1618 xStartText = 100000; // Don't limit to visible indentation on empty line
1619 // This line is empty, so use indentation of first next line with text
1620 indentSpace = Platform::Maximum(indentSpace,
1621 model.pdoc->GetLineIndentation(lineNextWithText));
1624 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1625 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1626 if (xIndent < xStartText) {
1627 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1628 (ll->xHighlightGuide == xIndent));
1634 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1635 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1637 if (subLine >= ll->lines) {
1638 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1639 return; // No further drawing
1642 // See if something overrides the line background color.
1643 const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1645 const int posLineStart = model.pdoc->LineStart(line);
1647 const Range lineRange = ll->SubLineRange(subLine);
1648 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1650 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1651 if (phase & drawBack) {
1652 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker);
1654 xStart += static_cast<int>(ll->wrapIndent);
1657 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1658 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1659 subLine, background);
1660 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1661 xStart, subLine, subLineStart, background);
1664 if (phase & drawIndicatorsBack) {
1665 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true);
1666 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1667 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1670 if (phase & drawText) {
1671 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1672 subLine, background);
1675 if (phase & drawIndentationGuides) {
1676 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1679 if (phase & drawIndicatorsFore) {
1680 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false);
1683 // End of the drawing of the current line
1684 if (phasesDraw == phasesOne) {
1685 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1686 xStart, subLine, subLineStart, background);
1689 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1690 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1693 if (phase & drawLineTranslucent) {
1694 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1698 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1699 bool expanded = model.cs.GetExpanded(line);
1700 const int level = model.pdoc->GetLevel(line);
1701 const int levelNext = model.pdoc->GetLevel(line + 1);
1702 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1703 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
1704 // Paint the line above the fold
1705 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1707 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1708 PRectangle rcFoldLine = rcLine;
1709 rcFoldLine.bottom = rcFoldLine.top + 1;
1710 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1712 // Paint the line below the fold
1713 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1715 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1716 PRectangle rcFoldLine = rcLine;
1717 rcFoldLine.top = rcFoldLine.bottom - 1;
1718 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1723 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1724 PRectangle rcClient, const ViewStyle &vsDraw) {
1725 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1726 // serifs and italic stems for aliased text.
1727 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1729 // Do the painting
1730 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1732 Surface *surface = surfaceWindow;
1733 if (bufferedDraw) {
1734 surface = pixmapLine;
1735 PLATFORM_ASSERT(pixmapLine->Initialised());
1737 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1738 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1740 const Point ptOrigin = model.GetVisibleOriginInMain();
1742 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1743 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1745 SelectionPosition posCaret = model.sel.RangeMain().caret;
1746 if (model.posDrag.IsValid())
1747 posCaret = model.posDrag;
1748 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1750 PRectangle rcTextArea = rcClient;
1751 if (vsDraw.marginInside) {
1752 rcTextArea.left += vsDraw.textStart;
1753 rcTextArea.right -= vsDraw.rightMarginWidth;
1754 } else {
1755 rcTextArea = rcArea;
1758 // Remove selection margin from drawing area so text will not be drawn
1759 // on it in unbuffered mode.
1760 if (!bufferedDraw && vsDraw.marginInside) {
1761 PRectangle rcClipText = rcTextArea;
1762 rcClipText.left -= leftTextOverlap;
1763 surfaceWindow->SetClip(rcClipText);
1766 // Loop on visible lines
1767 //double durLayout = 0.0;
1768 //double durPaint = 0.0;
1769 //double durCopy = 0.0;
1770 //ElapsedTime etWhole;
1772 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1773 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1775 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1776 AutoLineLayout ll(llc, 0);
1777 std::vector<DrawPhase> phases;
1778 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1779 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1780 phases.push_back(phase);
1782 } else {
1783 phases.push_back(drawAll);
1785 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1786 int ypos = 0;
1787 if (!bufferedDraw)
1788 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1789 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1790 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1791 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1793 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1794 // Only visible lines should be handled by the code within the loop
1795 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1796 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1797 const int subLine = visibleLine - lineStartSet;
1799 // Copy this line and its styles from the document into local arrays
1800 // and determine the x position at which each character starts.
1801 //ElapsedTime et;
1802 if (lineDoc != lineDocPrevious) {
1803 ll.Set(0);
1804 ll.Set(RetrieveLineLayout(lineDoc, model));
1805 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1806 lineDocPrevious = lineDoc;
1808 //durLayout += et.Duration(true);
1810 if (ll) {
1811 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1812 ll->hotspot = model.GetHotSpotRange();
1814 PRectangle rcLine = rcTextArea;
1815 rcLine.top = static_cast<XYPOSITION>(ypos);
1816 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
1818 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
1820 // Highlight the current braces if any
1821 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
1822 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
1824 if (leftTextOverlap && bufferedDraw) {
1825 PRectangle rcSpacer = rcLine;
1826 rcSpacer.right = rcSpacer.left;
1827 rcSpacer.left -= 1;
1828 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
1831 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
1832 //durPaint += et.Duration(true);
1834 // Restore the previous styles for the brace highlights in case layout is in cache.
1835 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
1837 if (*it & drawFoldLines) {
1838 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
1841 if (*it & drawCarets) {
1842 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
1845 if (bufferedDraw) {
1846 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
1847 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
1848 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
1849 yposScreen + vsDraw.lineHeight);
1850 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
1853 lineWidthMaxSeen = Platform::Maximum(
1854 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
1855 //durCopy += et.Duration(true);
1858 if (!bufferedDraw) {
1859 ypos += vsDraw.lineHeight;
1862 yposScreen += vsDraw.lineHeight;
1863 visibleLine++;
1866 ll.Set(0);
1867 //if (durPaint < 0.00000001)
1868 // durPaint = 0.00000001;
1870 // Right column limit indicator
1871 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
1872 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
1873 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
1874 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
1875 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1876 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
1877 if (vsDraw.edgeState == EDGE_LINE) {
1878 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1879 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
1880 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1881 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour);
1884 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1886 //Platform::DebugPrintf(
1887 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1888 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1892 // Space (3 space characters) between line numbers and text when printing.
1893 #define lineNumberPrintSpace " "
1895 ColourDesired InvertedLight(ColourDesired orig) {
1896 unsigned int r = orig.GetRed();
1897 unsigned int g = orig.GetGreen();
1898 unsigned int b = orig.GetBlue();
1899 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1900 unsigned int il = 0xff - l;
1901 if (l == 0)
1902 return ColourDesired(0xff, 0xff, 0xff);
1903 r = r * il / l;
1904 g = g * il / l;
1905 b = b * il / l;
1906 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1909 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
1910 const EditModel &model, const ViewStyle &vs) {
1911 // Can't use measurements cached for screen
1912 posCache.Clear();
1914 ViewStyle vsPrint(vs);
1915 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
1917 // Modify the view style for printing as do not normally want any of the transient features to be printed
1918 // Printing supports only the line number margin.
1919 int lineNumberIndex = -1;
1920 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1921 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
1922 lineNumberIndex = margin;
1923 } else {
1924 vsPrint.ms[margin].width = 0;
1927 vsPrint.fixedColumnWidth = 0;
1928 vsPrint.zoomLevel = printParameters.magnification;
1929 // Don't show indentation guides
1930 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1931 vsPrint.viewIndentationGuides = ivNone;
1932 // Don't show the selection when printing
1933 vsPrint.selColours.back.isSet = false;
1934 vsPrint.selColours.fore.isSet = false;
1935 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
1936 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
1937 vsPrint.whitespaceColours.back.isSet = false;
1938 vsPrint.whitespaceColours.fore.isSet = false;
1939 vsPrint.showCaretLineBackground = false;
1940 vsPrint.alwaysShowCaretLineBackground = false;
1941 // Don't highlight matching braces using indicators
1942 vsPrint.braceHighlightIndicatorSet = false;
1943 vsPrint.braceBadLightIndicatorSet = false;
1945 // Set colours for printing according to users settings
1946 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
1947 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
1948 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
1949 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
1950 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
1951 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
1952 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1953 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
1954 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1955 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
1956 if (sty <= STYLE_DEFAULT) {
1957 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1961 // White background for the line numbers
1962 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
1964 // Printing uses different margins, so reset screen margins
1965 vsPrint.leftMarginWidth = 0;
1966 vsPrint.rightMarginWidth = 0;
1968 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
1969 // Determining width must happen after fonts have been realised in Refresh
1970 int lineNumberWidth = 0;
1971 if (lineNumberIndex >= 0) {
1972 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
1973 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
1974 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
1975 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
1978 int linePrintStart = model.pdoc->LineFromPosition(pfr->chrg.cpMin);
1979 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
1980 if (linePrintLast < linePrintStart)
1981 linePrintLast = linePrintStart;
1982 int linePrintMax = model.pdoc->LineFromPosition(pfr->chrg.cpMax);
1983 if (linePrintLast > linePrintMax)
1984 linePrintLast = linePrintMax;
1985 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
1986 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
1987 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
1988 int endPosPrint = model.pdoc->Length();
1989 if (linePrintLast < model.pdoc->LinesTotal())
1990 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
1992 // Ensure we are styled to where we are formatting.
1993 model.pdoc->EnsureStyledTo(endPosPrint);
1995 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
1996 int ypos = pfr->rc.top;
1998 int lineDoc = linePrintStart;
2000 int nPrintPos = pfr->chrg.cpMin;
2001 int visibleLine = 0;
2002 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
2003 if (printParameters.wrapState == eWrapNone)
2004 widthPrint = LineLayout::wrapWidthInfinite;
2006 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
2008 // When printing, the hdc and hdcTarget may be the same, so
2009 // changing the state of surfaceMeasure may change the underlying
2010 // state of surface. Therefore, any cached state is discarded before
2011 // using each surface.
2012 surfaceMeasure->FlushCachedState();
2014 // Copy this line and its styles from the document into local arrays
2015 // and determine the x position at which each character starts.
2016 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2017 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2019 ll.containsCaret = false;
2021 PRectangle rcLine = PRectangle::FromInts(
2022 pfr->rc.left,
2023 ypos,
2024 pfr->rc.right - 1,
2025 ypos + vsPrint.lineHeight);
2027 // When document line is wrapped over multiple display lines, find where
2028 // to start printing from to ensure a particular position is on the first
2029 // line of the page.
2030 if (visibleLine == 0) {
2031 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2032 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2033 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2034 visibleLine = -iwl;
2038 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2039 visibleLine = -(ll.lines - 1);
2043 if (draw && lineNumberWidth &&
2044 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2045 (visibleLine >= 0)) {
2046 char number[100];
2047 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2048 PRectangle rcNumber = rcLine;
2049 rcNumber.right = rcNumber.left + lineNumberWidth;
2050 // Right justify
2051 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2052 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2053 surface->FlushCachedState();
2054 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2055 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2056 vsPrint.styles[STYLE_LINENUMBER].fore,
2057 vsPrint.styles[STYLE_LINENUMBER].back);
2060 // Draw the line
2061 surface->FlushCachedState();
2063 for (int iwl = 0; iwl < ll.lines; iwl++) {
2064 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2065 if (visibleLine >= 0) {
2066 if (draw) {
2067 rcLine.top = static_cast<XYPOSITION>(ypos);
2068 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2069 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2071 ypos += vsPrint.lineHeight;
2073 visibleLine++;
2074 if (iwl == ll.lines - 1)
2075 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2076 else
2077 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2081 ++lineDoc;
2084 // Clear cache so measurements are not used for screen
2085 posCache.Clear();
2087 return nPrintPos;