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