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