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.
17 #include "Scintilla.h"
19 #include "SplitVector.h"
20 #include "Partitioning.h"
21 #include "RunStyles.h"
22 #include "Indicator.h"
24 #include "LineMarker.h"
26 #include "ViewStyle.h"
29 using namespace Scintilla
;
32 MarginStyle::MarginStyle() :
33 style(SC_MARGIN_SYMBOL
), width(0), mask(0), sensitive(false), cursor(SC_CURSORREVERSEARROW
) {
36 // A list of the fontnames - avoids wasting space in each style
37 FontNames::FontNames() {
40 FontNames::~FontNames() {
44 void FontNames::Clear() {
45 for (std::vector
<char *>::const_iterator it
=names
.begin(); it
!= names
.end(); ++it
) {
51 const char *FontNames::Save(const char *name
) {
55 for (std::vector
<char *>::const_iterator it
=names
.begin(); it
!= names
.end(); ++it
) {
56 if (strcmp(*it
, name
) == 0) {
60 const size_t lenName
= strlen(name
) + 1;
61 char *nameSave
= new char[lenName
];
62 memcpy(nameSave
, name
, lenName
);
63 names
.push_back(nameSave
);
67 FontRealised::FontRealised() {
70 FontRealised::~FontRealised() {
74 void FontRealised::Realise(Surface
&surface
, int zoomLevel
, int technology
, const FontSpecification
&fs
) {
75 PLATFORM_ASSERT(fs
.fontName
);
76 sizeZoomed
= fs
.size
+ zoomLevel
* SC_FONT_SIZE_MULTIPLIER
;
77 if (sizeZoomed
<= 2 * SC_FONT_SIZE_MULTIPLIER
) // Hangs if sizeZoomed <= 1
78 sizeZoomed
= 2 * SC_FONT_SIZE_MULTIPLIER
;
80 float deviceHeight
= static_cast<float>(surface
.DeviceHeightFont(sizeZoomed
));
81 FontParameters
fp(fs
.fontName
, deviceHeight
/ SC_FONT_SIZE_MULTIPLIER
, fs
.weight
, fs
.italic
, fs
.extraFontFlag
, technology
, fs
.characterSet
);
84 ascent
= static_cast<unsigned int>(surface
.Ascent(font
));
85 descent
= static_cast<unsigned int>(surface
.Descent(font
));
86 aveCharWidth
= surface
.AverageCharWidth(font
);
87 spaceWidth
= surface
.WidthChar(font
, ' ');
90 ViewStyle::ViewStyle() {
94 ViewStyle::ViewStyle(const ViewStyle
&source
) {
95 Init(source
.styles
.size());
96 for (unsigned int sty
=0; sty
<source
.styles
.size(); sty
++) {
97 styles
[sty
] = source
.styles
[sty
];
98 // Can't just copy fontname as its lifetime is relative to its owning ViewStyle
99 styles
[sty
].fontName
= fontNames
.Save(source
.styles
[sty
].fontName
);
101 nextExtendedStyle
= source
.nextExtendedStyle
;
102 for (int mrk
=0; mrk
<=MARKER_MAX
; mrk
++) {
103 markers
[mrk
] = source
.markers
[mrk
];
105 CalcLargestMarkerHeight();
106 indicatorsDynamic
= 0;
107 indicatorsSetFore
= 0;
108 for (int ind
=0; ind
<=INDIC_MAX
; ind
++) {
109 indicators
[ind
] = source
.indicators
[ind
];
110 if (indicators
[ind
].IsDynamic())
112 if (indicators
[ind
].OverridesTextFore())
116 selColours
= source
.selColours
;
117 selAdditionalForeground
= source
.selAdditionalForeground
;
118 selAdditionalBackground
= source
.selAdditionalBackground
;
119 selBackground2
= source
.selBackground2
;
120 selAlpha
= source
.selAlpha
;
121 selAdditionalAlpha
= source
.selAdditionalAlpha
;
122 selEOLFilled
= source
.selEOLFilled
;
124 foldmarginColour
= source
.foldmarginColour
;
125 foldmarginHighlightColour
= source
.foldmarginHighlightColour
;
127 hotspotColours
= source
.hotspotColours
;
128 hotspotUnderline
= source
.hotspotUnderline
;
129 hotspotSingleLine
= source
.hotspotSingleLine
;
131 whitespaceColours
= source
.whitespaceColours
;
132 controlCharSymbol
= source
.controlCharSymbol
;
133 controlCharWidth
= source
.controlCharWidth
;
134 selbar
= source
.selbar
;
135 selbarlight
= source
.selbarlight
;
136 caretcolour
= source
.caretcolour
;
137 additionalCaretColour
= source
.additionalCaretColour
;
138 showCaretLineBackground
= source
.showCaretLineBackground
;
139 alwaysShowCaretLineBackground
= source
.alwaysShowCaretLineBackground
;
140 caretLineBackground
= source
.caretLineBackground
;
141 caretLineAlpha
= source
.caretLineAlpha
;
142 caretStyle
= source
.caretStyle
;
143 caretWidth
= source
.caretWidth
;
144 someStylesProtected
= false;
145 someStylesForceCase
= false;
146 leftMarginWidth
= source
.leftMarginWidth
;
147 rightMarginWidth
= source
.rightMarginWidth
;
149 maskInLine
= source
.maskInLine
;
150 maskDrawInText
= source
.maskDrawInText
;
151 fixedColumnWidth
= source
.fixedColumnWidth
;
152 marginInside
= source
.marginInside
;
153 textStart
= source
.textStart
;
154 zoomLevel
= source
.zoomLevel
;
155 viewWhitespace
= source
.viewWhitespace
;
156 tabDrawMode
= source
.tabDrawMode
;
157 whitespaceSize
= source
.whitespaceSize
;
158 viewIndentationGuides
= source
.viewIndentationGuides
;
159 viewEOL
= source
.viewEOL
;
160 extraFontFlag
= source
.extraFontFlag
;
161 extraAscent
= source
.extraAscent
;
162 extraDescent
= source
.extraDescent
;
163 marginStyleOffset
= source
.marginStyleOffset
;
164 annotationVisible
= source
.annotationVisible
;
165 annotationStyleOffset
= source
.annotationStyleOffset
;
166 braceHighlightIndicatorSet
= source
.braceHighlightIndicatorSet
;
167 braceHighlightIndicator
= source
.braceHighlightIndicator
;
168 braceBadLightIndicatorSet
= source
.braceBadLightIndicatorSet
;
169 braceBadLightIndicator
= source
.braceBadLightIndicator
;
171 edgeState
= source
.edgeState
;
172 theEdge
= source
.theEdge
;
173 theMultiEdge
= source
.theMultiEdge
;
175 marginNumberPadding
= source
.marginNumberPadding
;
176 ctrlCharPadding
= source
.ctrlCharPadding
;
177 lastSegItalicsOffset
= source
.lastSegItalicsOffset
;
179 wrapState
= source
.wrapState
;
180 wrapVisualFlags
= source
.wrapVisualFlags
;
181 wrapVisualFlagsLocation
= source
.wrapVisualFlagsLocation
;
182 wrapVisualStartIndent
= source
.wrapVisualStartIndent
;
183 wrapIndentMode
= source
.wrapIndentMode
;
186 ViewStyle::~ViewStyle() {
188 for (FontMap::iterator it
= fonts
.begin(); it
!= fonts
.end(); ++it
) {
194 void ViewStyle::CalculateMarginWidthAndMask() {
195 fixedColumnWidth
= marginInside
? leftMarginWidth
: 0;
196 maskInLine
= 0xffffffff;
197 int maskDefinedMarkers
= 0;
198 for (size_t margin
= 0; margin
< ms
.size(); margin
++) {
199 fixedColumnWidth
+= ms
[margin
].width
;
200 if (ms
[margin
].width
> 0)
201 maskInLine
&= ~ms
[margin
].mask
;
202 maskDefinedMarkers
|= ms
[margin
].mask
;
205 for (int markBit
= 0; markBit
< 32; markBit
++) {
206 const int maskBit
= 1 << markBit
;
207 switch (markers
[markBit
].markType
) {
209 maskInLine
&= ~maskBit
;
211 case SC_MARK_BACKGROUND
:
212 case SC_MARK_UNDERLINE
:
213 maskInLine
&= ~maskBit
;
214 maskDrawInText
|= maskDefinedMarkers
& maskBit
;
220 void ViewStyle::Init(size_t stylesSize_
) {
221 AllocStyles(stylesSize_
);
222 nextExtendedStyle
= 256;
226 // There are no image markers by default, so no need for calling CalcLargestMarkerHeight()
227 largestMarkerHeight
= 0;
229 indicators
[0] = Indicator(INDIC_SQUIGGLE
, ColourDesired(0, 0x7f, 0));
230 indicators
[1] = Indicator(INDIC_TT
, ColourDesired(0, 0, 0xff));
231 indicators
[2] = Indicator(INDIC_PLAIN
, ColourDesired(0xff, 0, 0));
233 technology
= SC_TECHNOLOGY_DEFAULT
;
234 indicatorsDynamic
= 0;
235 indicatorsSetFore
= 0;
242 tabWidth
= spaceWidth
* 8;
244 selColours
.fore
= ColourOptional(ColourDesired(0xff, 0, 0));
245 selColours
.back
= ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0), true);
246 selAdditionalForeground
= ColourDesired(0xff, 0, 0);
247 selAdditionalBackground
= ColourDesired(0xd7, 0xd7, 0xd7);
248 selBackground2
= ColourDesired(0xb0, 0xb0, 0xb0);
249 selAlpha
= SC_ALPHA_NOALPHA
;
250 selAdditionalAlpha
= SC_ALPHA_NOALPHA
;
251 selEOLFilled
= false;
253 foldmarginColour
= ColourOptional(ColourDesired(0xff, 0, 0));
254 foldmarginHighlightColour
= ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0));
256 whitespaceColours
.fore
= ColourOptional();
257 whitespaceColours
.back
= ColourOptional(ColourDesired(0xff, 0xff, 0xff));
258 controlCharSymbol
= 0; /* Draw the control characters */
259 controlCharWidth
= 0;
260 selbar
= Platform::Chrome();
261 selbarlight
= Platform::ChromeHighlight();
262 styles
[STYLE_LINENUMBER
].fore
= ColourDesired(0, 0, 0);
263 styles
[STYLE_LINENUMBER
].back
= Platform::Chrome();
264 caretcolour
= ColourDesired(0, 0, 0);
265 additionalCaretColour
= ColourDesired(0x7f, 0x7f, 0x7f);
266 showCaretLineBackground
= false;
267 alwaysShowCaretLineBackground
= false;
268 caretLineBackground
= ColourDesired(0xff, 0xff, 0);
269 caretLineAlpha
= SC_ALPHA_NOALPHA
;
270 caretStyle
= CARETSTYLE_LINE
;
272 someStylesProtected
= false;
273 someStylesForceCase
= false;
275 hotspotColours
.fore
= ColourOptional(ColourDesired(0, 0, 0xff));
276 hotspotColours
.back
= ColourOptional(ColourDesired(0xff, 0xff, 0xff));
277 hotspotUnderline
= true;
278 hotspotSingleLine
= true;
281 rightMarginWidth
= 1;
282 ms
.resize(SC_MAX_MARGIN
+ 1);
283 ms
[0].style
= SC_MARGIN_NUMBER
;
286 ms
[1].style
= SC_MARGIN_SYMBOL
;
288 ms
[1].mask
= ~SC_MASK_FOLDERS
;
289 ms
[2].style
= SC_MARGIN_SYMBOL
;
293 CalculateMarginWidthAndMask();
294 textStart
= marginInside
? fixedColumnWidth
: leftMarginWidth
;
296 viewWhitespace
= wsInvisible
;
297 tabDrawMode
= tdLongArrow
;
299 viewIndentationGuides
= ivNone
;
304 marginStyleOffset
= 0;
305 annotationVisible
= ANNOTATION_HIDDEN
;
306 annotationStyleOffset
= 0;
307 braceHighlightIndicatorSet
= false;
308 braceHighlightIndicator
= 0;
309 braceBadLightIndicatorSet
= false;
310 braceBadLightIndicator
= 0;
312 edgeState
= EDGE_NONE
;
313 theEdge
= EdgeProperties(0, ColourDesired(0xc0, 0xc0, 0xc0));
315 marginNumberPadding
= 3;
316 ctrlCharPadding
= 3; // +3 For a blank on front and rounded edge each side
317 lastSegItalicsOffset
= 2;
319 wrapState
= eWrapNone
;
321 wrapVisualFlagsLocation
= 0;
322 wrapVisualStartIndent
= 0;
323 wrapIndentMode
= SC_WRAPINDENT_FIXED
;
326 void ViewStyle::Refresh(Surface
&surface
, int tabInChars
) {
327 for (FontMap::iterator it
= fonts
.begin(); it
!= fonts
.end(); ++it
) {
332 selbar
= Platform::Chrome();
333 selbarlight
= Platform::ChromeHighlight();
335 for (unsigned int i
=0; i
<styles
.size(); i
++) {
336 styles
[i
].extraFontFlag
= extraFontFlag
;
339 CreateAndAddFont(styles
[STYLE_DEFAULT
]);
340 for (unsigned int j
=0; j
<styles
.size(); j
++) {
341 CreateAndAddFont(styles
[j
]);
344 for (FontMap::iterator it
= fonts
.begin(); it
!= fonts
.end(); ++it
) {
345 it
->second
->Realise(surface
, zoomLevel
, technology
, it
->first
);
348 for (unsigned int k
=0; k
<styles
.size(); k
++) {
349 FontRealised
*fr
= Find(styles
[k
]);
350 styles
[k
].Copy(fr
->font
, *fr
);
352 indicatorsDynamic
= 0;
353 indicatorsSetFore
= 0;
354 for (int ind
= 0; ind
<= INDIC_MAX
; ind
++) {
355 if (indicators
[ind
].IsDynamic())
357 if (indicators
[ind
].OverridesTextFore())
362 FindMaxAscentDescent();
363 maxAscent
+= extraAscent
;
364 maxDescent
+= extraDescent
;
365 lineHeight
= maxAscent
+ maxDescent
;
366 lineOverlap
= lineHeight
/ 10;
369 if (lineOverlap
> lineHeight
)
370 lineOverlap
= lineHeight
;
372 someStylesProtected
= false;
373 someStylesForceCase
= false;
374 for (unsigned int l
=0; l
<styles
.size(); l
++) {
375 if (styles
[l
].IsProtected()) {
376 someStylesProtected
= true;
378 if (styles
[l
].caseForce
!= Style::caseMixed
) {
379 someStylesForceCase
= true;
383 aveCharWidth
= styles
[STYLE_DEFAULT
].aveCharWidth
;
384 spaceWidth
= styles
[STYLE_DEFAULT
].spaceWidth
;
385 tabWidth
= spaceWidth
* tabInChars
;
387 controlCharWidth
= 0.0;
388 if (controlCharSymbol
>= 32) {
389 controlCharWidth
= surface
.WidthChar(styles
[STYLE_CONTROLCHAR
].font
, static_cast<char>(controlCharSymbol
));
392 CalculateMarginWidthAndMask();
393 textStart
= marginInside
? fixedColumnWidth
: leftMarginWidth
;
396 void ViewStyle::ReleaseAllExtendedStyles() {
397 nextExtendedStyle
= 256;
400 int ViewStyle::AllocateExtendedStyles(int numberStyles
) {
401 int startRange
= static_cast<int>(nextExtendedStyle
);
402 nextExtendedStyle
+= numberStyles
;
403 EnsureStyle(nextExtendedStyle
);
404 for (size_t i
=startRange
; i
<nextExtendedStyle
; i
++) {
405 styles
[i
].ClearTo(styles
[STYLE_DEFAULT
]);
410 void ViewStyle::EnsureStyle(size_t index
) {
411 if (index
>= styles
.size()) {
412 AllocStyles(index
+1);
416 void ViewStyle::ResetDefaultStyle() {
417 styles
[STYLE_DEFAULT
].Clear(ColourDesired(0,0,0),
418 ColourDesired(0xff,0xff,0xff),
419 Platform::DefaultFontSize() * SC_FONT_SIZE_MULTIPLIER
, fontNames
.Save(Platform::DefaultFont()),
421 SC_WEIGHT_NORMAL
, false, false, false, Style::caseMixed
, true, true, false);
424 void ViewStyle::ClearStyles() {
425 // Reset all styles to be like the default style
426 for (unsigned int i
=0; i
<styles
.size(); i
++) {
427 if (i
!= STYLE_DEFAULT
) {
428 styles
[i
].ClearTo(styles
[STYLE_DEFAULT
]);
431 styles
[STYLE_LINENUMBER
].back
= Platform::Chrome();
433 // Set call tip fore/back to match the values previously set for call tips
434 styles
[STYLE_CALLTIP
].back
= ColourDesired(0xff, 0xff, 0xff);
435 styles
[STYLE_CALLTIP
].fore
= ColourDesired(0x80, 0x80, 0x80);
438 void ViewStyle::SetStyleFontName(int styleIndex
, const char *name
) {
439 styles
[styleIndex
].fontName
= fontNames
.Save(name
);
442 bool ViewStyle::ProtectionActive() const {
443 return someStylesProtected
;
446 int ViewStyle::ExternalMarginWidth() const {
447 return marginInside
? 0 : fixedColumnWidth
;
450 int ViewStyle::MarginFromLocation(Point pt
) const {
452 int x
= textStart
- fixedColumnWidth
;
453 for (size_t i
= 0; i
< ms
.size(); i
++) {
454 if ((pt
.x
>= x
) && (pt
.x
< x
+ ms
[i
].width
))
455 margin
= static_cast<int>(i
);
461 bool ViewStyle::ValidStyle(size_t styleIndex
) const {
462 return styleIndex
< styles
.size();
465 void ViewStyle::CalcLargestMarkerHeight() {
466 largestMarkerHeight
= 0;
467 for (int m
= 0; m
<= MARKER_MAX
; ++m
) {
468 switch (markers
[m
].markType
) {
470 if (markers
[m
].pxpm
&& markers
[m
].pxpm
->GetHeight() > largestMarkerHeight
)
471 largestMarkerHeight
= markers
[m
].pxpm
->GetHeight();
473 case SC_MARK_RGBAIMAGE
:
474 if (markers
[m
].image
&& markers
[m
].image
->GetHeight() > largestMarkerHeight
)
475 largestMarkerHeight
= markers
[m
].image
->GetHeight();
481 // See if something overrides the line background color: Either if caret is on the line
482 // and background color is set for that, or if a marker is defined that forces its background
483 // color onto the line, or if a marker is defined but has no selection margin in which to
484 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
485 // with the earlier taking precedence. When multiple markers cause background override,
486 // the color for the highest numbered one is used.
487 ColourOptional
ViewStyle::Background(int marksOfLine
, bool caretActive
, bool lineContainsCaret
) const {
488 ColourOptional background
;
489 if ((caretActive
|| alwaysShowCaretLineBackground
) && showCaretLineBackground
&& (caretLineAlpha
== SC_ALPHA_NOALPHA
) && lineContainsCaret
) {
490 background
= ColourOptional(caretLineBackground
, true);
492 if (!background
.isSet
&& marksOfLine
) {
493 int marks
= marksOfLine
;
494 for (int markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
495 if ((marks
& 1) && (markers
[markBit
].markType
== SC_MARK_BACKGROUND
) &&
496 (markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
497 background
= ColourOptional(markers
[markBit
].back
, true);
502 if (!background
.isSet
&& maskInLine
) {
503 int marksMasked
= marksOfLine
& maskInLine
;
505 for (int markBit
= 0; (markBit
< 32) && marksMasked
; markBit
++) {
506 if ((marksMasked
& 1) &&
507 (markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
508 background
= ColourOptional(markers
[markBit
].back
, true);
517 bool ViewStyle::SelectionBackgroundDrawn() const {
518 return selColours
.back
.isSet
&&
519 ((selAlpha
== SC_ALPHA_NOALPHA
) || (selAdditionalAlpha
== SC_ALPHA_NOALPHA
));
522 bool ViewStyle::WhitespaceBackgroundDrawn() const {
523 return (viewWhitespace
!= wsInvisible
) && (whitespaceColours
.back
.isSet
);
526 bool ViewStyle::WhiteSpaceVisible(bool inIndent
) const {
527 return (!inIndent
&& viewWhitespace
== wsVisibleAfterIndent
) ||
528 (inIndent
&& viewWhitespace
== wsVisibleOnlyInIndent
) ||
529 viewWhitespace
== wsVisibleAlways
;
532 ColourDesired
ViewStyle::WrapColour() const {
533 if (whitespaceColours
.fore
.isSet
)
534 return whitespaceColours
.fore
;
536 return styles
[STYLE_DEFAULT
].fore
;
539 bool ViewStyle::SetWrapState(int wrapState_
) {
540 WrapMode wrapStateWanted
;
541 switch (wrapState_
) {
543 wrapStateWanted
= eWrapWord
;
546 wrapStateWanted
= eWrapChar
;
548 case SC_WRAP_WHITESPACE
:
549 wrapStateWanted
= eWrapWhitespace
;
552 wrapStateWanted
= eWrapNone
;
555 bool changed
= wrapState
!= wrapStateWanted
;
556 wrapState
= wrapStateWanted
;
560 bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_
) {
561 bool changed
= wrapVisualFlags
!= wrapVisualFlags_
;
562 wrapVisualFlags
= wrapVisualFlags_
;
566 bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_
) {
567 bool changed
= wrapVisualFlagsLocation
!= wrapVisualFlagsLocation_
;
568 wrapVisualFlagsLocation
= wrapVisualFlagsLocation_
;
572 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_
) {
573 bool changed
= wrapVisualStartIndent
!= wrapVisualStartIndent_
;
574 wrapVisualStartIndent
= wrapVisualStartIndent_
;
578 bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_
) {
579 bool changed
= wrapIndentMode
!= wrapIndentMode_
;
580 wrapIndentMode
= wrapIndentMode_
;
584 void ViewStyle::AllocStyles(size_t sizeNew
) {
585 size_t i
=styles
.size();
586 styles
.resize(sizeNew
);
587 if (styles
.size() > STYLE_DEFAULT
) {
588 for (; i
<sizeNew
; i
++) {
589 if (i
!= STYLE_DEFAULT
) {
590 styles
[i
].ClearTo(styles
[STYLE_DEFAULT
]);
596 void ViewStyle::CreateAndAddFont(const FontSpecification
&fs
) {
598 FontMap::iterator it
= fonts
.find(fs
);
599 if (it
== fonts
.end()) {
600 fonts
[fs
] = new FontRealised();
605 FontRealised
*ViewStyle::Find(const FontSpecification
&fs
) {
606 if (!fs
.fontName
) // Invalid specification so return arbitrary object
607 return fonts
.begin()->second
;
608 FontMap::iterator it
= fonts
.find(fs
);
609 if (it
!= fonts
.end()) {
610 // Should always reach here since map was just set for all styles
616 void ViewStyle::FindMaxAscentDescent() {
617 for (FontMap::const_iterator it
= fonts
.begin(); it
!= fonts
.end(); ++it
) {
618 if (maxAscent
< it
->second
->ascent
)
619 maxAscent
= it
->second
->ascent
;
620 if (maxDescent
< it
->second
->descent
)
621 maxDescent
= it
->second
->descent
;