1 // Scintilla source code edit control
2 /** @file ViewStyle.cxx
3 ** Store information on how the document is to be viewed.
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.
20 #include "Scintilla.h"
22 #include "UniqueString.h"
23 #include "Indicator.h"
25 #include "LineMarker.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() {
43 void FontNames::Clear() {
47 const char *FontNames::Save(const char *name
) {
51 for (const UniqueString
&nm
: names
) {
52 if (strcmp(nm
.get(), name
) == 0) {
57 names
.push_back(UniqueStringCopy(name
));
58 return names
.back().get();
61 FontRealised::FontRealised() {
64 FontRealised::~FontRealised() {
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
);
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) {
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
;
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() {
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
;
190 maskInLine
&= ~m
.mask
;
191 maskDefinedMarkers
|= m
.mask
;
194 for (int markBit
= 0; markBit
< 32; markBit
++) {
195 const int maskBit
= 1U << markBit
;
196 switch (markers
[markBit
].markType
) {
198 maskInLine
&= ~maskBit
;
200 case SC_MARK_BACKGROUND
:
201 case SC_MARK_UNDERLINE
:
202 maskInLine
&= ~maskBit
;
203 maskDrawInText
|= maskDefinedMarkers
& maskBit
;
209 void ViewStyle::Init(size_t stylesSize_
) {
210 AllocStyles(stylesSize_
);
211 nextExtendedStyle
= 256;
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;
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);
256 showCaretLineBackground
= false;
257 alwaysShowCaretLineBackground
= false;
258 caretLineBackground
= ColourDesired(0xff, 0xff, 0);
259 caretLineAlpha
= SC_ALPHA_NOALPHA
;
260 caretStyle
= CARETSTYLE_LINE
;
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;
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
);
277 CalculateMarginWidthAndMask();
278 textStart
= marginInside
? fixedColumnWidth
: leftMarginWidth
;
280 viewWhitespace
= wsInvisible
;
281 tabDrawMode
= tdLongArrow
;
283 viewIndentationGuides
= ivNone
;
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
;
305 wrapVisualFlagsLocation
= 0;
306 wrapVisualStartIndent
= 0;
307 wrapIndentMode
= SC_WRAPINDENT_FIXED
;
310 void ViewStyle::Refresh(Surface
&surface
, int tabInChars
) {
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(); });
346 FindMaxAscentDescent();
347 maxAscent
+= extraAscent
;
348 maxDescent
+= extraDescent
;
349 lineHeight
= maxAscent
+ maxDescent
;
350 lineOverlap
= lineHeight
/ 10;
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
]);
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()),
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 {
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
);
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
) {
449 if (marker
.pxpm
&& marker
.pxpm
->GetHeight() > largestMarkerHeight
)
450 largestMarkerHeight
= marker
.pxpm
->GetHeight();
452 case SC_MARK_RGBAIMAGE
:
453 if (marker
.image
&& marker
.image
->GetHeight() > largestMarkerHeight
)
454 largestMarkerHeight
= marker
.image
->GetHeight();
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);
491 if (!background
.isSet
&& maskInLine
) {
492 int marksMasked
= marksOfLine
& maskInLine
;
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);
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
;
525 return styles
[STYLE_DEFAULT
].fore
;
528 bool ViewStyle::SetWrapState(int wrapState_
) {
529 WrapMode wrapStateWanted
;
530 switch (wrapState_
) {
532 wrapStateWanted
= eWrapWord
;
535 wrapStateWanted
= eWrapChar
;
537 case SC_WRAP_WHITESPACE
:
538 wrapStateWanted
= eWrapWhitespace
;
541 wrapStateWanted
= eWrapNone
;
544 const bool changed
= wrapState
!= wrapStateWanted
;
545 wrapState
= wrapStateWanted
;
549 bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_
) {
550 const bool changed
= wrapVisualFlags
!= wrapVisualFlags_
;
551 wrapVisualFlags
= wrapVisualFlags_
;
555 bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_
) {
556 const bool changed
= wrapVisualFlagsLocation
!= wrapVisualFlagsLocation_
;
557 wrapVisualFlagsLocation
= wrapVisualFlagsLocation_
;
561 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_
) {
562 const bool changed
= wrapVisualStartIndent
!= wrapVisualStartIndent_
;
563 wrapVisualStartIndent
= wrapVisualStartIndent_
;
567 bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_
) {
568 const bool changed
= wrapIndentMode
!= wrapIndentMode_
;
569 wrapIndentMode
= wrapIndentMode_
;
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
) {
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();
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
;