Scintilla 4.0.3
[TortoiseGit.git] / ext / scintilla / src / ViewStyle.cxx
blob9a4c9593b27bc3bce605104b41cc8aefc7358f5b
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.
8 #include <cstddef>
9 #include <cassert>
10 #include <cstring>
12 #include <stdexcept>
13 #include <vector>
14 #include <map>
15 #include <algorithm>
16 #include <memory>
18 #include "Platform.h"
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"
29 using namespace Scintilla;
31 MarginStyle::MarginStyle(int style_, int width_, int mask_) :
32 style(style_), width(width_), mask(mask_), sensitive(false), cursor(SC_CURSORREVERSEARROW) {
35 // A list of the fontnames - avoids wasting space in each style
36 FontNames::FontNames() {
39 FontNames::~FontNames() {
40 Clear();
43 void FontNames::Clear() {
44 names.clear();
47 const char *FontNames::Save(const char *name) {
48 if (!name)
49 return nullptr;
51 for (const UniqueString &nm : names) {
52 if (strcmp(nm.get(), name) == 0) {
53 return nm.get();
57 names.push_back(UniqueStringCopy(name));
58 return names.back().get();
61 FontRealised::FontRealised() {
64 FontRealised::~FontRealised() {
65 font.Release();
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;
74 float deviceHeight = static_cast<float>(surface.DeviceHeightFont(sizeZoomed));
75 FontParameters fp(fs.fontName, deviceHeight / SC_FONT_SIZE_MULTIPLIER, fs.weight, fs.italic, fs.extraFontFlag, technology, fs.characterSet);
76 font.Create(fp);
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.WidthChar(font, ' ');
85 ViewStyle::ViewStyle() : markers(MARKER_MAX + 1), indicators(INDIC_MAX + 1) {
86 Init();
89 // Copy constructor only called when printing copies the screen ViewStyle so it can be
90 // modified for printing styles.
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);
98 nextExtendedStyle = source.nextExtendedStyle;
99 markers = source.markers;
100 CalcLargestMarkerHeight();
102 indicators = source.indicators;
104 indicatorsDynamic = source.indicatorsDynamic;
105 indicatorsSetFore = source.indicatorsSetFore;
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;
115 foldmarginColour = source.foldmarginColour;
116 foldmarginHighlightColour = source.foldmarginHighlightColour;
118 hotspotColours = source.hotspotColours;
119 hotspotUnderline = source.hotspotUnderline;
120 hotspotSingleLine = source.hotspotSingleLine;
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;
163 edgeState = source.edgeState;
164 theEdge = source.theEdge;
165 theMultiEdge = source.theMultiEdge;
167 marginNumberPadding = source.marginNumberPadding;
168 ctrlCharPadding = source.ctrlCharPadding;
169 lastSegItalicsOffset = source.lastSegItalicsOffset;
171 wrapState = source.wrapState;
172 wrapVisualFlags = source.wrapVisualFlags;
173 wrapVisualFlagsLocation = source.wrapVisualFlagsLocation;
174 wrapVisualStartIndent = source.wrapVisualStartIndent;
175 wrapIndentMode = source.wrapIndentMode;
178 ViewStyle::~ViewStyle() {
179 styles.clear();
180 fonts.clear();
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;
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;
209 void ViewStyle::Init(size_t stylesSize_) {
210 AllocStyles(stylesSize_);
211 nextExtendedStyle = 256;
212 fontNames.Clear();
213 ResetDefaultStyle();
215 // There are no image markers by default, so no need for calling CalcLargestMarkerHeight()
216 largestMarkerHeight = 0;
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));
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;
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;
242 foldmarginColour = ColourOptional(ColourDesired(0xff, 0, 0));
243 foldmarginHighlightColour = ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0));
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;
265 hotspotColours.fore = ColourOptional(ColourDesired(0, 0, 0xff));
266 hotspotColours.back = ColourOptional(ColourDesired(0xff, 0xff, 0xff));
267 hotspotUnderline = true;
268 hotspotSingleLine = true;
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;
296 edgeState = EDGE_NONE;
297 theEdge = EdgeProperties(0, ColourDesired(0xc0, 0xc0, 0xc0));
299 marginNumberPadding = 3;
300 ctrlCharPadding = 3; // +3 For a blank on front and rounded edge each side
301 lastSegItalicsOffset = 2;
303 wrapState = eWrapNone;
304 wrapVisualFlags = 0;
305 wrapVisualFlagsLocation = 0;
306 wrapVisualStartIndent = 0;
307 wrapIndentMode = SC_WRAPINDENT_FIXED;
310 void ViewStyle::Refresh(Surface &surface, int tabInChars) {
311 fonts.clear();
313 selbar = Platform::Chrome();
314 selbarlight = Platform::ChromeHighlight();
316 // Apply the extra font flag which controls text drawing quality to each style.
317 for (Style &style : styles) {
318 style.extraFontFlag = extraFontFlag;
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);
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);
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);
338 indicatorsDynamic = std::any_of(indicators.cbegin(), indicators.cend(),
339 [](const Indicator &indicator) { return indicator.IsDynamic(); });
341 indicatorsSetFore = std::any_of(indicators.cbegin(), indicators.cend(),
342 [](const Indicator &indicator) { return indicator.OverridesTextFore(); });
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;
356 someStylesProtected = std::any_of(styles.cbegin(), styles.cend(),
357 [](const Style &style) { return style.IsProtected(); });
359 someStylesForceCase = std::any_of(styles.cbegin(), styles.cend(),
360 [](const Style &style) { return style.caseForce != Style::caseMixed; });
362 aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth;
363 spaceWidth = styles[STYLE_DEFAULT].spaceWidth;
364 tabWidth = spaceWidth * tabInChars;
366 controlCharWidth = 0.0;
367 if (controlCharSymbol >= 32) {
368 controlCharWidth = surface.WidthChar(styles[STYLE_CONTROLCHAR].font, static_cast<char>(controlCharSymbol));
371 CalculateMarginWidthAndMask();
372 textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
375 void ViewStyle::ReleaseAllExtendedStyles() {
376 nextExtendedStyle = 256;
379 int ViewStyle::AllocateExtendedStyles(int numberStyles) {
380 int startRange = static_cast<int>(nextExtendedStyle);
381 nextExtendedStyle += numberStyles;
382 EnsureStyle(nextExtendedStyle);
383 for (size_t i=startRange; i<nextExtendedStyle; i++) {
384 styles[i].ClearTo(styles[STYLE_DEFAULT]);
386 return startRange;
389 void ViewStyle::EnsureStyle(size_t index) {
390 if (index >= styles.size()) {
391 AllocStyles(index+1);
395 void ViewStyle::ResetDefaultStyle() {
396 styles[STYLE_DEFAULT].Clear(ColourDesired(0,0,0),
397 ColourDesired(0xff,0xff,0xff),
398 Platform::DefaultFontSize() * SC_FONT_SIZE_MULTIPLIER, fontNames.Save(Platform::DefaultFont()),
399 SC_CHARSET_DEFAULT,
400 SC_WEIGHT_NORMAL, false, false, false, Style::caseMixed, true, true, false);
403 void ViewStyle::ClearStyles() {
404 // Reset all styles to be like the default style
405 for (unsigned int i=0; i<styles.size(); i++) {
406 if (i != STYLE_DEFAULT) {
407 styles[i].ClearTo(styles[STYLE_DEFAULT]);
410 styles[STYLE_LINENUMBER].back = Platform::Chrome();
412 // Set call tip fore/back to match the values previously set for call tips
413 styles[STYLE_CALLTIP].back = ColourDesired(0xff, 0xff, 0xff);
414 styles[STYLE_CALLTIP].fore = ColourDesired(0x80, 0x80, 0x80);
417 void ViewStyle::SetStyleFontName(int styleIndex, const char *name) {
418 styles[styleIndex].fontName = fontNames.Save(name);
421 bool ViewStyle::ProtectionActive() const {
422 return someStylesProtected;
425 int ViewStyle::ExternalMarginWidth() const {
426 return marginInside ? 0 : fixedColumnWidth;
429 int ViewStyle::MarginFromLocation(Point pt) const {
430 int margin = -1;
431 int x = textStart - fixedColumnWidth;
432 for (size_t i = 0; i < ms.size(); i++) {
433 if ((pt.x >= x) && (pt.x < x + ms[i].width))
434 margin = static_cast<int>(i);
435 x += ms[i].width;
437 return margin;
440 bool ViewStyle::ValidStyle(size_t styleIndex) const {
441 return styleIndex < styles.size();
444 void ViewStyle::CalcLargestMarkerHeight() {
445 largestMarkerHeight = 0;
446 for (const LineMarker &marker : markers) {
447 switch (marker.markType) {
448 case SC_MARK_PIXMAP:
449 if (marker.pxpm && marker.pxpm->GetHeight() > largestMarkerHeight)
450 largestMarkerHeight = marker.pxpm->GetHeight();
451 break;
452 case SC_MARK_RGBAIMAGE:
453 if (marker.image && marker.image->GetHeight() > largestMarkerHeight)
454 largestMarkerHeight = marker.image->GetHeight();
455 break;
460 int ViewStyle::GetFrameWidth() const {
461 return static_cast<int>(std::clamp(caretLineFrame, 1, lineHeight / 3));
464 bool ViewStyle::IsLineFrameOpaque(bool caretActive, bool lineContainsCaret) const {
465 return caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
466 (caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret;
469 // See if something overrides the line background color: Either if caret is on the line
470 // and background color is set for that, or if a marker is defined that forces its background
471 // color onto the line, or if a marker is defined but has no selection margin in which to
472 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
473 // with the earlier taking precedence. When multiple markers cause background override,
474 // the color for the highest numbered one is used.
475 ColourOptional ViewStyle::Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const {
476 ColourOptional background;
477 if (!caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
478 (caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret) {
479 background = ColourOptional(caretLineBackground, true);
481 if (!background.isSet && marksOfLine) {
482 int marks = marksOfLine;
483 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
484 if ((marks & 1) && (markers[markBit].markType == SC_MARK_BACKGROUND) &&
485 (markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
486 background = ColourOptional(markers[markBit].back, true);
488 marks >>= 1;
491 if (!background.isSet && maskInLine) {
492 int marksMasked = marksOfLine & maskInLine;
493 if (marksMasked) {
494 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
495 if ((marksMasked & 1) &&
496 (markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
497 background = ColourOptional(markers[markBit].back, true);
499 marksMasked >>= 1;
503 return background;
506 bool ViewStyle::SelectionBackgroundDrawn() const {
507 return selColours.back.isSet &&
508 ((selAlpha == SC_ALPHA_NOALPHA) || (selAdditionalAlpha == SC_ALPHA_NOALPHA));
511 bool ViewStyle::WhitespaceBackgroundDrawn() const {
512 return (viewWhitespace != wsInvisible) && (whitespaceColours.back.isSet);
515 bool ViewStyle::WhiteSpaceVisible(bool inIndent) const {
516 return (!inIndent && viewWhitespace == wsVisibleAfterIndent) ||
517 (inIndent && viewWhitespace == wsVisibleOnlyInIndent) ||
518 viewWhitespace == wsVisibleAlways;
521 ColourDesired ViewStyle::WrapColour() const {
522 if (whitespaceColours.fore.isSet)
523 return whitespaceColours.fore;
524 else
525 return styles[STYLE_DEFAULT].fore;
528 bool ViewStyle::SetWrapState(int wrapState_) {
529 WrapMode wrapStateWanted;
530 switch (wrapState_) {
531 case SC_WRAP_WORD:
532 wrapStateWanted = eWrapWord;
533 break;
534 case SC_WRAP_CHAR:
535 wrapStateWanted = eWrapChar;
536 break;
537 case SC_WRAP_WHITESPACE:
538 wrapStateWanted = eWrapWhitespace;
539 break;
540 default:
541 wrapStateWanted = eWrapNone;
542 break;
544 const bool changed = wrapState != wrapStateWanted;
545 wrapState = wrapStateWanted;
546 return changed;
549 bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_) {
550 const bool changed = wrapVisualFlags != wrapVisualFlags_;
551 wrapVisualFlags = wrapVisualFlags_;
552 return changed;
555 bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_) {
556 const bool changed = wrapVisualFlagsLocation != wrapVisualFlagsLocation_;
557 wrapVisualFlagsLocation = wrapVisualFlagsLocation_;
558 return changed;
561 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_) {
562 const bool changed = wrapVisualStartIndent != wrapVisualStartIndent_;
563 wrapVisualStartIndent = wrapVisualStartIndent_;
564 return changed;
567 bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_) {
568 const bool changed = wrapIndentMode != wrapIndentMode_;
569 wrapIndentMode = wrapIndentMode_;
570 return changed;
573 void ViewStyle::AllocStyles(size_t sizeNew) {
574 size_t i=styles.size();
575 styles.resize(sizeNew);
576 if (styles.size() > STYLE_DEFAULT) {
577 for (; i<sizeNew; i++) {
578 if (i != STYLE_DEFAULT) {
579 styles[i].ClearTo(styles[STYLE_DEFAULT]);
585 void ViewStyle::CreateAndAddFont(const FontSpecification &fs) {
586 if (fs.fontName) {
587 FontMap::iterator it = fonts.find(fs);
588 if (it == fonts.end()) {
589 fonts[fs] = std::unique_ptr<FontRealised>(new FontRealised());
594 FontRealised *ViewStyle::Find(const FontSpecification &fs) {
595 if (!fs.fontName) // Invalid specification so return arbitrary object
596 return fonts.begin()->second.get();
597 FontMap::iterator it = fonts.find(fs);
598 if (it != fonts.end()) {
599 // Should always reach here since map was just set for all styles
600 return it->second.get();
602 return 0;
605 void ViewStyle::FindMaxAscentDescent() {
606 for (FontMap::const_iterator it = fonts.cbegin(); it != fonts.cend(); ++it) {
607 if (maxAscent < it->second->ascent)
608 maxAscent = it->second->ascent;
609 if (maxDescent < it->second->descent)
610 maxDescent = it->second->descent;