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.
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <math.h>
12 #include <assert.h>
13 #include <ctype.h>
14
15 #include <string>
16 #include <vector>
17 #include <map>
18 #include <algorithm>
19 #include <memory>
20
21 #include "Platform.h"
22
23 #include "ILexer.h"
24 #include "Scintilla.h"
25
26 #include "StringCopy.h"
27 #include "SplitVector.h"
28 #include "Partitioning.h"
29 #include "RunStyles.h"
30 #include "ContractionState.h"
31 #include "CellBuffer.h"
32 #include "PerLine.h"
33 #include "KeyMap.h"
34 #include "Indicator.h"
35 #include "XPM.h"
36 #include "LineMarker.h"
37 #include "Style.h"
38 #include "ViewStyle.h"
39 #include "CharClassify.h"
40 #include "Decoration.h"
41 #include "CaseFolder.h"
42 #include "Document.h"
43 #include "UniConversion.h"
44 #include "Selection.h"
45 #include "PositionCache.h"
46 #include "EditModel.h"
47 #include "MarginView.h"
48 #include "EditView.h"
49
50 #ifdef SCI_NAMESPACE
51 using namespace Scintilla;
52 #endif
53
IsControlCharacter(int ch)54 static inline bool IsControlCharacter(int ch) {
55 // iscntrl returns true for lots of chars > 127 which are displayable
56 return ch >= 0 && ch < ' ';
57 }
58
PrintParameters()59 PrintParameters::PrintParameters() {
60 magnification = 0;
61 colourMode = SC_PRINT_NORMAL;
62 wrapState = eWrapWord;
63 }
64
65 #ifdef SCI_NAMESPACE
66 namespace Scintilla {
67 #endif
68
ValidStyledText(const ViewStyle & vs,size_t styleOffset,const StyledText & st)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;
74 }
75 } else {
76 if (!vs.ValidStyle(styleOffset + st.style))
77 return false;
78 }
79 return true;
80 }
81
WidthStyledText(Surface * surface,const ViewStyle & vs,int styleOffset,const char * text,const unsigned char * styles,size_t len)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;
95 }
96 return width;
97 }
98
WidestLineWidth(Surface * surface,const ViewStyle & vs,int styleOffset,const StyledText & st)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)));
111 }
112 if (widthSubLine > widthMax)
113 widthMax = widthSubLine;
114 start += lenLine + 1;
115 }
116 return widthMax;
117 }
118
DrawTextNoClipPhase(Surface * surface,PRectangle rc,const Style & style,XYPOSITION ybase,const char * s,int len,DrawPhase phase)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);
129 }
130 } else if (phase & drawText) {
131 surface->DrawTextTransparent(rc, fontText, ybase, s, len, style.fore);
132 }
133 }
134
DrawStyledText(Surface * surface,const ViewStyle & vs,int styleOffset,PRectangle rcText,const StyledText & st,size_t start,size_t length,DrawPhase phase)135 void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
136 const StyledText &st, size_t start, size_t length, DrawPhase phase) {
137
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;
158 }
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);
164 }
165 }
166
167 #ifdef SCI_NAMESPACE
168 }
169 #endif
170
171 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
172
EditView()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);
188 }
189
~EditView()190 EditView::~EditView() {
191 delete ldTabstops;
192 ldTabstops = NULL;
193 }
194
SetTwoPhaseDraw(bool twoPhaseDraw)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;
200 }
201
SetPhasesDraw(int phases)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;
207 }
208
LinesOverlap() const209 bool EditView::LinesOverlap() const {
210 return phasesDraw == phasesMultiple;
211 }
212
ClearAllTabstops()213 void EditView::ClearAllTabstops() {
214 delete ldTabstops;
215 ldTabstops = 0;
216 }
217
NextTabstopPos(int line,int x,int tabWidth) const218 int EditView::NextTabstopPos(int line, int x, int tabWidth) const {
219 int next = GetNextTabstop(line, x);
220 if (next > 0)
221 return next;
222 return ((((x + 2) / tabWidth) + 1) * tabWidth);
223 }
224
ClearTabstops(int line)225 bool EditView::ClearTabstops(int line) {
226 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
227 return lt && lt->ClearTabstops(line);
228 }
229
AddTabstop(int line,int x)230 bool EditView::AddTabstop(int line, int x) {
231 if (!ldTabstops) {
232 ldTabstops = new LineTabstops();
233 }
234 LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops);
235 return lt && lt->AddTabstop(line, x);
236 }
237
GetNextTabstop(int line,int x) const238 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;
244 }
245 }
246
LinesAddedOrRemoved(int lineOfPos,int linesAdded)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);
252 }
253 } else {
254 for (int line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {
255 ldTabstops->RemoveLine(line);
256 }
257 }
258 }
259 }
260
DropGraphics(bool freeObjects)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();
276 }
277 }
278
AllocateGraphics(const ViewStyle & vsDraw)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);
286 }
287
ControlCharacterString(unsigned char ch)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"
294 };
295 if (ch < ELEMENTS(reps)) {
296 return reps[ch];
297 } else {
298 return "BAD";
299 }
300 }
301
DrawTabArrow(Surface * surface,PRectangle rcTab,int ymid)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;
308 }
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);
317 }
318
RefreshPixMaps(Surface * surfaceWindow,WindowID wid,const ViewStyle & vsDraw)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);
333 }
334 }
335 }
336
RetrieveLineLayout(int lineNumber,const EditModel & model)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());
345 }
346
347 /**
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.
351 */
LayoutLine(const EditModel & model,int line,Surface * surface,const ViewStyle & vstyle,LineLayout * ll,int width)352 void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
353 if (!ll)
354 return;
355
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;
363 }
364 if (ll->validity == LineLayout::llCheckTextAndStyle) {
365 int lineLength = posLineEnd - posLineStart;
366 if (!vstyle.viewEOL) {
367 lineLength = model.pdoc->LineEnd(line) - posLineStart;
368 }
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++;
391 }
392 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
393 if (allSame) {
394 ll->validity = LineLayout::llPositions;
395 } else {
396 ll->validity = LineLayout::llInvalid;
397 }
398 } else {
399 ll->validity = LineLayout::llInvalid;
400 }
401 }
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;
409 }
410 } else {
411 ll->edgeColumn = -1;
412 }
413
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;
423 }
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));
432 }
433 }
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
438
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;
443
444 BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs);
445 while (bfLayout.More()) {
446
447 const TextSegment ts = bfLayout.Next();
448
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 int x = static_cast<int>(ll->positions[ts.start]);
456 const int tabWidth = static_cast<int>(vstyle.tabWidth);
457 representationWidth = static_cast<XYPOSITION>(NextTabstopPos(line, x, tabWidth) - ll->positions[ts.start]);
458 } else {
459 if (representationWidth <= 0.0) {
460 XYPOSITION positionsRepr[256]; // Should expand when needed
461 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
462 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, model.pdoc);
463 representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding;
464 }
465 }
466 for (int ii = 0; ii < ts.length; ii++)
467 ll->positions[ts.start + 1 + ii] = representationWidth;
468 } else {
469 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
470 // Over half the segments are single characters and of these about half are space characters.
471 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
472 } else {
473 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
474 ts.length, ll->positions + ts.start + 1, model.pdoc);
475 }
476 }
477 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
478 }
479
480 for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) {
481 ll->positions[posToIncrease] += ll->positions[ts.start];
482 }
483 }
484
485 // Small hack to make lines that end with italics not cut off the edge of the last character
486 if (lastSegItalics) {
487 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
488 }
489 ll->numCharsInLine = numCharsInLine;
490 ll->numCharsBeforeEOL = numCharsBeforeEOL;
491 ll->validity = LineLayout::llPositions;
492 }
493 // Hard to cope when too narrow, so just assume there is space
494 if (width < 20) {
495 width = 20;
496 }
497 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
498 ll->widthLine = width;
499 if (width == LineLayout::wrapWidthInfinite) {
500 ll->lines = 1;
501 } else if (width > ll->positions[ll->numCharsInLine]) {
502 // Simple common case where line does not need wrapping.
503 ll->lines = 1;
504 } else {
505 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
506 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
507 }
508 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
509 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
510 wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;
511 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
512 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
513 }
514 ll->wrapIndent = wrapAddIndent;
515 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
516 for (int i = 0; i < ll->numCharsInLine; i++) {
517 if (!IsSpaceOrTab(ll->chars[i])) {
518 ll->wrapIndent += ll->positions[i]; // Add line indent
519 break;
520 }
521 }
522 // Check for text width minimum
523 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
524 ll->wrapIndent = wrapAddIndent;
525 // Check for wrapIndent minimum
526 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
527 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
528 ll->lines = 0;
529 // Calculate line start positions based upon width.
530 int lastGoodBreak = 0;
531 int lastLineStart = 0;
532 XYACCUMULATOR startOffset = 0;
533 int p = 0;
534 while (p < ll->numCharsInLine) {
535 if ((ll->positions[p + 1] - startOffset) >= width) {
536 if (lastGoodBreak == lastLineStart) {
537 // Try moving to start of last character
538 if (p > 0) {
539 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
540 - posLineStart;
541 }
542 if (lastGoodBreak == lastLineStart) {
543 // Ensure at least one character on line.
544 lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
545 - posLineStart;
546 }
547 }
548 lastLineStart = lastGoodBreak;
549 ll->lines++;
550 ll->SetLineStart(ll->lines, lastGoodBreak);
551 startOffset = ll->positions[lastGoodBreak];
552 // take into account the space for start wrap mark and indent
553 startOffset -= ll->wrapIndent;
554 p = lastGoodBreak + 1;
555 continue;
556 }
557 if (p > 0) {
558 if (vstyle.wrapState == eWrapChar) {
559 lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1)
560 - posLineStart;
561 p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
562 continue;
563 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
564 lastGoodBreak = p;
565 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
566 lastGoodBreak = p;
567 }
568 }
569 p++;
570 }
571 ll->lines++;
572 }
573 ll->validity = LineLayout::llLines;
574 }
575 }
576
LocationFromPosition(Surface * surface,const EditModel & model,SelectionPosition pos,int topLine,const ViewStyle & vs)577 Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) {
578 Point pt;
579 if (pos.Position() == INVALID_POSITION)
580 return pt;
581 const int line = model.pdoc->LineFromPosition(pos.Position());
582 const int lineVisible = model.cs.DisplayFromDoc(line);
583 //Platform::DebugPrintf("line=%d\n", line);
584 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
585 if (surface && ll) {
586 const int posLineStart = model.pdoc->LineStart(line);
587 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
588 const int posInLine = pos.Position() - posLineStart;
589 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
590 pt.y += (lineVisible - topLine) * vs.lineHeight;
591 pt.x += vs.textStart - model.xOffset;
592 }
593 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
594 return pt;
595 }
596
SPositionFromLocation(Surface * surface,const EditModel & model,Point pt,bool canReturnInvalid,bool charPosition,bool virtualSpace,const ViewStyle & vs)597 SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) {
598 pt.x = pt.x - vs.textStart;
599 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
600 if (!canReturnInvalid && (visibleLine < 0))
601 visibleLine = 0;
602 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
603 if (canReturnInvalid && (lineDoc < 0))
604 return SelectionPosition(INVALID_POSITION);
605 if (lineDoc >= model.pdoc->LinesTotal())
606 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length());
607 const int posLineStart = model.pdoc->LineStart(lineDoc);
608 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
609 if (surface && ll) {
610 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
611 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
612 const int subLine = visibleLine - lineStartSet;
613 if (subLine < ll->lines) {
614 const Range rangeSubLine = ll->SubLineRange(subLine);
615 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
616 if (subLine > 0) // Wrapped
617 pt.x -= ll->wrapIndent;
618 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
619 if (positionInLine < rangeSubLine.end) {
620 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
621 }
622 if (virtualSpace) {
623 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
624 const int spaceOffset = static_cast<int>(
625 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
626 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
627 } else if (canReturnInvalid) {
628 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
629 return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
630 }
631 } else {
632 return SelectionPosition(rangeSubLine.end + posLineStart);
633 }
634 }
635 if (!canReturnInvalid)
636 return SelectionPosition(ll->numCharsInLine + posLineStart);
637 }
638 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
639 }
640
641 /**
642 * Find the document position corresponding to an x coordinate on a particular document line.
643 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
644 * This method is used for rectangular selections and does not work on wrapped lines.
645 */
SPositionFromLineX(Surface * surface,const EditModel & model,int lineDoc,int x,const ViewStyle & vs)646 SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) {
647 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
648 if (surface && ll) {
649 const int posLineStart = model.pdoc->LineStart(lineDoc);
650 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
651 const Range rangeSubLine = ll->SubLineRange(0);
652 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
653 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
654 if (positionInLine < rangeSubLine.end) {
655 return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
656 }
657 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
658 const int spaceOffset = static_cast<int>(
659 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
660 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
661 }
662 return SelectionPosition(0);
663 }
664
DisplayFromPosition(Surface * surface,const EditModel & model,int pos,const ViewStyle & vs)665 int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) {
666 int lineDoc = model.pdoc->LineFromPosition(pos);
667 int lineDisplay = model.cs.DisplayFromDoc(lineDoc);
668 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
669 if (surface && ll) {
670 LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);
671 unsigned int posLineStart = model.pdoc->LineStart(lineDoc);
672 int posInLine = pos - posLineStart;
673 lineDisplay--; // To make up for first increment ahead.
674 for (int subLine = 0; subLine < ll->lines; subLine++) {
675 if (posInLine >= ll->LineStart(subLine)) {
676 lineDisplay++;
677 }
678 }
679 }
680 return lineDisplay;
681 }
682
StartEndDisplayLine(Surface * surface,const EditModel & model,int pos,bool start,const ViewStyle & vs)683 int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) {
684 int line = model.pdoc->LineFromPosition(pos);
685 AutoLineLayout ll(llc, RetrieveLineLayout(line, model));
686 int posRet = INVALID_POSITION;
687 if (surface && ll) {
688 unsigned int posLineStart = model.pdoc->LineStart(line);
689 LayoutLine(model, line, surface, vs, ll, model.wrapWidth);
690 int posInLine = pos - posLineStart;
691 if (posInLine <= ll->maxLineLength) {
692 for (int subLine = 0; subLine < ll->lines; subLine++) {
693 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
694 if (start) {
695 posRet = ll->LineStart(subLine) + posLineStart;
696 } else {
697 if (subLine == ll->lines - 1)
698 posRet = ll->LineStart(subLine + 1) + posLineStart;
699 else
700 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
701 }
702 }
703 }
704 }
705 }
706 return posRet;
707 }
708
SelectionBackground(const ViewStyle & vsDraw,bool main,bool primarySelection)709 static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) {
710 return main ?
711 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
712 vsDraw.selAdditionalBackground;
713 }
714
TextBackground(const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,ColourOptional background,int inSelection,bool inHotspot,int styleMain,int i)715 static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
716 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) {
717 if (inSelection == 1) {
718 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
719 return SelectionBackground(vsDraw, true, model.primarySelection);
720 }
721 } else if (inSelection == 2) {
722 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
723 return SelectionBackground(vsDraw, false, model.primarySelection);
724 }
725 } else {
726 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
727 (i >= ll->edgeColumn) &&
728 (i < ll->numCharsBeforeEOL))
729 return vsDraw.edgecolour;
730 if (inHotspot && vsDraw.hotspotColours.back.isSet)
731 return vsDraw.hotspotColours.back;
732 }
733 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
734 return background;
735 } else {
736 return vsDraw.styles[styleMain].back;
737 }
738 }
739
DrawIndentGuide(Surface * surface,int lineVisible,int lineHeight,int start,PRectangle rcSegment,bool highlight)740 void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
741 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
742 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
743 surface->Copy(rcCopyArea, from,
744 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
745 }
746
SimpleAlphaRectangle(Surface * surface,PRectangle rc,ColourDesired fill,int alpha)747 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
748 if (alpha != SC_ALPHA_NOALPHA) {
749 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
750 }
751 }
752
DrawTextBlob(Surface * surface,const ViewStyle & vsDraw,PRectangle rcSegment,const char * s,ColourDesired textBack,ColourDesired textFore,bool fillBackground)753 static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
754 const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) {
755 if (fillBackground) {
756 surface->FillRectangle(rcSegment, textBack);
757 }
758 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
759 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
760 surface->InternalLeading(ctrlCharsFont));
761 PRectangle rcCChar = rcSegment;
762 rcCChar.left = rcCChar.left + 1;
763 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
764 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
765 PRectangle rcCentral = rcCChar;
766 rcCentral.top++;
767 rcCentral.bottom--;
768 surface->FillRectangle(rcCentral, textFore);
769 PRectangle rcChar = rcCChar;
770 rcChar.left++;
771 rcChar.right--;
772 surface->DrawTextClipped(rcChar, ctrlCharsFont,
773 rcSegment.top + vsDraw.maxAscent, s, static_cast<int>(s ? strlen(s) : 0),
774 textBack, textFore);
775 }
776
DrawEOL(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,PRectangle rcLine,int line,int lineEnd,int xStart,int subLine,XYACCUMULATOR subLineStart,ColourOptional background)777 void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
778 PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
779 ColourOptional background) {
780
781 const int posLineStart = model.pdoc->LineStart(line);
782 PRectangle rcSegment = rcLine;
783
784 const bool lastSubLine = subLine == (ll->lines - 1);
785 XYPOSITION virtualSpace = 0;
786 if (lastSubLine) {
787 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
788 virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;
789 }
790 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
791
792 // Fill the virtual space and show selections within it
793 if (virtualSpace) {
794 rcSegment.left = xEol + xStart;
795 rcSegment.right = xEol + xStart + virtualSpace;
796 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
797 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
798 SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));
799 for (size_t r = 0; r<model.sel.Count(); r++) {
800 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
801 if (alpha == SC_ALPHA_NOALPHA) {
802 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
803 if (!portion.Empty()) {
804 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
805 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
806 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
807 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
808 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
809 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
810 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
811 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection));
812 }
813 }
814 }
815 }
816 }
817
818 int eolInSelection = 0;
819 int alpha = SC_ALPHA_NOALPHA;
820 if (!hideSelection) {
821 int posAfterLineEnd = model.pdoc->LineStart(line + 1);
822 eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0;
823 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
824 }
825
826 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
827 XYPOSITION blobsWidth = 0;
828 if (lastSubLine) {
829 for (int eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
830 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
831 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart)+virtualSpace;
832 blobsWidth += rcSegment.Width();
833 char hexits[4];
834 const char *ctrlChar;
835 unsigned char chEOL = ll->chars[eolPos];
836 int styleMain = ll->styles[eolPos];
837 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);
838 if (UTF8IsAscii(chEOL)) {
839 ctrlChar = ControlCharacterString(chEOL);
840 } else {
841 const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
842 if (repr) {
843 ctrlChar = repr->stringRep.c_str();
844 eolPos = ll->numCharsInLine;
845 } else {
846 sprintf(hexits, "x%2X", chEOL);
847 ctrlChar = hexits;
848 }
849 }
850 ColourDesired textFore = vsDraw.styles[styleMain].fore;
851 if (eolInSelection && vsDraw.selColours.fore.isSet) {
852 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
853 }
854 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) {
855 if (alpha == SC_ALPHA_NOALPHA) {
856 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
857 } else {
858 surface->FillRectangle(rcSegment, textBack);
859 }
860 } else {
861 surface->FillRectangle(rcSegment, textBack);
862 }
863 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne);
864 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
865 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
866 }
867 }
868 }
869
870 // Draw the eol-is-selected rectangle
871 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
872 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
873
874 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
875 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
876 } else {
877 if (background.isSet) {
878 surface->FillRectangle(rcSegment, background);
879 } else if (line < model.pdoc->LinesTotal() - 1) {
880 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
881 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
882 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
883 } else {
884 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
885 }
886 if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
887 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
888 }
889 }
890
891 // Fill the remainder of the line
892 rcSegment.left = rcSegment.right;
893 if (rcSegment.left < rcLine.left)
894 rcSegment.left = rcLine.left;
895 rcSegment.right = rcLine.right;
896
897 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
898 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection));
899 } else {
900 if (background.isSet) {
901 surface->FillRectangle(rcSegment, background);
902 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
903 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
904 } else {
905 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
906 }
907 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
908 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha);
909 }
910 }
911
912 bool drawWrapMarkEnd = false;
913
914 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
915 if (subLine + 1 < ll->lines) {
916 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
917 }
918 }
919
920 if (drawWrapMarkEnd) {
921 PRectangle rcPlace = rcSegment;
922
923 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
924 rcPlace.left = xEol + xStart + virtualSpace;
925 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
926 } else {
927 // rcLine is clipped to text area
928 rcPlace.right = rcLine.right;
929 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
930 }
931 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
932 }
933 }
934
DrawIndicator(int indicNum,int startPos,int endPos,Surface * surface,const ViewStyle & vsDraw,const LineLayout * ll,int xStart,PRectangle rcLine,int subLine)935 static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
936 const LineLayout *ll, int xStart, PRectangle rcLine, int subLine) {
937 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
938 PRectangle rcIndic(
939 ll->positions[startPos] + xStart - subLineStart,
940 rcLine.top + vsDraw.maxAscent,
941 ll->positions[endPos] + xStart - subLineStart,
942 rcLine.top + vsDraw.maxAscent + 3);
943 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
944 }
945
DrawIndicators(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int line,int xStart,PRectangle rcLine,int subLine,int lineEnd,bool under)946 static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
947 int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under) {
948 // Draw decorators
949 const int posLineStart = model.pdoc->LineStart(line);
950 const int lineStart = ll->LineStart(subLine);
951 const int posLineEnd = posLineStart + lineEnd;
952
953 for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) {
954 if (under == vsDraw.indicators[deco->indicator].under) {
955 int startPos = posLineStart + lineStart;
956 if (!deco->rs.ValueAt(startPos)) {
957 startPos = deco->rs.EndRun(startPos);
958 }
959 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
960 int endPos = deco->rs.EndRun(startPos);
961 if (endPos > posLineEnd)
962 endPos = posLineEnd;
963 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
964 surface, vsDraw, ll, xStart, rcLine, subLine);
965 startPos = endPos;
966 if (!deco->rs.ValueAt(startPos)) {
967 startPos = deco->rs.EndRun(startPos);
968 }
969 }
970 }
971 }
972
973 // Use indicators to highlight matching braces
974 if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
975 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) {
976 int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
977 if (under == vsDraw.indicators[braceIndicator].under) {
978 Range rangeLine(posLineStart + lineStart, posLineEnd);
979 if (rangeLine.ContainsCharacter(model.braces[0])) {
980 int braceOffset = model.braces[0] - posLineStart;
981 if (braceOffset < ll->numCharsInLine) {
982 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
983 }
984 }
985 if (rangeLine.ContainsCharacter(model.braces[1])) {
986 int braceOffset = model.braces[1] - posLineStart;
987 if (braceOffset < ll->numCharsInLine) {
988 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine);
989 }
990 }
991 }
992 }
993 }
994
DrawAnnotation(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int line,int xStart,PRectangle rcLine,int subLine,DrawPhase phase)995 void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
996 int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
997 int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
998 PRectangle rcSegment = rcLine;
999 int annotationLine = subLine - ll->lines;
1000 const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);
1001 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
1002 if (phase & drawBack) {
1003 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
1004 }
1005 rcSegment.left = static_cast<XYPOSITION>(xStart);
1006 if (model.trackLineWidth || (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1007 // Only care about calculating width if tracking or need to draw box
1008 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
1009 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
1010 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
1011 }
1012 if (widthAnnotation > lineWidthMaxSeen)
1013 lineWidthMaxSeen = widthAnnotation;
1014 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
1015 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
1016 rcSegment.right = rcSegment.left + widthAnnotation;
1017 }
1018 }
1019 const int annotationLines = model.pdoc->AnnotationLines(line);
1020 size_t start = 0;
1021 size_t lengthAnnotation = stAnnotation.LineLength(start);
1022 int lineInAnnotation = 0;
1023 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
1024 start += lengthAnnotation + 1;
1025 lengthAnnotation = stAnnotation.LineLength(start);
1026 lineInAnnotation++;
1027 }
1028 PRectangle rcText = rcSegment;
1029 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1030 surface->FillRectangle(rcText,
1031 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
1032 rcText.left += vsDraw.spaceWidth;
1033 }
1034 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
1035 stAnnotation, start, lengthAnnotation, phase);
1036 if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
1037 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
1038 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1039 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
1040 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1041 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
1042 if (subLine == ll->lines) {
1043 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
1044 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
1045 }
1046 if (subLine == ll->lines + annotationLines - 1) {
1047 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
1048 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
1049 }
1050 }
1051 }
1052 }
1053
DrawBlockCaret(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int subLine,int xStart,int offset,int posCaret,PRectangle rcCaret,ColourDesired caretColour)1054 static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1055 int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
1056
1057 int lineStart = ll->LineStart(subLine);
1058 int posBefore = posCaret;
1059 int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);
1060 int numCharsToDraw = posAfter - posCaret;
1061
1062 // Work out where the starting and ending offsets are. We need to
1063 // see if the previous character shares horizontal space, such as a
1064 // glyph / combining character. If so we'll need to draw that too.
1065 int offsetFirstChar = offset;
1066 int offsetLastChar = offset + (posAfter - posCaret);
1067 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
1068 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
1069 // The char does not share horizontal space
1070 break;
1071 }
1072 // Char shares horizontal space, update the numChars to draw
1073 // Update posBefore to point to the prev char
1074 posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);
1075 numCharsToDraw = posAfter - posBefore;
1076 offsetFirstChar = offset - (posCaret - posBefore);
1077 }
1078
1079 // See if the next character shares horizontal space, if so we'll
1080 // need to draw that too.
1081 if (offsetFirstChar < 0)
1082 offsetFirstChar = 0;
1083 numCharsToDraw = offsetLastChar - offsetFirstChar;
1084 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
1085 // Update posAfter to point to the 2nd next char, this is where
1086 // the next character ends, and 2nd next begins. We'll need
1087 // to compare these two
1088 posBefore = posAfter;
1089 posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);
1090 offsetLastChar = offset + (posAfter - posCaret);
1091 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
1092 // The char does not share horizontal space
1093 break;
1094 }
1095 // Char shares horizontal space, update the numChars to draw
1096 numCharsToDraw = offsetLastChar - offsetFirstChar;
1097 }
1098
1099 // We now know what to draw, update the caret drawing rectangle
1100 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
1101 rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;
1102
1103 // Adjust caret position to take into account any word wrapping symbols.
1104 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
1105 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
1106 rcCaret.left += wordWrapCharWidth;
1107 rcCaret.right += wordWrapCharWidth;
1108 }
1109
1110 // This character is where the caret block is, we override the colours
1111 // (inversed) for drawing the caret here.
1112 int styleMain = ll->styles[offsetFirstChar];
1113 FontAlias fontText = vsDraw.styles[styleMain].font;
1114 surface->DrawTextClipped(rcCaret, fontText,
1115 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
1116 numCharsToDraw, vsDraw.styles[styleMain].back,
1117 caretColour);
1118 }
1119
DrawCarets(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int lineDoc,int xStart,PRectangle rcLine,int subLine) const1120 void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1121 int lineDoc, int xStart, PRectangle rcLine, int subLine) const {
1122 // When drag is active it is the only caret drawn
1123 bool drawDrag = model.posDrag.IsValid();
1124 if (hideSelection && !drawDrag)
1125 return;
1126 const int posLineStart = model.pdoc->LineStart(lineDoc);
1127 // For each selection draw
1128 for (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {
1129 const bool mainCaret = r == model.sel.Main();
1130 const SelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);
1131 const int offset = posCaret.Position() - posLineStart;
1132 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1133 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
1134 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
1135 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
1136 if (ll->wrapIndent != 0) {
1137 int lineStart = ll->LineStart(subLine);
1138 if (lineStart != 0) // Wrapped
1139 xposCaret += ll->wrapIndent;
1140 }
1141 bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);
1142 bool caretVisibleState = additionalCaretsVisible || mainCaret;
1143 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
1144 ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
1145 bool caretAtEOF = false;
1146 bool caretAtEOL = false;
1147 bool drawBlockCaret = false;
1148 XYPOSITION widthOverstrikeCaret;
1149 XYPOSITION caretWidthOffset = 0;
1150 PRectangle rcCaret = rcLine;
1151
1152 if (posCaret.Position() == model.pdoc->Length()) { // At end of document
1153 caretAtEOF = true;
1154 widthOverstrikeCaret = vsDraw.aveCharWidth;
1155 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
1156 caretAtEOL = true;
1157 widthOverstrikeCaret = vsDraw.aveCharWidth;
1158 } else {
1159 const int widthChar = model.pdoc->LenChar(posCaret.Position());
1160 widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];
1161 }
1162 if (widthOverstrikeCaret < 3) // Make sure its visible
1163 widthOverstrikeCaret = 3;
1164
1165 if (xposCaret > 0)
1166 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
1167 xposCaret += xStart;
1168 if (model.posDrag.IsValid()) {
1169 /* Dragging text, use a line caret */
1170 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1171 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1172 } else if (model.inOverstrike && drawOverstrikeCaret) {
1173 /* Overstrike (insert mode), use a modified bar caret */
1174 rcCaret.top = rcCaret.bottom - 2;
1175 rcCaret.left = xposCaret + 1;
1176 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
1177 } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) {
1178 /* Block caret */
1179 rcCaret.left = xposCaret;
1180 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
1181 drawBlockCaret = true;
1182 rcCaret.right = xposCaret + widthOverstrikeCaret;
1183 } else {
1184 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
1185 }
1186 } else {
1187 /* Line caret */
1188 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
1189 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
1190 }
1191 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
1192 if (drawBlockCaret) {
1193 DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
1194 } else {
1195 surface->FillRectangle(rcCaret, caretColour);
1196 }
1197 }
1198 }
1199 if (drawDrag)
1200 break;
1201 }
1202 }
1203
DrawWrapIndentAndMarker(Surface * surface,const ViewStyle & vsDraw,const LineLayout * ll,int xStart,PRectangle rcLine,ColourOptional background)1204 static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,
1205 int xStart, PRectangle rcLine, ColourOptional background) {
1206 // default bgnd here..
1207 surface->FillRectangle(rcLine, background.isSet ? background :
1208 vsDraw.styles[STYLE_DEFAULT].back);
1209
1210 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
1211
1212 // draw continuation rect
1213 PRectangle rcPlace = rcLine;
1214
1215 rcPlace.left = static_cast<XYPOSITION>(xStart);
1216 rcPlace.right = rcPlace.left + ll->wrapIndent;
1217
1218 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
1219 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
1220 else
1221 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
1222
1223 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
1224 }
1225 }
1226
DrawBackground(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,PRectangle rcLine,Range lineRange,int posLineStart,int xStart,int subLine,ColourOptional background) const1227 void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1228 PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1229 int subLine, ColourOptional background) const {
1230
1231 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1232 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1233 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1234 // Does not take margin into account but not significant
1235 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1236
1237 BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs);
1238
1239 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1240
1241 // Background drawing loop
1242 while (bfBack.More()) {
1243
1244 const TextSegment ts = bfBack.Next();
1245 const int i = ts.end() - 1;
1246 const int iDoc = i + posLineStart;
1247
1248 PRectangle rcSegment = rcLine;
1249 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1250 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1251 // Only try to draw if really visible - enhances performance by not calling environment to
1252 // draw strings that are completely past the right side of the window.
1253 if (rcSegment.Intersects(rcLine)) {
1254 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1255 if (rcSegment.left < rcLine.left)
1256 rcSegment.left = rcLine.left;
1257 if (rcSegment.right > rcLine.right)
1258 rcSegment.right = rcLine.right;
1259
1260 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1261 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1262 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection,
1263 inHotspot, ll->styles[i], i);
1264 if (ts.representation) {
1265 if (ll->chars[i] == '\t') {
1266 // Tab display
1267 if (drawWhitespaceBackground &&
1268 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1269 textBack = vsDraw.whitespaceColours.back;
1270 } else {
1271 // Blob display
1272 inIndentation = false;
1273 }
1274 surface->FillRectangle(rcSegment, textBack);
1275 } else {
1276 // Normal text display
1277 surface->FillRectangle(rcSegment, textBack);
1278 if (vsDraw.viewWhitespace != wsInvisible ||
1279 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
1280 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1281 if (ll->chars[cpos + ts.start] == ' ') {
1282 if (drawWhitespaceBackground &&
1283 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1284 PRectangle rcSpace(
1285 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1286 rcSegment.top,
1287 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1288 rcSegment.bottom);
1289 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
1290 }
1291 } else {
1292 inIndentation = false;
1293 }
1294 }
1295 }
1296 }
1297 } else if (rcSegment.left > rcLine.right) {
1298 break;
1299 }
1300 }
1301 }
1302
DrawEdgeLine(Surface * surface,const ViewStyle & vsDraw,const LineLayout * ll,PRectangle rcLine,Range lineRange,int xStart)1303 static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
1304 Range lineRange, int xStart) {
1305 if (vsDraw.edgeState == EDGE_LINE) {
1306 PRectangle rcSegment = rcLine;
1307 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1308 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
1309 if ((ll->wrapIndent != 0) && (lineRange.start != 0))
1310 rcSegment.left -= ll->wrapIndent;
1311 rcSegment.right = rcSegment.left + 1;
1312 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
1313 }
1314 }
1315
1316 // Draw underline mark as part of background if not transparent
DrawMarkUnderline(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,int line,PRectangle rcLine)1317 static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,
1318 int line, PRectangle rcLine) {
1319 int marks = model.pdoc->GetMark(line);
1320 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1321 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
1322 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
1323 PRectangle rcUnderline = rcLine;
1324 rcUnderline.top = rcUnderline.bottom - 2;
1325 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
1326 }
1327 marks >>= 1;
1328 }
1329 }
DrawTranslucentSelection(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int line,PRectangle rcLine,int subLine,Range lineRange,int xStart)1330 static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1331 int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) {
1332 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {
1333 const int posLineStart = model.pdoc->LineStart(line);
1334 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1335 // For each selection draw
1336 int virtualSpaces = 0;
1337 if (subLine == (ll->lines - 1)) {
1338 virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));
1339 }
1340 SelectionPosition posStart(posLineStart + lineRange.start);
1341 SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);
1342 SelectionSegment virtualSpaceRange(posStart, posEnd);
1343 for (size_t r = 0; r < model.sel.Count(); r++) {
1344 int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
1345 if (alpha != SC_ALPHA_NOALPHA) {
1346 SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);
1347 if (!portion.Empty()) {
1348 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
1349 PRectangle rcSegment = rcLine;
1350 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
1351 static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth;
1352 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
1353 static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth;
1354 if ((ll->wrapIndent != 0) && (lineRange.start != 0)) {
1355 if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
1356 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
1357 }
1358 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
1359 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
1360 if (rcSegment.right > rcLine.left)
1361 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);
1362 }
1363 }
1364 }
1365 }
1366 }
1367
1368 // Draw any translucent whole line states
DrawTranslucentLineState(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int line,PRectangle rcLine)1369 static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1370 int line, PRectangle rcLine) {
1371 if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
1372 SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
1373 }
1374 int marks = model.pdoc->GetMark(line);
1375 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1376 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
1377 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1378 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
1379 PRectangle rcUnderline = rcLine;
1380 rcUnderline.top = rcUnderline.bottom - 2;
1381 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1382 }
1383 marks >>= 1;
1384 }
1385 if (vsDraw.maskInLine) {
1386 int marksMasked = model.pdoc->GetMark(line) & vsDraw.maskInLine;
1387 if (marksMasked) {
1388 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
1389 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
1390 SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
1391 }
1392 marksMasked >>= 1;
1393 }
1394 }
1395 }
1396 }
1397
DrawForeground(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int lineVisible,PRectangle rcLine,Range lineRange,int posLineStart,int xStart,int subLine,ColourOptional background)1398 void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1399 int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart,
1400 int subLine, ColourOptional background) {
1401
1402 const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();
1403 const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet;
1404 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
1405
1406 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1407 const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;
1408
1409 // Does not take margin into account but not significant
1410 const int xStartVisible = static_cast<int>(subLineStart)-xStart;
1411
1412 // Foreground drawing loop
1413 BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible,
1414 (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs);
1415
1416 while (bfFore.More()) {
1417
1418 const TextSegment ts = bfFore.Next();
1419 const int i = ts.end() - 1;
1420 const int iDoc = i + posLineStart;
1421
1422 PRectangle rcSegment = rcLine;
1423 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
1424 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
1425 // Only try to draw if really visible - enhances performance by not calling environment to
1426 // draw strings that are completely past the right side of the window.
1427 if (rcSegment.Intersects(rcLine)) {
1428 int styleMain = ll->styles[i];
1429 ColourDesired textFore = vsDraw.styles[styleMain].fore;
1430 FontAlias textFont = vsDraw.styles[styleMain].font;
1431 //hotspot foreground
1432 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
1433 if (inHotspot) {
1434 if (vsDraw.hotspotColours.fore.isSet)
1435 textFore = vsDraw.hotspotColours.fore;
1436 }
1437 const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc);
1438 if (inSelection && (vsDraw.selColours.fore.isSet)) {
1439 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
1440 }
1441 ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
1442 if (ts.representation) {
1443 if (ll->chars[i] == '\t') {
1444 // Tab display
1445 if (phasesDraw == phasesOne) {
1446 if (drawWhitespaceBackground &&
1447 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
1448 textBack = vsDraw.whitespaceColours.back;
1449 surface->FillRectangle(rcSegment, textBack);
1450 }
1451 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1452 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
1453 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
1454 indentCount++) {
1455 if (indentCount > 0) {
1456 int xIndent = static_cast<int>(indentCount * indentWidth);
1457 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1458 (ll->xHighlightGuide == xIndent));
1459 }
1460 }
1461 }
1462 if (vsDraw.viewWhitespace != wsInvisible) {
1463 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1464 if (vsDraw.whitespaceColours.fore.isSet)
1465 textFore = vsDraw.whitespaceColours.fore;
1466 surface->PenColour(textFore);
1467 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
1468 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
1469 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
1470 }
1471 }
1472 } else {
1473 inIndentation = false;
1474 if (vsDraw.controlCharSymbol >= 32) {
1475 // Using one font for all control characters so it can be controlled independently to ensure
1476 // the box goes around the characters tightly. Seems to be no way to work out what height
1477 // is taken by an individual character - internal leading gives varying results.
1478 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
1479 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
1480 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
1481 rcSegment.top + vsDraw.maxAscent,
1482 cc, 1, textBack, textFore);
1483 } else {
1484 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(),
1485 textBack, textFore, phasesDraw == phasesOne);
1486 }
1487 }
1488 } else {
1489 // Normal text display
1490 if (vsDraw.styles[styleMain].visible) {
1491 if (phasesDraw != phasesOne) {
1492 surface->DrawTextTransparent(rcSegment, textFont,
1493 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1494 i - ts.start + 1, textFore);
1495 } else {
1496 surface->DrawTextNoClip(rcSegment, textFont,
1497 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
1498 i - ts.start + 1, textFore, textBack);
1499 }
1500 }
1501 if (vsDraw.viewWhitespace != wsInvisible ||
1502 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
1503 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
1504 if (ll->chars[cpos + ts.start] == ' ') {
1505 if (vsDraw.viewWhitespace != wsInvisible) {
1506 if (vsDraw.whitespaceColours.fore.isSet)
1507 textFore = vsDraw.whitespaceColours.fore;
1508 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
1509 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
1510 if ((phasesDraw == phasesOne) && drawWhitespaceBackground &&
1511 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
1512 textBack = vsDraw.whitespaceColours.back;
1513 PRectangle rcSpace(
1514 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
1515 rcSegment.top,
1516 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
1517 rcSegment.bottom);
1518 surface->FillRectangle(rcSpace, textBack);
1519 }
1520 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
1521 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
1522 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
1523 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
1524 surface->FillRectangle(rcDot, textFore);
1525 }
1526 }
1527 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
1528 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
1529 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
1530 indentCount++) {
1531 if (indentCount > 0) {
1532 int xIndent = static_cast<int>(indentCount * indentWidth);
1533 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
1534 (ll->xHighlightGuide == xIndent));
1535 }
1536 }
1537 }
1538 } else {
1539 inIndentation = false;
1540 }
1541 }
1542 }
1543 }
1544 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
1545 PRectangle rcUL = rcSegment;
1546 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1547 rcUL.bottom = rcUL.top + 1;
1548 if (vsDraw.hotspotColours.fore.isSet)
1549 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
1550 else
1551 surface->FillRectangle(rcUL, textFore);
1552 } else if (vsDraw.styles[styleMain].underline) {
1553 PRectangle rcUL = rcSegment;
1554 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
1555 rcUL.bottom = rcUL.top + 1;
1556 surface->FillRectangle(rcUL, textFore);
1557 }
1558 } else if (rcSegment.left > rcLine.right) {
1559 break;
1560 }
1561 }
1562 }
1563
DrawIndentGuidesOverEmpty(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int line,int lineVisible,PRectangle rcLine,int xStart,int subLine)1564 void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1565 int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) {
1566 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
1567 && (subLine == 0)) {
1568 const int posLineStart = model.pdoc->LineStart(line);
1569 int indentSpace = model.pdoc->GetLineIndentation(line);
1570 int xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);
1571
1572 // Find the most recent line with some text
1573
1574 int lineLastWithText = line;
1575 while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) {
1576 lineLastWithText--;
1577 }
1578 if (lineLastWithText < line) {
1579 xStartText = 100000; // Don't limit to visible indentation on empty line
1580 // This line is empty, so use indentation of last line with text
1581 int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);
1582 int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
1583 if (isFoldHeader) {
1584 // Level is one more level than parent
1585 indentLastWithText += model.pdoc->IndentSize();
1586 }
1587 if (vsDraw.viewIndentationGuides == ivLookForward) {
1588 // In viLookForward mode, previous line only used if it is a fold header
1589 if (isFoldHeader) {
1590 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1591 }
1592 } else { // viLookBoth
1593 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
1594 }
1595 }
1596
1597 int lineNextWithText = line;
1598 while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {
1599 lineNextWithText++;
1600 }
1601 if (lineNextWithText > line) {
1602 xStartText = 100000; // Don't limit to visible indentation on empty line
1603 // This line is empty, so use indentation of first next line with text
1604 indentSpace = Platform::Maximum(indentSpace,
1605 model.pdoc->GetLineIndentation(lineNextWithText));
1606 }
1607
1608 for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {
1609 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
1610 if (xIndent < xStartText) {
1611 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine,
1612 (ll->xHighlightGuide == xIndent));
1613 }
1614 }
1615 }
1616 }
1617
DrawLine(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,const LineLayout * ll,int line,int lineVisible,int xStart,PRectangle rcLine,int subLine,DrawPhase phase)1618 void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
1619 int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {
1620
1621 if (subLine >= ll->lines) {
1622 DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);
1623 return; // No further drawing
1624 }
1625
1626 // See if something overrides the line background color.
1627 const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret);
1628
1629 const int posLineStart = model.pdoc->LineStart(line);
1630
1631 const Range lineRange = ll->SubLineRange(subLine);
1632 const XYACCUMULATOR subLineStart = ll->positions[lineRange.start];
1633
1634 if ((ll->wrapIndent != 0) && (subLine > 0)) {
1635 if (phase & drawBack) {
1636 DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background);
1637 }
1638 xStart += static_cast<int>(ll->wrapIndent);
1639 }
1640
1641 if ((phasesDraw != phasesOne) && (phase & drawBack)) {
1642 DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart,
1643 subLine, background);
1644 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1645 xStart, subLine, subLineStart, background);
1646 }
1647
1648 if (phase & drawIndicatorsBack) {
1649 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true);
1650 DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);
1651 DrawMarkUnderline(surface, model, vsDraw, line, rcLine);
1652 }
1653
1654 if (phase & drawText) {
1655 DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart,
1656 subLine, background);
1657 }
1658
1659 if (phase & drawIndentationGuides) {
1660 DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine);
1661 }
1662
1663 if (phase & drawIndicatorsFore) {
1664 DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false);
1665 }
1666
1667 // End of the drawing of the current line
1668 if (phasesDraw == phasesOne) {
1669 DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end,
1670 xStart, subLine, subLineStart, background);
1671 }
1672
1673 if (!hideSelection && (phase & drawSelectionTranslucent)) {
1674 DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart);
1675 }
1676
1677 if (phase & drawLineTranslucent) {
1678 DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine);
1679 }
1680 }
1681
DrawFoldLines(Surface * surface,const EditModel & model,const ViewStyle & vsDraw,int line,PRectangle rcLine)1682 static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) {
1683 bool expanded = model.cs.GetExpanded(line);
1684 const int level = model.pdoc->GetLevel(line);
1685 const int levelNext = model.pdoc->GetLevel(line + 1);
1686 if ((level & SC_FOLDLEVELHEADERFLAG) &&
1687 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
1688 // Paint the line above the fold
1689 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
1690 ||
1691 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
1692 PRectangle rcFoldLine = rcLine;
1693 rcFoldLine.bottom = rcFoldLine.top + 1;
1694 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1695 }
1696 // Paint the line below the fold
1697 if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
1698 ||
1699 (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
1700 PRectangle rcFoldLine = rcLine;
1701 rcFoldLine.top = rcFoldLine.bottom - 1;
1702 surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore);
1703 }
1704 }
1705 }
1706
PaintText(Surface * surfaceWindow,const EditModel & model,PRectangle rcArea,PRectangle rcClient,const ViewStyle & vsDraw)1707 void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea,
1708 PRectangle rcClient, const ViewStyle &vsDraw) {
1709 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1710 // serifs and italic stems for aliased text.
1711 const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;
1712
1713 // Do the painting
1714 if (rcArea.right > vsDraw.textStart - leftTextOverlap) {
1715
1716 Surface *surface = surfaceWindow;
1717 if (bufferedDraw) {
1718 surface = pixmapLine;
1719 PLATFORM_ASSERT(pixmapLine->Initialised());
1720 }
1721 surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);
1722 surface->SetDBCSMode(model.pdoc->dbcsCodePage);
1723
1724 const Point ptOrigin = model.GetVisibleOriginInMain();
1725
1726 const int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;
1727 const int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);
1728
1729 SelectionPosition posCaret = model.sel.RangeMain().caret;
1730 if (model.posDrag.IsValid())
1731 posCaret = model.posDrag;
1732 const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position());
1733
1734 PRectangle rcTextArea = rcClient;
1735 if (vsDraw.marginInside) {
1736 rcTextArea.left += vsDraw.textStart;
1737 rcTextArea.right -= vsDraw.rightMarginWidth;
1738 } else {
1739 rcTextArea = rcArea;
1740 }
1741
1742 // Remove selection margin from drawing area so text will not be drawn
1743 // on it in unbuffered mode.
1744 if (!bufferedDraw && vsDraw.marginInside) {
1745 PRectangle rcClipText = rcTextArea;
1746 rcClipText.left -= leftTextOverlap;
1747 surfaceWindow->SetClip(rcClipText);
1748 }
1749
1750 // Loop on visible lines
1751 //double durLayout = 0.0;
1752 //double durPaint = 0.0;
1753 //double durCopy = 0.0;
1754 //ElapsedTime etWhole;
1755
1756 const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) ||
1757 (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD)));
1758
1759 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
1760 AutoLineLayout ll(llc, 0);
1761 std::vector<DrawPhase> phases;
1762 if ((phasesDraw == phasesMultiple) && !bufferedDraw) {
1763 for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast<DrawPhase>(phase * 2)) {
1764 phases.push_back(phase);
1765 }
1766 } else {
1767 phases.push_back(drawAll);
1768 }
1769 for (std::vector<DrawPhase>::iterator it = phases.begin(); it != phases.end(); ++it) {
1770 int ypos = 0;
1771 if (!bufferedDraw)
1772 ypos += screenLinePaintFirst * vsDraw.lineHeight;
1773 int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
1774 int visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
1775 while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
1776
1777 const int lineDoc = model.cs.DocFromDisplay(visibleLine);
1778 // Only visible lines should be handled by the code within the loop
1779 PLATFORM_ASSERT(model.cs.GetVisible(lineDoc));
1780 const int lineStartSet = model.cs.DisplayFromDoc(lineDoc);
1781 const int subLine = visibleLine - lineStartSet;
1782
1783 // Copy this line and its styles from the document into local arrays
1784 // and determine the x position at which each character starts.
1785 //ElapsedTime et;
1786 if (lineDoc != lineDocPrevious) {
1787 ll.Set(0);
1788 ll.Set(RetrieveLineLayout(lineDoc, model));
1789 LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth);
1790 lineDocPrevious = lineDoc;
1791 }
1792 //durLayout += et.Duration(true);
1793
1794 if (ll) {
1795 ll->containsCaret = !hideSelection && (lineDoc == lineCaret);
1796 ll->hotspot = model.GetHotSpotRange();
1797
1798 PRectangle rcLine = rcTextArea;
1799 rcLine.top = static_cast<XYPOSITION>(ypos);
1800 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);
1801
1802 Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1));
1803
1804 // Highlight the current braces if any
1805 ll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),
1806 static_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);
1807
1808 if (leftTextOverlap && bufferedDraw) {
1809 PRectangle rcSpacer = rcLine;
1810 rcSpacer.right = rcSpacer.left;
1811 rcSpacer.left -= 1;
1812 surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);
1813 }
1814
1815 DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it);
1816 //durPaint += et.Duration(true);
1817
1818 // Restore the previous styles for the brace highlights in case layout is in cache.
1819 ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);
1820
1821 if (*it & drawFoldLines) {
1822 DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine);
1823 }
1824
1825 if (*it & drawCarets) {
1826 DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine);
1827 }
1828
1829 if (bufferedDraw) {
1830 Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);
1831 PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,
1832 static_cast<int>(rcClient.right - vsDraw.rightMarginWidth),
1833 yposScreen + vsDraw.lineHeight);
1834 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
1835 }
1836
1837 lineWidthMaxSeen = Platform::Maximum(
1838 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
1839 //durCopy += et.Duration(true);
1840 }
1841
1842 if (!bufferedDraw) {
1843 ypos += vsDraw.lineHeight;
1844 }
1845
1846 yposScreen += vsDraw.lineHeight;
1847 visibleLine++;
1848 }
1849 }
1850 ll.Set(0);
1851 //if (durPaint < 0.00000001)
1852 // durPaint = 0.00000001;
1853
1854 // Right column limit indicator
1855 PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;
1856 rcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);
1857 rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);
1858 rcBeyondEOF.top = static_cast<XYPOSITION>((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);
1859 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
1860 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back);
1861 if (vsDraw.edgeState == EDGE_LINE) {
1862 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
1863 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
1864 rcBeyondEOF.right = rcBeyondEOF.left + 1;
1865 surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour);
1866 }
1867 }
1868 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1869
1870 //Platform::DebugPrintf(
1871 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1872 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1873 }
1874 }
1875
1876 // Space (3 space characters) between line numbers and text when printing.
1877 #define lineNumberPrintSpace " "
1878
InvertedLight(ColourDesired orig)1879 ColourDesired InvertedLight(ColourDesired orig) {
1880 unsigned int r = orig.GetRed();
1881 unsigned int g = orig.GetGreen();
1882 unsigned int b = orig.GetBlue();
1883 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
1884 unsigned int il = 0xff - l;
1885 if (l == 0)
1886 return ColourDesired(0xff, 0xff, 0xff);
1887 r = r * il / l;
1888 g = g * il / l;
1889 b = b * il / l;
1890 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
1891 }
1892
FormatRange(bool draw,Sci_RangeToFormat * pfr,Surface * surface,Surface * surfaceMeasure,const EditModel & model,const ViewStyle & vs)1893 long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
1894 const EditModel &model, const ViewStyle &vs) {
1895 // Can't use measurements cached for screen
1896 posCache.Clear();
1897
1898 ViewStyle vsPrint(vs);
1899 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
1900
1901 // Modify the view style for printing as do not normally want any of the transient features to be printed
1902 // Printing supports only the line number margin.
1903 int lineNumberIndex = -1;
1904 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1905 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
1906 lineNumberIndex = margin;
1907 } else {
1908 vsPrint.ms[margin].width = 0;
1909 }
1910 }
1911 vsPrint.fixedColumnWidth = 0;
1912 vsPrint.zoomLevel = printParameters.magnification;
1913 // Don't show indentation guides
1914 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1915 vsPrint.viewIndentationGuides = ivNone;
1916 // Don't show the selection when printing
1917 vsPrint.selColours.back.isSet = false;
1918 vsPrint.selColours.fore.isSet = false;
1919 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
1920 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
1921 vsPrint.whitespaceColours.back.isSet = false;
1922 vsPrint.whitespaceColours.fore.isSet = false;
1923 vsPrint.showCaretLineBackground = false;
1924 vsPrint.alwaysShowCaretLineBackground = false;
1925 // Don't highlight matching braces using indicators
1926 vsPrint.braceHighlightIndicatorSet = false;
1927 vsPrint.braceBadLightIndicatorSet = false;
1928
1929 // Set colours for printing according to users settings
1930 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
1931 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
1932 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
1933 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
1934 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
1935 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
1936 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1937 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
1938 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1939 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
1940 if (sty <= STYLE_DEFAULT) {
1941 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
1942 }
1943 }
1944 }
1945 // White background for the line numbers
1946 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
1947
1948 // Printing uses different margins, so reset screen margins
1949 vsPrint.leftMarginWidth = 0;
1950 vsPrint.rightMarginWidth = 0;
1951
1952 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);
1953 // Determining width must happen after fonts have been realised in Refresh
1954 int lineNumberWidth = 0;
1955 if (lineNumberIndex >= 0) {
1956 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
1957 "99999" lineNumberPrintSpace, 5 + static_cast<int>(strlen(lineNumberPrintSpace))));
1958 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
1959 vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth
1960 }
1961
1962 int linePrintStart = model.pdoc->LineFromPosition(pfr->chrg.cpMin);
1963 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
1964 if (linePrintLast < linePrintStart)
1965 linePrintLast = linePrintStart;
1966 int linePrintMax = model.pdoc->LineFromPosition(pfr->chrg.cpMax);
1967 if (linePrintLast > linePrintMax)
1968 linePrintLast = linePrintMax;
1969 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
1970 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
1971 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
1972 int endPosPrint = model.pdoc->Length();
1973 if (linePrintLast < model.pdoc->LinesTotal())
1974 endPosPrint = model.pdoc->LineStart(linePrintLast + 1);
1975
1976 // Ensure we are styled to where we are formatting.
1977 model.pdoc->EnsureStyledTo(endPosPrint);
1978
1979 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
1980 int ypos = pfr->rc.top;
1981
1982 int lineDoc = linePrintStart;
1983
1984 int nPrintPos = pfr->chrg.cpMin;
1985 int visibleLine = 0;
1986 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
1987 if (printParameters.wrapState == eWrapNone)
1988 widthPrint = LineLayout::wrapWidthInfinite;
1989
1990 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
1991
1992 // When printing, the hdc and hdcTarget may be the same, so
1993 // changing the state of surfaceMeasure may change the underlying
1994 // state of surface. Therefore, any cached state is discarded before
1995 // using each surface.
1996 surfaceMeasure->FlushCachedState();
1997
1998 // Copy this line and its styles from the document into local arrays
1999 // and determine the x position at which each character starts.
2000 LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1);
2001 LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
2002
2003 ll.containsCaret = false;
2004
2005 PRectangle rcLine = PRectangle::FromInts(
2006 pfr->rc.left,
2007 ypos,
2008 pfr->rc.right - 1,
2009 ypos + vsPrint.lineHeight);
2010
2011 // When document line is wrapped over multiple display lines, find where
2012 // to start printing from to ensure a particular position is on the first
2013 // line of the page.
2014 if (visibleLine == 0) {
2015 int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc);
2016 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
2017 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
2018 visibleLine = -iwl;
2019 }
2020 }
2021
2022 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
2023 visibleLine = -(ll.lines - 1);
2024 }
2025 }
2026
2027 if (draw && lineNumberWidth &&
2028 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
2029 (visibleLine >= 0)) {
2030 char number[100];
2031 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
2032 PRectangle rcNumber = rcLine;
2033 rcNumber.right = rcNumber.left + lineNumberWidth;
2034 // Right justify
2035 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
2036 vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast<int>(strlen(number)));
2037 surface->FlushCachedState();
2038 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
2039 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, static_cast<int>(strlen(number)),
2040 vsPrint.styles[STYLE_LINENUMBER].fore,
2041 vsPrint.styles[STYLE_LINENUMBER].back);
2042 }
2043
2044 // Draw the line
2045 surface->FlushCachedState();
2046
2047 for (int iwl = 0; iwl < ll.lines; iwl++) {
2048 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
2049 if (visibleLine >= 0) {
2050 if (draw) {
2051 rcLine.top = static_cast<XYPOSITION>(ypos);
2052 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
2053 DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll);
2054 }
2055 ypos += vsPrint.lineHeight;
2056 }
2057 visibleLine++;
2058 if (iwl == ll.lines - 1)
2059 nPrintPos = model.pdoc->LineStart(lineDoc + 1);
2060 else
2061 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
2062 }
2063 }
2064
2065 ++lineDoc;
2066 }
2067
2068 // Clear cache so measurements are not used for screen
2069 posCache.Clear();
2070
2071 return nPrintPos;
2072 }
2073