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