Update Tortoise.pot
[TortoiseGit.git] / ext / scintilla / src / ViewStyle.cxx
blobac660f7ff2949cc1ed98d165e1752fc8aea3a20e
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 #ifdef SCI_NAMESPACE
30 using namespace Scintilla;
31 #endif
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() {
42 Clear();
45 void FontNames::Clear() {
46 names.clear();
49 const char *FontNames::Save(const char *name) {
50 if (!name)
51 return nullptr;
53 for (const UniqueString &nm : names) {
54 if (strcmp(nm.get(), name) == 0) {
55 return nm.get();
59 names.push_back(UniqueStringCopy(name));
60 return names.back().get();
63 FontRealised::FontRealised() {
66 FontRealised::~FontRealised() {
67 font.Release();
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);
78 font.Create(fp);
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) {
88 Init();
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;
142 ms = source.ms;
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() {
181 styles.clear();
182 fonts.clear();
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;
191 if (m.width > 0)
192 maskInLine &= ~m.mask;
193 maskDefinedMarkers |= m.mask;
195 maskDrawInText = 0;
196 for (int markBit = 0; markBit < 32; markBit++) {
197 const int maskBit = 1 << markBit;
198 switch (markers[markBit].markType) {
199 case SC_MARK_EMPTY:
200 maskInLine &= ~maskBit;
201 break;
202 case SC_MARK_BACKGROUND:
203 case SC_MARK_UNDERLINE:
204 maskInLine &= ~maskBit;
205 maskDrawInText |= maskDefinedMarkers & maskBit;
206 break;
211 void ViewStyle::Init(size_t stylesSize_) {
212 AllocStyles(stylesSize_);
213 nextExtendedStyle = 256;
214 fontNames.Clear();
215 ResetDefaultStyle();
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;
227 lineHeight = 1;
228 lineOverlap = 0;
229 maxAscent = 1;
230 maxDescent = 1;
231 aveCharWidth = 8;
232 spaceWidth = 8;
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);
257 caretLineFrame = 0;
258 showCaretLineBackground = false;
259 alwaysShowCaretLineBackground = false;
260 caretLineBackground = ColourDesired(0xff, 0xff, 0);
261 caretLineAlpha = SC_ALPHA_NOALPHA;
262 caretStyle = CARETSTYLE_LINE;
263 caretWidth = 1;
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;
272 leftMarginWidth = 1;
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);
278 marginInside = true;
279 CalculateMarginWidthAndMask();
280 textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
281 zoomLevel = 0;
282 viewWhitespace = wsInvisible;
283 tabDrawMode = tdLongArrow;
284 whitespaceSize = 1;
285 viewIndentationGuides = ivNone;
286 viewEOL = false;
287 extraFontFlag = 0;
288 extraAscent = 0;
289 extraDescent = 0;
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;
306 wrapVisualFlags = 0;
307 wrapVisualFlagsLocation = 0;
308 wrapVisualStartIndent = 0;
309 wrapIndentMode = SC_WRAPINDENT_FIXED;
312 void ViewStyle::Refresh(Surface &surface, int tabInChars) {
313 fonts.clear();
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(); });
346 maxAscent = 1;
347 maxDescent = 1;
348 FindMaxAscentDescent();
349 maxAscent += extraAscent;
350 maxDescent += extraDescent;
351 lineHeight = maxAscent + maxDescent;
352 lineOverlap = lineHeight / 10;
353 if (lineOverlap < 2)
354 lineOverlap = 2;
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]);
388 return startRange;
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()),
401 SC_CHARSET_DEFAULT,
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 {
432 int margin = -1;
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);
437 x += ms[i].width;
439 return margin;
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) {
450 case SC_MARK_PIXMAP:
451 if (marker.pxpm && marker.pxpm->GetHeight() > largestMarkerHeight)
452 largestMarkerHeight = marker.pxpm->GetHeight();
453 break;
454 case SC_MARK_RGBAIMAGE:
455 if (marker.image && marker.image->GetHeight() > largestMarkerHeight)
456 largestMarkerHeight = marker.image->GetHeight();
457 break;
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);
490 marks >>= 1;
493 if (!background.isSet && maskInLine) {
494 int marksMasked = marksOfLine & maskInLine;
495 if (marksMasked) {
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);
501 marksMasked >>= 1;
505 return background;
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;
526 else
527 return styles[STYLE_DEFAULT].fore;
530 bool ViewStyle::SetWrapState(int wrapState_) {
531 WrapMode wrapStateWanted;
532 switch (wrapState_) {
533 case SC_WRAP_WORD:
534 wrapStateWanted = eWrapWord;
535 break;
536 case SC_WRAP_CHAR:
537 wrapStateWanted = eWrapChar;
538 break;
539 case SC_WRAP_WHITESPACE:
540 wrapStateWanted = eWrapWhitespace;
541 break;
542 default:
543 wrapStateWanted = eWrapNone;
544 break;
546 const bool changed = wrapState != wrapStateWanted;
547 wrapState = wrapStateWanted;
548 return changed;
551 bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_) {
552 const bool changed = wrapVisualFlags != wrapVisualFlags_;
553 wrapVisualFlags = wrapVisualFlags_;
554 return changed;
557 bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_) {
558 const bool changed = wrapVisualFlagsLocation != wrapVisualFlagsLocation_;
559 wrapVisualFlagsLocation = wrapVisualFlagsLocation_;
560 return changed;
563 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_) {
564 const bool changed = wrapVisualStartIndent != wrapVisualStartIndent_;
565 wrapVisualStartIndent = wrapVisualStartIndent_;
566 return changed;
569 bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_) {
570 const bool changed = wrapIndentMode != wrapIndentMode_;
571 wrapIndentMode = wrapIndentMode_;
572 return changed;
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) {
588 if (fs.fontName) {
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();
604 return 0;
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;