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