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 	eolAnnotationVisible = source.eolAnnotationVisible;
133 	eolAnnotationStyleOffset = source.eolAnnotationStyleOffset;
134 	braceHighlightIndicatorSet = source.braceHighlightIndicatorSet;
135 	braceHighlightIndicator = source.braceHighlightIndicator;
136 	braceBadLightIndicatorSet = source.braceBadLightIndicatorSet;
137 	braceBadLightIndicator = source.braceBadLightIndicator;
138 
139 	edgeState = source.edgeState;
140 	theEdge = source.theEdge;
141 	theMultiEdge = source.theMultiEdge;
142 
143 	marginNumberPadding = source.marginNumberPadding;
144 	ctrlCharPadding = source.ctrlCharPadding;
145 	lastSegItalicsOffset = source.lastSegItalicsOffset;
146 
147 	wrapState = source.wrapState;
148 	wrapVisualFlags = source.wrapVisualFlags;
149 	wrapVisualFlagsLocation = source.wrapVisualFlagsLocation;
150 	wrapVisualStartIndent = source.wrapVisualStartIndent;
151 	wrapIndentMode = source.wrapIndentMode;
152 }
153 
~ViewStyle()154 ViewStyle::~ViewStyle() {
155 	styles.clear();
156 	fonts.clear();
157 }
158 
CalculateMarginWidthAndMask()159 void ViewStyle::CalculateMarginWidthAndMask() noexcept {
160 	fixedColumnWidth = marginInside ? leftMarginWidth : 0;
161 	maskInLine = 0xffffffff;
162 	int maskDefinedMarkers = 0;
163 	for (const MarginStyle &m : ms) {
164 		fixedColumnWidth += m.width;
165 		if (m.width > 0)
166 			maskInLine &= ~m.mask;
167 		maskDefinedMarkers |= m.mask;
168 	}
169 	maskDrawInText = 0;
170 	for (int markBit = 0; markBit < 32; markBit++) {
171 		const int maskBit = 1U << markBit;
172 		switch (markers[markBit].markType) {
173 		case SC_MARK_EMPTY:
174 			maskInLine &= ~maskBit;
175 			break;
176 		case SC_MARK_BACKGROUND:
177 		case SC_MARK_UNDERLINE:
178 			maskInLine &= ~maskBit;
179 			maskDrawInText |= maskDefinedMarkers & maskBit;
180 			break;
181 		}
182 	}
183 }
184 
Init(size_t stylesSize_)185 void ViewStyle::Init(size_t stylesSize_) {
186 	AllocStyles(stylesSize_);
187 	nextExtendedStyle = 256;
188 	fontNames.Clear();
189 	ResetDefaultStyle();
190 
191 	// There are no image markers by default, so no need for calling CalcLargestMarkerHeight()
192 	largestMarkerHeight = 0;
193 
194 	indicators[0] = Indicator(INDIC_SQUIGGLE, ColourDesired(0, 0x7f, 0));
195 	indicators[1] = Indicator(INDIC_TT, ColourDesired(0, 0, 0xff));
196 	indicators[2] = Indicator(INDIC_PLAIN, ColourDesired(0xff, 0, 0));
197 
198 	technology = SC_TECHNOLOGY_DEFAULT;
199 	indicatorsDynamic = false;
200 	indicatorsSetFore = false;
201 	lineHeight = 1;
202 	lineOverlap = 0;
203 	maxAscent = 1;
204 	maxDescent = 1;
205 	aveCharWidth = 8;
206 	spaceWidth = 8;
207 	tabWidth = spaceWidth * 8;
208 
209 	selColours.fore = ColourOptional(ColourDesired(0xff, 0, 0));
210 	selColours.back = ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0), true);
211 	selAdditionalForeground = ColourDesired(0xff, 0, 0);
212 	selAdditionalBackground = ColourDesired(0xd7, 0xd7, 0xd7);
213 	selBackground2 = ColourDesired(0xb0, 0xb0, 0xb0);
214 	selAlpha = SC_ALPHA_NOALPHA;
215 	selAdditionalAlpha = SC_ALPHA_NOALPHA;
216 	selEOLFilled = false;
217 
218 	foldmarginColour = ColourOptional(ColourDesired(0xff, 0, 0));
219 	foldmarginHighlightColour = ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0));
220 
221 	whitespaceColours.fore = ColourOptional();
222 	whitespaceColours.back = ColourOptional(ColourDesired(0xff, 0xff, 0xff));
223 	controlCharSymbol = 0;	/* Draw the control characters */
224 	controlCharWidth = 0;
225 	selbar = Platform::Chrome();
226 	selbarlight = Platform::ChromeHighlight();
227 	styles[STYLE_LINENUMBER].fore = ColourDesired(0, 0, 0);
228 	styles[STYLE_LINENUMBER].back = Platform::Chrome();
229 	caretcolour = ColourDesired(0, 0, 0);
230 	additionalCaretColour = ColourDesired(0x7f, 0x7f, 0x7f);
231 	caretLineFrame = 0;
232 	showCaretLineBackground = false;
233 	alwaysShowCaretLineBackground = false;
234 	caretLineBackground = ColourDesired(0xff, 0xff, 0);
235 	caretLineAlpha = SC_ALPHA_NOALPHA;
236 	caretStyle = CARETSTYLE_LINE;
237 	caretWidth = 1;
238 	someStylesProtected = false;
239 	someStylesForceCase = false;
240 
241 	hotspotColours.fore = ColourOptional(ColourDesired(0, 0, 0xff));
242 	hotspotColours.back = ColourOptional(ColourDesired(0xff, 0xff, 0xff));
243 	hotspotUnderline = true;
244 	hotspotSingleLine = true;
245 
246 	leftMarginWidth = 1;
247 	rightMarginWidth = 1;
248 	ms.resize(SC_MAX_MARGIN + 1);
249 	ms[0] = MarginStyle(SC_MARGIN_NUMBER);
250 	ms[1] = MarginStyle(SC_MARGIN_SYMBOL, 16, ~SC_MASK_FOLDERS);
251 	ms[2] = MarginStyle(SC_MARGIN_SYMBOL);
252 	marginInside = true;
253 	CalculateMarginWidthAndMask();
254 	textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
255 	zoomLevel = 0;
256 	viewWhitespace = wsInvisible;
257 	tabDrawMode = tdLongArrow;
258 	whitespaceSize = 1;
259 	viewIndentationGuides = ivNone;
260 	viewEOL = false;
261 	extraFontFlag = 0;
262 	extraAscent = 0;
263 	extraDescent = 0;
264 	marginStyleOffset = 0;
265 	annotationVisible = ANNOTATION_HIDDEN;
266 	annotationStyleOffset = 0;
267 	eolAnnotationVisible = EOLANNOTATION_HIDDEN;
268 	eolAnnotationStyleOffset = 0;
269 	braceHighlightIndicatorSet = false;
270 	braceHighlightIndicator = 0;
271 	braceBadLightIndicatorSet = false;
272 	braceBadLightIndicator = 0;
273 
274 	edgeState = EDGE_NONE;
275 	theEdge = EdgeProperties(0, ColourDesired(0xc0, 0xc0, 0xc0));
276 
277 	marginNumberPadding = 3;
278 	ctrlCharPadding = 3; // +3 For a blank on front and rounded edge each side
279 	lastSegItalicsOffset = 2;
280 
281 	wrapState = WrapMode::none;
282 	wrapVisualFlags = 0;
283 	wrapVisualFlagsLocation = 0;
284 	wrapVisualStartIndent = 0;
285 	wrapIndentMode = SC_WRAPINDENT_FIXED;
286 }
287 
Refresh(Surface & surface,int tabInChars)288 void ViewStyle::Refresh(Surface &surface, int tabInChars) {
289 	fonts.clear();
290 
291 	selbar = Platform::Chrome();
292 	selbarlight = Platform::ChromeHighlight();
293 
294 	// Apply the extra font flag which controls text drawing quality to each style.
295 	for (Style &style : styles) {
296 		style.extraFontFlag = extraFontFlag;
297 	}
298 
299 	// Create a FontRealised object for each unique font in the styles.
300 	CreateAndAddFont(styles[STYLE_DEFAULT]);
301 	for (const Style &style : styles) {
302 		CreateAndAddFont(style);
303 	}
304 
305 	// Ask platform to allocate each unique font.
306 	for (std::pair<const FontSpecification, std::unique_ptr<FontRealised>> &font : fonts) {
307 		font.second->Realise(surface, zoomLevel, technology, font.first);
308 	}
309 
310 	// Set the platform font handle and measurements for each style.
311 	for (Style &style : styles) {
312 		FontRealised *fr = Find(style);
313 		style.Copy(fr->font, *fr);
314 	}
315 
316 	indicatorsDynamic = std::any_of(indicators.cbegin(), indicators.cend(),
317 		[](const Indicator &indicator) noexcept { return indicator.IsDynamic(); });
318 
319 	indicatorsSetFore = std::any_of(indicators.cbegin(), indicators.cend(),
320 		[](const Indicator &indicator) noexcept { return indicator.OverridesTextFore(); });
321 
322 	maxAscent = 1;
323 	maxDescent = 1;
324 	FindMaxAscentDescent();
325 	maxAscent += extraAscent;
326 	maxDescent += extraDescent;
327 	lineHeight = maxAscent + maxDescent;
328 	lineOverlap = lineHeight / 10;
329 	if (lineOverlap < 2)
330 		lineOverlap = 2;
331 	if (lineOverlap > lineHeight)
332 		lineOverlap = lineHeight;
333 
334 	someStylesProtected = std::any_of(styles.cbegin(), styles.cend(),
335 		[](const Style &style) noexcept { return style.IsProtected(); });
336 
337 	someStylesForceCase = std::any_of(styles.cbegin(), styles.cend(),
338 		[](const Style &style) noexcept { return style.caseForce != Style::caseMixed; });
339 
340 	aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth;
341 	spaceWidth = styles[STYLE_DEFAULT].spaceWidth;
342 	tabWidth = spaceWidth * tabInChars;
343 
344 	controlCharWidth = 0.0;
345 	if (controlCharSymbol >= 32) {
346 		const char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
347 		controlCharWidth = surface.WidthText(styles[STYLE_CONTROLCHAR].font, cc);
348 	}
349 
350 	CalculateMarginWidthAndMask();
351 	textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
352 }
353 
ReleaseAllExtendedStyles()354 void ViewStyle::ReleaseAllExtendedStyles() noexcept {
355 	nextExtendedStyle = 256;
356 }
357 
AllocateExtendedStyles(int numberStyles)358 int ViewStyle::AllocateExtendedStyles(int numberStyles) {
359 	const int startRange = nextExtendedStyle;
360 	nextExtendedStyle += numberStyles;
361 	EnsureStyle(nextExtendedStyle);
362 	for (int i=startRange; i<nextExtendedStyle; i++) {
363 		styles[i].ClearTo(styles[STYLE_DEFAULT]);
364 	}
365 	return startRange;
366 }
367 
EnsureStyle(size_t index)368 void ViewStyle::EnsureStyle(size_t index) {
369 	if (index >= styles.size()) {
370 		AllocStyles(index+1);
371 	}
372 }
373 
ResetDefaultStyle()374 void ViewStyle::ResetDefaultStyle() {
375 	styles[STYLE_DEFAULT].Clear(ColourDesired(0,0,0),
376 	        ColourDesired(0xff,0xff,0xff),
377 	        Platform::DefaultFontSize() * SC_FONT_SIZE_MULTIPLIER, fontNames.Save(Platform::DefaultFont()),
378 	        SC_CHARSET_DEFAULT,
379 	        SC_WEIGHT_NORMAL, false, false, false, Style::caseMixed, true, true, false);
380 }
381 
ClearStyles()382 void ViewStyle::ClearStyles() {
383 	// Reset all styles to be like the default style
384 	for (size_t i=0; i<styles.size(); i++) {
385 		if (i != STYLE_DEFAULT) {
386 			styles[i].ClearTo(styles[STYLE_DEFAULT]);
387 		}
388 	}
389 	styles[STYLE_LINENUMBER].back = Platform::Chrome();
390 
391 	// Set call tip fore/back to match the values previously set for call tips
392 	styles[STYLE_CALLTIP].back = ColourDesired(0xff, 0xff, 0xff);
393 	styles[STYLE_CALLTIP].fore = ColourDesired(0x80, 0x80, 0x80);
394 }
395 
SetStyleFontName(int styleIndex,const char * name)396 void ViewStyle::SetStyleFontName(int styleIndex, const char *name) {
397 	styles[styleIndex].fontName = fontNames.Save(name);
398 }
399 
ProtectionActive() const400 bool ViewStyle::ProtectionActive() const noexcept {
401 	return someStylesProtected;
402 }
403 
ExternalMarginWidth() const404 int ViewStyle::ExternalMarginWidth() const noexcept {
405 	return marginInside ? 0 : fixedColumnWidth;
406 }
407 
MarginFromLocation(Point pt) const408 int ViewStyle::MarginFromLocation(Point pt) const noexcept {
409 	int margin = -1;
410 	int x = marginInside ? 0 : -fixedColumnWidth;
411 	for (size_t i = 0; i < ms.size(); i++) {
412 		if ((pt.x >= x) && (pt.x < x + ms[i].width))
413 			margin = static_cast<int>(i);
414 		x += ms[i].width;
415 	}
416 	return margin;
417 }
418 
ValidStyle(size_t styleIndex) const419 bool ViewStyle::ValidStyle(size_t styleIndex) const noexcept {
420 	return styleIndex < styles.size();
421 }
422 
CalcLargestMarkerHeight()423 void ViewStyle::CalcLargestMarkerHeight() noexcept {
424 	largestMarkerHeight = 0;
425 	for (const LineMarker &marker : markers) {
426 		switch (marker.markType) {
427 		case SC_MARK_PIXMAP:
428 			if (marker.pxpm && marker.pxpm->GetHeight() > largestMarkerHeight)
429 				largestMarkerHeight = marker.pxpm->GetHeight();
430 			break;
431 		case SC_MARK_RGBAIMAGE:
432 			if (marker.image && marker.image->GetHeight() > largestMarkerHeight)
433 				largestMarkerHeight = marker.image->GetHeight();
434 			break;
435 		}
436 	}
437 }
438 
GetFrameWidth() const439 int ViewStyle::GetFrameWidth() const noexcept {
440 	return std::clamp(caretLineFrame, 1, lineHeight / 3);
441 }
442 
IsLineFrameOpaque(bool caretActive,bool lineContainsCaret) const443 bool ViewStyle::IsLineFrameOpaque(bool caretActive, bool lineContainsCaret) const noexcept {
444 	return caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
445 		(caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret;
446 }
447 
448 // See if something overrides the line background colour:  Either if caret is on the line
449 // and background colour is set for that, or if a marker is defined that forces its background
450 // colour onto the line, or if a marker is defined but has no selection margin in which to
451 // display itself (as long as it's not an SC_MARK_EMPTY marker).  These are checked in order
452 // with the earlier taking precedence.  When multiple markers cause background override,
453 // the colour for the highest numbered one is used.
Background(int marksOfLine,bool caretActive,bool lineContainsCaret) const454 ColourOptional ViewStyle::Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const noexcept {
455 	ColourOptional background;
456 	if (!caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
457 		(caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret) {
458 		background = ColourOptional(caretLineBackground, true);
459 	}
460 	if (!background.isSet && marksOfLine) {
461 		int marks = marksOfLine;
462 		for (int markBit = 0; (markBit < 32) && marks; markBit++) {
463 			if ((marks & 1) && (markers[markBit].markType == SC_MARK_BACKGROUND) &&
464 				(markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
465 				background = ColourOptional(markers[markBit].back, true);
466 			}
467 			marks >>= 1;
468 		}
469 	}
470 	if (!background.isSet && maskInLine) {
471 		int marksMasked = marksOfLine & maskInLine;
472 		if (marksMasked) {
473 			for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
474 				if ((marksMasked & 1) &&
475 					(markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
476 					background = ColourOptional(markers[markBit].back, true);
477 				}
478 				marksMasked >>= 1;
479 			}
480 		}
481 	}
482 	return background;
483 }
484 
SelectionBackgroundDrawn() const485 bool ViewStyle::SelectionBackgroundDrawn() const noexcept {
486 	return selColours.back.isSet &&
487 		((selAlpha == SC_ALPHA_NOALPHA) || (selAdditionalAlpha == SC_ALPHA_NOALPHA));
488 }
489 
WhitespaceBackgroundDrawn() const490 bool ViewStyle::WhitespaceBackgroundDrawn() const noexcept {
491 	return (viewWhitespace != wsInvisible) && (whitespaceColours.back.isSet);
492 }
493 
WhiteSpaceVisible(bool inIndent) const494 bool ViewStyle::WhiteSpaceVisible(bool inIndent) const noexcept {
495 	return (!inIndent && viewWhitespace == wsVisibleAfterIndent) ||
496 		(inIndent && viewWhitespace == wsVisibleOnlyInIndent) ||
497 		viewWhitespace == wsVisibleAlways;
498 }
499 
WrapColour() const500 ColourDesired ViewStyle::WrapColour() const noexcept {
501 	if (whitespaceColours.fore.isSet)
502 		return whitespaceColours.fore;
503 	else
504 		return styles[STYLE_DEFAULT].fore;
505 }
506 
507 // Insert new edge in sorted order.
AddMultiEdge(uptr_t wParam,sptr_t lParam)508 void ViewStyle::AddMultiEdge(uptr_t wParam, sptr_t lParam) {
509 	const int column = static_cast<int>(wParam);
510 	theMultiEdge.insert(
511 		std::upper_bound(theMultiEdge.begin(), theMultiEdge.end(), column,
512 			[](const EdgeProperties &a, const EdgeProperties &b) {
513 				return a.column < b.column;
514 			}),
515 		EdgeProperties(column, lParam));
516 }
517 
SetWrapState(int wrapState_)518 bool ViewStyle::SetWrapState(int wrapState_) noexcept {
519 	WrapMode wrapStateWanted;
520 	switch (wrapState_) {
521 	case SC_WRAP_WORD:
522 		wrapStateWanted = WrapMode::word;
523 		break;
524 	case SC_WRAP_CHAR:
525 		wrapStateWanted = WrapMode::character;
526 		break;
527 	case SC_WRAP_WHITESPACE:
528 		wrapStateWanted = WrapMode::whitespace;
529 		break;
530 	default:
531 		wrapStateWanted = WrapMode::none;
532 		break;
533 	}
534 	const bool changed = wrapState != wrapStateWanted;
535 	wrapState = wrapStateWanted;
536 	return changed;
537 }
538 
SetWrapVisualFlags(int wrapVisualFlags_)539 bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_) noexcept {
540 	const bool changed = wrapVisualFlags != wrapVisualFlags_;
541 	wrapVisualFlags = wrapVisualFlags_;
542 	return changed;
543 }
544 
SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_)545 bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_) noexcept {
546 	const bool changed = wrapVisualFlagsLocation != wrapVisualFlagsLocation_;
547 	wrapVisualFlagsLocation = wrapVisualFlagsLocation_;
548 	return changed;
549 }
550 
SetWrapVisualStartIndent(int wrapVisualStartIndent_)551 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_) noexcept {
552 	const bool changed = wrapVisualStartIndent != wrapVisualStartIndent_;
553 	wrapVisualStartIndent = wrapVisualStartIndent_;
554 	return changed;
555 }
556 
SetWrapIndentMode(int wrapIndentMode_)557 bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_) noexcept {
558 	const bool changed = wrapIndentMode != wrapIndentMode_;
559 	wrapIndentMode = wrapIndentMode_;
560 	return changed;
561 }
562 
IsBlockCaretStyle() const563 bool ViewStyle::IsBlockCaretStyle() const noexcept {
564 	return ((caretStyle & CARETSTYLE_INS_MASK) == CARETSTYLE_BLOCK) ||
565 		(caretStyle & CARETSTYLE_OVERSTRIKE_BLOCK) != 0;
566 }
567 
IsCaretVisible() const568 bool ViewStyle::IsCaretVisible() const noexcept {
569 	return caretWidth > 0 && caretStyle != CARETSTYLE_INVISIBLE;
570 }
571 
DrawCaretInsideSelection(bool inOverstrike,bool imeCaretBlockOverride) const572 bool ViewStyle::DrawCaretInsideSelection(bool inOverstrike, bool imeCaretBlockOverride) const noexcept {
573 	if (caretStyle & CARETSTYLE_BLOCK_AFTER)
574 		return false;
575 	return ((caretStyle & CARETSTYLE_INS_MASK) == CARETSTYLE_BLOCK) ||
576 		(inOverstrike && (caretStyle & CARETSTYLE_OVERSTRIKE_BLOCK) != 0) ||
577 		imeCaretBlockOverride;
578 }
579 
CaretShapeForMode(bool inOverstrike) const580 ViewStyle::CaretShape ViewStyle::CaretShapeForMode(bool inOverstrike) const noexcept {
581 	if (inOverstrike) {
582 		return (caretStyle & CARETSTYLE_OVERSTRIKE_BLOCK) ? CaretShape::block : CaretShape::bar;
583 	}
584 
585 	const int caret = caretStyle & CARETSTYLE_INS_MASK;
586 	return (caret <= CARETSTYLE_BLOCK) ? static_cast<CaretShape>(caret) : CaretShape::line;
587 }
588 
AllocStyles(size_t sizeNew)589 void ViewStyle::AllocStyles(size_t sizeNew) {
590 	size_t i=styles.size();
591 	styles.resize(sizeNew);
592 	if (styles.size() > STYLE_DEFAULT) {
593 		for (; i<sizeNew; i++) {
594 			if (i != STYLE_DEFAULT) {
595 				styles[i].ClearTo(styles[STYLE_DEFAULT]);
596 			}
597 		}
598 	}
599 }
600 
CreateAndAddFont(const FontSpecification & fs)601 void ViewStyle::CreateAndAddFont(const FontSpecification &fs) {
602 	if (fs.fontName) {
603 		FontMap::iterator it = fonts.find(fs);
604 		if (it == fonts.end()) {
605 			fonts[fs] = std::make_unique<FontRealised>();
606 		}
607 	}
608 }
609 
Find(const FontSpecification & fs)610 FontRealised *ViewStyle::Find(const FontSpecification &fs) {
611 	if (!fs.fontName)	// Invalid specification so return arbitrary object
612 		return fonts.begin()->second.get();
613 	FontMap::iterator it = fonts.find(fs);
614 	if (it != fonts.end()) {
615 		// Should always reach here since map was just set for all styles
616 		return it->second.get();
617 	}
618 	return nullptr;
619 }
620 
FindMaxAscentDescent()621 void ViewStyle::FindMaxAscentDescent() {
622 	for (FontMap::const_iterator it = fonts.cbegin(); it != fonts.cend(); ++it) {
623 		if (maxAscent < it->second->ascent)
624 			maxAscent = it->second->ascent;
625 		if (maxDescent < it->second->descent)
626 			maxDescent = it->second->descent;
627 	}
628 }
629