1 // Scintilla source code edit control
2 /** @file ViewStyle.cxx
3 ** Store information on how the document is to be viewed.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <cstddef>
9 #include <cassert>
10 #include <cstring>
11
12 #include <stdexcept>
13 #include <vector>
14 #include <map>
15 #include <algorithm>
16 #include <memory>
17
18 #include "Platform.h"
19
20 #include "Scintilla.h"
21 #include "Position.h"
22 #include "UniqueString.h"
23 #include "Indicator.h"
24 #include "XPM.h"
25 #include "LineMarker.h"
26 #include "Style.h"
27 #include "ViewStyle.h"
28
29 using namespace Scintilla;
30
MarginStyle(int style_,int width_,int mask_)31 MarginStyle::MarginStyle(int style_, int width_, int mask_) noexcept :
32 style(style_), width(width_), mask(mask_), sensitive(false), cursor(SC_CURSORREVERSEARROW) {
33 }
34
35 FontRealised::FontRealised() noexcept = default;
36
~FontRealised()37 FontRealised::~FontRealised() {
38 font.Release();
39 }
40
Realise(Surface & surface,int zoomLevel,int technology,const FontSpecification & fs)41 void FontRealised::Realise(Surface &surface, int zoomLevel, int technology, const FontSpecification &fs) {
42 PLATFORM_ASSERT(fs.fontName);
43 sizeZoomed = fs.size + zoomLevel * SC_FONT_SIZE_MULTIPLIER;
44 if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
45 sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
46
47 const float deviceHeight = static_cast<float>(surface.DeviceHeightFont(sizeZoomed));
48 const FontParameters fp(fs.fontName, deviceHeight / SC_FONT_SIZE_MULTIPLIER, fs.weight, fs.italic, fs.extraFontFlag, technology, fs.characterSet);
49 font.Create(fp);
50
51 ascent = static_cast<unsigned int>(surface.Ascent(font));
52 descent = static_cast<unsigned int>(surface.Descent(font));
53 capitalHeight = surface.Ascent(font) - surface.InternalLeading(font);
54 aveCharWidth = surface.AverageCharWidth(font);
55 spaceWidth = surface.WidthText(font, " ", 1);
56 }
57
ViewStyle()58 ViewStyle::ViewStyle() : markers(MARKER_MAX + 1), indicators(INDICATOR_MAX + 1) {
59 Init();
60 }
61
62 // Copy constructor only called when printing copies the screen ViewStyle so it can be
63 // modified for printing styles.
ViewStyle(const ViewStyle & source)64 ViewStyle::ViewStyle(const ViewStyle &source) : markers(MARKER_MAX + 1), indicators(INDICATOR_MAX + 1) {
65 Init(source.styles.size());
66 styles = source.styles;
67 for (size_t sty=0; sty<source.styles.size(); sty++) {
68 // Can't just copy fontName as its lifetime is relative to its owning ViewStyle
69 styles[sty].fontName = fontNames.Save(source.styles[sty].fontName);
70 }
71 nextExtendedStyle = source.nextExtendedStyle;
72 markers = source.markers;
73 CalcLargestMarkerHeight();
74
75 indicators = source.indicators;
76
77 indicatorsDynamic = source.indicatorsDynamic;
78 indicatorsSetFore = source.indicatorsSetFore;
79
80 selColours = source.selColours;
81 selAdditionalForeground = source.selAdditionalForeground;
82 selAdditionalBackground = source.selAdditionalBackground;
83 selBackground2 = source.selBackground2;
84 selAlpha = source.selAlpha;
85 selAdditionalAlpha = source.selAdditionalAlpha;
86 selEOLFilled = source.selEOLFilled;
87
88 foldmarginColour = source.foldmarginColour;
89 foldmarginHighlightColour = source.foldmarginHighlightColour;
90
91 hotspotColours = source.hotspotColours;
92 hotspotUnderline = source.hotspotUnderline;
93 hotspotSingleLine = source.hotspotSingleLine;
94
95 whitespaceColours = source.whitespaceColours;
96 controlCharSymbol = source.controlCharSymbol;
97 controlCharWidth = source.controlCharWidth;
98 selbar = source.selbar;
99 selbarlight = source.selbarlight;
100 caretcolour = source.caretcolour;
101 additionalCaretColour = source.additionalCaretColour;
102 caretLineFrame = source.caretLineFrame;
103 showCaretLineBackground = source.showCaretLineBackground;
104 alwaysShowCaretLineBackground = source.alwaysShowCaretLineBackground;
105 caretLineBackground = source.caretLineBackground;
106 caretLineAlpha = source.caretLineAlpha;
107 caretStyle = source.caretStyle;
108 caretWidth = source.caretWidth;
109 someStylesProtected = false;
110 someStylesForceCase = false;
111 leftMarginWidth = source.leftMarginWidth;
112 rightMarginWidth = source.rightMarginWidth;
113 ms = source.ms;
114 maskInLine = source.maskInLine;
115 maskDrawInText = source.maskDrawInText;
116 fixedColumnWidth = source.fixedColumnWidth;
117 marginInside = source.marginInside;
118 textStart = source.textStart;
119 zoomLevel = source.zoomLevel;
120 viewWhitespace = source.viewWhitespace;
121 tabDrawMode = source.tabDrawMode;
122 whitespaceSize = source.whitespaceSize;
123 viewIndentationGuides = source.viewIndentationGuides;
124 viewEOL = source.viewEOL;
125 extraFontFlag = source.extraFontFlag;
126 extraAscent = source.extraAscent;
127 extraDescent = source.extraDescent;
128 marginStyleOffset = source.marginStyleOffset;
129 annotationVisible = source.annotationVisible;
130 annotationStyleOffset = source.annotationStyleOffset;
131 eolAnnotationVisible = source.eolAnnotationVisible;
132 eolAnnotationStyleOffset = source.eolAnnotationStyleOffset;
133 braceHighlightIndicatorSet = source.braceHighlightIndicatorSet;
134 braceHighlightIndicator = source.braceHighlightIndicator;
135 braceBadLightIndicatorSet = source.braceBadLightIndicatorSet;
136 braceBadLightIndicator = source.braceBadLightIndicator;
137
138 edgeState = source.edgeState;
139 theEdge = source.theEdge;
140 theMultiEdge = source.theMultiEdge;
141
142 marginNumberPadding = source.marginNumberPadding;
143 ctrlCharPadding = source.ctrlCharPadding;
144 lastSegItalicsOffset = source.lastSegItalicsOffset;
145
146 wrapState = source.wrapState;
147 wrapVisualFlags = source.wrapVisualFlags;
148 wrapVisualFlagsLocation = source.wrapVisualFlagsLocation;
149 wrapVisualStartIndent = source.wrapVisualStartIndent;
150 wrapIndentMode = source.wrapIndentMode;
151 }
152
~ViewStyle()153 ViewStyle::~ViewStyle() {
154 styles.clear();
155 fonts.clear();
156 }
157
CalculateMarginWidthAndMask()158 void ViewStyle::CalculateMarginWidthAndMask() noexcept {
159 fixedColumnWidth = marginInside ? leftMarginWidth : 0;
160 maskInLine = 0xffffffff;
161 int maskDefinedMarkers = 0;
162 for (const MarginStyle &m : ms) {
163 fixedColumnWidth += m.width;
164 if (m.width > 0)
165 maskInLine &= ~m.mask;
166 maskDefinedMarkers |= m.mask;
167 }
168 maskDrawInText = 0;
169 for (int markBit = 0; markBit < 32; markBit++) {
170 const int maskBit = 1U << markBit;
171 switch (markers[markBit].markType) {
172 case SC_MARK_EMPTY:
173 maskInLine &= ~maskBit;
174 break;
175 case SC_MARK_BACKGROUND:
176 case SC_MARK_UNDERLINE:
177 maskInLine &= ~maskBit;
178 maskDrawInText |= maskDefinedMarkers & maskBit;
179 break;
180 }
181 }
182 }
183
Init(size_t stylesSize_)184 void ViewStyle::Init(size_t stylesSize_) {
185 AllocStyles(stylesSize_);
186 nextExtendedStyle = 256;
187 fontNames.Clear();
188 ResetDefaultStyle();
189
190 // There are no image markers by default, so no need for calling CalcLargestMarkerHeight()
191 largestMarkerHeight = 0;
192
193 indicators[0] = Indicator(INDIC_SQUIGGLE, ColourDesired(0, 0x7f, 0));
194 indicators[1] = Indicator(INDIC_TT, ColourDesired(0, 0, 0xff));
195 indicators[2] = Indicator(INDIC_PLAIN, ColourDesired(0xff, 0, 0));
196
197 technology = SC_TECHNOLOGY_DEFAULT;
198 indicatorsDynamic = false;
199 indicatorsSetFore = false;
200 lineHeight = 1;
201 lineOverlap = 0;
202 maxAscent = 1;
203 maxDescent = 1;
204 aveCharWidth = 8;
205 spaceWidth = 8;
206 tabWidth = spaceWidth * 8;
207
208 selColours.fore = ColourOptional(ColourDesired(0xff, 0, 0));
209 selColours.back = ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0), true);
210 selAdditionalForeground = ColourDesired(0xff, 0, 0);
211 selAdditionalBackground = ColourDesired(0xd7, 0xd7, 0xd7);
212 selBackground2 = ColourDesired(0xb0, 0xb0, 0xb0);
213 selAlpha = SC_ALPHA_NOALPHA;
214 selAdditionalAlpha = SC_ALPHA_NOALPHA;
215 selEOLFilled = false;
216
217 foldmarginColour = ColourOptional(ColourDesired(0xff, 0, 0));
218 foldmarginHighlightColour = ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0));
219
220 whitespaceColours.fore = ColourOptional();
221 whitespaceColours.back = ColourOptional(ColourDesired(0xff, 0xff, 0xff));
222 controlCharSymbol = 0; /* Draw the control characters */
223 controlCharWidth = 0;
224 selbar = Platform::Chrome();
225 selbarlight = Platform::ChromeHighlight();
226 styles[STYLE_LINENUMBER].fore = ColourDesired(0, 0, 0);
227 styles[STYLE_LINENUMBER].back = Platform::Chrome();
228 caretcolour = ColourDesired(0, 0, 0);
229 additionalCaretColour = ColourDesired(0x7f, 0x7f, 0x7f);
230 caretLineFrame = 0;
231 showCaretLineBackground = false;
232 alwaysShowCaretLineBackground = false;
233 caretLineBackground = ColourDesired(0xff, 0xff, 0);
234 caretLineAlpha = SC_ALPHA_NOALPHA;
235 caretStyle = CARETSTYLE_LINE;
236 caretWidth = 1;
237 someStylesProtected = false;
238 someStylesForceCase = false;
239
240 hotspotColours.fore = ColourOptional(ColourDesired(0, 0, 0xff));
241 hotspotColours.back = ColourOptional(ColourDesired(0xff, 0xff, 0xff));
242 hotspotUnderline = true;
243 hotspotSingleLine = true;
244
245 leftMarginWidth = 1;
246 rightMarginWidth = 1;
247 ms.resize(SC_MAX_MARGIN + 1);
248 ms[0] = MarginStyle(SC_MARGIN_NUMBER);
249 ms[1] = MarginStyle(SC_MARGIN_SYMBOL, 16, ~SC_MASK_FOLDERS);
250 ms[2] = MarginStyle(SC_MARGIN_SYMBOL);
251 marginInside = true;
252 CalculateMarginWidthAndMask();
253 textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
254 zoomLevel = 0;
255 viewWhitespace = wsInvisible;
256 tabDrawMode = tdLongArrow;
257 whitespaceSize = 1;
258 viewIndentationGuides = ivNone;
259 viewEOL = false;
260 extraFontFlag = 0;
261 extraAscent = 0;
262 extraDescent = 0;
263 marginStyleOffset = 0;
264 annotationVisible = ANNOTATION_HIDDEN;
265 annotationStyleOffset = 0;
266 eolAnnotationVisible = EOLANNOTATION_HIDDEN;
267 eolAnnotationStyleOffset = 0;
268 braceHighlightIndicatorSet = false;
269 braceHighlightIndicator = 0;
270 braceBadLightIndicatorSet = false;
271 braceBadLightIndicator = 0;
272
273 edgeState = EDGE_NONE;
274 theEdge = EdgeProperties(0, ColourDesired(0xc0, 0xc0, 0xc0));
275
276 marginNumberPadding = 3;
277 ctrlCharPadding = 3; // +3 For a blank on front and rounded edge each side
278 lastSegItalicsOffset = 2;
279
280 wrapState = WrapMode::none;
281 wrapVisualFlags = 0;
282 wrapVisualFlagsLocation = 0;
283 wrapVisualStartIndent = 0;
284 wrapIndentMode = SC_WRAPINDENT_FIXED;
285 }
286
Refresh(Surface & surface,int tabInChars)287 void ViewStyle::Refresh(Surface &surface, int tabInChars) {
288 fonts.clear();
289
290 selbar = Platform::Chrome();
291 selbarlight = Platform::ChromeHighlight();
292
293 // Apply the extra font flag which controls text drawing quality to each style.
294 for (Style &style : styles) {
295 style.extraFontFlag = extraFontFlag;
296 }
297
298 // Create a FontRealised object for each unique font in the styles.
299 CreateAndAddFont(styles[STYLE_DEFAULT]);
300 for (const Style &style : styles) {
301 CreateAndAddFont(style);
302 }
303
304 // Ask platform to allocate each unique font.
305 for (std::pair<const FontSpecification, std::unique_ptr<FontRealised>> &font : fonts) {
306 font.second->Realise(surface, zoomLevel, technology, font.first);
307 }
308
309 // Set the platform font handle and measurements for each style.
310 for (Style &style : styles) {
311 FontRealised *fr = Find(style);
312 style.Copy(fr->font, *fr);
313 }
314
315 indicatorsDynamic = std::any_of(indicators.cbegin(), indicators.cend(),
316 [](const Indicator &indicator) noexcept { return indicator.IsDynamic(); });
317
318 indicatorsSetFore = std::any_of(indicators.cbegin(), indicators.cend(),
319 [](const Indicator &indicator) noexcept { return indicator.OverridesTextFore(); });
320
321 maxAscent = 1;
322 maxDescent = 1;
323 FindMaxAscentDescent();
324 maxAscent += extraAscent;
325 maxDescent += extraDescent;
326 lineHeight = maxAscent + maxDescent;
327 lineOverlap = lineHeight / 10;
328 if (lineOverlap < 2)
329 lineOverlap = 2;
330 if (lineOverlap > lineHeight)
331 lineOverlap = lineHeight;
332
333 someStylesProtected = std::any_of(styles.cbegin(), styles.cend(),
334 [](const Style &style) noexcept { return style.IsProtected(); });
335
336 someStylesForceCase = std::any_of(styles.cbegin(), styles.cend(),
337 [](const Style &style) noexcept { return style.caseForce != Style::caseMixed; });
338
339 aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth;
340 spaceWidth = styles[STYLE_DEFAULT].spaceWidth;
341 tabWidth = spaceWidth * tabInChars;
342
343 controlCharWidth = 0.0;
344 if (controlCharSymbol >= 32) {
345 const char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
346 controlCharWidth = surface.WidthText(styles[STYLE_CONTROLCHAR].font, cc, 1);
347 }
348
349 CalculateMarginWidthAndMask();
350 textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
351 }
352
ReleaseAllExtendedStyles()353 void ViewStyle::ReleaseAllExtendedStyles() noexcept {
354 nextExtendedStyle = 256;
355 }
356
AllocateExtendedStyles(int numberStyles)357 int ViewStyle::AllocateExtendedStyles(int numberStyles) {
358 const int startRange = nextExtendedStyle;
359 nextExtendedStyle += numberStyles;
360 EnsureStyle(nextExtendedStyle);
361 for (int i=startRange; i<nextExtendedStyle; i++) {
362 styles[i].ClearTo(styles[STYLE_DEFAULT]);
363 }
364 return startRange;
365 }
366
EnsureStyle(size_t index)367 void ViewStyle::EnsureStyle(size_t index) {
368 if (index >= styles.size()) {
369 AllocStyles(index+1);
370 }
371 }
372
ResetDefaultStyle()373 void ViewStyle::ResetDefaultStyle() {
374 styles[STYLE_DEFAULT].Clear(ColourDesired(0,0,0),
375 ColourDesired(0xff,0xff,0xff),
376 Platform::DefaultFontSize() * SC_FONT_SIZE_MULTIPLIER, fontNames.Save(Platform::DefaultFont()),
377 SC_CHARSET_DEFAULT,
378 SC_WEIGHT_NORMAL, false, false, false, Style::caseMixed, true, true, false);
379 }
380
ClearStyles()381 void ViewStyle::ClearStyles() {
382 // Reset all styles to be like the default style
383 for (size_t i=0; i<styles.size(); i++) {
384 if (i != STYLE_DEFAULT) {
385 styles[i].ClearTo(styles[STYLE_DEFAULT]);
386 }
387 }
388 styles[STYLE_LINENUMBER].back = Platform::Chrome();
389
390 // Set call tip fore/back to match the values previously set for call tips
391 styles[STYLE_CALLTIP].back = ColourDesired(0xff, 0xff, 0xff);
392 styles[STYLE_CALLTIP].fore = ColourDesired(0x80, 0x80, 0x80);
393 }
394
SetStyleFontName(int styleIndex,const char * name)395 void ViewStyle::SetStyleFontName(int styleIndex, const char *name) {
396 styles[styleIndex].fontName = fontNames.Save(name);
397 }
398
ProtectionActive() const399 bool ViewStyle::ProtectionActive() const noexcept {
400 return someStylesProtected;
401 }
402
ExternalMarginWidth() const403 int ViewStyle::ExternalMarginWidth() const noexcept {
404 return marginInside ? 0 : fixedColumnWidth;
405 }
406
MarginFromLocation(Point pt) const407 int ViewStyle::MarginFromLocation(Point pt) const noexcept {
408 int margin = -1;
409 int x = marginInside ? 0 : -fixedColumnWidth;
410 for (size_t i = 0; i < ms.size(); i++) {
411 if ((pt.x >= x) && (pt.x < x + ms[i].width))
412 margin = static_cast<int>(i);
413 x += ms[i].width;
414 }
415 return margin;
416 }
417
ValidStyle(size_t styleIndex) const418 bool ViewStyle::ValidStyle(size_t styleIndex) const noexcept {
419 return styleIndex < styles.size();
420 }
421
CalcLargestMarkerHeight()422 void ViewStyle::CalcLargestMarkerHeight() noexcept {
423 largestMarkerHeight = 0;
424 for (const LineMarker &marker : markers) {
425 switch (marker.markType) {
426 case SC_MARK_PIXMAP:
427 if (marker.pxpm && marker.pxpm->GetHeight() > largestMarkerHeight)
428 largestMarkerHeight = marker.pxpm->GetHeight();
429 break;
430 case SC_MARK_RGBAIMAGE:
431 if (marker.image && marker.image->GetHeight() > largestMarkerHeight)
432 largestMarkerHeight = marker.image->GetHeight();
433 break;
434 }
435 }
436 }
437
GetFrameWidth() const438 int ViewStyle::GetFrameWidth() const noexcept {
439 return Sci::clamp(caretLineFrame, 1, lineHeight / 3);
440 }
441
IsLineFrameOpaque(bool caretActive,bool lineContainsCaret) const442 bool ViewStyle::IsLineFrameOpaque(bool caretActive, bool lineContainsCaret) const noexcept {
443 return caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
444 (caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret;
445 }
446
447 // See if something overrides the line background colour: Either if caret is on the line
448 // and background colour is set for that, or if a marker is defined that forces its background
449 // colour onto the line, or if a marker is defined but has no selection margin in which to
450 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
451 // with the earlier taking precedence. When multiple markers cause background override,
452 // the colour for the highest numbered one is used.
Background(int marksOfLine,bool caretActive,bool lineContainsCaret) const453 ColourOptional ViewStyle::Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const noexcept {
454 ColourOptional background;
455 if (!caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
456 (caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret) {
457 background = ColourOptional(caretLineBackground, true);
458 }
459 if (!background.isSet && marksOfLine) {
460 int marks = marksOfLine;
461 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
462 if ((marks & 1) && (markers[markBit].markType == SC_MARK_BACKGROUND) &&
463 (markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
464 background = ColourOptional(markers[markBit].back, true);
465 }
466 marks >>= 1;
467 }
468 }
469 if (!background.isSet && maskInLine) {
470 int marksMasked = marksOfLine & maskInLine;
471 if (marksMasked) {
472 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
473 if ((marksMasked & 1) &&
474 (markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
475 background = ColourOptional(markers[markBit].back, true);
476 }
477 marksMasked >>= 1;
478 }
479 }
480 }
481 return background;
482 }
483
SelectionBackgroundDrawn() const484 bool ViewStyle::SelectionBackgroundDrawn() const noexcept {
485 return selColours.back.isSet &&
486 ((selAlpha == SC_ALPHA_NOALPHA) || (selAdditionalAlpha == SC_ALPHA_NOALPHA));
487 }
488
WhitespaceBackgroundDrawn() const489 bool ViewStyle::WhitespaceBackgroundDrawn() const noexcept {
490 return (viewWhitespace != wsInvisible) && (whitespaceColours.back.isSet);
491 }
492
WhiteSpaceVisible(bool inIndent) const493 bool ViewStyle::WhiteSpaceVisible(bool inIndent) const noexcept {
494 return (!inIndent && viewWhitespace == wsVisibleAfterIndent) ||
495 (inIndent && viewWhitespace == wsVisibleOnlyInIndent) ||
496 viewWhitespace == wsVisibleAlways;
497 }
498
WrapColour() const499 ColourDesired ViewStyle::WrapColour() const noexcept {
500 if (whitespaceColours.fore.isSet)
501 return whitespaceColours.fore;
502 else
503 return styles[STYLE_DEFAULT].fore;
504 }
505
506 // Insert new edge in sorted order.
AddMultiEdge(uptr_t wParam,sptr_t lParam)507 void ViewStyle::AddMultiEdge(uptr_t wParam, sptr_t lParam) {
508 const int column = static_cast<int>(wParam);
509 theMultiEdge.insert(
510 std::upper_bound(theMultiEdge.begin(), theMultiEdge.end(), column,
511 [](const EdgeProperties &a, const EdgeProperties &b) {
512 return a.column < b.column;
513 }),
514 EdgeProperties(column, lParam));
515 }
516
SetWrapState(int wrapState_)517 bool ViewStyle::SetWrapState(int wrapState_) noexcept {
518 WrapMode wrapStateWanted;
519 switch (wrapState_) {
520 case SC_WRAP_WORD:
521 wrapStateWanted = WrapMode::word;
522 break;
523 case SC_WRAP_CHAR:
524 wrapStateWanted = WrapMode::character;
525 break;
526 case SC_WRAP_WHITESPACE:
527 wrapStateWanted = WrapMode::whitespace;
528 break;
529 default:
530 wrapStateWanted = WrapMode::none;
531 break;
532 }
533 const bool changed = wrapState != wrapStateWanted;
534 wrapState = wrapStateWanted;
535 return changed;
536 }
537
SetWrapVisualFlags(int wrapVisualFlags_)538 bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_) noexcept {
539 const bool changed = wrapVisualFlags != wrapVisualFlags_;
540 wrapVisualFlags = wrapVisualFlags_;
541 return changed;
542 }
543
SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_)544 bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_) noexcept {
545 const bool changed = wrapVisualFlagsLocation != wrapVisualFlagsLocation_;
546 wrapVisualFlagsLocation = wrapVisualFlagsLocation_;
547 return changed;
548 }
549
SetWrapVisualStartIndent(int wrapVisualStartIndent_)550 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_) noexcept {
551 const bool changed = wrapVisualStartIndent != wrapVisualStartIndent_;
552 wrapVisualStartIndent = wrapVisualStartIndent_;
553 return changed;
554 }
555
SetWrapIndentMode(int wrapIndentMode_)556 bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_) noexcept {
557 const bool changed = wrapIndentMode != wrapIndentMode_;
558 wrapIndentMode = wrapIndentMode_;
559 return changed;
560 }
561
IsBlockCaretStyle() const562 bool ViewStyle::IsBlockCaretStyle() const noexcept {
563 return ((caretStyle & CARETSTYLE_INS_MASK) == CARETSTYLE_BLOCK) ||
564 (caretStyle & CARETSTYLE_OVERSTRIKE_BLOCK) != 0;
565 }
566
IsCaretVisible() const567 bool ViewStyle::IsCaretVisible() const noexcept {
568 return caretWidth > 0 && caretStyle != CARETSTYLE_INVISIBLE;
569 }
570
DrawCaretInsideSelection(bool inOverstrike,bool imeCaretBlockOverride) const571 bool ViewStyle::DrawCaretInsideSelection(bool inOverstrike, bool imeCaretBlockOverride) const noexcept {
572 if (caretStyle & CARETSTYLE_BLOCK_AFTER)
573 return false;
574 return ((caretStyle & CARETSTYLE_INS_MASK) == CARETSTYLE_BLOCK) ||
575 (inOverstrike && (caretStyle & CARETSTYLE_OVERSTRIKE_BLOCK) != 0) ||
576 imeCaretBlockOverride;
577 }
578
CaretShapeForMode(bool inOverstrike) const579 ViewStyle::CaretShape ViewStyle::CaretShapeForMode(bool inOverstrike) const noexcept {
580 if (inOverstrike) {
581 return (caretStyle & CARETSTYLE_OVERSTRIKE_BLOCK) ? CaretShape::block : CaretShape::bar;
582 }
583
584 const int caret = caretStyle & CARETSTYLE_INS_MASK;
585 return (caret <= CARETSTYLE_BLOCK) ? static_cast<CaretShape>(caret) : CaretShape::line;
586 }
587
AllocStyles(size_t sizeNew)588 void ViewStyle::AllocStyles(size_t sizeNew) {
589 size_t i=styles.size();
590 styles.resize(sizeNew);
591 if (styles.size() > STYLE_DEFAULT) {
592 for (; i<sizeNew; i++) {
593 if (i != STYLE_DEFAULT) {
594 styles[i].ClearTo(styles[STYLE_DEFAULT]);
595 }
596 }
597 }
598 }
599
CreateAndAddFont(const FontSpecification & fs)600 void ViewStyle::CreateAndAddFont(const FontSpecification &fs) {
601 if (fs.fontName) {
602 FontMap::iterator it = fonts.find(fs);
603 if (it == fonts.end()) {
604 fonts[fs] = Sci::make_unique<FontRealised>();
605 }
606 }
607 }
608
Find(const FontSpecification & fs)609 FontRealised *ViewStyle::Find(const FontSpecification &fs) {
610 if (!fs.fontName) // Invalid specification so return arbitrary object
611 return fonts.begin()->second.get();
612 FontMap::iterator it = fonts.find(fs);
613 if (it != fonts.end()) {
614 // Should always reach here since map was just set for all styles
615 return it->second.get();
616 }
617 return nullptr;
618 }
619
FindMaxAscentDescent()620 void ViewStyle::FindMaxAscentDescent() {
621 for (FontMap::const_iterator it = fonts.cbegin(); it != fonts.cend(); ++it) {
622 if (maxAscent < it->second->ascent)
623 maxAscent = it->second->ascent;
624 if (maxDescent < it->second->descent)
625 maxDescent = it->second->descent;
626 }
627 }
628