Update Scintilla to 3.5.1 pre-release
[geany-mirror.git] / scintilla / src / EditView.cxx
blob7d5860ef8adfac6bc0bb337eaa5fd889a225026f
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"
50 #ifdef SCI_NAMESPACE
51 using namespace Scintilla;
52 #endif
54 static inline bool IsControlCharacter(int ch) {
55 // iscntrl returns true for lots of chars > 127 which are displayable
56 return ch >= 0 && ch < ' ';
59 PrintParameters::PrintParameters() {
60 magnification = 0;
61 colourMode = SC_PRINT_NORMAL;
62 wrapState = eWrapWord;
65 #ifdef SCI_NAMESPACE
66 namespace Scintilla {
67 #endif
69 bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) {
70 if (st.multipleStyles) {
71 for (size_t iStyle = 0; iStyle<st.length; iStyle++) {
72 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
73 return false;
75 } else {
76 if (!vs.ValidStyle(styleOffset + st.style))
77 return false;
79 return true;
82 static int WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset,
83 const char *text, const unsigned char *styles, size_t len) {
84 int width = 0;
85 size_t start = 0;
86 while (start < len) {
87 size_t style = styles[start];
88 size_t endSegment = start;
89 while ((endSegment + 1 < len) && (static_cast<size_t>(styles[endSegment + 1]) == style))
90 endSegment++;
91 FontAlias fontText = vs.styles[style + styleOffset].font;
92 width += static_cast<int>(surface->WidthText(fontText, text + start,
93 static_cast<int>(endSegment - start + 1)));
94 start = endSegment + 1;
96 return width;
99 int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) {
100 int widthMax = 0;
101 size_t start = 0;
102 while (start < st.length) {
103 size_t lenLine = st.LineLength(start);
104 int widthSubLine;
105 if (st.multipleStyles) {
106 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
107 } else {
108 FontAlias fontText = vs.styles[styleOffset + st.style].font;
109 widthSubLine = static_cast<int>(surface->WidthText(fontText,
110 st.text + start, static_cast<int>(lenLine)));
112 if (widthSubLine > widthMax)
113 widthMax = widthSubLine;
114 start += lenLine + 1;
116 return widthMax;
119 void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,
120 const char *s, int len, DrawPhase phase) {
121 FontAlias fontText = style.font;
122 if (phase & drawBack) {
123 if (phase & drawText) {
124 // Drawing both
125 surface->DrawTextNoClip(rc, fontText, ybase, s, len,
126 style.fore, style.back);
127 } else {
128 surface->FillRectangle(rc, style.back);
130 } else if (phase & drawText) {
131 surface->DrawTextTransparent(rc, fontText, ybase, s, len, style.fore);
135 void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
136 const StyledText &st, size_t start, size_t length, DrawPhase phase) {
138 if (st.multipleStyles) {
139 int x = static_cast<int>(rcText.left);
140 size_t i = 0;
141 while (i < length) {
142 size_t end = i;
143 size_t style = st.styles[i + start];
144 while (end < length - 1 && st.styles[start + end + 1] == style)
145 end++;
146 style += styleOffset;
147 FontAlias fontText = vs.styles[style].font;
148 const int width = static_cast<int>(surface->WidthText(fontText,
149 st.text + start + i, static_cast<int>(end - i + 1)));
150 PRectangle rcSegment = rcText;
151 rcSegment.left = static_cast<XYPOSITION>(x);
152 rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
153 DrawTextNoClipPhase(surface, rcSegment, vs.styles[style],
154 rcText.top + vs.maxAscent, st.text + start + i,
155 static_cast<int>(end - i + 1), phase);
156 x += width;
157 i = end + 1;
159 } else {
160 const size_t style = st.style + styleOffset;
161 DrawTextNoClipPhase(surface, rcText, vs.styles[style],
162 rcText.top + vs.maxAscent, st.text + start,
163 static_cast<int>(length), phase);
167 #ifdef SCI_NAMESPACE
169 #endif
171 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
173 EditView::EditView() {
174 ldTabstops = NULL;
175 hideSelection = false;
176 drawOverstrikeCaret = true;
177 bufferedDraw = true;
178 phasesDraw = phasesTwo;
179 lineWidthMaxSeen = 0;
180 additionalCaretsBlink = true;
181 additionalCaretsVisible = true;
182 imeCaretBlockOverride = false;
183 pixmapLine = 0;
184 pixmapIndentGuide = 0;
185 pixmapIndentGuideHighlight = 0;
186 llc.SetLevel(LineLayoutCache::llcCaret);
187 posCache.SetSize(0x400);
190 EditView::~EditView() {
191 delete ldTabstops;
192 ldTabstops = NULL;
195 bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) {
196 const PhasesDraw phasesDrawNew = twoPhaseDraw ? phasesTwo : phasesOne;
197 const bool redraw = phasesDraw != phasesDrawNew;
198 phasesDraw = phasesDrawNew;
199 return redraw;
202 bool EditView::SetPhasesDraw(int phases) {
203 const PhasesDraw phasesDrawNew = static_cast<PhasesDraw>(phases);
204 const bool redraw = phasesDraw != phasesDrawNew;
205 phasesDraw = phasesDrawNew;
206 return redraw;
209 bool EditView::LinesOverlap() const {
210 return phasesDraw == phasesMultiple;
213 void EditView::ClearAllTabstops() {
214 delete ldTabstops;
215 ldTabstops = 0;
218 XYPOSITION EditView::NextTabstopPos(int line, XYPOSITION x, XYPOSITION tabWidth) const {
219 int next = GetNextTabstop(line, static_cast<int>(x + 2));
220 if (next > 0)
221 return static_cast<XYPOSITION>(next);
222 return (static_cast<int>((x + 2) / tabWidth) + 1) * tabWidth;
225 bool EditView::ClearTabstops(int line) {
226 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
227 return lt && lt->ClearTabstops(line);
230 bool EditView::AddTabstop(int line, int x) {
231 if (!ldTabstops) {
232 ldTabstops = new LineTabstops();
234 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
235 return lt && lt->AddTabstop(line, x);
238 int EditView::GetNextTabstop(int line, int x) const {
239 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
240 if (lt) {
241 return lt->GetNextTabstop(line, x);
242 } else {
243 return 0;
247 void EditView::LinesAddedOrRemoved(int lineOfPos, int linesAdded) {
248 if (ldTabstops) {
249 if (linesAdded > 0) {
250 for (int line = lineOfPos; line < lineOfPos + linesAdded; line++) {
251 ldTabstops->InsertLine(line);
253 } else {
254 for (int line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {
255 ldTabstops->RemoveLine(line);
261 void EditView::DropGraphics(bool freeObjects) {
262 if (freeObjects) {
263 delete pixmapLine;
264 pixmapLine = 0;
265 delete pixmapIndentGuide;
266 pixmapIndentGuide = 0;
267 delete pixmapIndentGuideHighlight;
268 pixmapIndentGuideHighlight = 0;
269 } else {
270 if (pixmapLine)
271 pixmapLine->Release();
272 if (pixmapIndentGuide)
273 pixmapIndentGuide->Release();
274 if (pixmapIndentGuideHighlight)
275 pixmapIndentGuideHighlight->Release();
279 void EditView::AllocateGraphics(const ViewStyle &vsDraw) {
280 if (!pixmapLine)
281 pixmapLine = Surface::Allocate(vsDraw.technology);
282 if (!pixmapIndentGuide)
283 pixmapIndentGuide = Surface::Allocate(vsDraw.technology);
284 if (!pixmapIndentGuideHighlight)
285 pixmapIndentGuideHighlight = Surface::Allocate(vsDraw.technology);
288 const char *ControlCharacterString(unsigned char ch) {
289 const char *reps[] = {
290 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
291 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
292 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
293 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
295 if (ch < ELEMENTS(reps)) {
296 return reps[ch];
297 } else {
298 return "BAD";
302 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
303 int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
304 int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
305 if (xhead <= rcTab.left) {
306 ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
307 xhead = static_cast<int>(rcTab.left) - 1;
309 if ((rcTab.left + 2) < (rcTab.right - 1))
310 surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
311 else
312 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
313 surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
314 surface->LineTo(xhead, ymid - ydiff);
315 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
316 surface->LineTo(xhead, ymid + ydiff);
319 void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
320 if (!pixmapIndentGuide->Initialised()) {
321 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
322 pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
323 pixmapIndentGuideHighlight->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid);
324 PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight);
325 pixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[STYLE_INDENTGUIDE].back);
326 pixmapIndentGuide->PenColour(vsDraw.styles[STYLE_INDENTGUIDE].fore);
327 pixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[STYLE_BRACELIGHT].back);
328 pixmapIndentGuideHighlight->PenColour(vsDraw.styles[STYLE_BRACELIGHT].fore);
329 for (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) {
330 PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
331 pixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[STYLE_INDENTGUIDE].fore);
332 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[STYLE_BRACELIGHT].fore);
337 LineLayout *EditView::RetrieveLineLayout(int lineNumber, const EditModel &model) {
338 int posLineStart = model.pdoc->LineStart(lineNumber);
339 int posLineEnd = model.pdoc->LineStart(lineNumber + 1);
340 PLATFORM_ASSERT(posLineEnd >= posLineStart);
341 int lineCaret = model.pdoc->LineFromPosition(model.sel.MainCaret());
342 return llc.Retrieve(lineNumber, lineCaret,
343 posLineEnd - posLineStart, model.pdoc->GetStyleClock(),
344 model.LinesOnScreen() + 1, model.pdoc->LinesTotal());
348 * Fill in the LineLayout data for the given line.
349 * Copy the given @a line and its styles from the document into local arrays.
350 * Also determine the x position at which each character starts.
352 void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
353 if (!ll)
354 return;
356 PLATFORM_ASSERT(line < model.pdoc->LinesTotal());
357 PLATFORM_ASSERT(ll->chars != NULL);
358 int posLineStart = model.pdoc->LineStart(line);
359 int posLineEnd = model.pdoc->LineStart(line + 1);
360 // If the line is very long, limit the treatment to a length that should fit in the viewport
361 if (posLineEnd >(posLineStart + ll->maxLineLength)) {
362 posLineEnd = posLineStart + ll->maxLineLength;
364 if (ll->validity == LineLayout::llCheckTextAndStyle) {
365 int lineLength = posLineEnd - posLineStart;
366 if (!vstyle.viewEOL) {
367 lineLength = model.pdoc->LineEnd(line) - posLineStart;
369 if (lineLength == ll->numCharsInLine) {
370 // See if chars, styles, indicators, are all the same
371 bool allSame = true;
372 // Check base line layout
373 char styleByte = 0;
374 int numCharsInLine = 0;
375 while (numCharsInLine < lineLength) {
376 int charInDoc = numCharsInLine + posLineStart;
377 char chDoc = model.pdoc->CharAt(charInDoc);
378 styleByte = model.pdoc->StyleAt(charInDoc);
379 allSame = allSame &&
380 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte));
381 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
382 allSame = allSame &&
383 (ll->chars[numCharsInLine] == chDoc);
384 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
385 allSame = allSame &&
386 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
387 else // Style::caseUpper
388 allSame = allSame &&
389 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
390 numCharsInLine++;
392 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
393 if (allSame) {
394 ll->validity = LineLayout::llPositions;
395 } else {
396 ll->validity = LineLayout::llInvalid;
398 } else {
399 ll->validity = LineLayout::llInvalid;
402 if (ll->validity == LineLayout::llInvalid) {
403 ll->widthLine = LineLayout::wrapWidthInfinite;
404 ll->lines = 1;
405 if (vstyle.edgeState == EDGE_BACKGROUND) {
406 ll->edgeColumn = model.pdoc->FindColumn(line, vstyle.theEdge);
407 if (ll->edgeColumn >= posLineStart) {
408 ll->edgeColumn -= posLineStart;
410 } else {
411 ll->edgeColumn = -1;
414 // Fill base line layout
415 const int lineLength = posLineEnd - posLineStart;
416 model.pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
417 model.pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
418 int numCharsBeforeEOL = model.pdoc->LineEnd(line) - posLineStart;
419 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
420 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
421 const unsigned char styleByte = ll->styles[styleInLine];
422 ll->styles[styleInLine] = styleByte;
424 const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
425 if (vstyle.someStylesForceCase) {
426 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
427 char chDoc = ll->chars[charInLine];
428 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
429 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
430 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
431 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
434 ll->xHighlightGuide = 0;
435 // Extra element at the end of the line to hold end x position and act as
436 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
437 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
439 // Layout the line, determining the position of each character,
440 // with an extra element at the end for the end of the line.
441 ll->positions[0] = 0;
442 bool lastSegItalics = false;
444 BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs);
445 while (bfLayout.More()) {
447 const TextSegment ts = bfLayout.Next();
449 std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f);
450 if (vstyle.styles[ll->styles[ts.start]].visible) {
451 if (ts.representation) {
452 XYPOSITION representationWidth = vstyle.controlCharWidth;
453 if (ll->chars[ts.start] == '\t') {
454 // Tab is a special case of representation, taking a variable amount of space
455 const XYPOSITION x = ll->positions[ts.start];
456 representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start];
457 } else {
458 if (representationWidth <= 0.0) {
459 XYPOSITION positionsRepr[256]; // Should expand when needed
460 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
461 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
462 representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
465 for (int ii = 0; ii < ts.length; ii++)
466 ll->positions[ts.start + 1 + ii] = representationWidth;
467 } else {
468 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
469 // Over half the segments are single characters and of these about half are space characters.
470 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
471 } else {
472 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
473 ts.length, ll->positions + ts.start + 1, model.pdoc);
476 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
479 for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
480 ll->positions[posToIncrease] += ll->positions[ts.start];
484 // Small hack to make lines that end with italics not cut off the edge of the last character
485 if (lastSegItalics) {
486 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
488 ll->numCharsInLine = numCharsInLine;
489 ll->numCharsBeforeEOL = numCharsBeforeEOL;
490 ll->validity = LineLayout::llPositions;
492 // Hard to cope when too narrow, so just assume there is space
493 if (width < 20) {
494 width = 20;
496 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
497 ll->widthLine = width;
498 if (width == LineLayout::wrapWidthInfinite) {
499 ll->lines = 1;
500 } else if (width > ll->positions[ll->numCharsInLine]) {
501 // Simple common case where line does not need wrapping.
502 ll->lines = 1;
503 } else {
504 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
505 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
507 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
508 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
509 wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
510 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
511 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
513 ll->wrapIndent = wrapAddIndent;
514 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
515 for (int i = 0; i < ll->numCharsInLine; i++) {
516 if (!IsSpaceOrTab(ll->chars[i])) {
517 ll->wrapIndent += ll->positions[i]; // Add line indent
518 break;
521 // Check for text width minimum
522 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
523 ll->wrapIndent = wrapAddIndent;
524 // Check for wrapIndent minimum
525 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
526 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
527 ll->lines = 0;
528 // Calculate line start positions based upon width.
529 int lastGoodBreak = 0;
530 int lastLineStart = 0;
531 XYACCUMULATOR startOffset = 0;
532 int p = 0;
533 while (p < ll->numCharsInLine) {
534 if ((ll->positions[p + 1] - startOffset) >= width) {
535 if (lastGoodBreak == lastLineStart) {
536 // Try moving to start of last character
537 if (p > 0) {
538 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
539 - posLineStart;
541 if (lastGoodBreak == lastLineStart) {
542 // Ensure at least one character on line.
543 lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
544 - posLineStart;
547 lastLineStart = lastGoodBreak;
548 ll->lines++;
549 ll->SetLineStart(ll->lines, lastGoodBreak);
550 startOffset = ll->positions[lastGoodBreak];
551 // take into account the space for start wrap mark and indent
552 startOffset -= ll->wrapIndent;
553 p = lastGoodBreak + 1;
554 continue;
556 if (p > 0) {
557 if (vstyle.wrapState == eWrapChar) {
558 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
559 - posLineStart;
560 p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
561 continue;
562 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
563 lastGoodBreak = p;
564 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
565 lastGoodBreak = p;
568 p++;
570 ll->lines++;
572 ll->validity = LineLayout::llLines;
576 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) {
577 Point pt;
578 if (pos.Position() == INVALID_POSITION)
579 return pt;
580 const int line = model.pdoc->LineFromPosition(pos.Position());
581 const int lineVisible = model.cs.DisplayFromDoc(line);
582 //Platform::DebugPrintf("line=%d\n", line);
583 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
584 if (surface && ll) {
585 const int posLineStart = model.pdoc->LineStart(line);
586 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
587 const int posInLine = pos.Position() - posLineStart;
588 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
589 pt.y += (lineVisible - topLine) * vs.lineHeight;
590 pt.x += vs.textStart - model.xOffset;
592 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
593 return pt;
596 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
597 pt.x = pt.x - vs.textStart;
598 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
599 if (!canReturnInvalid && (visibleLine < 0))
600 visibleLine = 0;
601 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
602 if (canReturnInvalid && (lineDoc < 0))
603 return SelectionPosition(INVALID_POSITION);
604 if (lineDoc >= model.pdoc->LinesTotal())
605 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length());
606 const int posLineStart = model.pdoc->LineStart(lineDoc);
607 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
608 if (surface && ll) {
609 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
610 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
611 const int subLine = visibleLine - lineStartSet;
612 if (subLine < ll->lines) {
613 const Range rangeSubLine = ll->SubLineRange(subLine);
614 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
615 if (subLine > 0) // Wrapped
616 pt.x -= ll->wrapIndent;
617 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
618 if (positionInLine < rangeSubLine.end) {
619 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
621 if (virtualSpace) {
622 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
623 const int spaceOffset = static_cast<int>(
624 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
625 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
626 } else if (canReturnInvalid) {
627 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
628 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
630 } else {
631 return SelectionPosition(rangeSubLine.end + posLineStart);
634 if (!canReturnInvalid)
635 return SelectionPosition(ll->numCharsInLine + posLineStart);
637 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
641 * Find the document position corresponding to an x coordinate on a particular document line.
642 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
643 * This method is used for rectangular selections and does not work on wrapped lines.
645 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
646 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
647 if (surface && ll) {
648 const int posLineStart = model.pdoc->LineStart(lineDoc);
649 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
650 const Range rangeSubLine = ll->SubLineRange(0);
651 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
652 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
653 if (positionInLine < rangeSubLine.end) {
654 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
656 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
657 const int spaceOffset = static_cast<int>(
658 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
659 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
661 return SelectionPosition(0);
664 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
665 int lineDoc = model.pdoc->LineFromPosition(pos);
666 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
667 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
668 if (surface && ll) {
669 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
670 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
671 int posInLine = pos - posLineStart;
672 lineDisplay--; // To make up for first increment ahead.
673 for (int subLine = 0; subLine < ll->lines; subLine++) {
674 if (posInLine >= ll->LineStart(subLine)) {
675 lineDisplay++;
679 return lineDisplay;
682 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
683 int line = model.pdoc->LineFromPosition(pos);
684 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
685 int posRet = INVALID_POSITION;
686 if (surface && ll) {
687 unsigned int posLineStart = model.pdoc->LineStart(line);
688 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
689 int posInLine = pos - posLineStart;
690 if (posInLine <= ll->maxLineLength) {
691 for (int subLine = 0; subLine < ll->lines; subLine++) {
692 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
693 if (start) {
694 posRet = ll->LineStart(subLine) + posLineStart;
695 } else {
696 if (subLine == ll->lines - 1)
697 posRet = ll->LineStart(subLine + 1) + posLineStart;
698 else
699 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
705 return posRet;
708 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
709 return main ?
710 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
711 vsDraw.selAdditionalBackground;
714 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
715 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
716 if (inSelection == 1) {
717 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
718 return SelectionBackground(vsDraw, true, model.primarySelection);
720 } else if (inSelection == 2) {
721 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
722 return SelectionBackground(vsDraw, false, model.primarySelection);
724 } else {
725 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
726 (i >= ll->edgeColumn) &&
727 (i < ll->numCharsBeforeEOL))
728 return vsDraw.edgecolour;
729 if (inHotspot && vsDraw.hotspotColours.back.isSet)
730 return vsDraw.hotspotColours.back;
732 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
733 return background;
734 } else {
735 return vsDraw.styles[styleMain].back;
739 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
740 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
741 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
742 surface->Copy(rcCopyArea, from,
743 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
746 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
747 if (alpha != SC_ALPHA_NOALPHA) {
748 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
752 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
753 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
754 if (fillBackground) {
755 surface->FillRectangle(rcSegment, textBack);
757 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
758 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
759 surface->InternalLeading(ctrlCharsFont));
760 PRectangle rcCChar = rcSegment;
761 rcCChar.left = rcCChar.left + 1;
762 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
763 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
764 PRectangle rcCentral = rcCChar;
765 rcCentral.top++;
766 rcCentral.bottom--;
767 surface->FillRectangle(rcCentral, textFore);
768 PRectangle rcChar = rcCChar;
769 rcChar.left++;
770 rcChar.right--;
771 surface->DrawTextClipped(rcChar, ctrlCharsFont,
772 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
773 textBack, textFore);
776 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
777 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
778 ColourOptional background) {
780 const int posLineStart = model.pdoc->LineStart(line);
781 PRectangle rcSegment = rcLine;
783 const bool lastSubLine = subLine == (ll->lines - 1);
784 XYPOSITION virtualSpace = 0;
785 if (lastSubLine) {
786 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
787 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
789 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
791 // Fill the virtual space and show selections within it
792 if (virtualSpace) {
793 rcSegment.left = xEol + xStart;
794 rcSegment.right = xEol + xStart + virtualSpace;
795 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
796 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
797 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
798 for (size_t r = 0; r<model.sel.Count(); r++) {
799 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
800 if (alpha == SC_ALPHA_NOALPHA) {
801 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
802 if (!portion.Empty()) {
803 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
804 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
805 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
806 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
807 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
808 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
809 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
810 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
817 int eolInSelection = 0;
818 int alpha = SC_ALPHA_NOALPHA;
819 if (!hideSelection) {
820 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
821 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
822 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
825 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
826 XYPOSITION blobsWidth = 0;
827 if (lastSubLine) {
828 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
829 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
830 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
831 blobsWidth += rcSegment.Width();
832 char hexits[4];
833 const char *ctrlChar;
834 unsigned char chEOL = ll->chars[eolPos];
835 int styleMain = ll->styles[eolPos];
836 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
837 if (UTF8IsAscii(chEOL)) {
838 ctrlChar = ControlCharacterString(chEOL);
839 } else {
840 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
841 if (repr) {
842 ctrlChar = repr->stringRep.c_str();
843 eolPos = ll->numCharsInLine;
844 } else {
845 sprintf(hexits, "x%2X", chEOL);
846 ctrlChar = hexits;
849 ColourDesired textFore = vsDraw.styles[styleMain].fore;
850 if (eolInSelection && vsDraw.selColours.fore.isSet) {
851 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
853 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
854 if (alpha == SC_ALPHA_NOALPHA) {
855 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
856 } else {
857 surface->FillRectangle(rcSegment, textBack);
859 } else {
860 surface->FillRectangle(rcSegment, textBack);
862 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
863 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
864 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
869 // Draw the eol-is-selected rectangle
870 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
871 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
873 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
874 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
875 } else {
876 if (background.isSet) {
877 surface->FillRectangle(rcSegment, background);
878 } else if (line < model.pdoc->LinesTotal() - 1) {
879 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
880 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
881 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
882 } else {
883 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
885 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
886 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
890 // Fill the remainder of the line
891 rcSegment.left = rcSegment.right;
892 if (rcSegment.left < rcLine.left)
893 rcSegment.left = rcLine.left;
894 rcSegment.right = rcLine.right;
896 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
897 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
898 } else {
899 if (background.isSet) {
900 surface->FillRectangle(rcSegment, background);
901 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
902 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
903 } else {
904 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
906 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
907 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
911 bool drawWrapMarkEnd = false;
913 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
914 if (subLine + 1 < ll->lines) {
915 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
919 if (drawWrapMarkEnd) {
920 PRectangle rcPlace = rcSegment;
922 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
923 rcPlace.left = xEol + xStart + virtualSpace;
924 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
925 } else {
926 // rcLine is clipped to text area
927 rcPlace.right = rcLine.right;
928 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
930 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
934 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
935 const LineLayout *ll, int xStart, PRectangle rcLine, int subLine) {
936 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
937 PRectangle rcIndic(
938 ll->positions[startPos] + xStart - subLineStart,
939 rcLine.top + vsDraw.maxAscent,
940 ll->positions[endPos] + xStart - subLineStart,
941 rcLine.top + vsDraw.maxAscent + 3);
942 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
945 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
946 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under) {
947 // Draw decorators
948 const int posLineStart = model.pdoc->LineStart(line);
949 const int lineStart = ll->LineStart(subLine);
950 const int posLineEnd = posLineStart + lineEnd;
952 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
953 if (under == vsDraw.indicators[deco->indicator].under) {
954 int startPos = posLineStart + lineStart;
955 if (!deco->rs.ValueAt(startPos)) {
956 startPos = deco->rs.EndRun(startPos);
958 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
959 int endPos = deco->rs.EndRun(startPos);
960 if (endPos > posLineEnd)
961 endPos = posLineEnd;
962 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
963 surface, vsDraw, ll, xStart, rcLine, subLine);
964 startPos = endPos;
965 if (!deco->rs.ValueAt(startPos)) {
966 startPos = deco->rs.EndRun(startPos);
972 // Use indicators to highlight matching braces
973 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
974 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
975 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
976 if (under == vsDraw.indicators[braceIndicator].under) {
977 Range rangeLine(posLineStart + lineStart, posLineEnd);
978 if (rangeLine.ContainsCharacter(model.braces[0])) {
979 int braceOffset = model.braces[0] - posLineStart;
980 if (braceOffset < ll->numCharsInLine) {
981 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
984 if (rangeLine.ContainsCharacter(model.braces[1])) {
985 int braceOffset = model.braces[1] - posLineStart;
986 if (braceOffset < ll->numCharsInLine) {
987 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
994 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
995 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
996 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
997 PRectangle rcSegment = rcLine;
998 int annotationLine = subLine - ll->lines;
999 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1000 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1001 if (phase & drawBack) {
1002 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1004 rcSegment.left = static_cast<XYPOSITION>(xStart);
1005 if (model.trackLineWidth || (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1006 // Only care about calculating width if tracking or need to draw box
1007 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1008 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
1009 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1011 if (widthAnnotation > lineWidthMaxSeen)
1012 lineWidthMaxSeen = widthAnnotation;
1013 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
1014 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1015 rcSegment.right = rcSegment.left + widthAnnotation;
1018 const int annotationLines = model.pdoc->AnnotationLines(line);
1019 size_t start = 0;
1020 size_t lengthAnnotation = stAnnotation.LineLength(start);
1021 int lineInAnnotation = 0;
1022 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1023 start += lengthAnnotation + 1;
1024 lengthAnnotation = stAnnotation.LineLength(start);
1025 lineInAnnotation++;
1027 PRectangle rcText = rcSegment;
1028 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1029 surface->FillRectangle(rcText,
1030 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1031 rcText.left += vsDraw.spaceWidth;
1033 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1034 stAnnotation, start, lengthAnnotation, phase);
1035 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1036 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1037 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1038 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1039 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1040 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1041 if (subLine == ll->lines) {
1042 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1043 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1045 if (subLine == ll->lines + annotationLines - 1) {
1046 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1047 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1053 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1054 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1056 int lineStart = ll->LineStart(subLine);
1057 int posBefore = posCaret;
1058 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1059 int numCharsToDraw = posAfter - posCaret;
1061 // Work out where the starting and ending offsets are. We need to
1062 // see if the previous character shares horizontal space, such as a
1063 // glyph / combining character. If so we'll need to draw that too.
1064 int offsetFirstChar = offset;
1065 int offsetLastChar = offset + (posAfter - posCaret);
1066 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1067 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1068 // The char does not share horizontal space
1069 break;
1071 // Char shares horizontal space, update the numChars to draw
1072 // Update posBefore to point to the prev char
1073 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1074 numCharsToDraw = posAfter - posBefore;
1075 offsetFirstChar = offset - (posCaret - posBefore);
1078 // See if the next character shares horizontal space, if so we'll
1079 // need to draw that too.
1080 if (offsetFirstChar < 0)
1081 offsetFirstChar = 0;
1082 numCharsToDraw = offsetLastChar - offsetFirstChar;
1083 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1084 // Update posAfter to point to the 2nd next char, this is where
1085 // the next character ends, and 2nd next begins. We'll need
1086 // to compare these two
1087 posBefore = posAfter;
1088 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1089 offsetLastChar = offset + (posAfter - posCaret);
1090 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1091 // The char does not share horizontal space
1092 break;
1094 // Char shares horizontal space, update the numChars to draw
1095 numCharsToDraw = offsetLastChar - offsetFirstChar;
1098 // We now know what to draw, update the caret drawing rectangle
1099 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1100 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1102 // Adjust caret position to take into account any word wrapping symbols.
1103 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1104 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1105 rcCaret.left += wordWrapCharWidth;
1106 rcCaret.right += wordWrapCharWidth;
1109 // This character is where the caret block is, we override the colours
1110 // (inversed) for drawing the caret here.
1111 int styleMain = ll->styles[offsetFirstChar];
1112 FontAlias fontText = vsDraw.styles[styleMain].font;
1113 surface->DrawTextClipped(rcCaret, fontText,
1114 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1115 numCharsToDraw, vsDraw.styles[styleMain].back,
1116 caretColour);
1119 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1120 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1121 // When drag is active it is the only caret drawn
1122 bool drawDrag = model.posDrag.IsValid();
1123 if (hideSelection && !drawDrag)
1124 return;
1125 const int posLineStart = model.pdoc->LineStart(lineDoc);
1126 // For each selection draw
1127 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1128 const bool mainCaret = r == model.sel.Main();
1129 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1130 const int offset = posCaret.Position() - posLineStart;
1131 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1132 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1133 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1134 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1135 if (ll->wrapIndent != 0) {
1136 int lineStart = ll->LineStart(subLine);
1137 if (lineStart != 0) // Wrapped
1138 xposCaret += ll->wrapIndent;
1140 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1141 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1142 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1143 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1144 bool caretAtEOF = false;
1145 bool caretAtEOL = false;
1146 bool drawBlockCaret = false;
1147 XYPOSITION widthOverstrikeCaret;
1148 XYPOSITION caretWidthOffset = 0;
1149 PRectangle rcCaret = rcLine;
1151 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1152 caretAtEOF = true;
1153 widthOverstrikeCaret = vsDraw.aveCharWidth;
1154 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1155 caretAtEOL = true;
1156 widthOverstrikeCaret = vsDraw.aveCharWidth;
1157 } else {
1158 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1159 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1161 if (widthOverstrikeCaret < 3) // Make sure its visible
1162 widthOverstrikeCaret = 3;
1164 if (xposCaret > 0)
1165 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1166 xposCaret += xStart;
1167 if (model.posDrag.IsValid()) {
1168 /* Dragging text, use a line caret */
1169 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1170 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1171 } else if (model.inOverstrike && drawOverstrikeCaret) {
1172 /* Overstrike (insert mode), use a modified bar caret */
1173 rcCaret.top = rcCaret.bottom - 2;
1174 rcCaret.left = xposCaret + 1;
1175 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1176 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1177 /* Block caret */
1178 rcCaret.left = xposCaret;
1179 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1180 drawBlockCaret = true;
1181 rcCaret.right = xposCaret + widthOverstrikeCaret;
1182 } else {
1183 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1185 } else {
1186 /* Line caret */
1187 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1188 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1190 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1191 if (drawBlockCaret) {
1192 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1193 } else {
1194 surface->FillRectangle(rcCaret, caretColour);
1198 if (drawDrag)
1199 break;
1203 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1204 int xStart, PRectangle rcLine, ColourOptional background) {
1205 // default bgnd here..
1206 surface->FillRectangle(rcLine, background.isSet ? background :
1207 vsDraw.styles[STYLE_DEFAULT].back);
1209 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1211 // draw continuation rect
1212 PRectangle rcPlace = rcLine;
1214 rcPlace.left = static_cast<XYPOSITION>(xStart);
1215 rcPlace.right = rcPlace.left + ll->wrapIndent;
1217 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1218 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1219 else
1220 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1222 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1226 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1227 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1228 int subLine, ColourOptional background) const {
1230 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1231 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1232 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1233 // Does not take margin into account but not significant
1234 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1236 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs);
1238 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1240 // Background drawing loop
1241 while (bfBack.More()) {
1243 const TextSegment ts = bfBack.Next();
1244 const int i = ts.end() - 1;
1245 const int iDoc = i + posLineStart;
1247 PRectangle rcSegment = rcLine;
1248 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1249 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1250 // Only try to draw if really visible - enhances performance by not calling environment to
1251 // draw strings that are completely past the right side of the window.
1252 if (rcSegment.Intersects(rcLine)) {
1253 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1254 if (rcSegment.left < rcLine.left)
1255 rcSegment.left = rcLine.left;
1256 if (rcSegment.right > rcLine.right)
1257 rcSegment.right = rcLine.right;
1259 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1260 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1261 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1262 inHotspot, ll->styles[i], i);
1263 if (ts.representation) {
1264 if (ll->chars[i] == '\t') {
1265 // Tab display
1266 if (drawWhitespaceBackground &&
1267 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1268 textBack = vsDraw.whitespaceColours.back;
1269 } else {
1270 // Blob display
1271 inIndentation = false;
1273 surface->FillRectangle(rcSegment, textBack);
1274 } else {
1275 // Normal text display
1276 surface->FillRectangle(rcSegment, textBack);
1277 if (vsDraw.viewWhitespace != wsInvisible ||
1278 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
1279 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1280 if (ll->chars[cpos + ts.start] == ' ') {
1281 if (drawWhitespaceBackground &&
1282 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1283 PRectangle rcSpace(
1284 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1285 rcSegment.top,
1286 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1287 rcSegment.bottom);
1288 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1290 } else {
1291 inIndentation = false;
1296 } else if (rcSegment.left > rcLine.right) {
1297 break;
1302 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1303 Range lineRange, int xStart) {
1304 if (vsDraw.edgeState == EDGE_LINE) {
1305 PRectangle rcSegment = rcLine;
1306 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1307 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1308 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1309 rcSegment.left -= ll->wrapIndent;
1310 rcSegment.right = rcSegment.left + 1;
1311 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
1315 // Draw underline mark as part of background if not transparent
1316 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1317 int line, PRectangle rcLine) {
1318 int marks = model.pdoc->GetMark(line);
1319 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1320 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1321 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1322 PRectangle rcUnderline = rcLine;
1323 rcUnderline.top = rcUnderline.bottom - 2;
1324 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1326 marks >>= 1;
1329 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1330 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1331 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1332 const int posLineStart = model.pdoc->LineStart(line);
1333 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1334 // For each selection draw
1335 int virtualSpaces = 0;
1336 if (subLine == (ll->lines - 1)) {
1337 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1339 SelectionPosition posStart(posLineStart + lineRange.start);
1340 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1341 SelectionSegment virtualSpaceRange(posStart, posEnd);
1342 for (size_t r = 0; r < model.sel.Count(); r++) {
1343 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1344 if (alpha != SC_ALPHA_NOALPHA) {
1345 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1346 if (!portion.Empty()) {
1347 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1348 PRectangle rcSegment = rcLine;
1349 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1350 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1351 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1352 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1353 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1354 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1355 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1357 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1358 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1359 if (rcSegment.right > rcLine.left)
1360 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1367 // Draw any translucent whole line states
1368 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1369 int line, PRectangle rcLine) {
1370 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1371 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1373 int marks = model.pdoc->GetMark(line);
1374 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1375 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
1376 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1377 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
1378 PRectangle rcUnderline = rcLine;
1379 rcUnderline.top = rcUnderline.bottom - 2;
1380 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1382 marks >>= 1;
1384 if (vsDraw.maskInLine) {
1385 int marksMasked = model.pdoc->GetMark(line) & vsDraw.maskInLine;
1386 if (marksMasked) {
1387 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
1388 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
1389 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1391 marksMasked >>= 1;
1397 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1398 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1399 int subLine, ColourOptional background) {
1401 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1402 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1403 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1405 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1406 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1408 // Does not take margin into account but not significant
1409 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1411 // Foreground drawing loop
1412 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1413 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs);
1415 while (bfFore.More()) {
1417 const TextSegment ts = bfFore.Next();
1418 const int i = ts.end() - 1;
1419 const int iDoc = i + posLineStart;
1421 PRectangle rcSegment = rcLine;
1422 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1423 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1424 // Only try to draw if really visible - enhances performance by not calling environment to
1425 // draw strings that are completely past the right side of the window.
1426 if (rcSegment.Intersects(rcLine)) {
1427 int styleMain = ll->styles[i];
1428 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1429 FontAlias textFont = vsDraw.styles[styleMain].font;
1430 //hotspot foreground
1431 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1432 if (inHotspot) {
1433 if (vsDraw.hotspotColours.fore.isSet)
1434 textFore = vsDraw.hotspotColours.fore;
1436 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1437 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1438 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1440 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1441 if (ts.representation) {
1442 if (ll->chars[i] == '\t') {
1443 // Tab display
1444 if (phasesDraw == phasesOne) {
1445 if (drawWhitespaceBackground &&
1446 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1447 textBack = vsDraw.whitespaceColours.back;
1448 surface->FillRectangle(rcSegment, textBack);
1450 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1451 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1452 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1453 indentCount++) {
1454 if (indentCount > 0) {
1455 int xIndent = static_cast<int>(indentCount * indentWidth);
1456 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1457 (ll->xHighlightGuide == xIndent));
1461 if (vsDraw.viewWhitespace != wsInvisible) {
1462 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1463 if (vsDraw.whitespaceColours.fore.isSet)
1464 textFore = vsDraw.whitespaceColours.fore;
1465 surface->PenColour(textFore);
1466 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
1467 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1468 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1471 } else {
1472 inIndentation = false;
1473 if (vsDraw.controlCharSymbol >= 32) {
1474 // Using one font for all control characters so it can be controlled independently to ensure
1475 // the box goes around the characters tightly. Seems to be no way to work out what height
1476 // is taken by an individual character - internal leading gives varying results.
1477 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1478 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1479 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1480 rcSegment.top + vsDraw.maxAscent,
1481 cc, 1, textBack, textFore);
1482 } else {
1483 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1484 textBack, textFore, phasesDraw == phasesOne);
1487 } else {
1488 // Normal text display
1489 if (vsDraw.styles[styleMain].visible) {
1490 if (phasesDraw != phasesOne) {
1491 surface->DrawTextTransparent(rcSegment, textFont,
1492 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1493 i - ts.start + 1, textFore);
1494 } else {
1495 surface->DrawTextNoClip(rcSegment, textFont,
1496 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1497 i - ts.start + 1, textFore, textBack);
1500 if (vsDraw.viewWhitespace != wsInvisible ||
1501 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1502 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1503 if (ll->chars[cpos + ts.start] == ' ') {
1504 if (vsDraw.viewWhitespace != wsInvisible) {
1505 if (vsDraw.whitespaceColours.fore.isSet)
1506 textFore = vsDraw.whitespaceColours.fore;
1507 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1508 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1509 if ((phasesDraw == phasesOne) && drawWhitespaceBackground &&
1510 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1511 textBack = vsDraw.whitespaceColours.back;
1512 PRectangle rcSpace(
1513 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1514 rcSegment.top,
1515 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1516 rcSegment.bottom);
1517 surface->FillRectangle(rcSpace, textBack);
1519 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
1520 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1521 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1522 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1523 surface->FillRectangle(rcDot, textFore);
1526 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1527 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1528 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1529 indentCount++) {
1530 if (indentCount > 0) {
1531 int xIndent = static_cast<int>(indentCount * indentWidth);
1532 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1533 (ll->xHighlightGuide == xIndent));
1537 } else {
1538 inIndentation = false;
1543 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1544 PRectangle rcUL = rcSegment;
1545 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1546 rcUL.bottom = rcUL.top + 1;
1547 if (vsDraw.hotspotColours.fore.isSet)
1548 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1549 else
1550 surface->FillRectangle(rcUL, textFore);
1551 } else if (vsDraw.styles[styleMain].underline) {
1552 PRectangle rcUL = rcSegment;
1553 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1554 rcUL.bottom = rcUL.top + 1;
1555 surface->FillRectangle(rcUL, textFore);
1557 } else if (rcSegment.left > rcLine.right) {
1558 break;
1563 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1564 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1565 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1566 && (subLine == 0)) {
1567 const int posLineStart = model.pdoc->LineStart(line);
1568 int indentSpace = model.pdoc->GetLineIndentation(line);
1569 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1571 // Find the most recent line with some text
1573 int lineLastWithText = line;
1574 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1575 lineLastWithText--;
1577 if (lineLastWithText < line) {
1578 xStartText = 100000; // Don't limit to visible indentation on empty line
1579 // This line is empty, so use indentation of last line with text
1580 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1581 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1582 if (isFoldHeader) {
1583 // Level is one more level than parent
1584 indentLastWithText += model.pdoc->IndentSize();
1586 if (vsDraw.viewIndentationGuides == ivLookForward) {
1587 // In viLookForward mode, previous line only used if it is a fold header
1588 if (isFoldHeader) {
1589 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1591 } else { // viLookBoth
1592 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1596 int lineNextWithText = line;
1597 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1598 lineNextWithText++;
1600 if (lineNextWithText > line) {
1601 xStartText = 100000; // Don't limit to visible indentation on empty line
1602 // This line is empty, so use indentation of first next line with text
1603 indentSpace = Platform::Maximum(indentSpace,
1604 model.pdoc->GetLineIndentation(lineNextWithText));
1607 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1608 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1609 if (xIndent < xStartText) {
1610 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1611 (ll->xHighlightGuide == xIndent));
1617 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1618 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1620 if (subLine >= ll->lines) {
1621 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1622 return; // No further drawing
1625 // See if something overrides the line background color.
1626 const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1628 const int posLineStart = model.pdoc->LineStart(line);
1630 const Range lineRange = ll->SubLineRange(subLine);
1631 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1633 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1634 if (phase & drawBack) {
1635 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background);
1637 xStart += static_cast<int>(ll->wrapIndent);
1640 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1641 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1642 subLine, background);
1643 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1644 xStart, subLine, subLineStart, background);
1647 if (phase & drawIndicatorsBack) {
1648 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true);
1649 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1650 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1653 if (phase & drawText) {
1654 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1655 subLine, background);
1658 if (phase & drawIndentationGuides) {
1659 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1662 if (phase & drawIndicatorsFore) {
1663 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false);
1666 // End of the drawing of the current line
1667 if (phasesDraw == phasesOne) {
1668 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1669 xStart, subLine, subLineStart, background);
1672 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1673 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1676 if (phase & drawLineTranslucent) {
1677 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1681 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1682 bool expanded = model.cs.GetExpanded(line);
1683 const int level = model.pdoc->GetLevel(line);
1684 const int levelNext = model.pdoc->GetLevel(line + 1);
1685 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1686 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
1687 // Paint the line above the fold
1688 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1690 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1691 PRectangle rcFoldLine = rcLine;
1692 rcFoldLine.bottom = rcFoldLine.top + 1;
1693 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1695 // Paint the line below the fold
1696 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1698 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1699 PRectangle rcFoldLine = rcLine;
1700 rcFoldLine.top = rcFoldLine.bottom - 1;
1701 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1706 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1707 PRectangle rcClient, const ViewStyle &vsDraw) {
1708 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1709 // serifs and italic stems for aliased text.
1710 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1712 // Do the painting
1713 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1715 Surface *surface = surfaceWindow;
1716 if (bufferedDraw) {
1717 surface = pixmapLine;
1718 PLATFORM_ASSERT(pixmapLine->Initialised());
1720 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1721 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1723 const Point ptOrigin = model.GetVisibleOriginInMain();
1725 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1726 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1728 SelectionPosition posCaret = model.sel.RangeMain().caret;
1729 if (model.posDrag.IsValid())
1730 posCaret = model.posDrag;
1731 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1733 PRectangle rcTextArea = rcClient;
1734 if (vsDraw.marginInside) {
1735 rcTextArea.left += vsDraw.textStart;
1736 rcTextArea.right -= vsDraw.rightMarginWidth;
1737 } else {
1738 rcTextArea = rcArea;
1741 // Remove selection margin from drawing area so text will not be drawn
1742 // on it in unbuffered mode.
1743 if (!bufferedDraw && vsDraw.marginInside) {
1744 PRectangle rcClipText = rcTextArea;
1745 rcClipText.left -= leftTextOverlap;
1746 surfaceWindow->SetClip(rcClipText);
1749 // Loop on visible lines
1750 //double durLayout = 0.0;
1751 //double durPaint = 0.0;
1752 //double durCopy = 0.0;
1753 //ElapsedTime etWhole;
1755 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1756 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1758 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1759 AutoLineLayout ll(llc, 0);
1760 std::vector<DrawPhase> phases;
1761 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1762 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1763 phases.push_back(phase);
1765 } else {
1766 phases.push_back(drawAll);
1768 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1769 int ypos = 0;
1770 if (!bufferedDraw)
1771 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1772 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1773 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1774 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1776 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1777 // Only visible lines should be handled by the code within the loop
1778 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1779 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1780 const int subLine = visibleLine - lineStartSet;
1782 // Copy this line and its styles from the document into local arrays
1783 // and determine the x position at which each character starts.
1784 //ElapsedTime et;
1785 if (lineDoc != lineDocPrevious) {
1786 ll.Set(0);
1787 ll.Set(RetrieveLineLayout(lineDoc, model));
1788 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1789 lineDocPrevious = lineDoc;
1791 //durLayout += et.Duration(true);
1793 if (ll) {
1794 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1795 ll->hotspot = model.GetHotSpotRange();
1797 PRectangle rcLine = rcTextArea;
1798 rcLine.top = static_cast<XYPOSITION>(ypos);
1799 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
1801 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
1803 // Highlight the current braces if any
1804 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
1805 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
1807 if (leftTextOverlap && bufferedDraw) {
1808 PRectangle rcSpacer = rcLine;
1809 rcSpacer.right = rcSpacer.left;
1810 rcSpacer.left -= 1;
1811 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
1814 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
1815 //durPaint += et.Duration(true);
1817 // Restore the previous styles for the brace highlights in case layout is in cache.
1818 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
1820 if (*it & drawFoldLines) {
1821 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
1824 if (*it & drawCarets) {
1825 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
1828 if (bufferedDraw) {
1829 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
1830 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
1831 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
1832 yposScreen + vsDraw.lineHeight);
1833 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
1836 lineWidthMaxSeen = Platform::Maximum(
1837 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
1838 //durCopy += et.Duration(true);
1841 if (!bufferedDraw) {
1842 ypos += vsDraw.lineHeight;
1845 yposScreen += vsDraw.lineHeight;
1846 visibleLine++;
1849 ll.Set(0);
1850 //if (durPaint < 0.00000001)
1851 // durPaint = 0.00000001;
1853 // Right column limit indicator
1854 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
1855 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
1856 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
1857 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
1858 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1859 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
1860 if (vsDraw.edgeState == EDGE_LINE) {
1861 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1862 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
1863 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1864 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour);
1867 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1869 //Platform::DebugPrintf(
1870 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1871 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1875 // Space (3 space characters) between line numbers and text when printing.
1876 #define lineNumberPrintSpace " "
1878 ColourDesired InvertedLight(ColourDesired orig) {
1879 unsigned int r = orig.GetRed();
1880 unsigned int g = orig.GetGreen();
1881 unsigned int b = orig.GetBlue();
1882 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1883 unsigned int il = 0xff - l;
1884 if (l == 0)
1885 return ColourDesired(0xff, 0xff, 0xff);
1886 r = r * il / l;
1887 g = g * il / l;
1888 b = b * il / l;
1889 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1892 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
1893 const EditModel &model, const ViewStyle &vs) {
1894 // Can't use measurements cached for screen
1895 posCache.Clear();
1897 ViewStyle vsPrint(vs);
1898 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
1900 // Modify the view style for printing as do not normally want any of the transient features to be printed
1901 // Printing supports only the line number margin.
1902 int lineNumberIndex = -1;
1903 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1904 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
1905 lineNumberIndex = margin;
1906 } else {
1907 vsPrint.ms[margin].width = 0;
1910 vsPrint.fixedColumnWidth = 0;
1911 vsPrint.zoomLevel = printParameters.magnification;
1912 // Don't show indentation guides
1913 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1914 vsPrint.viewIndentationGuides = ivNone;
1915 // Don't show the selection when printing
1916 vsPrint.selColours.back.isSet = false;
1917 vsPrint.selColours.fore.isSet = false;
1918 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
1919 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
1920 vsPrint.whitespaceColours.back.isSet = false;
1921 vsPrint.whitespaceColours.fore.isSet = false;
1922 vsPrint.showCaretLineBackground = false;
1923 vsPrint.alwaysShowCaretLineBackground = false;
1924 // Don't highlight matching braces using indicators
1925 vsPrint.braceHighlightIndicatorSet = false;
1926 vsPrint.braceBadLightIndicatorSet = false;
1928 // Set colours for printing according to users settings
1929 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
1930 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
1931 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
1932 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
1933 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
1934 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
1935 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1936 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
1937 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1938 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
1939 if (sty <= STYLE_DEFAULT) {
1940 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1944 // White background for the line numbers
1945 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
1947 // Printing uses different margins, so reset screen margins
1948 vsPrint.leftMarginWidth = 0;
1949 vsPrint.rightMarginWidth = 0;
1951 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
1952 // Determining width must happen after fonts have been realised in Refresh
1953 int lineNumberWidth = 0;
1954 if (lineNumberIndex >= 0) {
1955 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
1956 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
1957 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
1958 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
1961 int linePrintStart = model.pdoc->LineFromPosition(pfr->chrg.cpMin);
1962 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
1963 if (linePrintLast < linePrintStart)
1964 linePrintLast = linePrintStart;
1965 int linePrintMax = model.pdoc->LineFromPosition(pfr->chrg.cpMax);
1966 if (linePrintLast > linePrintMax)
1967 linePrintLast = linePrintMax;
1968 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
1969 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
1970 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
1971 int endPosPrint = model.pdoc->Length();
1972 if (linePrintLast < model.pdoc->LinesTotal())
1973 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
1975 // Ensure we are styled to where we are formatting.
1976 model.pdoc->EnsureStyledTo(endPosPrint);
1978 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
1979 int ypos = pfr->rc.top;
1981 int lineDoc = linePrintStart;
1983 int nPrintPos = pfr->chrg.cpMin;
1984 int visibleLine = 0;
1985 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
1986 if (printParameters.wrapState == eWrapNone)
1987 widthPrint = LineLayout::wrapWidthInfinite;
1989 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
1991 // When printing, the hdc and hdcTarget may be the same, so
1992 // changing the state of surfaceMeasure may change the underlying
1993 // state of surface. Therefore, any cached state is discarded before
1994 // using each surface.
1995 surfaceMeasure->FlushCachedState();
1997 // Copy this line and its styles from the document into local arrays
1998 // and determine the x position at which each character starts.
1999 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2000 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2002 ll.containsCaret = false;
2004 PRectangle rcLine = PRectangle::FromInts(
2005 pfr->rc.left,
2006 ypos,
2007 pfr->rc.right - 1,
2008 ypos + vsPrint.lineHeight);
2010 // When document line is wrapped over multiple display lines, find where
2011 // to start printing from to ensure a particular position is on the first
2012 // line of the page.
2013 if (visibleLine == 0) {
2014 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2015 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2016 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2017 visibleLine = -iwl;
2021 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2022 visibleLine = -(ll.lines - 1);
2026 if (draw && lineNumberWidth &&
2027 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2028 (visibleLine >= 0)) {
2029 char number[100];
2030 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2031 PRectangle rcNumber = rcLine;
2032 rcNumber.right = rcNumber.left + lineNumberWidth;
2033 // Right justify
2034 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2035 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2036 surface->FlushCachedState();
2037 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2038 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2039 vsPrint.styles[STYLE_LINENUMBER].fore,
2040 vsPrint.styles[STYLE_LINENUMBER].back);
2043 // Draw the line
2044 surface->FlushCachedState();
2046 for (int iwl = 0; iwl < ll.lines; iwl++) {
2047 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2048 if (visibleLine >= 0) {
2049 if (draw) {
2050 rcLine.top = static_cast<XYPOSITION>(ypos);
2051 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2052 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2054 ypos += vsPrint.lineHeight;
2056 visibleLine++;
2057 if (iwl == ll.lines - 1)
2058 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2059 else
2060 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2064 ++lineDoc;
2067 // Clear cache so measurements are not used for screen
2068 posCache.Clear();
2070 return nPrintPos;