1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3 SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
4
5 SPDX-License-Identifier: MIT
6*/
7
8#include "format.h"
9#include "definition.h"
10#include "format_p.h"
11#include "textstyledata_p.h"
12#include "themedata_p.h"
13#include "xml_p.h"
14
15#include <QColor>
16#include <QMetaEnum>
17#include <QXmlStreamReader>
18
19using namespace KSyntaxHighlighting;
20
21static Theme::TextStyle stringToDefaultFormat(QStringView str)
22{
23 if (!str.startsWith(s: QLatin1String("ds"))) {
24 return Theme::Normal;
25 }
26
27 const auto metaEnum = QMetaEnum::fromType<Theme::TextStyle>();
28
29 bool ok = false;
30 const auto value = metaEnum.keyToValue(key: str.sliced(pos: 2).toLatin1().constData(), ok: &ok);
31 if (!ok || value < 0) {
32 return Theme::Normal;
33 }
34 return static_cast<Theme::TextStyle>(value);
35}
36
37FormatPrivate *FormatPrivate::detachAndGet(Format &format)
38{
39 format.d.detach();
40 return format.d.data();
41}
42
43TextStyleData FormatPrivate::styleOverride(const Theme &theme) const
44{
45 return ThemeData::get(theme)->textStyleOverride(definitionName, attributeName: name);
46}
47
48QColor FormatPrivate::color(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const
49{
50 const auto overrideStyle = styleOverride(theme);
51 if (overrideStyle.*styleColor) {
52 return QColor::fromRgb(rgb: overrideStyle.*styleColor);
53 }
54 // use QColor::fromRgba for QRgb => QColor conversion to avoid unset colors == black!
55 return QColor::fromRgba(rgba: style.*styleColor ? style.*styleColor : (theme.*themeColor)(defaultStyle));
56}
57
58bool FormatPrivate::hasColor(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const
59{
60 // use QColor::fromRgba for background QRgb => QColor conversion to avoid unset colors == black!
61 return color(theme, styleColor, themeColor) != QColor::fromRgba(rgba: (theme.*themeColor)(Theme::Normal)) && (style.*styleColor || (theme.*themeColor)(defaultStyle) || styleOverride(theme).*styleColor);
62}
63
64static QExplicitlySharedDataPointer<FormatPrivate> &sharedDefaultPrivate()
65{
66 static QExplicitlySharedDataPointer<FormatPrivate> def(new FormatPrivate);
67 return def;
68}
69
70Format::Format()
71 : d(sharedDefaultPrivate())
72{
73}
74
75Format::Format(const Format &other)
76 : d(other.d)
77{
78}
79
80Format::~Format()
81{
82}
83
84Format &Format::operator=(const Format &other)
85{
86 d = other.d;
87 return *this;
88}
89
90bool Format::isValid() const
91{
92 return !d->name.isEmpty();
93}
94
95QString Format::name() const
96{
97 return d->name;
98}
99
100int Format::id() const
101{
102 return d->id;
103}
104
105Theme::TextStyle Format::textStyle() const
106{
107 return d->defaultStyle;
108}
109
110bool Format::isDefaultTextStyle(const Theme &theme) const
111{
112 // use QColor::fromRgba for background QRgb => QColor conversion to avoid unset colors == black!
113 return (!hasTextColor(theme)) && (!hasBackgroundColor(theme)) && (selectedTextColor(theme).rgba() == theme.selectedTextColor(style: Theme::Normal))
114 && (selectedBackgroundColor(theme).rgba() == (theme.selectedBackgroundColor(style: Theme::Normal))) && (isBold(theme) == theme.isBold(style: Theme::Normal))
115 && (isItalic(theme) == theme.isItalic(style: Theme::Normal)) && (isUnderline(theme) == theme.isUnderline(style: Theme::Normal))
116 && (isStrikeThrough(theme) == theme.isStrikeThrough(style: Theme::Normal));
117}
118
119bool Format::hasTextColor(const Theme &theme) const
120{
121 return d->hasColor(theme, styleColor: &TextStyleData::textColor, themeColor: &Theme::textColor);
122}
123
124QColor Format::textColor(const Theme &theme) const
125{
126 return d->color(theme, styleColor: &TextStyleData::textColor, themeColor: &Theme::textColor);
127}
128
129QColor Format::selectedTextColor(const Theme &theme) const
130{
131 return d->color(theme, styleColor: &TextStyleData::selectedTextColor, themeColor: &Theme::selectedTextColor);
132}
133
134bool Format::hasBackgroundColor(const Theme &theme) const
135{
136 return d->hasColor(theme, styleColor: &TextStyleData::backgroundColor, themeColor: &Theme::backgroundColor);
137}
138
139QColor Format::backgroundColor(const Theme &theme) const
140{
141 return d->color(theme, styleColor: &TextStyleData::backgroundColor, themeColor: &Theme::backgroundColor);
142}
143
144QColor Format::selectedBackgroundColor(const Theme &theme) const
145{
146 return d->color(theme, styleColor: &TextStyleData::selectedBackgroundColor, themeColor: &Theme::selectedBackgroundColor);
147}
148
149bool Format::isBold(const Theme &theme) const
150{
151 const auto overrideStyle = d->styleOverride(theme);
152 if (overrideStyle.hasBold) {
153 return overrideStyle.bold;
154 }
155 return d->style.hasBold ? d->style.bold : theme.isBold(style: d->defaultStyle);
156}
157
158bool Format::isItalic(const Theme &theme) const
159{
160 const auto overrideStyle = d->styleOverride(theme);
161 if (overrideStyle.hasItalic) {
162 return overrideStyle.italic;
163 }
164 return d->style.hasItalic ? d->style.italic : theme.isItalic(style: d->defaultStyle);
165}
166
167bool Format::isUnderline(const Theme &theme) const
168{
169 const auto overrideStyle = d->styleOverride(theme);
170 if (overrideStyle.hasUnderline) {
171 return overrideStyle.underline;
172 }
173 return d->style.hasUnderline ? d->style.underline : theme.isUnderline(style: d->defaultStyle);
174}
175
176bool Format::isStrikeThrough(const Theme &theme) const
177{
178 const auto overrideStyle = d->styleOverride(theme);
179 if (overrideStyle.hasStrikeThrough) {
180 return overrideStyle.strikeThrough;
181 }
182 return d->style.hasStrikeThrough ? d->style.strikeThrough : theme.isStrikeThrough(style: d->defaultStyle);
183}
184
185bool Format::spellCheck() const
186{
187 return d->spellCheck;
188}
189
190bool Format::hasBoldOverride() const
191{
192 return d->style.hasBold;
193}
194
195bool Format::hasItalicOverride() const
196{
197 return d->style.hasItalic;
198}
199
200bool Format::hasUnderlineOverride() const
201{
202 return d->style.hasUnderline;
203}
204
205bool Format::hasStrikeThroughOverride() const
206{
207 return d->style.hasStrikeThrough;
208}
209
210bool Format::hasTextColorOverride() const
211{
212 return d->style.textColor;
213}
214
215bool Format::hasBackgroundColorOverride() const
216{
217 return d->style.backgroundColor;
218}
219
220bool Format::hasSelectedTextColorOverride() const
221{
222 return d->style.selectedTextColor;
223}
224
225bool Format::hasSelectedBackgroundColorOverride() const
226{
227 return d->style.selectedBackgroundColor;
228}
229
230void FormatPrivate::load(QXmlStreamReader &reader)
231{
232 name = reader.attributes().value(qualifiedName: QLatin1String("name")).toString();
233 defaultStyle = stringToDefaultFormat(str: reader.attributes().value(qualifiedName: QLatin1String("defStyleNum")));
234
235 QStringView attribute = reader.attributes().value(qualifiedName: QLatin1String("color"));
236 if (!attribute.isEmpty()) {
237 style.textColor = QColor(attribute).rgba();
238 }
239
240 attribute = reader.attributes().value(qualifiedName: QLatin1String("selColor"));
241 if (!attribute.isEmpty()) {
242 style.selectedTextColor = QColor(attribute).rgba();
243 }
244
245 attribute = reader.attributes().value(qualifiedName: QLatin1String("backgroundColor"));
246 if (!attribute.isEmpty()) {
247 style.backgroundColor = QColor(attribute).rgba();
248 }
249
250 attribute = reader.attributes().value(qualifiedName: QLatin1String("selBackgroundColor"));
251 if (!attribute.isEmpty()) {
252 style.selectedBackgroundColor = QColor(attribute).rgba();
253 }
254
255 attribute = reader.attributes().value(qualifiedName: QLatin1String("italic"));
256 if (!attribute.isEmpty()) {
257 style.hasItalic = true;
258 style.italic = Xml::attrToBool(str: attribute);
259 }
260
261 attribute = reader.attributes().value(qualifiedName: QLatin1String("bold"));
262 if (!attribute.isEmpty()) {
263 style.hasBold = true;
264 style.bold = Xml::attrToBool(str: attribute);
265 }
266
267 attribute = reader.attributes().value(qualifiedName: QLatin1String("underline"));
268 if (!attribute.isEmpty()) {
269 style.hasUnderline = true;
270 style.underline = Xml::attrToBool(str: attribute);
271 }
272
273 attribute = reader.attributes().value(qualifiedName: QLatin1String("strikeOut"));
274 if (!attribute.isEmpty()) {
275 style.hasStrikeThrough = true;
276 style.strikeThrough = Xml::attrToBool(str: attribute);
277 }
278
279 attribute = reader.attributes().value(qualifiedName: QLatin1String("spellChecking"));
280 if (!attribute.isEmpty()) {
281 spellCheck = Xml::attrToBool(str: attribute);
282 }
283}
284

source code of syntax-highlighting/src/lib/format.cpp