1 // Scintilla source code edit control
3 ** Defines the appearance of the main text area of the editor window.
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.
25 #include "Scintilla.h"
27 #include "StringCopy.h"
29 #include "SplitVector.h"
30 #include "Partitioning.h"
31 #include "RunStyles.h"
32 #include "ContractionState.h"
33 #include "CellBuffer.h"
36 #include "Indicator.h"
38 #include "LineMarker.h"
40 #include "ViewStyle.h"
41 #include "CharClassify.h"
42 #include "Decoration.h"
43 #include "CaseFolder.h"
45 #include "UniConversion.h"
46 #include "Selection.h"
47 #include "PositionCache.h"
48 #include "EditModel.h"
49 #include "MarginView.h"
53 using namespace Scintilla
;
56 static inline bool IsControlCharacter(int ch
) {
57 // iscntrl returns true for lots of chars > 127 which are displayable
58 return ch
>= 0 && ch
< ' ';
61 PrintParameters::PrintParameters() {
63 colourMode
= SC_PRINT_NORMAL
;
64 wrapState
= eWrapWord
;
71 bool ValidStyledText(const ViewStyle
&vs
, size_t styleOffset
, const StyledText
&st
) {
72 if (st
.multipleStyles
) {
73 for (size_t iStyle
= 0; iStyle
<st
.length
; iStyle
++) {
74 if (!vs
.ValidStyle(styleOffset
+ st
.styles
[iStyle
]))
78 if (!vs
.ValidStyle(styleOffset
+ st
.style
))
84 static int WidthStyledText(Surface
*surface
, const ViewStyle
&vs
, int styleOffset
,
85 const char *text
, const unsigned char *styles
, size_t len
) {
89 size_t style
= styles
[start
];
90 size_t endSegment
= start
;
91 while ((endSegment
+ 1 < len
) && (static_cast<size_t>(styles
[endSegment
+ 1]) == style
))
93 FontAlias fontText
= vs
.styles
[style
+ styleOffset
].font
;
94 width
+= static_cast<int>(surface
->WidthText(fontText
, text
+ start
,
95 static_cast<int>(endSegment
- start
+ 1)));
96 start
= endSegment
+ 1;
101 int WidestLineWidth(Surface
*surface
, const ViewStyle
&vs
, int styleOffset
, const StyledText
&st
) {
104 while (start
< st
.length
) {
105 size_t lenLine
= st
.LineLength(start
);
107 if (st
.multipleStyles
) {
108 widthSubLine
= WidthStyledText(surface
, vs
, styleOffset
, st
.text
+ start
, st
.styles
+ start
, lenLine
);
110 FontAlias fontText
= vs
.styles
[styleOffset
+ st
.style
].font
;
111 widthSubLine
= static_cast<int>(surface
->WidthText(fontText
,
112 st
.text
+ start
, static_cast<int>(lenLine
)));
114 if (widthSubLine
> widthMax
)
115 widthMax
= widthSubLine
;
116 start
+= lenLine
+ 1;
121 void DrawTextNoClipPhase(Surface
*surface
, PRectangle rc
, const Style
&style
, XYPOSITION ybase
,
122 const char *s
, int len
, DrawPhase phase
) {
123 FontAlias fontText
= style
.font
;
124 if (phase
& drawBack
) {
125 if (phase
& drawText
) {
127 surface
->DrawTextNoClip(rc
, fontText
, ybase
, s
, len
,
128 style
.fore
, style
.back
);
130 surface
->FillRectangle(rc
, style
.back
);
132 } else if (phase
& drawText
) {
133 surface
->DrawTextTransparent(rc
, fontText
, ybase
, s
, len
, style
.fore
);
137 void DrawStyledText(Surface
*surface
, const ViewStyle
&vs
, int styleOffset
, PRectangle rcText
,
138 const StyledText
&st
, size_t start
, size_t length
, DrawPhase phase
) {
140 if (st
.multipleStyles
) {
141 int x
= static_cast<int>(rcText
.left
);
145 size_t style
= st
.styles
[i
+ start
];
146 while (end
< length
- 1 && st
.styles
[start
+ end
+ 1] == style
)
148 style
+= styleOffset
;
149 FontAlias fontText
= vs
.styles
[style
].font
;
150 const int width
= static_cast<int>(surface
->WidthText(fontText
,
151 st
.text
+ start
+ i
, static_cast<int>(end
- i
+ 1)));
152 PRectangle rcSegment
= rcText
;
153 rcSegment
.left
= static_cast<XYPOSITION
>(x
);
154 rcSegment
.right
= static_cast<XYPOSITION
>(x
+ width
+ 1);
155 DrawTextNoClipPhase(surface
, rcSegment
, vs
.styles
[style
],
156 rcText
.top
+ vs
.maxAscent
, st
.text
+ start
+ i
,
157 static_cast<int>(end
- i
+ 1), phase
);
162 const size_t style
= st
.style
+ styleOffset
;
163 DrawTextNoClipPhase(surface
, rcText
, vs
.styles
[style
],
164 rcText
.top
+ vs
.maxAscent
, st
.text
+ start
,
165 static_cast<int>(length
), phase
);
173 const XYPOSITION epsilon
= 0.0001f
; // A small nudge to avoid floating point precision issues
175 EditView::EditView() {
177 tabWidthMinimumPixels
= 2; // needed for calculating tab stops for fractional proportional fonts
178 hideSelection
= false;
179 drawOverstrikeCaret
= true;
181 phasesDraw
= phasesTwo
;
182 lineWidthMaxSeen
= 0;
183 additionalCaretsBlink
= true;
184 additionalCaretsVisible
= true;
185 imeCaretBlockOverride
= false;
187 pixmapIndentGuide
= 0;
188 pixmapIndentGuideHighlight
= 0;
189 llc
.SetLevel(LineLayoutCache::llcCaret
);
190 posCache
.SetSize(0x400);
192 customDrawTabArrow
= NULL
;
193 customDrawWrapMarker
= NULL
;
196 EditView::~EditView() {
201 bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw
) {
202 const PhasesDraw phasesDrawNew
= twoPhaseDraw
? phasesTwo
: phasesOne
;
203 const bool redraw
= phasesDraw
!= phasesDrawNew
;
204 phasesDraw
= phasesDrawNew
;
208 bool EditView::SetPhasesDraw(int phases
) {
209 const PhasesDraw phasesDrawNew
= static_cast<PhasesDraw
>(phases
);
210 const bool redraw
= phasesDraw
!= phasesDrawNew
;
211 phasesDraw
= phasesDrawNew
;
215 bool EditView::LinesOverlap() const {
216 return phasesDraw
== phasesMultiple
;
219 void EditView::ClearAllTabstops() {
224 XYPOSITION
EditView::NextTabstopPos(int line
, XYPOSITION x
, XYPOSITION tabWidth
) const {
225 int next
= GetNextTabstop(line
, static_cast<int>(x
+ tabWidthMinimumPixels
));
227 return static_cast<XYPOSITION
>(next
);
228 return (static_cast<int>((x
+ tabWidthMinimumPixels
) / tabWidth
) + 1) * tabWidth
;
231 bool EditView::ClearTabstops(int line
) {
232 LineTabstops
*lt
= static_cast<LineTabstops
*>(ldTabstops
);
233 return lt
&& lt
->ClearTabstops(line
);
236 bool EditView::AddTabstop(int line
, int x
) {
238 ldTabstops
= new LineTabstops();
240 LineTabstops
*lt
= static_cast<LineTabstops
*>(ldTabstops
);
241 return lt
&& lt
->AddTabstop(line
, x
);
244 int EditView::GetNextTabstop(int line
, int x
) const {
245 LineTabstops
*lt
= static_cast<LineTabstops
*>(ldTabstops
);
247 return lt
->GetNextTabstop(line
, x
);
253 void EditView::LinesAddedOrRemoved(int lineOfPos
, int linesAdded
) {
255 if (linesAdded
> 0) {
256 for (int line
= lineOfPos
; line
< lineOfPos
+ linesAdded
; line
++) {
257 ldTabstops
->InsertLine(line
);
260 for (int line
= (lineOfPos
+ -linesAdded
) - 1; line
>= lineOfPos
; line
--) {
261 ldTabstops
->RemoveLine(line
);
267 void EditView::DropGraphics(bool freeObjects
) {
271 delete pixmapIndentGuide
;
272 pixmapIndentGuide
= 0;
273 delete pixmapIndentGuideHighlight
;
274 pixmapIndentGuideHighlight
= 0;
277 pixmapLine
->Release();
278 if (pixmapIndentGuide
)
279 pixmapIndentGuide
->Release();
280 if (pixmapIndentGuideHighlight
)
281 pixmapIndentGuideHighlight
->Release();
285 void EditView::AllocateGraphics(const ViewStyle
&vsDraw
) {
287 pixmapLine
= Surface::Allocate(vsDraw
.technology
);
288 if (!pixmapIndentGuide
)
289 pixmapIndentGuide
= Surface::Allocate(vsDraw
.technology
);
290 if (!pixmapIndentGuideHighlight
)
291 pixmapIndentGuideHighlight
= Surface::Allocate(vsDraw
.technology
);
294 static const char *ControlCharacterString(unsigned char ch
) {
295 const char *reps
[] = {
296 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
297 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
298 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
299 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
301 if (ch
< ELEMENTS(reps
)) {
308 static void DrawTabArrow(Surface
*surface
, PRectangle rcTab
, int ymid
) {
309 int ydiff
= static_cast<int>(rcTab
.bottom
- rcTab
.top
) / 2;
310 int xhead
= static_cast<int>(rcTab
.right
) - 1 - ydiff
;
311 if (xhead
<= rcTab
.left
) {
312 ydiff
-= static_cast<int>(rcTab
.left
) - xhead
- 1;
313 xhead
= static_cast<int>(rcTab
.left
) - 1;
315 if ((rcTab
.left
+ 2) < (rcTab
.right
- 1))
316 surface
->MoveTo(static_cast<int>(rcTab
.left
) + 2, ymid
);
318 surface
->MoveTo(static_cast<int>(rcTab
.right
) - 1, ymid
);
319 surface
->LineTo(static_cast<int>(rcTab
.right
) - 1, ymid
);
320 surface
->LineTo(xhead
, ymid
- ydiff
);
321 surface
->MoveTo(static_cast<int>(rcTab
.right
) - 1, ymid
);
322 surface
->LineTo(xhead
, ymid
+ ydiff
);
325 void EditView::RefreshPixMaps(Surface
*surfaceWindow
, WindowID wid
, const ViewStyle
&vsDraw
) {
326 if (!pixmapIndentGuide
->Initialised()) {
327 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
328 pixmapIndentGuide
->InitPixMap(1, vsDraw
.lineHeight
+ 1, surfaceWindow
, wid
);
329 pixmapIndentGuideHighlight
->InitPixMap(1, vsDraw
.lineHeight
+ 1, surfaceWindow
, wid
);
330 PRectangle rcIG
= PRectangle::FromInts(0, 0, 1, vsDraw
.lineHeight
);
331 pixmapIndentGuide
->FillRectangle(rcIG
, vsDraw
.styles
[STYLE_INDENTGUIDE
].back
);
332 pixmapIndentGuide
->PenColour(vsDraw
.styles
[STYLE_INDENTGUIDE
].fore
);
333 pixmapIndentGuideHighlight
->FillRectangle(rcIG
, vsDraw
.styles
[STYLE_BRACELIGHT
].back
);
334 pixmapIndentGuideHighlight
->PenColour(vsDraw
.styles
[STYLE_BRACELIGHT
].fore
);
335 for (int stripe
= 1; stripe
< vsDraw
.lineHeight
+ 1; stripe
+= 2) {
336 PRectangle rcPixel
= PRectangle::FromInts(0, stripe
, 1, stripe
+ 1);
337 pixmapIndentGuide
->FillRectangle(rcPixel
, vsDraw
.styles
[STYLE_INDENTGUIDE
].fore
);
338 pixmapIndentGuideHighlight
->FillRectangle(rcPixel
, vsDraw
.styles
[STYLE_BRACELIGHT
].fore
);
343 LineLayout
*EditView::RetrieveLineLayout(int lineNumber
, const EditModel
&model
) {
344 int posLineStart
= model
.pdoc
->LineStart(lineNumber
);
345 int posLineEnd
= model
.pdoc
->LineStart(lineNumber
+ 1);
346 PLATFORM_ASSERT(posLineEnd
>= posLineStart
);
347 int lineCaret
= model
.pdoc
->LineFromPosition(model
.sel
.MainCaret());
348 return llc
.Retrieve(lineNumber
, lineCaret
,
349 posLineEnd
- posLineStart
, model
.pdoc
->GetStyleClock(),
350 model
.LinesOnScreen() + 1, model
.pdoc
->LinesTotal());
354 * Fill in the LineLayout data for the given line.
355 * Copy the given @a line and its styles from the document into local arrays.
356 * Also determine the x position at which each character starts.
358 void EditView::LayoutLine(const EditModel
&model
, int line
, Surface
*surface
, const ViewStyle
&vstyle
, LineLayout
*ll
, int width
) {
362 PLATFORM_ASSERT(line
< model
.pdoc
->LinesTotal());
363 PLATFORM_ASSERT(ll
->chars
!= NULL
);
364 int posLineStart
= model
.pdoc
->LineStart(line
);
365 int posLineEnd
= model
.pdoc
->LineStart(line
+ 1);
366 // If the line is very long, limit the treatment to a length that should fit in the viewport
367 if (posLineEnd
>(posLineStart
+ ll
->maxLineLength
)) {
368 posLineEnd
= posLineStart
+ ll
->maxLineLength
;
370 if (ll
->validity
== LineLayout::llCheckTextAndStyle
) {
371 int lineLength
= posLineEnd
- posLineStart
;
372 if (!vstyle
.viewEOL
) {
373 lineLength
= model
.pdoc
->LineEnd(line
) - posLineStart
;
375 if (lineLength
== ll
->numCharsInLine
) {
376 // See if chars, styles, indicators, are all the same
378 // Check base line layout
380 int numCharsInLine
= 0;
381 while (numCharsInLine
< lineLength
) {
382 int charInDoc
= numCharsInLine
+ posLineStart
;
383 char chDoc
= model
.pdoc
->CharAt(charInDoc
);
384 styleByte
= model
.pdoc
->StyleIndexAt(charInDoc
);
386 (ll
->styles
[numCharsInLine
] == styleByte
);
387 if (vstyle
.styles
[ll
->styles
[numCharsInLine
]].caseForce
== Style::caseMixed
)
389 (ll
->chars
[numCharsInLine
] == chDoc
);
390 else if (vstyle
.styles
[ll
->styles
[numCharsInLine
]].caseForce
== Style::caseLower
)
392 (ll
->chars
[numCharsInLine
] == static_cast<char>(tolower(chDoc
)));
393 else if (vstyle
.styles
[ll
->styles
[numCharsInLine
]].caseForce
== Style::caseUpper
)
395 (ll
->chars
[numCharsInLine
] == static_cast<char>(toupper(chDoc
)));
396 else { // Style::caseCamel
397 if ((model
.pdoc
->WordCharClass(ll
->chars
[numCharsInLine
]) == CharClassify::ccWord
) &&
398 ((numCharsInLine
== 0) || (model
.pdoc
->WordCharClass(ll
->chars
[numCharsInLine
- 1]) != CharClassify::ccWord
))) {
399 allSame
= allSame
&& (ll
->chars
[numCharsInLine
] == static_cast<char>(toupper(chDoc
)));
401 allSame
= allSame
&& (ll
->chars
[numCharsInLine
] == static_cast<char>(tolower(chDoc
)));
406 allSame
= allSame
&& (ll
->styles
[numCharsInLine
] == styleByte
); // For eolFilled
408 ll
->validity
= LineLayout::llPositions
;
410 ll
->validity
= LineLayout::llInvalid
;
413 ll
->validity
= LineLayout::llInvalid
;
416 if (ll
->validity
== LineLayout::llInvalid
) {
417 ll
->widthLine
= LineLayout::wrapWidthInfinite
;
419 if (vstyle
.edgeState
== EDGE_BACKGROUND
) {
420 ll
->edgeColumn
= model
.pdoc
->FindColumn(line
, vstyle
.theEdge
);
421 if (ll
->edgeColumn
>= posLineStart
) {
422 ll
->edgeColumn
-= posLineStart
;
428 // Fill base line layout
429 const int lineLength
= posLineEnd
- posLineStart
;
430 model
.pdoc
->GetCharRange(ll
->chars
, posLineStart
, lineLength
);
431 model
.pdoc
->GetStyleRange(ll
->styles
, posLineStart
, lineLength
);
432 int numCharsBeforeEOL
= model
.pdoc
->LineEnd(line
) - posLineStart
;
433 const int numCharsInLine
= (vstyle
.viewEOL
) ? lineLength
: numCharsBeforeEOL
;
434 for (int styleInLine
= 0; styleInLine
< numCharsInLine
; styleInLine
++) {
435 const unsigned char styleByte
= ll
->styles
[styleInLine
];
436 ll
->styles
[styleInLine
] = styleByte
;
438 const unsigned char styleByteLast
= (lineLength
> 0) ? ll
->styles
[lineLength
- 1] : 0;
439 if (vstyle
.someStylesForceCase
) {
440 for (int charInLine
= 0; charInLine
<lineLength
; charInLine
++) {
441 char chDoc
= ll
->chars
[charInLine
];
442 if (vstyle
.styles
[ll
->styles
[charInLine
]].caseForce
== Style::caseUpper
)
443 ll
->chars
[charInLine
] = static_cast<char>(toupper(chDoc
));
444 else if (vstyle
.styles
[ll
->styles
[charInLine
]].caseForce
== Style::caseLower
)
445 ll
->chars
[charInLine
] = static_cast<char>(tolower(chDoc
));
446 else if (vstyle
.styles
[ll
->styles
[charInLine
]].caseForce
== Style::caseCamel
) {
447 if ((model
.pdoc
->WordCharClass(ll
->chars
[charInLine
]) == CharClassify::ccWord
) &&
448 ((charInLine
== 0) || (model
.pdoc
->WordCharClass(ll
->chars
[charInLine
- 1]) != CharClassify::ccWord
))) {
449 ll
->chars
[charInLine
] = static_cast<char>(toupper(chDoc
));
451 ll
->chars
[charInLine
] = static_cast<char>(tolower(chDoc
));
456 ll
->xHighlightGuide
= 0;
457 // Extra element at the end of the line to hold end x position and act as
458 ll
->chars
[numCharsInLine
] = 0; // Also triggers processing in the loops as this is a control character
459 ll
->styles
[numCharsInLine
] = styleByteLast
; // For eolFilled
461 // Layout the line, determining the position of each character,
462 // with an extra element at the end for the end of the line.
463 ll
->positions
[0] = 0;
464 bool lastSegItalics
= false;
466 BreakFinder
bfLayout(ll
, NULL
, Range(0, numCharsInLine
), posLineStart
, 0, false, model
.pdoc
, &model
.reprs
, NULL
);
467 while (bfLayout
.More()) {
469 const TextSegment ts
= bfLayout
.Next();
471 std::fill(&ll
->positions
[ts
.start
+ 1], &ll
->positions
[ts
.end() + 1], 0.0f
);
472 if (vstyle
.styles
[ll
->styles
[ts
.start
]].visible
) {
473 if (ts
.representation
) {
474 XYPOSITION representationWidth
= vstyle
.controlCharWidth
;
475 if (ll
->chars
[ts
.start
] == '\t') {
476 // Tab is a special case of representation, taking a variable amount of space
477 const XYPOSITION x
= ll
->positions
[ts
.start
];
478 representationWidth
= NextTabstopPos(line
, x
, vstyle
.tabWidth
) - ll
->positions
[ts
.start
];
480 if (representationWidth
<= 0.0) {
481 XYPOSITION positionsRepr
[256]; // Should expand when needed
482 posCache
.MeasureWidths(surface
, vstyle
, STYLE_CONTROLCHAR
, ts
.representation
->stringRep
.c_str(),
483 static_cast<unsigned int>(ts
.representation
->stringRep
.length()), positionsRepr
, model
.pdoc
);
484 representationWidth
= positionsRepr
[ts
.representation
->stringRep
.length() - 1] + vstyle
.ctrlCharPadding
;
487 for (int ii
= 0; ii
< ts
.length
; ii
++)
488 ll
->positions
[ts
.start
+ 1 + ii
] = representationWidth
;
490 if ((ts
.length
== 1) && (' ' == ll
->chars
[ts
.start
])) {
491 // Over half the segments are single characters and of these about half are space characters.
492 ll
->positions
[ts
.start
+ 1] = vstyle
.styles
[ll
->styles
[ts
.start
]].spaceWidth
;
494 posCache
.MeasureWidths(surface
, vstyle
, ll
->styles
[ts
.start
], ll
->chars
+ ts
.start
,
495 ts
.length
, ll
->positions
+ ts
.start
+ 1, model
.pdoc
);
498 lastSegItalics
= (!ts
.representation
) && ((ll
->chars
[ts
.end() - 1] != ' ') && vstyle
.styles
[ll
->styles
[ts
.start
]].italic
);
501 for (int posToIncrease
= ts
.start
+ 1; posToIncrease
<= ts
.end(); posToIncrease
++) {
502 ll
->positions
[posToIncrease
] += ll
->positions
[ts
.start
];
506 // Small hack to make lines that end with italics not cut off the edge of the last character
507 if (lastSegItalics
) {
508 ll
->positions
[numCharsInLine
] += vstyle
.lastSegItalicsOffset
;
510 ll
->numCharsInLine
= numCharsInLine
;
511 ll
->numCharsBeforeEOL
= numCharsBeforeEOL
;
512 ll
->validity
= LineLayout::llPositions
;
514 // Hard to cope when too narrow, so just assume there is space
518 if ((ll
->validity
== LineLayout::llPositions
) || (ll
->widthLine
!= width
)) {
519 ll
->widthLine
= width
;
520 if (width
== LineLayout::wrapWidthInfinite
) {
522 } else if (width
> ll
->positions
[ll
->numCharsInLine
]) {
523 // Simple common case where line does not need wrapping.
526 if (vstyle
.wrapVisualFlags
& SC_WRAPVISUALFLAG_END
) {
527 width
-= static_cast<int>(vstyle
.aveCharWidth
); // take into account the space for end wrap mark
529 XYPOSITION wrapAddIndent
= 0; // This will be added to initial indent of line
530 if (vstyle
.wrapIndentMode
== SC_WRAPINDENT_INDENT
) {
531 wrapAddIndent
= model
.pdoc
->IndentSize() * vstyle
.spaceWidth
;
532 } else if (vstyle
.wrapIndentMode
== SC_WRAPINDENT_FIXED
) {
533 wrapAddIndent
= vstyle
.wrapVisualStartIndent
* vstyle
.aveCharWidth
;
535 ll
->wrapIndent
= wrapAddIndent
;
536 if (vstyle
.wrapIndentMode
!= SC_WRAPINDENT_FIXED
)
537 for (int i
= 0; i
< ll
->numCharsInLine
; i
++) {
538 if (!IsSpaceOrTab(ll
->chars
[i
])) {
539 ll
->wrapIndent
+= ll
->positions
[i
]; // Add line indent
543 // Check for text width minimum
544 if (ll
->wrapIndent
> width
- static_cast<int>(vstyle
.aveCharWidth
) * 15)
545 ll
->wrapIndent
= wrapAddIndent
;
546 // Check for wrapIndent minimum
547 if ((vstyle
.wrapVisualFlags
& SC_WRAPVISUALFLAG_START
) && (ll
->wrapIndent
< vstyle
.aveCharWidth
))
548 ll
->wrapIndent
= vstyle
.aveCharWidth
; // Indent to show start visual
550 // Calculate line start positions based upon width.
551 int lastGoodBreak
= 0;
552 int lastLineStart
= 0;
553 XYACCUMULATOR startOffset
= 0;
555 while (p
< ll
->numCharsInLine
) {
556 if ((ll
->positions
[p
+ 1] - startOffset
) >= width
) {
557 if (lastGoodBreak
== lastLineStart
) {
558 // Try moving to start of last character
560 lastGoodBreak
= model
.pdoc
->MovePositionOutsideChar(p
+ posLineStart
, -1)
563 if (lastGoodBreak
== lastLineStart
) {
564 // Ensure at least one character on line.
565 lastGoodBreak
= model
.pdoc
->MovePositionOutsideChar(lastGoodBreak
+ posLineStart
+ 1, 1)
569 lastLineStart
= lastGoodBreak
;
571 ll
->SetLineStart(ll
->lines
, lastGoodBreak
);
572 startOffset
= ll
->positions
[lastGoodBreak
];
573 // take into account the space for start wrap mark and indent
574 startOffset
-= ll
->wrapIndent
;
575 p
= lastGoodBreak
+ 1;
579 if (vstyle
.wrapState
== eWrapChar
) {
580 lastGoodBreak
= model
.pdoc
->MovePositionOutsideChar(p
+ posLineStart
, -1)
582 p
= model
.pdoc
->MovePositionOutsideChar(p
+ 1 + posLineStart
, 1) - posLineStart
;
584 } else if ((vstyle
.wrapState
== eWrapWord
) && (ll
->styles
[p
] != ll
->styles
[p
- 1])) {
586 } else if (IsSpaceOrTab(ll
->chars
[p
- 1]) && !IsSpaceOrTab(ll
->chars
[p
])) {
594 ll
->validity
= LineLayout::llLines
;
598 Point
EditView::LocationFromPosition(Surface
*surface
, const EditModel
&model
, SelectionPosition pos
, int topLine
, const ViewStyle
&vs
) {
600 if (pos
.Position() == INVALID_POSITION
)
602 const int line
= model
.pdoc
->LineFromPosition(pos
.Position());
603 const int lineVisible
= model
.cs
.DisplayFromDoc(line
);
604 //Platform::DebugPrintf("line=%d\n", line);
605 AutoLineLayout
ll(llc
, RetrieveLineLayout(line
, model
));
607 const int posLineStart
= model
.pdoc
->LineStart(line
);
608 LayoutLine(model
, line
, surface
, vs
, ll
, model
.wrapWidth
);
609 const int posInLine
= pos
.Position() - posLineStart
;
610 pt
= ll
->PointFromPosition(posInLine
, vs
.lineHeight
);
611 pt
.y
+= (lineVisible
- topLine
) * vs
.lineHeight
;
612 pt
.x
+= vs
.textStart
- model
.xOffset
;
614 pt
.x
+= pos
.VirtualSpace() * vs
.styles
[ll
->EndLineStyle()].spaceWidth
;
618 SelectionPosition
EditView::SPositionFromLocation(Surface
*surface
, const EditModel
&model
, Point pt
, bool canReturnInvalid
, bool charPosition
, bool virtualSpace
, const ViewStyle
&vs
) {
619 pt
.x
= pt
.x
- vs
.textStart
;
620 int visibleLine
= static_cast<int>(floor(pt
.y
/ vs
.lineHeight
));
621 if (!canReturnInvalid
&& (visibleLine
< 0))
623 const int lineDoc
= model
.cs
.DocFromDisplay(visibleLine
);
624 if (canReturnInvalid
&& (lineDoc
< 0))
625 return SelectionPosition(INVALID_POSITION
);
626 if (lineDoc
>= model
.pdoc
->LinesTotal())
627 return SelectionPosition(canReturnInvalid
? INVALID_POSITION
: model
.pdoc
->Length());
628 const int posLineStart
= model
.pdoc
->LineStart(lineDoc
);
629 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineDoc
, model
));
631 LayoutLine(model
, lineDoc
, surface
, vs
, ll
, model
.wrapWidth
);
632 const int lineStartSet
= model
.cs
.DisplayFromDoc(lineDoc
);
633 const int subLine
= visibleLine
- lineStartSet
;
634 if (subLine
< ll
->lines
) {
635 const Range rangeSubLine
= ll
->SubLineRange(subLine
);
636 const XYPOSITION subLineStart
= ll
->positions
[rangeSubLine
.start
];
637 if (subLine
> 0) // Wrapped
638 pt
.x
-= ll
->wrapIndent
;
639 const int positionInLine
= ll
->FindPositionFromX(pt
.x
+ subLineStart
, rangeSubLine
, charPosition
);
640 if (positionInLine
< rangeSubLine
.end
) {
641 return SelectionPosition(model
.pdoc
->MovePositionOutsideChar(positionInLine
+ posLineStart
, 1));
644 const XYPOSITION spaceWidth
= vs
.styles
[ll
->EndLineStyle()].spaceWidth
;
645 const int spaceOffset
= static_cast<int>(
646 (pt
.x
+ subLineStart
- ll
->positions
[rangeSubLine
.end
] + spaceWidth
/ 2) / spaceWidth
);
647 return SelectionPosition(rangeSubLine
.end
+ posLineStart
, spaceOffset
);
648 } else if (canReturnInvalid
) {
649 if (pt
.x
< (ll
->positions
[rangeSubLine
.end
] - subLineStart
)) {
650 return SelectionPosition(model
.pdoc
->MovePositionOutsideChar(rangeSubLine
.end
+ posLineStart
, 1));
653 return SelectionPosition(rangeSubLine
.end
+ posLineStart
);
656 if (!canReturnInvalid
)
657 return SelectionPosition(ll
->numCharsInLine
+ posLineStart
);
659 return SelectionPosition(canReturnInvalid
? INVALID_POSITION
: posLineStart
);
663 * Find the document position corresponding to an x coordinate on a particular document line.
664 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
665 * This method is used for rectangular selections and does not work on wrapped lines.
667 SelectionPosition
EditView::SPositionFromLineX(Surface
*surface
, const EditModel
&model
, int lineDoc
, int x
, const ViewStyle
&vs
) {
668 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineDoc
, model
));
670 const int posLineStart
= model
.pdoc
->LineStart(lineDoc
);
671 LayoutLine(model
, lineDoc
, surface
, vs
, ll
, model
.wrapWidth
);
672 const Range rangeSubLine
= ll
->SubLineRange(0);
673 const XYPOSITION subLineStart
= ll
->positions
[rangeSubLine
.start
];
674 const int positionInLine
= ll
->FindPositionFromX(x
+ subLineStart
, rangeSubLine
, false);
675 if (positionInLine
< rangeSubLine
.end
) {
676 return SelectionPosition(model
.pdoc
->MovePositionOutsideChar(positionInLine
+ posLineStart
, 1));
678 const XYPOSITION spaceWidth
= vs
.styles
[ll
->EndLineStyle()].spaceWidth
;
679 const int spaceOffset
= static_cast<int>(
680 (x
+ subLineStart
- ll
->positions
[rangeSubLine
.end
] + spaceWidth
/ 2) / spaceWidth
);
681 return SelectionPosition(rangeSubLine
.end
+ posLineStart
, spaceOffset
);
683 return SelectionPosition(0);
686 int EditView::DisplayFromPosition(Surface
*surface
, const EditModel
&model
, int pos
, const ViewStyle
&vs
) {
687 int lineDoc
= model
.pdoc
->LineFromPosition(pos
);
688 int lineDisplay
= model
.cs
.DisplayFromDoc(lineDoc
);
689 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineDoc
, model
));
691 LayoutLine(model
, lineDoc
, surface
, vs
, ll
, model
.wrapWidth
);
692 unsigned int posLineStart
= model
.pdoc
->LineStart(lineDoc
);
693 int posInLine
= pos
- posLineStart
;
694 lineDisplay
--; // To make up for first increment ahead.
695 for (int subLine
= 0; subLine
< ll
->lines
; subLine
++) {
696 if (posInLine
>= ll
->LineStart(subLine
)) {
704 int EditView::StartEndDisplayLine(Surface
*surface
, const EditModel
&model
, int pos
, bool start
, const ViewStyle
&vs
) {
705 int line
= model
.pdoc
->LineFromPosition(pos
);
706 AutoLineLayout
ll(llc
, RetrieveLineLayout(line
, model
));
707 int posRet
= INVALID_POSITION
;
709 unsigned int posLineStart
= model
.pdoc
->LineStart(line
);
710 LayoutLine(model
, line
, surface
, vs
, ll
, model
.wrapWidth
);
711 int posInLine
= pos
- posLineStart
;
712 if (posInLine
<= ll
->maxLineLength
) {
713 for (int subLine
= 0; subLine
< ll
->lines
; subLine
++) {
714 if ((posInLine
>= ll
->LineStart(subLine
)) &&
715 (posInLine
<= ll
->LineStart(subLine
+ 1)) &&
716 (posInLine
<= ll
->numCharsBeforeEOL
)) {
718 posRet
= ll
->LineStart(subLine
) + posLineStart
;
720 if (subLine
== ll
->lines
- 1)
721 posRet
= ll
->numCharsBeforeEOL
+ posLineStart
;
723 posRet
= ll
->LineStart(subLine
+ 1) + posLineStart
- 1;
732 static ColourDesired
SelectionBackground(const ViewStyle
&vsDraw
, bool main
, bool primarySelection
) {
734 (primarySelection
? vsDraw
.selColours
.back
: vsDraw
.selBackground2
) :
735 vsDraw
.selAdditionalBackground
;
738 static ColourDesired
TextBackground(const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
739 ColourOptional background
, int inSelection
, bool inHotspot
, int styleMain
, int i
) {
740 if (inSelection
== 1) {
741 if (vsDraw
.selColours
.back
.isSet
&& (vsDraw
.selAlpha
== SC_ALPHA_NOALPHA
)) {
742 return SelectionBackground(vsDraw
, true, model
.primarySelection
);
744 } else if (inSelection
== 2) {
745 if (vsDraw
.selColours
.back
.isSet
&& (vsDraw
.selAdditionalAlpha
== SC_ALPHA_NOALPHA
)) {
746 return SelectionBackground(vsDraw
, false, model
.primarySelection
);
749 if ((vsDraw
.edgeState
== EDGE_BACKGROUND
) &&
750 (i
>= ll
->edgeColumn
) &&
751 (i
< ll
->numCharsBeforeEOL
))
752 return vsDraw
.edgecolour
;
753 if (inHotspot
&& vsDraw
.hotspotColours
.back
.isSet
)
754 return vsDraw
.hotspotColours
.back
;
756 if (background
.isSet
&& (styleMain
!= STYLE_BRACELIGHT
) && (styleMain
!= STYLE_BRACEBAD
)) {
759 return vsDraw
.styles
[styleMain
].back
;
763 void EditView::DrawIndentGuide(Surface
*surface
, int lineVisible
, int lineHeight
, int start
, PRectangle rcSegment
, bool highlight
) {
764 Point from
= Point::FromInts(0, ((lineVisible
& 1) && (lineHeight
& 1)) ? 1 : 0);
765 PRectangle rcCopyArea
= PRectangle::FromInts(start
+ 1, static_cast<int>(rcSegment
.top
), start
+ 2, static_cast<int>(rcSegment
.bottom
));
766 surface
->Copy(rcCopyArea
, from
,
767 highlight
? *pixmapIndentGuideHighlight
: *pixmapIndentGuide
);
770 static void SimpleAlphaRectangle(Surface
*surface
, PRectangle rc
, ColourDesired fill
, int alpha
) {
771 if (alpha
!= SC_ALPHA_NOALPHA
) {
772 surface
->AlphaRectangle(rc
, 0, fill
, alpha
, fill
, alpha
, 0);
776 static void DrawTextBlob(Surface
*surface
, const ViewStyle
&vsDraw
, PRectangle rcSegment
,
777 const char *s
, ColourDesired textBack
, ColourDesired textFore
, bool fillBackground
) {
778 if (rcSegment
.Empty())
780 if (fillBackground
) {
781 surface
->FillRectangle(rcSegment
, textBack
);
783 FontAlias ctrlCharsFont
= vsDraw
.styles
[STYLE_CONTROLCHAR
].font
;
784 int normalCharHeight
= static_cast<int>(surface
->Ascent(ctrlCharsFont
) -
785 surface
->InternalLeading(ctrlCharsFont
));
786 PRectangle rcCChar
= rcSegment
;
787 rcCChar
.left
= rcCChar
.left
+ 1;
788 rcCChar
.top
= rcSegment
.top
+ vsDraw
.maxAscent
- normalCharHeight
;
789 rcCChar
.bottom
= rcSegment
.top
+ vsDraw
.maxAscent
+ 1;
790 PRectangle rcCentral
= rcCChar
;
793 surface
->FillRectangle(rcCentral
, textFore
);
794 PRectangle rcChar
= rcCChar
;
797 surface
->DrawTextClipped(rcChar
, ctrlCharsFont
,
798 rcSegment
.top
+ vsDraw
.maxAscent
, s
, static_cast<int>(s
? strlen(s
) : 0),
802 void EditView::DrawEOL(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
803 PRectangle rcLine
, int line
, int lineEnd
, int xStart
, int subLine
, XYACCUMULATOR subLineStart
,
804 ColourOptional background
) {
806 const int posLineStart
= model
.pdoc
->LineStart(line
);
807 PRectangle rcSegment
= rcLine
;
809 const bool lastSubLine
= subLine
== (ll
->lines
- 1);
810 XYPOSITION virtualSpace
= 0;
812 const XYPOSITION spaceWidth
= vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
;
813 virtualSpace
= model
.sel
.VirtualSpaceFor(model
.pdoc
->LineEnd(line
)) * spaceWidth
;
815 XYPOSITION xEol
= static_cast<XYPOSITION
>(ll
->positions
[lineEnd
] - subLineStart
);
817 // Fill the virtual space and show selections within it
818 if (virtualSpace
> 0.0f
) {
819 rcSegment
.left
= xEol
+ xStart
;
820 rcSegment
.right
= xEol
+ xStart
+ virtualSpace
;
821 surface
->FillRectangle(rcSegment
, background
.isSet
? background
: vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
]].back
);
822 if (!hideSelection
&& ((vsDraw
.selAlpha
== SC_ALPHA_NOALPHA
) || (vsDraw
.selAdditionalAlpha
== SC_ALPHA_NOALPHA
))) {
823 SelectionSegment
virtualSpaceRange(SelectionPosition(model
.pdoc
->LineEnd(line
)), SelectionPosition(model
.pdoc
->LineEnd(line
), model
.sel
.VirtualSpaceFor(model
.pdoc
->LineEnd(line
))));
824 for (size_t r
= 0; r
<model
.sel
.Count(); r
++) {
825 int alpha
= (r
== model
.sel
.Main()) ? vsDraw
.selAlpha
: vsDraw
.selAdditionalAlpha
;
826 if (alpha
== SC_ALPHA_NOALPHA
) {
827 SelectionSegment portion
= model
.sel
.Range(r
).Intersect(virtualSpaceRange
);
828 if (!portion
.Empty()) {
829 const XYPOSITION spaceWidth
= vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
;
830 rcSegment
.left
= xStart
+ ll
->positions
[portion
.start
.Position() - posLineStart
] -
831 static_cast<XYPOSITION
>(subLineStart
)+portion
.start
.VirtualSpace() * spaceWidth
;
832 rcSegment
.right
= xStart
+ ll
->positions
[portion
.end
.Position() - posLineStart
] -
833 static_cast<XYPOSITION
>(subLineStart
)+portion
.end
.VirtualSpace() * spaceWidth
;
834 rcSegment
.left
= (rcSegment
.left
> rcLine
.left
) ? rcSegment
.left
: rcLine
.left
;
835 rcSegment
.right
= (rcSegment
.right
< rcLine
.right
) ? rcSegment
.right
: rcLine
.right
;
836 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, r
== model
.sel
.Main(), model
.primarySelection
));
843 int eolInSelection
= 0;
844 int alpha
= SC_ALPHA_NOALPHA
;
845 if (!hideSelection
) {
846 int posAfterLineEnd
= model
.pdoc
->LineStart(line
+ 1);
847 eolInSelection
= (subLine
== (ll
->lines
- 1)) ? model
.sel
.InSelectionForEOL(posAfterLineEnd
) : 0;
848 alpha
= (eolInSelection
== 1) ? vsDraw
.selAlpha
: vsDraw
.selAdditionalAlpha
;
851 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
852 XYPOSITION blobsWidth
= 0;
854 for (int eolPos
= ll
->numCharsBeforeEOL
; eolPos
<ll
->numCharsInLine
; eolPos
++) {
855 rcSegment
.left
= xStart
+ ll
->positions
[eolPos
] - static_cast<XYPOSITION
>(subLineStart
)+virtualSpace
;
856 rcSegment
.right
= xStart
+ ll
->positions
[eolPos
+ 1] - static_cast<XYPOSITION
>(subLineStart
)+virtualSpace
;
857 blobsWidth
+= rcSegment
.Width();
859 const char *ctrlChar
;
860 unsigned char chEOL
= ll
->chars
[eolPos
];
861 int styleMain
= ll
->styles
[eolPos
];
862 ColourDesired textBack
= TextBackground(model
, vsDraw
, ll
, background
, eolInSelection
, false, styleMain
, eolPos
);
863 if (UTF8IsAscii(chEOL
)) {
864 ctrlChar
= ControlCharacterString(chEOL
);
866 const Representation
*repr
= model
.reprs
.RepresentationFromCharacter(ll
->chars
+ eolPos
, ll
->numCharsInLine
- eolPos
);
868 ctrlChar
= repr
->stringRep
.c_str();
869 eolPos
= ll
->numCharsInLine
;
871 sprintf(hexits
, "x%2X", chEOL
);
875 ColourDesired textFore
= vsDraw
.styles
[styleMain
].fore
;
876 if (eolInSelection
&& vsDraw
.selColours
.fore
.isSet
) {
877 textFore
= (eolInSelection
== 1) ? vsDraw
.selColours
.fore
: vsDraw
.selAdditionalForeground
;
879 if (eolInSelection
&& vsDraw
.selColours
.back
.isSet
&& (line
< model
.pdoc
->LinesTotal() - 1)) {
880 if (alpha
== SC_ALPHA_NOALPHA
) {
881 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1, model
.primarySelection
));
883 surface
->FillRectangle(rcSegment
, textBack
);
886 surface
->FillRectangle(rcSegment
, textBack
);
888 DrawTextBlob(surface
, vsDraw
, rcSegment
, ctrlChar
, textBack
, textFore
, phasesDraw
== phasesOne
);
889 if (eolInSelection
&& vsDraw
.selColours
.back
.isSet
&& (line
< model
.pdoc
->LinesTotal() - 1) && (alpha
!= SC_ALPHA_NOALPHA
)) {
890 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1, model
.primarySelection
), alpha
);
895 // Draw the eol-is-selected rectangle
896 rcSegment
.left
= xEol
+ xStart
+ virtualSpace
+ blobsWidth
;
897 rcSegment
.right
= rcSegment
.left
+ vsDraw
.aveCharWidth
;
899 if (eolInSelection
&& vsDraw
.selColours
.back
.isSet
&& (line
< model
.pdoc
->LinesTotal() - 1) && (alpha
== SC_ALPHA_NOALPHA
)) {
900 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1, model
.primarySelection
));
902 if (background
.isSet
) {
903 surface
->FillRectangle(rcSegment
, background
);
904 } else if (line
< model
.pdoc
->LinesTotal() - 1) {
905 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
]].back
);
906 } else if (vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
]].eolFilled
) {
907 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
]].back
);
909 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[STYLE_DEFAULT
].back
);
911 if (eolInSelection
&& vsDraw
.selColours
.back
.isSet
&& (line
< model
.pdoc
->LinesTotal() - 1) && (alpha
!= SC_ALPHA_NOALPHA
)) {
912 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1, model
.primarySelection
), alpha
);
916 // Fill the remainder of the line
917 rcSegment
.left
= rcSegment
.right
;
918 if (rcSegment
.left
< rcLine
.left
)
919 rcSegment
.left
= rcLine
.left
;
920 rcSegment
.right
= rcLine
.right
;
922 if (eolInSelection
&& vsDraw
.selEOLFilled
&& vsDraw
.selColours
.back
.isSet
&& (line
< model
.pdoc
->LinesTotal() - 1) && (alpha
== SC_ALPHA_NOALPHA
)) {
923 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1, model
.primarySelection
));
925 if (background
.isSet
) {
926 surface
->FillRectangle(rcSegment
, background
);
927 } else if (vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
]].eolFilled
) {
928 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
]].back
);
930 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[STYLE_DEFAULT
].back
);
932 if (eolInSelection
&& vsDraw
.selEOLFilled
&& vsDraw
.selColours
.back
.isSet
&& (line
< model
.pdoc
->LinesTotal() - 1) && (alpha
!= SC_ALPHA_NOALPHA
)) {
933 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1, model
.primarySelection
), alpha
);
937 bool drawWrapMarkEnd
= false;
939 if (vsDraw
.wrapVisualFlags
& SC_WRAPVISUALFLAG_END
) {
940 if (subLine
+ 1 < ll
->lines
) {
941 drawWrapMarkEnd
= ll
->LineStart(subLine
+ 1) != 0;
945 if (drawWrapMarkEnd
) {
946 PRectangle rcPlace
= rcSegment
;
948 if (vsDraw
.wrapVisualFlagsLocation
& SC_WRAPVISUALFLAGLOC_END_BY_TEXT
) {
949 rcPlace
.left
= xEol
+ xStart
+ virtualSpace
;
950 rcPlace
.right
= rcPlace
.left
+ vsDraw
.aveCharWidth
;
952 // rcLine is clipped to text area
953 rcPlace
.right
= rcLine
.right
;
954 rcPlace
.left
= rcPlace
.right
- vsDraw
.aveCharWidth
;
956 if (customDrawWrapMarker
== NULL
) {
957 DrawWrapMarker(surface
, rcPlace
, true, vsDraw
.WrapColour());
959 customDrawWrapMarker(surface
, rcPlace
, true, vsDraw
.WrapColour());
964 static void DrawIndicator(int indicNum
, int startPos
, int endPos
, Surface
*surface
, const ViewStyle
&vsDraw
,
965 const LineLayout
*ll
, int xStart
, PRectangle rcLine
, int subLine
, Indicator::DrawState drawState
, int value
) {
966 const XYPOSITION subLineStart
= ll
->positions
[ll
->LineStart(subLine
)];
968 ll
->positions
[startPos
] + xStart
- subLineStart
,
969 rcLine
.top
+ vsDraw
.maxAscent
,
970 ll
->positions
[endPos
] + xStart
- subLineStart
,
971 rcLine
.top
+ vsDraw
.maxAscent
+ 3);
972 vsDraw
.indicators
[indicNum
].Draw(surface
, rcIndic
, rcLine
, drawState
, value
);
975 static void DrawIndicators(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
976 int line
, int xStart
, PRectangle rcLine
, int subLine
, int lineEnd
, bool under
, int hoverIndicatorPos
) {
978 const int posLineStart
= model
.pdoc
->LineStart(line
);
979 const int lineStart
= ll
->LineStart(subLine
);
980 const int posLineEnd
= posLineStart
+ lineEnd
;
982 for (Decoration
*deco
= model
.pdoc
->decorations
.root
; deco
; deco
= deco
->next
) {
983 if (under
== vsDraw
.indicators
[deco
->indicator
].under
) {
984 int startPos
= posLineStart
+ lineStart
;
985 if (!deco
->rs
.ValueAt(startPos
)) {
986 startPos
= deco
->rs
.EndRun(startPos
);
988 while ((startPos
< posLineEnd
) && (deco
->rs
.ValueAt(startPos
))) {
989 const Range
rangeRun(deco
->rs
.StartRun(startPos
), deco
->rs
.EndRun(startPos
));
990 const int endPos
= std::min(rangeRun
.end
, posLineEnd
);
991 const bool hover
= vsDraw
.indicators
[deco
->indicator
].IsDynamic() &&
992 rangeRun
.ContainsCharacter(hoverIndicatorPos
);
993 const int value
= deco
->rs
.ValueAt(startPos
);
994 Indicator::DrawState drawState
= hover
? Indicator::drawHover
: Indicator::drawNormal
;
995 DrawIndicator(deco
->indicator
, startPos
- posLineStart
, endPos
- posLineStart
,
996 surface
, vsDraw
, ll
, xStart
, rcLine
, subLine
, drawState
, value
);
998 if (!deco
->rs
.ValueAt(startPos
)) {
999 startPos
= deco
->rs
.EndRun(startPos
);
1005 // Use indicators to highlight matching braces
1006 if ((vsDraw
.braceHighlightIndicatorSet
&& (model
.bracesMatchStyle
== STYLE_BRACELIGHT
)) ||
1007 (vsDraw
.braceBadLightIndicatorSet
&& (model
.bracesMatchStyle
== STYLE_BRACEBAD
))) {
1008 int braceIndicator
= (model
.bracesMatchStyle
== STYLE_BRACELIGHT
) ? vsDraw
.braceHighlightIndicator
: vsDraw
.braceBadLightIndicator
;
1009 if (under
== vsDraw
.indicators
[braceIndicator
].under
) {
1010 Range
rangeLine(posLineStart
+ lineStart
, posLineEnd
);
1011 if (rangeLine
.ContainsCharacter(model
.braces
[0])) {
1012 int braceOffset
= model
.braces
[0] - posLineStart
;
1013 if (braceOffset
< ll
->numCharsInLine
) {
1014 DrawIndicator(braceIndicator
, braceOffset
, braceOffset
+ 1, surface
, vsDraw
, ll
, xStart
, rcLine
, subLine
, Indicator::drawNormal
, 1);
1017 if (rangeLine
.ContainsCharacter(model
.braces
[1])) {
1018 int braceOffset
= model
.braces
[1] - posLineStart
;
1019 if (braceOffset
< ll
->numCharsInLine
) {
1020 DrawIndicator(braceIndicator
, braceOffset
, braceOffset
+ 1, surface
, vsDraw
, ll
, xStart
, rcLine
, subLine
, Indicator::drawNormal
, 1);
1027 static bool AnnotationBoxedOrIndented(int annotationVisible
) {
1028 return annotationVisible
== ANNOTATION_BOXED
|| annotationVisible
== ANNOTATION_INDENTED
;
1031 void EditView::DrawAnnotation(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1032 int line
, int xStart
, PRectangle rcLine
, int subLine
, DrawPhase phase
) {
1033 int indent
= static_cast<int>(model
.pdoc
->GetLineIndentation(line
) * vsDraw
.spaceWidth
);
1034 PRectangle rcSegment
= rcLine
;
1035 int annotationLine
= subLine
- ll
->lines
;
1036 const StyledText stAnnotation
= model
.pdoc
->AnnotationStyledText(line
);
1037 if (stAnnotation
.text
&& ValidStyledText(vsDraw
, vsDraw
.annotationStyleOffset
, stAnnotation
)) {
1038 if (phase
& drawBack
) {
1039 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[0].back
);
1041 rcSegment
.left
= static_cast<XYPOSITION
>(xStart
);
1042 if (model
.trackLineWidth
|| AnnotationBoxedOrIndented(vsDraw
.annotationVisible
)) {
1043 // Only care about calculating width if tracking or need to draw indented box
1044 int widthAnnotation
= WidestLineWidth(surface
, vsDraw
, vsDraw
.annotationStyleOffset
, stAnnotation
);
1045 if (AnnotationBoxedOrIndented(vsDraw
.annotationVisible
)) {
1046 widthAnnotation
+= static_cast<int>(vsDraw
.spaceWidth
* 2); // Margins
1047 rcSegment
.left
= static_cast<XYPOSITION
>(xStart
+ indent
);
1048 rcSegment
.right
= rcSegment
.left
+ widthAnnotation
;
1050 if (widthAnnotation
> lineWidthMaxSeen
)
1051 lineWidthMaxSeen
= widthAnnotation
;
1053 const int annotationLines
= model
.pdoc
->AnnotationLines(line
);
1055 size_t lengthAnnotation
= stAnnotation
.LineLength(start
);
1056 int lineInAnnotation
= 0;
1057 while ((lineInAnnotation
< annotationLine
) && (start
< stAnnotation
.length
)) {
1058 start
+= lengthAnnotation
+ 1;
1059 lengthAnnotation
= stAnnotation
.LineLength(start
);
1062 PRectangle rcText
= rcSegment
;
1063 if ((phase
& drawBack
) && AnnotationBoxedOrIndented(vsDraw
.annotationVisible
)) {
1064 surface
->FillRectangle(rcText
,
1065 vsDraw
.styles
[stAnnotation
.StyleAt(start
) + vsDraw
.annotationStyleOffset
].back
);
1066 rcText
.left
+= vsDraw
.spaceWidth
;
1068 DrawStyledText(surface
, vsDraw
, vsDraw
.annotationStyleOffset
, rcText
,
1069 stAnnotation
, start
, lengthAnnotation
, phase
);
1070 if ((phase
& drawBack
) && (vsDraw
.annotationVisible
== ANNOTATION_BOXED
)) {
1071 surface
->PenColour(vsDraw
.styles
[vsDraw
.annotationStyleOffset
].fore
);
1072 surface
->MoveTo(static_cast<int>(rcSegment
.left
), static_cast<int>(rcSegment
.top
));
1073 surface
->LineTo(static_cast<int>(rcSegment
.left
), static_cast<int>(rcSegment
.bottom
));
1074 surface
->MoveTo(static_cast<int>(rcSegment
.right
), static_cast<int>(rcSegment
.top
));
1075 surface
->LineTo(static_cast<int>(rcSegment
.right
), static_cast<int>(rcSegment
.bottom
));
1076 if (subLine
== ll
->lines
) {
1077 surface
->MoveTo(static_cast<int>(rcSegment
.left
), static_cast<int>(rcSegment
.top
));
1078 surface
->LineTo(static_cast<int>(rcSegment
.right
), static_cast<int>(rcSegment
.top
));
1080 if (subLine
== ll
->lines
+ annotationLines
- 1) {
1081 surface
->MoveTo(static_cast<int>(rcSegment
.left
), static_cast<int>(rcSegment
.bottom
- 1));
1082 surface
->LineTo(static_cast<int>(rcSegment
.right
), static_cast<int>(rcSegment
.bottom
- 1));
1088 static void DrawBlockCaret(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1089 int subLine
, int xStart
, int offset
, int posCaret
, PRectangle rcCaret
, ColourDesired caretColour
) {
1091 int lineStart
= ll
->LineStart(subLine
);
1092 int posBefore
= posCaret
;
1093 int posAfter
= model
.pdoc
->MovePositionOutsideChar(posCaret
+ 1, 1);
1094 int numCharsToDraw
= posAfter
- posCaret
;
1096 // Work out where the starting and ending offsets are. We need to
1097 // see if the previous character shares horizontal space, such as a
1098 // glyph / combining character. If so we'll need to draw that too.
1099 int offsetFirstChar
= offset
;
1100 int offsetLastChar
= offset
+ (posAfter
- posCaret
);
1101 while ((posBefore
> 0) && ((offsetLastChar
- numCharsToDraw
) >= lineStart
)) {
1102 if ((ll
->positions
[offsetLastChar
] - ll
->positions
[offsetLastChar
- numCharsToDraw
]) > 0) {
1103 // The char does not share horizontal space
1106 // Char shares horizontal space, update the numChars to draw
1107 // Update posBefore to point to the prev char
1108 posBefore
= model
.pdoc
->MovePositionOutsideChar(posBefore
- 1, -1);
1109 numCharsToDraw
= posAfter
- posBefore
;
1110 offsetFirstChar
= offset
- (posCaret
- posBefore
);
1113 // See if the next character shares horizontal space, if so we'll
1114 // need to draw that too.
1115 if (offsetFirstChar
< 0)
1116 offsetFirstChar
= 0;
1117 numCharsToDraw
= offsetLastChar
- offsetFirstChar
;
1118 while ((offsetLastChar
< ll
->LineStart(subLine
+ 1)) && (offsetLastChar
<= ll
->numCharsInLine
)) {
1119 // Update posAfter to point to the 2nd next char, this is where
1120 // the next character ends, and 2nd next begins. We'll need
1121 // to compare these two
1122 posBefore
= posAfter
;
1123 posAfter
= model
.pdoc
->MovePositionOutsideChar(posAfter
+ 1, 1);
1124 offsetLastChar
= offset
+ (posAfter
- posCaret
);
1125 if ((ll
->positions
[offsetLastChar
] - ll
->positions
[offsetLastChar
- (posAfter
- posBefore
)]) > 0) {
1126 // The char does not share horizontal space
1129 // Char shares horizontal space, update the numChars to draw
1130 numCharsToDraw
= offsetLastChar
- offsetFirstChar
;
1133 // We now know what to draw, update the caret drawing rectangle
1134 rcCaret
.left
= ll
->positions
[offsetFirstChar
] - ll
->positions
[lineStart
] + xStart
;
1135 rcCaret
.right
= ll
->positions
[offsetFirstChar
+ numCharsToDraw
] - ll
->positions
[lineStart
] + xStart
;
1137 // Adjust caret position to take into account any word wrapping symbols.
1138 if ((ll
->wrapIndent
!= 0) && (lineStart
!= 0)) {
1139 XYPOSITION wordWrapCharWidth
= ll
->wrapIndent
;
1140 rcCaret
.left
+= wordWrapCharWidth
;
1141 rcCaret
.right
+= wordWrapCharWidth
;
1144 // This character is where the caret block is, we override the colours
1145 // (inversed) for drawing the caret here.
1146 int styleMain
= ll
->styles
[offsetFirstChar
];
1147 FontAlias fontText
= vsDraw
.styles
[styleMain
].font
;
1148 surface
->DrawTextClipped(rcCaret
, fontText
,
1149 rcCaret
.top
+ vsDraw
.maxAscent
, ll
->chars
+ offsetFirstChar
,
1150 numCharsToDraw
, vsDraw
.styles
[styleMain
].back
,
1154 void EditView::DrawCarets(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1155 int lineDoc
, int xStart
, PRectangle rcLine
, int subLine
) const {
1156 // When drag is active it is the only caret drawn
1157 bool drawDrag
= model
.posDrag
.IsValid();
1158 if (hideSelection
&& !drawDrag
)
1160 const int posLineStart
= model
.pdoc
->LineStart(lineDoc
);
1161 // For each selection draw
1162 for (size_t r
= 0; (r
<model
.sel
.Count()) || drawDrag
; r
++) {
1163 const bool mainCaret
= r
== model
.sel
.Main();
1164 const SelectionPosition posCaret
= (drawDrag
? model
.posDrag
: model
.sel
.Range(r
).caret
);
1165 const int offset
= posCaret
.Position() - posLineStart
;
1166 const XYPOSITION spaceWidth
= vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
;
1167 const XYPOSITION virtualOffset
= posCaret
.VirtualSpace() * spaceWidth
;
1168 if (ll
->InLine(offset
, subLine
) && offset
<= ll
->numCharsBeforeEOL
) {
1169 XYPOSITION xposCaret
= ll
->positions
[offset
] + virtualOffset
- ll
->positions
[ll
->LineStart(subLine
)];
1170 if (ll
->wrapIndent
!= 0) {
1171 int lineStart
= ll
->LineStart(subLine
);
1172 if (lineStart
!= 0) // Wrapped
1173 xposCaret
+= ll
->wrapIndent
;
1175 bool caretBlinkState
= (model
.caret
.active
&& model
.caret
.on
) || (!additionalCaretsBlink
&& !mainCaret
);
1176 bool caretVisibleState
= additionalCaretsVisible
|| mainCaret
;
1177 if ((xposCaret
>= 0) && (vsDraw
.caretWidth
> 0) && (vsDraw
.caretStyle
!= CARETSTYLE_INVISIBLE
) &&
1178 ((model
.posDrag
.IsValid()) || (caretBlinkState
&& caretVisibleState
))) {
1179 bool caretAtEOF
= false;
1180 bool caretAtEOL
= false;
1181 bool drawBlockCaret
= false;
1182 XYPOSITION widthOverstrikeCaret
;
1183 XYPOSITION caretWidthOffset
= 0;
1184 PRectangle rcCaret
= rcLine
;
1186 if (posCaret
.Position() == model
.pdoc
->Length()) { // At end of document
1188 widthOverstrikeCaret
= vsDraw
.aveCharWidth
;
1189 } else if ((posCaret
.Position() - posLineStart
) >= ll
->numCharsInLine
) { // At end of line
1191 widthOverstrikeCaret
= vsDraw
.aveCharWidth
;
1193 const int widthChar
= model
.pdoc
->LenChar(posCaret
.Position());
1194 widthOverstrikeCaret
= ll
->positions
[offset
+ widthChar
] - ll
->positions
[offset
];
1196 if (widthOverstrikeCaret
< 3) // Make sure its visible
1197 widthOverstrikeCaret
= 3;
1200 caretWidthOffset
= 0.51f
; // Move back so overlaps both character cells.
1201 xposCaret
+= xStart
;
1202 if (model
.posDrag
.IsValid()) {
1203 /* Dragging text, use a line caret */
1204 rcCaret
.left
= static_cast<XYPOSITION
>(RoundXYPosition(xposCaret
- caretWidthOffset
));
1205 rcCaret
.right
= rcCaret
.left
+ vsDraw
.caretWidth
;
1206 } else if (model
.inOverstrike
&& drawOverstrikeCaret
) {
1207 /* Overstrike (insert mode), use a modified bar caret */
1208 rcCaret
.top
= rcCaret
.bottom
- 2;
1209 rcCaret
.left
= xposCaret
+ 1;
1210 rcCaret
.right
= rcCaret
.left
+ widthOverstrikeCaret
- 1;
1211 } else if ((vsDraw
.caretStyle
== CARETSTYLE_BLOCK
) || imeCaretBlockOverride
) {
1213 rcCaret
.left
= xposCaret
;
1214 if (!caretAtEOL
&& !caretAtEOF
&& (ll
->chars
[offset
] != '\t') && !(IsControlCharacter(ll
->chars
[offset
]))) {
1215 drawBlockCaret
= true;
1216 rcCaret
.right
= xposCaret
+ widthOverstrikeCaret
;
1218 rcCaret
.right
= xposCaret
+ vsDraw
.aveCharWidth
;
1222 rcCaret
.left
= static_cast<XYPOSITION
>(RoundXYPosition(xposCaret
- caretWidthOffset
));
1223 rcCaret
.right
= rcCaret
.left
+ vsDraw
.caretWidth
;
1225 ColourDesired caretColour
= mainCaret
? vsDraw
.caretcolour
: vsDraw
.additionalCaretColour
;
1226 if (drawBlockCaret
) {
1227 DrawBlockCaret(surface
, model
, vsDraw
, ll
, subLine
, xStart
, offset
, posCaret
.Position(), rcCaret
, caretColour
);
1229 surface
->FillRectangle(rcCaret
, caretColour
);
1238 static void DrawWrapIndentAndMarker(Surface
*surface
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1239 int xStart
, PRectangle rcLine
, ColourOptional background
, DrawWrapMarkerFn customDrawWrapMarker
) {
1240 // default bgnd here..
1241 surface
->FillRectangle(rcLine
, background
.isSet
? background
:
1242 vsDraw
.styles
[STYLE_DEFAULT
].back
);
1244 if (vsDraw
.wrapVisualFlags
& SC_WRAPVISUALFLAG_START
) {
1246 // draw continuation rect
1247 PRectangle rcPlace
= rcLine
;
1249 rcPlace
.left
= static_cast<XYPOSITION
>(xStart
);
1250 rcPlace
.right
= rcPlace
.left
+ ll
->wrapIndent
;
1252 if (vsDraw
.wrapVisualFlagsLocation
& SC_WRAPVISUALFLAGLOC_START_BY_TEXT
)
1253 rcPlace
.left
= rcPlace
.right
- vsDraw
.aveCharWidth
;
1255 rcPlace
.right
= rcPlace
.left
+ vsDraw
.aveCharWidth
;
1257 if (customDrawWrapMarker
== NULL
) {
1258 DrawWrapMarker(surface
, rcPlace
, false, vsDraw
.WrapColour());
1260 customDrawWrapMarker(surface
, rcPlace
, false, vsDraw
.WrapColour());
1265 void EditView::DrawBackground(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1266 PRectangle rcLine
, Range lineRange
, int posLineStart
, int xStart
,
1267 int subLine
, ColourOptional background
) const {
1269 const bool selBackDrawn
= vsDraw
.SelectionBackgroundDrawn();
1270 bool inIndentation
= subLine
== 0; // Do not handle indentation except on first subline.
1271 const XYACCUMULATOR subLineStart
= ll
->positions
[lineRange
.start
];
1272 // Does not take margin into account but not significant
1273 const int xStartVisible
= static_cast<int>(subLineStart
)-xStart
;
1275 BreakFinder
bfBack(ll
, &model
.sel
, lineRange
, posLineStart
, xStartVisible
, selBackDrawn
, model
.pdoc
, &model
.reprs
, NULL
);
1277 const bool drawWhitespaceBackground
= vsDraw
.WhitespaceBackgroundDrawn() && !background
.isSet
;
1279 // Background drawing loop
1280 while (bfBack
.More()) {
1282 const TextSegment ts
= bfBack
.Next();
1283 const int i
= ts
.end() - 1;
1284 const int iDoc
= i
+ posLineStart
;
1286 PRectangle rcSegment
= rcLine
;
1287 rcSegment
.left
= ll
->positions
[ts
.start
] + xStart
- static_cast<XYPOSITION
>(subLineStart
);
1288 rcSegment
.right
= ll
->positions
[ts
.end()] + xStart
- static_cast<XYPOSITION
>(subLineStart
);
1289 // Only try to draw if really visible - enhances performance by not calling environment to
1290 // draw strings that are completely past the right side of the window.
1291 if (!rcSegment
.Empty() && rcSegment
.Intersects(rcLine
)) {
1292 // Clip to line rectangle, since may have a huge position which will not work with some platforms
1293 if (rcSegment
.left
< rcLine
.left
)
1294 rcSegment
.left
= rcLine
.left
;
1295 if (rcSegment
.right
> rcLine
.right
)
1296 rcSegment
.right
= rcLine
.right
;
1298 const int inSelection
= hideSelection
? 0 : model
.sel
.CharacterInSelection(iDoc
);
1299 const bool inHotspot
= (ll
->hotspot
.Valid()) && ll
->hotspot
.ContainsCharacter(iDoc
);
1300 ColourDesired textBack
= TextBackground(model
, vsDraw
, ll
, background
, inSelection
,
1301 inHotspot
, ll
->styles
[i
], i
);
1302 if (ts
.representation
) {
1303 if (ll
->chars
[i
] == '\t') {
1305 if (drawWhitespaceBackground
&& vsDraw
.WhiteSpaceVisible(inIndentation
))
1306 textBack
= vsDraw
.whitespaceColours
.back
;
1309 inIndentation
= false;
1311 surface
->FillRectangle(rcSegment
, textBack
);
1313 // Normal text display
1314 surface
->FillRectangle(rcSegment
, textBack
);
1315 if (vsDraw
.viewWhitespace
!= wsInvisible
) {
1316 for (int cpos
= 0; cpos
<= i
- ts
.start
; cpos
++) {
1317 if (ll
->chars
[cpos
+ ts
.start
] == ' ') {
1318 if (drawWhitespaceBackground
&& vsDraw
.WhiteSpaceVisible(inIndentation
)) {
1320 ll
->positions
[cpos
+ ts
.start
] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
1322 ll
->positions
[cpos
+ ts
.start
+ 1] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
1324 surface
->FillRectangle(rcSpace
, vsDraw
.whitespaceColours
.back
);
1327 inIndentation
= false;
1332 } else if (rcSegment
.left
> rcLine
.right
) {
1338 static void DrawEdgeLine(Surface
*surface
, const ViewStyle
&vsDraw
, const LineLayout
*ll
, PRectangle rcLine
,
1339 Range lineRange
, int xStart
) {
1340 if (vsDraw
.edgeState
== EDGE_LINE
) {
1341 PRectangle rcSegment
= rcLine
;
1342 int edgeX
= static_cast<int>(vsDraw
.theEdge
* vsDraw
.spaceWidth
);
1343 rcSegment
.left
= static_cast<XYPOSITION
>(edgeX
+ xStart
);
1344 if ((ll
->wrapIndent
!= 0) && (lineRange
.start
!= 0))
1345 rcSegment
.left
-= ll
->wrapIndent
;
1346 rcSegment
.right
= rcSegment
.left
+ 1;
1347 surface
->FillRectangle(rcSegment
, vsDraw
.edgecolour
);
1351 // Draw underline mark as part of background if not transparent
1352 static void DrawMarkUnderline(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
,
1353 int line
, PRectangle rcLine
) {
1354 int marks
= model
.pdoc
->GetMark(line
);
1355 for (int markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
1356 if ((marks
& 1) && (vsDraw
.markers
[markBit
].markType
== SC_MARK_UNDERLINE
) &&
1357 (vsDraw
.markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
1358 PRectangle rcUnderline
= rcLine
;
1359 rcUnderline
.top
= rcUnderline
.bottom
- 2;
1360 surface
->FillRectangle(rcUnderline
, vsDraw
.markers
[markBit
].back
);
1365 static void DrawTranslucentSelection(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1366 int line
, PRectangle rcLine
, int subLine
, Range lineRange
, int xStart
) {
1367 if ((vsDraw
.selAlpha
!= SC_ALPHA_NOALPHA
) || (vsDraw
.selAdditionalAlpha
!= SC_ALPHA_NOALPHA
)) {
1368 const int posLineStart
= model
.pdoc
->LineStart(line
);
1369 const XYACCUMULATOR subLineStart
= ll
->positions
[lineRange
.start
];
1370 // For each selection draw
1371 int virtualSpaces
= 0;
1372 if (subLine
== (ll
->lines
- 1)) {
1373 virtualSpaces
= model
.sel
.VirtualSpaceFor(model
.pdoc
->LineEnd(line
));
1375 SelectionPosition
posStart(posLineStart
+ lineRange
.start
);
1376 SelectionPosition
posEnd(posLineStart
+ lineRange
.end
, virtualSpaces
);
1377 SelectionSegment
virtualSpaceRange(posStart
, posEnd
);
1378 for (size_t r
= 0; r
< model
.sel
.Count(); r
++) {
1379 int alpha
= (r
== model
.sel
.Main()) ? vsDraw
.selAlpha
: vsDraw
.selAdditionalAlpha
;
1380 if (alpha
!= SC_ALPHA_NOALPHA
) {
1381 SelectionSegment portion
= model
.sel
.Range(r
).Intersect(virtualSpaceRange
);
1382 if (!portion
.Empty()) {
1383 const XYPOSITION spaceWidth
= vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
;
1384 PRectangle rcSegment
= rcLine
;
1385 rcSegment
.left
= xStart
+ ll
->positions
[portion
.start
.Position() - posLineStart
] -
1386 static_cast<XYPOSITION
>(subLineStart
)+portion
.start
.VirtualSpace() * spaceWidth
;
1387 rcSegment
.right
= xStart
+ ll
->positions
[portion
.end
.Position() - posLineStart
] -
1388 static_cast<XYPOSITION
>(subLineStart
)+portion
.end
.VirtualSpace() * spaceWidth
;
1389 if ((ll
->wrapIndent
!= 0) && (lineRange
.start
!= 0)) {
1390 if ((portion
.start
.Position() - posLineStart
) == lineRange
.start
&& model
.sel
.Range(r
).ContainsCharacter(portion
.start
.Position() - 1))
1391 rcSegment
.left
-= static_cast<int>(ll
->wrapIndent
); // indentation added to xStart was truncated to int, so we do the same here
1393 rcSegment
.left
= (rcSegment
.left
> rcLine
.left
) ? rcSegment
.left
: rcLine
.left
;
1394 rcSegment
.right
= (rcSegment
.right
< rcLine
.right
) ? rcSegment
.right
: rcLine
.right
;
1395 if (rcSegment
.right
> rcLine
.left
)
1396 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, r
== model
.sel
.Main(), model
.primarySelection
), alpha
);
1403 // Draw any translucent whole line states
1404 static void DrawTranslucentLineState(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1405 int line
, PRectangle rcLine
) {
1406 if ((model
.caret
.active
|| vsDraw
.alwaysShowCaretLineBackground
) && vsDraw
.showCaretLineBackground
&& ll
->containsCaret
) {
1407 SimpleAlphaRectangle(surface
, rcLine
, vsDraw
.caretLineBackground
, vsDraw
.caretLineAlpha
);
1409 const int marksOfLine
= model
.pdoc
->GetMark(line
);
1410 int marksDrawnInText
= marksOfLine
& vsDraw
.maskDrawInText
;
1411 for (int markBit
= 0; (markBit
< 32) && marksDrawnInText
; markBit
++) {
1412 if (marksDrawnInText
& 1) {
1413 if (vsDraw
.markers
[markBit
].markType
== SC_MARK_BACKGROUND
) {
1414 SimpleAlphaRectangle(surface
, rcLine
, vsDraw
.markers
[markBit
].back
, vsDraw
.markers
[markBit
].alpha
);
1415 } else if (vsDraw
.markers
[markBit
].markType
== SC_MARK_UNDERLINE
) {
1416 PRectangle rcUnderline
= rcLine
;
1417 rcUnderline
.top
= rcUnderline
.bottom
- 2;
1418 SimpleAlphaRectangle(surface
, rcUnderline
, vsDraw
.markers
[markBit
].back
, vsDraw
.markers
[markBit
].alpha
);
1421 marksDrawnInText
>>= 1;
1423 int marksDrawnInLine
= marksOfLine
& vsDraw
.maskInLine
;
1424 for (int markBit
= 0; (markBit
< 32) && marksDrawnInLine
; markBit
++) {
1425 if (marksDrawnInLine
& 1) {
1426 SimpleAlphaRectangle(surface
, rcLine
, vsDraw
.markers
[markBit
].back
, vsDraw
.markers
[markBit
].alpha
);
1428 marksDrawnInLine
>>= 1;
1432 void EditView::DrawForeground(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1433 int lineVisible
, PRectangle rcLine
, Range lineRange
, int posLineStart
, int xStart
,
1434 int subLine
, ColourOptional background
) {
1436 const bool selBackDrawn
= vsDraw
.SelectionBackgroundDrawn();
1437 const bool drawWhitespaceBackground
= vsDraw
.WhitespaceBackgroundDrawn() && !background
.isSet
;
1438 bool inIndentation
= subLine
== 0; // Do not handle indentation except on first subline.
1440 const XYACCUMULATOR subLineStart
= ll
->positions
[lineRange
.start
];
1441 const XYPOSITION indentWidth
= model
.pdoc
->IndentSize() * vsDraw
.spaceWidth
;
1443 // Does not take margin into account but not significant
1444 const int xStartVisible
= static_cast<int>(subLineStart
)-xStart
;
1446 // Foreground drawing loop
1447 BreakFinder
bfFore(ll
, &model
.sel
, lineRange
, posLineStart
, xStartVisible
,
1448 (((phasesDraw
== phasesOne
) && selBackDrawn
) || vsDraw
.selColours
.fore
.isSet
), model
.pdoc
, &model
.reprs
, &vsDraw
);
1450 while (bfFore
.More()) {
1452 const TextSegment ts
= bfFore
.Next();
1453 const int i
= ts
.end() - 1;
1454 const int iDoc
= i
+ posLineStart
;
1456 PRectangle rcSegment
= rcLine
;
1457 rcSegment
.left
= ll
->positions
[ts
.start
] + xStart
- static_cast<XYPOSITION
>(subLineStart
);
1458 rcSegment
.right
= ll
->positions
[ts
.end()] + xStart
- static_cast<XYPOSITION
>(subLineStart
);
1459 // Only try to draw if really visible - enhances performance by not calling environment to
1460 // draw strings that are completely past the right side of the window.
1461 if (rcSegment
.Intersects(rcLine
)) {
1462 int styleMain
= ll
->styles
[i
];
1463 ColourDesired textFore
= vsDraw
.styles
[styleMain
].fore
;
1464 FontAlias textFont
= vsDraw
.styles
[styleMain
].font
;
1465 //hotspot foreground
1466 const bool inHotspot
= (ll
->hotspot
.Valid()) && ll
->hotspot
.ContainsCharacter(iDoc
);
1468 if (vsDraw
.hotspotColours
.fore
.isSet
)
1469 textFore
= vsDraw
.hotspotColours
.fore
;
1471 if (vsDraw
.indicatorsSetFore
> 0) {
1472 // At least one indicator sets the text colour so see if it applies to this segment
1473 for (Decoration
*deco
= model
.pdoc
->decorations
.root
; deco
; deco
= deco
->next
) {
1474 const int indicatorValue
= deco
->rs
.ValueAt(ts
.start
+ posLineStart
);
1475 if (indicatorValue
) {
1476 const Indicator
&indicator
= vsDraw
.indicators
[deco
->indicator
];
1477 const bool hover
= indicator
.IsDynamic() &&
1478 ((model
.hoverIndicatorPos
>= ts
.start
+ posLineStart
) &&
1479 (model
.hoverIndicatorPos
<= ts
.end() + posLineStart
));
1481 if (indicator
.sacHover
.style
== INDIC_TEXTFORE
) {
1482 textFore
= indicator
.sacHover
.fore
;
1485 if (indicator
.sacNormal
.style
== INDIC_TEXTFORE
) {
1486 if (indicator
.Flags() & SC_INDICFLAG_VALUEFORE
)
1487 textFore
= indicatorValue
& SC_INDICVALUEMASK
;
1489 textFore
= indicator
.sacNormal
.fore
;
1495 const int inSelection
= hideSelection
? 0 : model
.sel
.CharacterInSelection(iDoc
);
1496 if (inSelection
&& (vsDraw
.selColours
.fore
.isSet
)) {
1497 textFore
= (inSelection
== 1) ? vsDraw
.selColours
.fore
: vsDraw
.selAdditionalForeground
;
1499 ColourDesired textBack
= TextBackground(model
, vsDraw
, ll
, background
, inSelection
, inHotspot
, styleMain
, i
);
1500 if (ts
.representation
) {
1501 if (ll
->chars
[i
] == '\t') {
1503 if (phasesDraw
== phasesOne
) {
1504 if (drawWhitespaceBackground
&& vsDraw
.WhiteSpaceVisible(inIndentation
))
1505 textBack
= vsDraw
.whitespaceColours
.back
;
1506 surface
->FillRectangle(rcSegment
, textBack
);
1508 if (inIndentation
&& vsDraw
.viewIndentationGuides
== ivReal
) {
1509 for (int indentCount
= static_cast<int>((ll
->positions
[i
] + epsilon
) / indentWidth
);
1510 indentCount
<= (ll
->positions
[i
+ 1] - epsilon
) / indentWidth
;
1512 if (indentCount
> 0) {
1513 int xIndent
= static_cast<int>(indentCount
* indentWidth
);
1514 DrawIndentGuide(surface
, lineVisible
, vsDraw
.lineHeight
, xIndent
+ xStart
, rcSegment
,
1515 (ll
->xHighlightGuide
== xIndent
));
1519 if (vsDraw
.viewWhitespace
!= wsInvisible
) {
1520 if (vsDraw
.WhiteSpaceVisible(inIndentation
)) {
1521 if (vsDraw
.whitespaceColours
.fore
.isSet
)
1522 textFore
= vsDraw
.whitespaceColours
.fore
;
1523 surface
->PenColour(textFore
);
1524 PRectangle
rcTab(rcSegment
.left
+ 1, rcSegment
.top
+ tabArrowHeight
,
1525 rcSegment
.right
- 1, rcSegment
.bottom
- vsDraw
.maxDescent
);
1526 if (customDrawTabArrow
== NULL
)
1527 DrawTabArrow(surface
, rcTab
, static_cast<int>(rcSegment
.top
+ vsDraw
.lineHeight
/ 2));
1529 customDrawTabArrow(surface
, rcTab
, static_cast<int>(rcSegment
.top
+ vsDraw
.lineHeight
/ 2));
1533 inIndentation
= false;
1534 if (vsDraw
.controlCharSymbol
>= 32) {
1535 // Using one font for all control characters so it can be controlled independently to ensure
1536 // the box goes around the characters tightly. Seems to be no way to work out what height
1537 // is taken by an individual character - internal leading gives varying results.
1538 FontAlias ctrlCharsFont
= vsDraw
.styles
[STYLE_CONTROLCHAR
].font
;
1539 char cc
[2] = { static_cast<char>(vsDraw
.controlCharSymbol
), '\0' };
1540 surface
->DrawTextNoClip(rcSegment
, ctrlCharsFont
,
1541 rcSegment
.top
+ vsDraw
.maxAscent
,
1542 cc
, 1, textBack
, textFore
);
1544 DrawTextBlob(surface
, vsDraw
, rcSegment
, ts
.representation
->stringRep
.c_str(),
1545 textBack
, textFore
, phasesDraw
== phasesOne
);
1549 // Normal text display
1550 if (vsDraw
.styles
[styleMain
].visible
) {
1551 if (phasesDraw
!= phasesOne
) {
1552 surface
->DrawTextTransparent(rcSegment
, textFont
,
1553 rcSegment
.top
+ vsDraw
.maxAscent
, ll
->chars
+ ts
.start
,
1554 i
- ts
.start
+ 1, textFore
);
1556 surface
->DrawTextNoClip(rcSegment
, textFont
,
1557 rcSegment
.top
+ vsDraw
.maxAscent
, ll
->chars
+ ts
.start
,
1558 i
- ts
.start
+ 1, textFore
, textBack
);
1561 if (vsDraw
.viewWhitespace
!= wsInvisible
||
1562 (inIndentation
&& vsDraw
.viewIndentationGuides
!= ivNone
)) {
1563 for (int cpos
= 0; cpos
<= i
- ts
.start
; cpos
++) {
1564 if (ll
->chars
[cpos
+ ts
.start
] == ' ') {
1565 if (vsDraw
.viewWhitespace
!= wsInvisible
) {
1566 if (vsDraw
.whitespaceColours
.fore
.isSet
)
1567 textFore
= vsDraw
.whitespaceColours
.fore
;
1568 if (vsDraw
.WhiteSpaceVisible(inIndentation
)) {
1569 XYPOSITION xmid
= (ll
->positions
[cpos
+ ts
.start
] + ll
->positions
[cpos
+ ts
.start
+ 1]) / 2;
1570 if ((phasesDraw
== phasesOne
) && drawWhitespaceBackground
) {
1571 textBack
= vsDraw
.whitespaceColours
.back
;
1573 ll
->positions
[cpos
+ ts
.start
] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
1575 ll
->positions
[cpos
+ ts
.start
+ 1] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
1577 surface
->FillRectangle(rcSpace
, textBack
);
1579 const int halfDotWidth
= vsDraw
.whitespaceSize
/ 2;
1580 PRectangle
rcDot(xmid
+ xStart
- halfDotWidth
- static_cast<XYPOSITION
>(subLineStart
),
1581 rcSegment
.top
+ vsDraw
.lineHeight
/ 2, 0.0f
, 0.0f
);
1582 rcDot
.right
= rcDot
.left
+ vsDraw
.whitespaceSize
;
1583 rcDot
.bottom
= rcDot
.top
+ vsDraw
.whitespaceSize
;
1584 surface
->FillRectangle(rcDot
, textFore
);
1587 if (inIndentation
&& vsDraw
.viewIndentationGuides
== ivReal
) {
1588 for (int indentCount
= static_cast<int>((ll
->positions
[cpos
+ ts
.start
] + epsilon
) / indentWidth
);
1589 indentCount
<= (ll
->positions
[cpos
+ ts
.start
+ 1] - epsilon
) / indentWidth
;
1591 if (indentCount
> 0) {
1592 int xIndent
= static_cast<int>(indentCount
* indentWidth
);
1593 DrawIndentGuide(surface
, lineVisible
, vsDraw
.lineHeight
, xIndent
+ xStart
, rcSegment
,
1594 (ll
->xHighlightGuide
== xIndent
));
1599 inIndentation
= false;
1604 if (ll
->hotspot
.Valid() && vsDraw
.hotspotUnderline
&& ll
->hotspot
.ContainsCharacter(iDoc
)) {
1605 PRectangle rcUL
= rcSegment
;
1606 rcUL
.top
= rcUL
.top
+ vsDraw
.maxAscent
+ 1;
1607 rcUL
.bottom
= rcUL
.top
+ 1;
1608 if (vsDraw
.hotspotColours
.fore
.isSet
)
1609 surface
->FillRectangle(rcUL
, vsDraw
.hotspotColours
.fore
);
1611 surface
->FillRectangle(rcUL
, textFore
);
1612 } else if (vsDraw
.styles
[styleMain
].underline
) {
1613 PRectangle rcUL
= rcSegment
;
1614 rcUL
.top
= rcUL
.top
+ vsDraw
.maxAscent
+ 1;
1615 rcUL
.bottom
= rcUL
.top
+ 1;
1616 surface
->FillRectangle(rcUL
, textFore
);
1618 } else if (rcSegment
.left
> rcLine
.right
) {
1624 void EditView::DrawIndentGuidesOverEmpty(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1625 int line
, int lineVisible
, PRectangle rcLine
, int xStart
, int subLine
) {
1626 if ((vsDraw
.viewIndentationGuides
== ivLookForward
|| vsDraw
.viewIndentationGuides
== ivLookBoth
)
1627 && (subLine
== 0)) {
1628 const int posLineStart
= model
.pdoc
->LineStart(line
);
1629 int indentSpace
= model
.pdoc
->GetLineIndentation(line
);
1630 int xStartText
= static_cast<int>(ll
->positions
[model
.pdoc
->GetLineIndentPosition(line
) - posLineStart
]);
1632 // Find the most recent line with some text
1634 int lineLastWithText
= line
;
1635 while (lineLastWithText
> Platform::Maximum(line
- 20, 0) && model
.pdoc
->IsWhiteLine(lineLastWithText
)) {
1638 if (lineLastWithText
< line
) {
1639 xStartText
= 100000; // Don't limit to visible indentation on empty line
1640 // This line is empty, so use indentation of last line with text
1641 int indentLastWithText
= model
.pdoc
->GetLineIndentation(lineLastWithText
);
1642 int isFoldHeader
= model
.pdoc
->GetLevel(lineLastWithText
) & SC_FOLDLEVELHEADERFLAG
;
1644 // Level is one more level than parent
1645 indentLastWithText
+= model
.pdoc
->IndentSize();
1647 if (vsDraw
.viewIndentationGuides
== ivLookForward
) {
1648 // In viLookForward mode, previous line only used if it is a fold header
1650 indentSpace
= Platform::Maximum(indentSpace
, indentLastWithText
);
1652 } else { // viLookBoth
1653 indentSpace
= Platform::Maximum(indentSpace
, indentLastWithText
);
1657 int lineNextWithText
= line
;
1658 while (lineNextWithText
< Platform::Minimum(line
+ 20, model
.pdoc
->LinesTotal()) && model
.pdoc
->IsWhiteLine(lineNextWithText
)) {
1661 if (lineNextWithText
> line
) {
1662 xStartText
= 100000; // Don't limit to visible indentation on empty line
1663 // This line is empty, so use indentation of first next line with text
1664 indentSpace
= Platform::Maximum(indentSpace
,
1665 model
.pdoc
->GetLineIndentation(lineNextWithText
));
1668 for (int indentPos
= model
.pdoc
->IndentSize(); indentPos
< indentSpace
; indentPos
+= model
.pdoc
->IndentSize()) {
1669 int xIndent
= static_cast<int>(indentPos
* vsDraw
.spaceWidth
);
1670 if (xIndent
< xStartText
) {
1671 DrawIndentGuide(surface
, lineVisible
, vsDraw
.lineHeight
, xIndent
+ xStart
, rcLine
,
1672 (ll
->xHighlightGuide
== xIndent
));
1678 void EditView::DrawLine(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, const LineLayout
*ll
,
1679 int line
, int lineVisible
, int xStart
, PRectangle rcLine
, int subLine
, DrawPhase phase
) {
1681 if (subLine
>= ll
->lines
) {
1682 DrawAnnotation(surface
, model
, vsDraw
, ll
, line
, xStart
, rcLine
, subLine
, phase
);
1683 return; // No further drawing
1686 // See if something overrides the line background color.
1687 const ColourOptional background
= vsDraw
.Background(model
.pdoc
->GetMark(line
), model
.caret
.active
, ll
->containsCaret
);
1689 const int posLineStart
= model
.pdoc
->LineStart(line
);
1691 const Range lineRange
= ll
->SubLineRange(subLine
);
1692 const XYACCUMULATOR subLineStart
= ll
->positions
[lineRange
.start
];
1694 if ((ll
->wrapIndent
!= 0) && (subLine
> 0)) {
1695 if (phase
& drawBack
) {
1696 DrawWrapIndentAndMarker(surface
, vsDraw
, ll
, xStart
, rcLine
, background
, customDrawWrapMarker
);
1698 xStart
+= static_cast<int>(ll
->wrapIndent
);
1701 if ((phasesDraw
!= phasesOne
) && (phase
& drawBack
)) {
1702 DrawBackground(surface
, model
, vsDraw
, ll
, rcLine
, lineRange
, posLineStart
, xStart
,
1703 subLine
, background
);
1704 DrawEOL(surface
, model
, vsDraw
, ll
, rcLine
, line
, lineRange
.end
,
1705 xStart
, subLine
, subLineStart
, background
);
1708 if (phase
& drawIndicatorsBack
) {
1709 DrawIndicators(surface
, model
, vsDraw
, ll
, line
, xStart
, rcLine
, subLine
, lineRange
.end
, true, model
.hoverIndicatorPos
);
1710 DrawEdgeLine(surface
, vsDraw
, ll
, rcLine
, lineRange
, xStart
);
1711 DrawMarkUnderline(surface
, model
, vsDraw
, line
, rcLine
);
1714 if (phase
& drawText
) {
1715 DrawForeground(surface
, model
, vsDraw
, ll
, lineVisible
, rcLine
, lineRange
, posLineStart
, xStart
,
1716 subLine
, background
);
1719 if (phase
& drawIndentationGuides
) {
1720 DrawIndentGuidesOverEmpty(surface
, model
, vsDraw
, ll
, line
, lineVisible
, rcLine
, xStart
, subLine
);
1723 if (phase
& drawIndicatorsFore
) {
1724 DrawIndicators(surface
, model
, vsDraw
, ll
, line
, xStart
, rcLine
, subLine
, lineRange
.end
, false, model
.hoverIndicatorPos
);
1727 // End of the drawing of the current line
1728 if (phasesDraw
== phasesOne
) {
1729 DrawEOL(surface
, model
, vsDraw
, ll
, rcLine
, line
, lineRange
.end
,
1730 xStart
, subLine
, subLineStart
, background
);
1733 if (!hideSelection
&& (phase
& drawSelectionTranslucent
)) {
1734 DrawTranslucentSelection(surface
, model
, vsDraw
, ll
, line
, rcLine
, subLine
, lineRange
, xStart
);
1737 if (phase
& drawLineTranslucent
) {
1738 DrawTranslucentLineState(surface
, model
, vsDraw
, ll
, line
, rcLine
);
1742 static void DrawFoldLines(Surface
*surface
, const EditModel
&model
, const ViewStyle
&vsDraw
, int line
, PRectangle rcLine
) {
1743 bool expanded
= model
.cs
.GetExpanded(line
);
1744 const int level
= model
.pdoc
->GetLevel(line
);
1745 const int levelNext
= model
.pdoc
->GetLevel(line
+ 1);
1746 if ((level
& SC_FOLDLEVELHEADERFLAG
) &&
1747 ((level
& SC_FOLDLEVELNUMBERMASK
) < (levelNext
& SC_FOLDLEVELNUMBERMASK
))) {
1748 // Paint the line above the fold
1749 if ((expanded
&& (model
.foldFlags
& SC_FOLDFLAG_LINEBEFORE_EXPANDED
))
1751 (!expanded
&& (model
.foldFlags
& SC_FOLDFLAG_LINEBEFORE_CONTRACTED
))) {
1752 PRectangle rcFoldLine
= rcLine
;
1753 rcFoldLine
.bottom
= rcFoldLine
.top
+ 1;
1754 surface
->FillRectangle(rcFoldLine
, vsDraw
.styles
[STYLE_DEFAULT
].fore
);
1756 // Paint the line below the fold
1757 if ((expanded
&& (model
.foldFlags
& SC_FOLDFLAG_LINEAFTER_EXPANDED
))
1759 (!expanded
&& (model
.foldFlags
& SC_FOLDFLAG_LINEAFTER_CONTRACTED
))) {
1760 PRectangle rcFoldLine
= rcLine
;
1761 rcFoldLine
.top
= rcFoldLine
.bottom
- 1;
1762 surface
->FillRectangle(rcFoldLine
, vsDraw
.styles
[STYLE_DEFAULT
].fore
);
1767 void EditView::PaintText(Surface
*surfaceWindow
, const EditModel
&model
, PRectangle rcArea
,
1768 PRectangle rcClient
, const ViewStyle
&vsDraw
) {
1769 // Allow text at start of line to overlap 1 pixel into the margin as this displays
1770 // serifs and italic stems for aliased text.
1771 const int leftTextOverlap
= ((model
.xOffset
== 0) && (vsDraw
.leftMarginWidth
> 0)) ? 1 : 0;
1774 if (rcArea
.right
> vsDraw
.textStart
- leftTextOverlap
) {
1776 Surface
*surface
= surfaceWindow
;
1778 surface
= pixmapLine
;
1779 PLATFORM_ASSERT(pixmapLine
->Initialised());
1781 surface
->SetUnicodeMode(SC_CP_UTF8
== model
.pdoc
->dbcsCodePage
);
1782 surface
->SetDBCSMode(model
.pdoc
->dbcsCodePage
);
1784 const Point ptOrigin
= model
.GetVisibleOriginInMain();
1786 const int screenLinePaintFirst
= static_cast<int>(rcArea
.top
) / vsDraw
.lineHeight
;
1787 const int xStart
= vsDraw
.textStart
- model
.xOffset
+ static_cast<int>(ptOrigin
.x
);
1789 SelectionPosition posCaret
= model
.sel
.RangeMain().caret
;
1790 if (model
.posDrag
.IsValid())
1791 posCaret
= model
.posDrag
;
1792 const int lineCaret
= model
.pdoc
->LineFromPosition(posCaret
.Position());
1794 PRectangle rcTextArea
= rcClient
;
1795 if (vsDraw
.marginInside
) {
1796 rcTextArea
.left
+= vsDraw
.textStart
;
1797 rcTextArea
.right
-= vsDraw
.rightMarginWidth
;
1799 rcTextArea
= rcArea
;
1802 // Remove selection margin from drawing area so text will not be drawn
1803 // on it in unbuffered mode.
1804 if (!bufferedDraw
&& vsDraw
.marginInside
) {
1805 PRectangle rcClipText
= rcTextArea
;
1806 rcClipText
.left
-= leftTextOverlap
;
1807 surfaceWindow
->SetClip(rcClipText
);
1810 // Loop on visible lines
1811 //double durLayout = 0.0;
1812 //double durPaint = 0.0;
1813 //double durCopy = 0.0;
1814 //ElapsedTime etWhole;
1816 const bool bracesIgnoreStyle
= ((vsDraw
.braceHighlightIndicatorSet
&& (model
.bracesMatchStyle
== STYLE_BRACELIGHT
)) ||
1817 (vsDraw
.braceBadLightIndicatorSet
&& (model
.bracesMatchStyle
== STYLE_BRACEBAD
)));
1819 int lineDocPrevious
= -1; // Used to avoid laying out one document line multiple times
1820 AutoLineLayout
ll(llc
, 0);
1821 std::vector
<DrawPhase
> phases
;
1822 if ((phasesDraw
== phasesMultiple
) && !bufferedDraw
) {
1823 for (DrawPhase phase
= drawBack
; phase
<= drawCarets
; phase
= static_cast<DrawPhase
>(phase
* 2)) {
1824 phases
.push_back(phase
);
1827 phases
.push_back(drawAll
);
1829 for (std::vector
<DrawPhase
>::iterator it
= phases
.begin(); it
!= phases
.end(); ++it
) {
1832 ypos
+= screenLinePaintFirst
* vsDraw
.lineHeight
;
1833 int yposScreen
= screenLinePaintFirst
* vsDraw
.lineHeight
;
1834 int visibleLine
= model
.TopLineOfMain() + screenLinePaintFirst
;
1835 while (visibleLine
< model
.cs
.LinesDisplayed() && yposScreen
< rcArea
.bottom
) {
1837 const int lineDoc
= model
.cs
.DocFromDisplay(visibleLine
);
1838 // Only visible lines should be handled by the code within the loop
1839 PLATFORM_ASSERT(model
.cs
.GetVisible(lineDoc
));
1840 const int lineStartSet
= model
.cs
.DisplayFromDoc(lineDoc
);
1841 const int subLine
= visibleLine
- lineStartSet
;
1843 // Copy this line and its styles from the document into local arrays
1844 // and determine the x position at which each character starts.
1846 if (lineDoc
!= lineDocPrevious
) {
1848 ll
.Set(RetrieveLineLayout(lineDoc
, model
));
1849 LayoutLine(model
, lineDoc
, surface
, vsDraw
, ll
, model
.wrapWidth
);
1850 lineDocPrevious
= lineDoc
;
1852 //durLayout += et.Duration(true);
1855 ll
->containsCaret
= !hideSelection
&& (lineDoc
== lineCaret
);
1856 ll
->hotspot
= model
.GetHotSpotRange();
1858 PRectangle rcLine
= rcTextArea
;
1859 rcLine
.top
= static_cast<XYPOSITION
>(ypos
);
1860 rcLine
.bottom
= static_cast<XYPOSITION
>(ypos
+ vsDraw
.lineHeight
);
1862 Range
rangeLine(model
.pdoc
->LineStart(lineDoc
), model
.pdoc
->LineStart(lineDoc
+ 1));
1864 // Highlight the current braces if any
1865 ll
->SetBracesHighlight(rangeLine
, model
.braces
, static_cast<char>(model
.bracesMatchStyle
),
1866 static_cast<int>(model
.highlightGuideColumn
* vsDraw
.spaceWidth
), bracesIgnoreStyle
);
1868 if (leftTextOverlap
&& bufferedDraw
) {
1869 PRectangle rcSpacer
= rcLine
;
1870 rcSpacer
.right
= rcSpacer
.left
;
1872 surface
->FillRectangle(rcSpacer
, vsDraw
.styles
[STYLE_DEFAULT
].back
);
1875 DrawLine(surface
, model
, vsDraw
, ll
, lineDoc
, visibleLine
, xStart
, rcLine
, subLine
, *it
);
1876 //durPaint += et.Duration(true);
1878 // Restore the previous styles for the brace highlights in case layout is in cache.
1879 ll
->RestoreBracesHighlight(rangeLine
, model
.braces
, bracesIgnoreStyle
);
1881 if (*it
& drawFoldLines
) {
1882 DrawFoldLines(surface
, model
, vsDraw
, lineDoc
, rcLine
);
1885 if (*it
& drawCarets
) {
1886 DrawCarets(surface
, model
, vsDraw
, ll
, lineDoc
, xStart
, rcLine
, subLine
);
1890 Point from
= Point::FromInts(vsDraw
.textStart
- leftTextOverlap
, 0);
1891 PRectangle rcCopyArea
= PRectangle::FromInts(vsDraw
.textStart
- leftTextOverlap
, yposScreen
,
1892 static_cast<int>(rcClient
.right
- vsDraw
.rightMarginWidth
),
1893 yposScreen
+ vsDraw
.lineHeight
);
1894 surfaceWindow
->Copy(rcCopyArea
, from
, *pixmapLine
);
1897 lineWidthMaxSeen
= Platform::Maximum(
1898 lineWidthMaxSeen
, static_cast<int>(ll
->positions
[ll
->numCharsInLine
]));
1899 //durCopy += et.Duration(true);
1902 if (!bufferedDraw
) {
1903 ypos
+= vsDraw
.lineHeight
;
1906 yposScreen
+= vsDraw
.lineHeight
;
1911 //if (durPaint < 0.00000001)
1912 // durPaint = 0.00000001;
1914 // Right column limit indicator
1915 PRectangle rcBeyondEOF
= (vsDraw
.marginInside
) ? rcClient
: rcArea
;
1916 rcBeyondEOF
.left
= static_cast<XYPOSITION
>(vsDraw
.textStart
);
1917 rcBeyondEOF
.right
= rcBeyondEOF
.right
- ((vsDraw
.marginInside
) ? vsDraw
.rightMarginWidth
: 0);
1918 rcBeyondEOF
.top
= static_cast<XYPOSITION
>((model
.cs
.LinesDisplayed() - model
.TopLineOfMain()) * vsDraw
.lineHeight
);
1919 if (rcBeyondEOF
.top
< rcBeyondEOF
.bottom
) {
1920 surfaceWindow
->FillRectangle(rcBeyondEOF
, vsDraw
.styles
[STYLE_DEFAULT
].back
);
1921 if (vsDraw
.edgeState
== EDGE_LINE
) {
1922 int edgeX
= static_cast<int>(vsDraw
.theEdge
* vsDraw
.spaceWidth
);
1923 rcBeyondEOF
.left
= static_cast<XYPOSITION
>(edgeX
+ xStart
);
1924 rcBeyondEOF
.right
= rcBeyondEOF
.left
+ 1;
1925 surfaceWindow
->FillRectangle(rcBeyondEOF
, vsDraw
.edgecolour
);
1928 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
1930 //Platform::DebugPrintf(
1931 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
1932 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
1936 // Space (3 space characters) between line numbers and text when printing.
1937 #define lineNumberPrintSpace " "
1939 static ColourDesired
InvertedLight(ColourDesired orig
) {
1940 unsigned int r
= orig
.GetRed();
1941 unsigned int g
= orig
.GetGreen();
1942 unsigned int b
= orig
.GetBlue();
1943 unsigned int l
= (r
+ g
+ b
) / 3; // There is a better calculation for this that matches human eye
1944 unsigned int il
= 0xff - l
;
1946 return ColourDesired(0xff, 0xff, 0xff);
1950 return ColourDesired(Platform::Minimum(r
, 0xff), Platform::Minimum(g
, 0xff), Platform::Minimum(b
, 0xff));
1953 long EditView::FormatRange(bool draw
, Sci_RangeToFormat
*pfr
, Surface
*surface
, Surface
*surfaceMeasure
,
1954 const EditModel
&model
, const ViewStyle
&vs
) {
1955 // Can't use measurements cached for screen
1958 ViewStyle
vsPrint(vs
);
1959 vsPrint
.technology
= SC_TECHNOLOGY_DEFAULT
;
1961 // Modify the view style for printing as do not normally want any of the transient features to be printed
1962 // Printing supports only the line number margin.
1963 int lineNumberIndex
= -1;
1964 for (int margin
= 0; margin
<= SC_MAX_MARGIN
; margin
++) {
1965 if ((vsPrint
.ms
[margin
].style
== SC_MARGIN_NUMBER
) && (vsPrint
.ms
[margin
].width
> 0)) {
1966 lineNumberIndex
= margin
;
1968 vsPrint
.ms
[margin
].width
= 0;
1971 vsPrint
.fixedColumnWidth
= 0;
1972 vsPrint
.zoomLevel
= printParameters
.magnification
;
1973 // Don't show indentation guides
1974 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
1975 vsPrint
.viewIndentationGuides
= ivNone
;
1976 // Don't show the selection when printing
1977 vsPrint
.selColours
.back
.isSet
= false;
1978 vsPrint
.selColours
.fore
.isSet
= false;
1979 vsPrint
.selAlpha
= SC_ALPHA_NOALPHA
;
1980 vsPrint
.selAdditionalAlpha
= SC_ALPHA_NOALPHA
;
1981 vsPrint
.whitespaceColours
.back
.isSet
= false;
1982 vsPrint
.whitespaceColours
.fore
.isSet
= false;
1983 vsPrint
.showCaretLineBackground
= false;
1984 vsPrint
.alwaysShowCaretLineBackground
= false;
1985 // Don't highlight matching braces using indicators
1986 vsPrint
.braceHighlightIndicatorSet
= false;
1987 vsPrint
.braceBadLightIndicatorSet
= false;
1989 // Set colours for printing according to users settings
1990 for (size_t sty
= 0; sty
< vsPrint
.styles
.size(); sty
++) {
1991 if (printParameters
.colourMode
== SC_PRINT_INVERTLIGHT
) {
1992 vsPrint
.styles
[sty
].fore
= InvertedLight(vsPrint
.styles
[sty
].fore
);
1993 vsPrint
.styles
[sty
].back
= InvertedLight(vsPrint
.styles
[sty
].back
);
1994 } else if (printParameters
.colourMode
== SC_PRINT_BLACKONWHITE
) {
1995 vsPrint
.styles
[sty
].fore
= ColourDesired(0, 0, 0);
1996 vsPrint
.styles
[sty
].back
= ColourDesired(0xff, 0xff, 0xff);
1997 } else if (printParameters
.colourMode
== SC_PRINT_COLOURONWHITE
) {
1998 vsPrint
.styles
[sty
].back
= ColourDesired(0xff, 0xff, 0xff);
1999 } else if (printParameters
.colourMode
== SC_PRINT_COLOURONWHITEDEFAULTBG
) {
2000 if (sty
<= STYLE_DEFAULT
) {
2001 vsPrint
.styles
[sty
].back
= ColourDesired(0xff, 0xff, 0xff);
2005 // White background for the line numbers
2006 vsPrint
.styles
[STYLE_LINENUMBER
].back
= ColourDesired(0xff, 0xff, 0xff);
2008 // Printing uses different margins, so reset screen margins
2009 vsPrint
.leftMarginWidth
= 0;
2010 vsPrint
.rightMarginWidth
= 0;
2012 vsPrint
.Refresh(*surfaceMeasure
, model
.pdoc
->tabInChars
);
2013 // Determining width must happen after fonts have been realised in Refresh
2014 int lineNumberWidth
= 0;
2015 if (lineNumberIndex
>= 0) {
2016 lineNumberWidth
= static_cast<int>(surfaceMeasure
->WidthText(vsPrint
.styles
[STYLE_LINENUMBER
].font
,
2017 "99999" lineNumberPrintSpace
, 5 + static_cast<int>(strlen(lineNumberPrintSpace
))));
2018 vsPrint
.ms
[lineNumberIndex
].width
= lineNumberWidth
;
2019 vsPrint
.Refresh(*surfaceMeasure
, model
.pdoc
->tabInChars
); // Recalculate fixedColumnWidth
2022 int linePrintStart
= model
.pdoc
->LineFromPosition(static_cast<int>(pfr
->chrg
.cpMin
));
2023 int linePrintLast
= linePrintStart
+ (pfr
->rc
.bottom
- pfr
->rc
.top
) / vsPrint
.lineHeight
- 1;
2024 if (linePrintLast
< linePrintStart
)
2025 linePrintLast
= linePrintStart
;
2026 int linePrintMax
= model
.pdoc
->LineFromPosition(static_cast<int>(pfr
->chrg
.cpMax
));
2027 if (linePrintLast
> linePrintMax
)
2028 linePrintLast
= linePrintMax
;
2029 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
2030 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
2031 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
2032 int endPosPrint
= model
.pdoc
->Length();
2033 if (linePrintLast
< model
.pdoc
->LinesTotal())
2034 endPosPrint
= model
.pdoc
->LineStart(linePrintLast
+ 1);
2036 // Ensure we are styled to where we are formatting.
2037 model
.pdoc
->EnsureStyledTo(endPosPrint
);
2039 int xStart
= vsPrint
.fixedColumnWidth
+ pfr
->rc
.left
;
2040 int ypos
= pfr
->rc
.top
;
2042 int lineDoc
= linePrintStart
;
2044 int nPrintPos
= static_cast<int>(pfr
->chrg
.cpMin
);
2045 int visibleLine
= 0;
2046 int widthPrint
= pfr
->rc
.right
- pfr
->rc
.left
- vsPrint
.fixedColumnWidth
;
2047 if (printParameters
.wrapState
== eWrapNone
)
2048 widthPrint
= LineLayout::wrapWidthInfinite
;
2050 while (lineDoc
<= linePrintLast
&& ypos
< pfr
->rc
.bottom
) {
2052 // When printing, the hdc and hdcTarget may be the same, so
2053 // changing the state of surfaceMeasure may change the underlying
2054 // state of surface. Therefore, any cached state is discarded before
2055 // using each surface.
2056 surfaceMeasure
->FlushCachedState();
2058 // Copy this line and its styles from the document into local arrays
2059 // and determine the x position at which each character starts.
2060 LineLayout
ll(model
.pdoc
->LineStart(lineDoc
+ 1) - model
.pdoc
->LineStart(lineDoc
) + 1);
2061 LayoutLine(model
, lineDoc
, surfaceMeasure
, vsPrint
, &ll
, widthPrint
);
2063 ll
.containsCaret
= false;
2065 PRectangle rcLine
= PRectangle::FromInts(
2069 ypos
+ vsPrint
.lineHeight
);
2071 // When document line is wrapped over multiple display lines, find where
2072 // to start printing from to ensure a particular position is on the first
2073 // line of the page.
2074 if (visibleLine
== 0) {
2075 int startWithinLine
= nPrintPos
- model
.pdoc
->LineStart(lineDoc
);
2076 for (int iwl
= 0; iwl
< ll
.lines
- 1; iwl
++) {
2077 if (ll
.LineStart(iwl
) <= startWithinLine
&& ll
.LineStart(iwl
+ 1) >= startWithinLine
) {
2082 if (ll
.lines
> 1 && startWithinLine
>= ll
.LineStart(ll
.lines
- 1)) {
2083 visibleLine
= -(ll
.lines
- 1);
2087 if (draw
&& lineNumberWidth
&&
2088 (ypos
+ vsPrint
.lineHeight
<= pfr
->rc
.bottom
) &&
2089 (visibleLine
>= 0)) {
2091 sprintf(number
, "%d" lineNumberPrintSpace
, lineDoc
+ 1);
2092 PRectangle rcNumber
= rcLine
;
2093 rcNumber
.right
= rcNumber
.left
+ lineNumberWidth
;
2095 rcNumber
.left
= rcNumber
.right
- surfaceMeasure
->WidthText(
2096 vsPrint
.styles
[STYLE_LINENUMBER
].font
, number
, static_cast<int>(strlen(number
)));
2097 surface
->FlushCachedState();
2098 surface
->DrawTextNoClip(rcNumber
, vsPrint
.styles
[STYLE_LINENUMBER
].font
,
2099 static_cast<XYPOSITION
>(ypos
+ vsPrint
.maxAscent
), number
, static_cast<int>(strlen(number
)),
2100 vsPrint
.styles
[STYLE_LINENUMBER
].fore
,
2101 vsPrint
.styles
[STYLE_LINENUMBER
].back
);
2105 surface
->FlushCachedState();
2107 for (int iwl
= 0; iwl
< ll
.lines
; iwl
++) {
2108 if (ypos
+ vsPrint
.lineHeight
<= pfr
->rc
.bottom
) {
2109 if (visibleLine
>= 0) {
2111 rcLine
.top
= static_cast<XYPOSITION
>(ypos
);
2112 rcLine
.bottom
= static_cast<XYPOSITION
>(ypos
+ vsPrint
.lineHeight
);
2113 DrawLine(surface
, model
, vsPrint
, &ll
, lineDoc
, visibleLine
, xStart
, rcLine
, iwl
, drawAll
);
2115 ypos
+= vsPrint
.lineHeight
;
2118 if (iwl
== ll
.lines
- 1)
2119 nPrintPos
= model
.pdoc
->LineStart(lineDoc
+ 1);
2121 nPrintPos
+= ll
.LineStart(iwl
+ 1) - ll
.LineStart(iwl
);
2128 // Clear cache so measurements are not used for screen