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"
30 using namespace Scintilla
;
33 MarginStyle::MarginStyle(int style_
, int width_
, int mask_
) :
34 style(style_
), width(width_
), mask(mask_
), sensitive(false), cursor(SC_CURSORREVERSEARROW
) {
37 // A list of the fontnames - avoids wasting space in each style
38 FontNames::FontNames() {
41 FontNames::~FontNames() {
45 void FontNames::Clear() {
49 const char *FontNames::Save(const char *name
) {
53 for (const UniqueString
&nm
: names
) {
54 if (strcmp(nm
.get(), name
) == 0) {
59 names
.push_back(UniqueStringCopy(name
));
60 return names
.back().get();
63 FontRealised::FontRealised() {
66 FontRealised::~FontRealised() {
70 void FontRealised::Realise(Surface
&surface
, int zoomLevel
, int technology
, const FontSpecification
&fs
) {
71 PLATFORM_ASSERT(fs
.fontName
);
72 sizeZoomed
= fs
.size
+ zoomLevel
* SC_FONT_SIZE_MULTIPLIER
;
73 if (sizeZoomed
<= 2 * SC_FONT_SIZE_MULTIPLIER
) // Hangs if sizeZoomed <= 1
74 sizeZoomed
= 2 * SC_FONT_SIZE_MULTIPLIER
;
76 float deviceHeight
= static_cast<float>(surface
.DeviceHeightFont(sizeZoomed
));
77 FontParameters
fp(fs
.fontName
, deviceHeight
/ SC_FONT_SIZE_MULTIPLIER
, fs
.weight
, fs
.italic
, fs
.extraFontFlag
, technology
, fs
.characterSet
);
80 ascent
= static_cast<unsigned int>(surface
.Ascent(font
));
81 descent
= static_cast<unsigned int>(surface
.Descent(font
));
82 capitalHeight
= surface
.Ascent(font
) - surface
.InternalLeading(font
);
83 aveCharWidth
= surface
.AverageCharWidth(font
);
84 spaceWidth
= surface
.WidthChar(font
, ' ');
87 ViewStyle::ViewStyle() : markers(MARKER_MAX
+ 1), indicators(INDIC_MAX
+ 1) {
91 // Copy constructor only called when printing copies the screen ViewStyle so it can be
92 // modified for printing styles.
93 ViewStyle::ViewStyle(const ViewStyle
&source
) : markers(MARKER_MAX
+ 1), indicators(INDIC_MAX
+ 1) {
94 Init(source
.styles
.size());
95 styles
= source
.styles
;
96 for (size_t sty
=0; sty
<source
.styles
.size(); sty
++) {
97 // Can't just copy fontName as its lifetime is relative to its owning ViewStyle
98 styles
[sty
].fontName
= fontNames
.Save(source
.styles
[sty
].fontName
);
100 nextExtendedStyle
= source
.nextExtendedStyle
;
101 markers
= source
.markers
;
102 CalcLargestMarkerHeight();
104 indicators
= source
.indicators
;
106 indicatorsDynamic
= source
.indicatorsDynamic
;
107 indicatorsSetFore
= source
.indicatorsSetFore
;
109 selColours
= source
.selColours
;
110 selAdditionalForeground
= source
.selAdditionalForeground
;
111 selAdditionalBackground
= source
.selAdditionalBackground
;
112 selBackground2
= source
.selBackground2
;
113 selAlpha
= source
.selAlpha
;
114 selAdditionalAlpha
= source
.selAdditionalAlpha
;
115 selEOLFilled
= source
.selEOLFilled
;
117 foldmarginColour
= source
.foldmarginColour
;
118 foldmarginHighlightColour
= source
.foldmarginHighlightColour
;
120 hotspotColours
= source
.hotspotColours
;
121 hotspotUnderline
= source
.hotspotUnderline
;
122 hotspotSingleLine
= source
.hotspotSingleLine
;
124 whitespaceColours
= source
.whitespaceColours
;
125 controlCharSymbol
= source
.controlCharSymbol
;
126 controlCharWidth
= source
.controlCharWidth
;
127 selbar
= source
.selbar
;
128 selbarlight
= source
.selbarlight
;
129 caretcolour
= source
.caretcolour
;
130 additionalCaretColour
= source
.additionalCaretColour
;
131 caretLineFrame
= source
.caretLineFrame
;
132 showCaretLineBackground
= source
.showCaretLineBackground
;
133 alwaysShowCaretLineBackground
= source
.alwaysShowCaretLineBackground
;
134 caretLineBackground
= source
.caretLineBackground
;
135 caretLineAlpha
= source
.caretLineAlpha
;
136 caretStyle
= source
.caretStyle
;
137 caretWidth
= source
.caretWidth
;
138 someStylesProtected
= false;
139 someStylesForceCase
= false;
140 leftMarginWidth
= source
.leftMarginWidth
;
141 rightMarginWidth
= source
.rightMarginWidth
;
143 maskInLine
= source
.maskInLine
;
144 maskDrawInText
= source
.maskDrawInText
;
145 fixedColumnWidth
= source
.fixedColumnWidth
;
146 marginInside
= source
.marginInside
;
147 textStart
= source
.textStart
;
148 zoomLevel
= source
.zoomLevel
;
149 viewWhitespace
= source
.viewWhitespace
;
150 tabDrawMode
= source
.tabDrawMode
;
151 whitespaceSize
= source
.whitespaceSize
;
152 viewIndentationGuides
= source
.viewIndentationGuides
;
153 viewEOL
= source
.viewEOL
;
154 extraFontFlag
= source
.extraFontFlag
;
155 extraAscent
= source
.extraAscent
;
156 extraDescent
= source
.extraDescent
;
157 marginStyleOffset
= source
.marginStyleOffset
;
158 annotationVisible
= source
.annotationVisible
;
159 annotationStyleOffset
= source
.annotationStyleOffset
;
160 braceHighlightIndicatorSet
= source
.braceHighlightIndicatorSet
;
161 braceHighlightIndicator
= source
.braceHighlightIndicator
;
162 braceBadLightIndicatorSet
= source
.braceBadLightIndicatorSet
;
163 braceBadLightIndicator
= source
.braceBadLightIndicator
;
165 edgeState
= source
.edgeState
;
166 theEdge
= source
.theEdge
;
167 theMultiEdge
= source
.theMultiEdge
;
169 marginNumberPadding
= source
.marginNumberPadding
;
170 ctrlCharPadding
= source
.ctrlCharPadding
;
171 lastSegItalicsOffset
= source
.lastSegItalicsOffset
;
173 wrapState
= source
.wrapState
;
174 wrapVisualFlags
= source
.wrapVisualFlags
;
175 wrapVisualFlagsLocation
= source
.wrapVisualFlagsLocation
;
176 wrapVisualStartIndent
= source
.wrapVisualStartIndent
;
177 wrapIndentMode
= source
.wrapIndentMode
;
180 ViewStyle::~ViewStyle() {
185 void ViewStyle::CalculateMarginWidthAndMask() {
186 fixedColumnWidth
= marginInside
? leftMarginWidth
: 0;
187 maskInLine
= 0xffffffff;
188 int maskDefinedMarkers
= 0;
189 for (const MarginStyle
&m
: ms
) {
190 fixedColumnWidth
+= m
.width
;
192 maskInLine
&= ~m
.mask
;
193 maskDefinedMarkers
|= m
.mask
;
196 for (int markBit
= 0; markBit
< 32; markBit
++) {
197 const int maskBit
= 1 << markBit
;
198 switch (markers
[markBit
].markType
) {
200 maskInLine
&= ~maskBit
;
202 case SC_MARK_BACKGROUND
:
203 case SC_MARK_UNDERLINE
:
204 maskInLine
&= ~maskBit
;
205 maskDrawInText
|= maskDefinedMarkers
& maskBit
;
211 void ViewStyle::Init(size_t stylesSize_
) {
212 AllocStyles(stylesSize_
);
213 nextExtendedStyle
= 256;
217 // There are no image markers by default, so no need for calling CalcLargestMarkerHeight()
218 largestMarkerHeight
= 0;
220 indicators
[0] = Indicator(INDIC_SQUIGGLE
, ColourDesired(0, 0x7f, 0));
221 indicators
[1] = Indicator(INDIC_TT
, ColourDesired(0, 0, 0xff));
222 indicators
[2] = Indicator(INDIC_PLAIN
, ColourDesired(0xff, 0, 0));
224 technology
= SC_TECHNOLOGY_DEFAULT
;
225 indicatorsDynamic
= false;
226 indicatorsSetFore
= false;
233 tabWidth
= spaceWidth
* 8;
235 selColours
.fore
= ColourOptional(ColourDesired(0xff, 0, 0));
236 selColours
.back
= ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0), true);
237 selAdditionalForeground
= ColourDesired(0xff, 0, 0);
238 selAdditionalBackground
= ColourDesired(0xd7, 0xd7, 0xd7);
239 selBackground2
= ColourDesired(0xb0, 0xb0, 0xb0);
240 selAlpha
= SC_ALPHA_NOALPHA
;
241 selAdditionalAlpha
= SC_ALPHA_NOALPHA
;
242 selEOLFilled
= false;
244 foldmarginColour
= ColourOptional(ColourDesired(0xff, 0, 0));
245 foldmarginHighlightColour
= ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0));
247 whitespaceColours
.fore
= ColourOptional();
248 whitespaceColours
.back
= ColourOptional(ColourDesired(0xff, 0xff, 0xff));
249 controlCharSymbol
= 0; /* Draw the control characters */
250 controlCharWidth
= 0;
251 selbar
= Platform::Chrome();
252 selbarlight
= Platform::ChromeHighlight();
253 styles
[STYLE_LINENUMBER
].fore
= ColourDesired(0, 0, 0);
254 styles
[STYLE_LINENUMBER
].back
= Platform::Chrome();
255 caretcolour
= ColourDesired(0, 0, 0);
256 additionalCaretColour
= ColourDesired(0x7f, 0x7f, 0x7f);
258 showCaretLineBackground
= false;
259 alwaysShowCaretLineBackground
= false;
260 caretLineBackground
= ColourDesired(0xff, 0xff, 0);
261 caretLineAlpha
= SC_ALPHA_NOALPHA
;
262 caretStyle
= CARETSTYLE_LINE
;
264 someStylesProtected
= false;
265 someStylesForceCase
= false;
267 hotspotColours
.fore
= ColourOptional(ColourDesired(0, 0, 0xff));
268 hotspotColours
.back
= ColourOptional(ColourDesired(0xff, 0xff, 0xff));
269 hotspotUnderline
= true;
270 hotspotSingleLine
= true;
273 rightMarginWidth
= 1;
274 ms
.resize(SC_MAX_MARGIN
+ 1);
275 ms
[0] = MarginStyle(SC_MARGIN_NUMBER
);
276 ms
[1] = MarginStyle(SC_MARGIN_SYMBOL
, 16, ~SC_MASK_FOLDERS
);
277 ms
[2] = MarginStyle(SC_MARGIN_SYMBOL
);
279 CalculateMarginWidthAndMask();
280 textStart
= marginInside
? fixedColumnWidth
: leftMarginWidth
;
282 viewWhitespace
= wsInvisible
;
283 tabDrawMode
= tdLongArrow
;
285 viewIndentationGuides
= ivNone
;
290 marginStyleOffset
= 0;
291 annotationVisible
= ANNOTATION_HIDDEN
;
292 annotationStyleOffset
= 0;
293 braceHighlightIndicatorSet
= false;
294 braceHighlightIndicator
= 0;
295 braceBadLightIndicatorSet
= false;
296 braceBadLightIndicator
= 0;
298 edgeState
= EDGE_NONE
;
299 theEdge
= EdgeProperties(0, ColourDesired(0xc0, 0xc0, 0xc0));
301 marginNumberPadding
= 3;
302 ctrlCharPadding
= 3; // +3 For a blank on front and rounded edge each side
303 lastSegItalicsOffset
= 2;
305 wrapState
= eWrapNone
;
307 wrapVisualFlagsLocation
= 0;
308 wrapVisualStartIndent
= 0;
309 wrapIndentMode
= SC_WRAPINDENT_FIXED
;
312 void ViewStyle::Refresh(Surface
&surface
, int tabInChars
) {
315 selbar
= Platform::Chrome();
316 selbarlight
= Platform::ChromeHighlight();
318 // Apply the extra font flag which controls text drawing quality to each style.
319 for (Style
&style
: styles
) {
320 style
.extraFontFlag
= extraFontFlag
;
323 // Create a FontRealised object for each unique font in the styles.
324 CreateAndAddFont(styles
[STYLE_DEFAULT
]);
325 for (const Style
&style
: styles
) {
326 CreateAndAddFont(style
);
329 // Ask platform to allocate each unique font.
330 for (std::pair
<const FontSpecification
, std::unique_ptr
<FontRealised
>> &font
: fonts
) {
331 font
.second
->Realise(surface
, zoomLevel
, technology
, font
.first
);
334 // Set the platform font handle and measurements for each style.
335 for (Style
&style
: styles
) {
336 FontRealised
*fr
= Find(style
);
337 style
.Copy(fr
->font
, *fr
);
340 indicatorsDynamic
= std::any_of(indicators
.cbegin(), indicators
.cend(),
341 [](const Indicator
&indicator
) { return indicator
.IsDynamic(); });
343 indicatorsSetFore
= std::any_of(indicators
.cbegin(), indicators
.cend(),
344 [](const Indicator
&indicator
) { return indicator
.OverridesTextFore(); });
348 FindMaxAscentDescent();
349 maxAscent
+= extraAscent
;
350 maxDescent
+= extraDescent
;
351 lineHeight
= maxAscent
+ maxDescent
;
352 lineOverlap
= lineHeight
/ 10;
355 if (lineOverlap
> lineHeight
)
356 lineOverlap
= lineHeight
;
358 someStylesProtected
= std::any_of(styles
.cbegin(), styles
.cend(),
359 [](const Style
&style
) { return style
.IsProtected(); });
361 someStylesForceCase
= std::any_of(styles
.cbegin(), styles
.cend(),
362 [](const Style
&style
) { return style
.caseForce
!= Style::caseMixed
; });
364 aveCharWidth
= styles
[STYLE_DEFAULT
].aveCharWidth
;
365 spaceWidth
= styles
[STYLE_DEFAULT
].spaceWidth
;
366 tabWidth
= spaceWidth
* tabInChars
;
368 controlCharWidth
= 0.0;
369 if (controlCharSymbol
>= 32) {
370 controlCharWidth
= surface
.WidthChar(styles
[STYLE_CONTROLCHAR
].font
, static_cast<char>(controlCharSymbol
));
373 CalculateMarginWidthAndMask();
374 textStart
= marginInside
? fixedColumnWidth
: leftMarginWidth
;
377 void ViewStyle::ReleaseAllExtendedStyles() {
378 nextExtendedStyle
= 256;
381 int ViewStyle::AllocateExtendedStyles(int numberStyles
) {
382 int startRange
= static_cast<int>(nextExtendedStyle
);
383 nextExtendedStyle
+= numberStyles
;
384 EnsureStyle(nextExtendedStyle
);
385 for (size_t i
=startRange
; i
<nextExtendedStyle
; i
++) {
386 styles
[i
].ClearTo(styles
[STYLE_DEFAULT
]);
391 void ViewStyle::EnsureStyle(size_t index
) {
392 if (index
>= styles
.size()) {
393 AllocStyles(index
+1);
397 void ViewStyle::ResetDefaultStyle() {
398 styles
[STYLE_DEFAULT
].Clear(ColourDesired(0,0,0),
399 ColourDesired(0xff,0xff,0xff),
400 Platform::DefaultFontSize() * SC_FONT_SIZE_MULTIPLIER
, fontNames
.Save(Platform::DefaultFont()),
402 SC_WEIGHT_NORMAL
, false, false, false, Style::caseMixed
, true, true, false);
405 void ViewStyle::ClearStyles() {
406 // Reset all styles to be like the default style
407 for (unsigned int i
=0; i
<styles
.size(); i
++) {
408 if (i
!= STYLE_DEFAULT
) {
409 styles
[i
].ClearTo(styles
[STYLE_DEFAULT
]);
412 styles
[STYLE_LINENUMBER
].back
= Platform::Chrome();
414 // Set call tip fore/back to match the values previously set for call tips
415 styles
[STYLE_CALLTIP
].back
= ColourDesired(0xff, 0xff, 0xff);
416 styles
[STYLE_CALLTIP
].fore
= ColourDesired(0x80, 0x80, 0x80);
419 void ViewStyle::SetStyleFontName(int styleIndex
, const char *name
) {
420 styles
[styleIndex
].fontName
= fontNames
.Save(name
);
423 bool ViewStyle::ProtectionActive() const {
424 return someStylesProtected
;
427 int ViewStyle::ExternalMarginWidth() const {
428 return marginInside
? 0 : fixedColumnWidth
;
431 int ViewStyle::MarginFromLocation(Point pt
) const {
433 int x
= textStart
- fixedColumnWidth
;
434 for (size_t i
= 0; i
< ms
.size(); i
++) {
435 if ((pt
.x
>= x
) && (pt
.x
< x
+ ms
[i
].width
))
436 margin
= static_cast<int>(i
);
442 bool ViewStyle::ValidStyle(size_t styleIndex
) const {
443 return styleIndex
< styles
.size();
446 void ViewStyle::CalcLargestMarkerHeight() {
447 largestMarkerHeight
= 0;
448 for (const LineMarker
&marker
: markers
) {
449 switch (marker
.markType
) {
451 if (marker
.pxpm
&& marker
.pxpm
->GetHeight() > largestMarkerHeight
)
452 largestMarkerHeight
= marker
.pxpm
->GetHeight();
454 case SC_MARK_RGBAIMAGE
:
455 if (marker
.image
&& marker
.image
->GetHeight() > largestMarkerHeight
)
456 largestMarkerHeight
= marker
.image
->GetHeight();
462 int ViewStyle::GetFrameWidth() const {
463 return Platform::Clamp(caretLineFrame
, 1, lineHeight
/ 3);
466 bool ViewStyle::IsLineFrameOpaque(bool caretActive
, bool lineContainsCaret
) const {
467 return caretLineFrame
&& (caretActive
|| alwaysShowCaretLineBackground
) && showCaretLineBackground
&&
468 (caretLineAlpha
== SC_ALPHA_NOALPHA
) && lineContainsCaret
;
471 // See if something overrides the line background color: Either if caret is on the line
472 // and background color is set for that, or if a marker is defined that forces its background
473 // color onto the line, or if a marker is defined but has no selection margin in which to
474 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
475 // with the earlier taking precedence. When multiple markers cause background override,
476 // the color for the highest numbered one is used.
477 ColourOptional
ViewStyle::Background(int marksOfLine
, bool caretActive
, bool lineContainsCaret
) const {
478 ColourOptional background
;
479 if (!caretLineFrame
&& (caretActive
|| alwaysShowCaretLineBackground
) && showCaretLineBackground
&&
480 (caretLineAlpha
== SC_ALPHA_NOALPHA
) && lineContainsCaret
) {
481 background
= ColourOptional(caretLineBackground
, true);
483 if (!background
.isSet
&& marksOfLine
) {
484 int marks
= marksOfLine
;
485 for (int markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
486 if ((marks
& 1) && (markers
[markBit
].markType
== SC_MARK_BACKGROUND
) &&
487 (markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
488 background
= ColourOptional(markers
[markBit
].back
, true);
493 if (!background
.isSet
&& maskInLine
) {
494 int marksMasked
= marksOfLine
& maskInLine
;
496 for (int markBit
= 0; (markBit
< 32) && marksMasked
; markBit
++) {
497 if ((marksMasked
& 1) &&
498 (markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
499 background
= ColourOptional(markers
[markBit
].back
, true);
508 bool ViewStyle::SelectionBackgroundDrawn() const {
509 return selColours
.back
.isSet
&&
510 ((selAlpha
== SC_ALPHA_NOALPHA
) || (selAdditionalAlpha
== SC_ALPHA_NOALPHA
));
513 bool ViewStyle::WhitespaceBackgroundDrawn() const {
514 return (viewWhitespace
!= wsInvisible
) && (whitespaceColours
.back
.isSet
);
517 bool ViewStyle::WhiteSpaceVisible(bool inIndent
) const {
518 return (!inIndent
&& viewWhitespace
== wsVisibleAfterIndent
) ||
519 (inIndent
&& viewWhitespace
== wsVisibleOnlyInIndent
) ||
520 viewWhitespace
== wsVisibleAlways
;
523 ColourDesired
ViewStyle::WrapColour() const {
524 if (whitespaceColours
.fore
.isSet
)
525 return whitespaceColours
.fore
;
527 return styles
[STYLE_DEFAULT
].fore
;
530 bool ViewStyle::SetWrapState(int wrapState_
) {
531 WrapMode wrapStateWanted
;
532 switch (wrapState_
) {
534 wrapStateWanted
= eWrapWord
;
537 wrapStateWanted
= eWrapChar
;
539 case SC_WRAP_WHITESPACE
:
540 wrapStateWanted
= eWrapWhitespace
;
543 wrapStateWanted
= eWrapNone
;
546 const bool changed
= wrapState
!= wrapStateWanted
;
547 wrapState
= wrapStateWanted
;
551 bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_
) {
552 const bool changed
= wrapVisualFlags
!= wrapVisualFlags_
;
553 wrapVisualFlags
= wrapVisualFlags_
;
557 bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_
) {
558 const bool changed
= wrapVisualFlagsLocation
!= wrapVisualFlagsLocation_
;
559 wrapVisualFlagsLocation
= wrapVisualFlagsLocation_
;
563 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_
) {
564 const bool changed
= wrapVisualStartIndent
!= wrapVisualStartIndent_
;
565 wrapVisualStartIndent
= wrapVisualStartIndent_
;
569 bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_
) {
570 const bool changed
= wrapIndentMode
!= wrapIndentMode_
;
571 wrapIndentMode
= wrapIndentMode_
;
575 void ViewStyle::AllocStyles(size_t sizeNew
) {
576 size_t i
=styles
.size();
577 styles
.resize(sizeNew
);
578 if (styles
.size() > STYLE_DEFAULT
) {
579 for (; i
<sizeNew
; i
++) {
580 if (i
!= STYLE_DEFAULT
) {
581 styles
[i
].ClearTo(styles
[STYLE_DEFAULT
]);
587 void ViewStyle::CreateAndAddFont(const FontSpecification
&fs
) {
589 FontMap::iterator it
= fonts
.find(fs
);
590 if (it
== fonts
.end()) {
591 fonts
[fs
] = std::unique_ptr
<FontRealised
>(new FontRealised());
596 FontRealised
*ViewStyle::Find(const FontSpecification
&fs
) {
597 if (!fs
.fontName
) // Invalid specification so return arbitrary object
598 return fonts
.begin()->second
.get();
599 FontMap::iterator it
= fonts
.find(fs
);
600 if (it
!= fonts
.end()) {
601 // Should always reach here since map was just set for all styles
602 return it
->second
.get();
607 void ViewStyle::FindMaxAscentDescent() {
608 for (FontMap::const_iterator it
= fonts
.cbegin(); it
!= fonts
.cend(); ++it
) {
609 if (maxAscent
< it
->second
->ascent
)
610 maxAscent
= it
->second
->ascent
;
611 if (maxDescent
< it
->second
->descent
)
612 maxDescent
= it
->second
->descent
;