KSyntaxHighlighting

themedata.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3 SPDX-FileCopyrightText: 2016 Dominik Haumann <dhaumann@kde.org>
4 SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
5
6 SPDX-License-Identifier: MIT
7*/
8
9#include "ksyntaxhighlighting_logging.h"
10#include "themedata_p.h"
11
12#include <QFile>
13#include <QFileInfo>
14#include <QJsonDocument>
15#include <QJsonObject>
16#include <QJsonValue>
17#include <QMetaEnum>
18
19using namespace KSyntaxHighlighting;
20
21ThemeData::ThemeData()
22{
23 memset(m_editorColors, 0, sizeof(m_editorColors));
24 m_textStyles.resize(QMetaEnum::fromType<Theme::TextStyle>().keyCount());
25}
26
27/**
28 * Convert QJsonValue @p val into a color, if possible. Valid colors are only
29 * in hex format: #aarrggbb. On error, returns 0x00000000.
30 */
31static inline QRgb readColor(const QJsonValue &val)
32{
33 const QRgb unsetColor = 0;
34 if (!val.isString()) {
35 return unsetColor;
36 }
37 const QString str = val.toString();
38 if (str.isEmpty() || str[0] != QLatin1Char('#')) {
39 return unsetColor;
40 }
41 const QColor color(str);
42 return color.isValid() ? color.rgba() : unsetColor;
43}
44
45static inline TextStyleData readThemeData(const QJsonObject &obj)
46{
47 TextStyleData td;
48
49 td.textColor = readColor(obj.value(QLatin1String("text-color")));
50 td.backgroundColor = readColor(obj.value(QLatin1String("background-color")));
51 td.selectedTextColor = readColor(obj.value(QLatin1String("selected-text-color")));
52 td.selectedBackgroundColor = readColor(obj.value(QLatin1String("selected-background-color")));
53
54 auto val = obj.value(QLatin1String("bold"));
55 if (val.isBool()) {
56 td.bold = val.toBool();
57 td.hasBold = true;
58 }
59 val = obj.value(QLatin1String("italic"));
60 if (val.isBool()) {
61 td.italic = val.toBool();
62 td.hasItalic = true;
63 }
64 val = obj.value(QLatin1String("underline"));
65 if (val.isBool()) {
66 td.underline = val.toBool();
67 td.hasUnderline = true;
68 }
69 val = obj.value(QLatin1String("strike-through"));
70 if (val.isBool()) {
71 td.strikeThrough = val.toBool();
72 td.hasStrikeThrough = true;
73 }
74
75 return td;
76}
77
78bool ThemeData::load(const QString &filePath)
79{
80 // flag first as done for the error cases
81 m_completelyLoaded = true;
82
83 QFile loadFile(filePath);
84 if (!loadFile.open(QIODevice::ReadOnly)) {
85 return false;
86 }
87 const QByteArray jsonData = loadFile.readAll();
88 // look for metadata object
89 int metaDataStart = jsonData.indexOf("\"metadata\"");
90 int start = jsonData.indexOf('{', metaDataStart);
91 int end = jsonData.indexOf("}", metaDataStart);
92 if (start < 0 || end < 0) {
93 qCWarning(Log) << "Failed to parse theme file" << filePath << ":"
94 << "no metadata object found";
95 return false;
96 }
97
98 QJsonParseError parseError;
99 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData.sliced(start, (end + 1) - start), &parseError);
100 if (parseError.error != QJsonParseError::NoError) {
101 qCWarning(Log) << "Failed to parse theme file" << filePath << ":" << parseError.errorString();
102 return false;
103 }
104
105 m_filePath = filePath;
106
107 // we need more data later
108 m_completelyLoaded = false;
109
110 // read metadata
111 QJsonObject metadata = jsonDoc.object();
112 m_name = metadata.value(QLatin1String("name")).toString();
113 m_revision = metadata.value(QLatin1String("revision")).toInt();
114 return true;
115}
116
117void ThemeData::loadComplete()
118{
119 if (m_completelyLoaded) {
120 return;
121 }
122 m_completelyLoaded = true;
123
124 QFile loadFile(m_filePath);
125 if (!loadFile.open(QIODevice::ReadOnly)) {
126 return;
127 }
128 const QByteArray jsonData = loadFile.readAll();
129
130 QJsonParseError parseError;
131 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
132 if (parseError.error != QJsonParseError::NoError) {
133 qCWarning(Log) << "Failed to parse theme file" << m_filePath << ":" << parseError.errorString();
134 return;
135 }
136
137 QJsonObject obj = jsonDoc.object();
138 // read text styles
139 const auto metaEnumStyle = QMetaEnum::fromType<Theme::TextStyle>();
140 const QJsonObject textStyles = obj.value(QLatin1String("text-styles")).toObject();
141 for (int i = 0; i < metaEnumStyle.keyCount(); ++i) {
142 Q_ASSERT(i == metaEnumStyle.value(i));
143 m_textStyles[i] = readThemeData(textStyles.value(QLatin1String(metaEnumStyle.key(i))).toObject());
144 }
145
146 // read editor colors
147 const auto metaEnumColor = QMetaEnum::fromType<Theme::EditorColorRole>();
148 const QJsonObject editorColors = obj.value(QLatin1String("editor-colors")).toObject();
149 for (int i = 0; i < metaEnumColor.keyCount(); ++i) {
150 Q_ASSERT(i == metaEnumColor.value(i));
151 m_editorColors[i] = readColor(editorColors.value(QLatin1String(metaEnumColor.key(i))));
152 }
153
154 // if we have no new key around for Theme::BackgroundColor => use old variants to be compatible
155 if (!editorColors.contains(QLatin1String(metaEnumColor.key(Theme::BackgroundColor)))) {
156 m_editorColors[Theme::BackgroundColor] = readColor(editorColors.value(QLatin1String("background-color")));
157 m_editorColors[Theme::TextSelection] = readColor(editorColors.value(QLatin1String("selection")));
158 m_editorColors[Theme::CurrentLine] = readColor(editorColors.value(QLatin1String("current-line")));
159 m_editorColors[Theme::SearchHighlight] = readColor(editorColors.value(QLatin1String("search-highlight")));
160 m_editorColors[Theme::ReplaceHighlight] = readColor(editorColors.value(QLatin1String("replace-highlight")));
161 m_editorColors[Theme::BracketMatching] = readColor(editorColors.value(QLatin1String("bracket-matching")));
162 m_editorColors[Theme::TabMarker] = readColor(editorColors.value(QLatin1String("tab-marker")));
163 m_editorColors[Theme::SpellChecking] = readColor(editorColors.value(QLatin1String("spell-checking")));
164 m_editorColors[Theme::IndentationLine] = readColor(editorColors.value(QLatin1String("indentation-line")));
165 m_editorColors[Theme::IconBorder] = readColor(editorColors.value(QLatin1String("icon-border")));
166 m_editorColors[Theme::CodeFolding] = readColor(editorColors.value(QLatin1String("code-folding")));
167 m_editorColors[Theme::LineNumbers] = readColor(editorColors.value(QLatin1String("line-numbers")));
168 m_editorColors[Theme::CurrentLineNumber] = readColor(editorColors.value(QLatin1String("current-line-number")));
169 m_editorColors[Theme::WordWrapMarker] = readColor(editorColors.value(QLatin1String("word-wrap-marker")));
170 m_editorColors[Theme::ModifiedLines] = readColor(editorColors.value(QLatin1String("modified-lines")));
171 m_editorColors[Theme::SavedLines] = readColor(editorColors.value(QLatin1String("saved-lines")));
172 m_editorColors[Theme::Separator] = readColor(editorColors.value(QLatin1String("separator")));
173 m_editorColors[Theme::MarkBookmark] = readColor(editorColors.value(QLatin1String("mark-bookmark")));
174 m_editorColors[Theme::MarkBreakpointActive] = readColor(editorColors.value(QLatin1String("mark-breakpoint-active")));
175 m_editorColors[Theme::MarkBreakpointReached] = readColor(editorColors.value(QLatin1String("mark-breakpoint-reached")));
176 m_editorColors[Theme::MarkBreakpointDisabled] = readColor(editorColors.value(QLatin1String("mark-breakpoint-disabled")));
177 m_editorColors[Theme::MarkExecution] = readColor(editorColors.value(QLatin1String("mark-execution")));
178 m_editorColors[Theme::MarkWarning] = readColor(editorColors.value(QLatin1String("mark-warning")));
179 m_editorColors[Theme::MarkError] = readColor(editorColors.value(QLatin1String("mark-error")));
180 m_editorColors[Theme::TemplateBackground] = readColor(editorColors.value(QLatin1String("template-background")));
181 m_editorColors[Theme::TemplatePlaceholder] = readColor(editorColors.value(QLatin1String("template-placeholder")));
182 m_editorColors[Theme::TemplateFocusedPlaceholder] = readColor(editorColors.value(QLatin1String("template-focused-placeholder")));
183 m_editorColors[Theme::TemplateReadOnlyPlaceholder] = readColor(editorColors.value(QLatin1String("template-read-only-placeholder")));
184 }
185
186 // read per-definition style overrides
187 const auto customStyles = obj.value(QLatin1String("custom-styles")).toObject();
188 for (auto it = customStyles.begin(); it != customStyles.end(); ++it) {
189 const auto obj = it.value().toObject();
190 auto &overrideStyle = m_textStyleOverrides[it.key()];
191 for (auto it2 = obj.begin(); it2 != obj.end(); ++it2) {
192 overrideStyle.insert(it2.key(), readThemeData(it2.value().toObject()));
193 }
194 }
195
196 return;
197}
198
199QString ThemeData::name() const
200{
201 return m_name;
202}
203
204int ThemeData::revision() const
205{
206 return m_revision;
207}
208
209bool ThemeData::isReadOnly() const
210{
211 return !QFileInfo(m_filePath).isWritable();
212}
213
214QString ThemeData::filePath() const
215{
216 return m_filePath;
217}
218
219TextStyleData ThemeData::textStyle(Theme::TextStyle style) const
220{
221 if (!m_completelyLoaded) {
222 const_cast<ThemeData *>(this)->loadComplete();
223 }
224 return m_textStyles[style];
225}
226
227QRgb ThemeData::textColor(Theme::TextStyle style) const
228{
229 return textStyle(style).textColor;
230}
231
232QRgb ThemeData::selectedTextColor(Theme::TextStyle style) const
233{
234 return textStyle(style).selectedTextColor;
235}
236
237QRgb ThemeData::backgroundColor(Theme::TextStyle style) const
238{
239 return textStyle(style).backgroundColor;
240}
241
242QRgb ThemeData::selectedBackgroundColor(Theme::TextStyle style) const
243{
244 return textStyle(style).selectedBackgroundColor;
245}
246
247bool ThemeData::isBold(Theme::TextStyle style) const
248{
249 return textStyle(style).bold;
250}
251
252bool ThemeData::isItalic(Theme::TextStyle style) const
253{
254 return textStyle(style).italic;
255}
256
257bool ThemeData::isUnderline(Theme::TextStyle style) const
258{
259 return textStyle(style).underline;
260}
261
262bool ThemeData::isStrikeThrough(Theme::TextStyle style) const
263{
264 return textStyle(style).strikeThrough;
265}
266
267QRgb ThemeData::editorColor(Theme::EditorColorRole role) const
268{
269 if (!m_completelyLoaded) {
270 const_cast<ThemeData *>(this)->loadComplete();
271 }
272 Q_ASSERT(static_cast<int>(role) >= 0 && static_cast<int>(role) <= static_cast<int>(Theme::TemplateReadOnlyPlaceholder));
273 return m_editorColors[role];
274}
275
276TextStyleData ThemeData::textStyleOverride(const QString &definitionName, const QString &attributeName) const
277{
278 if (!m_completelyLoaded) {
279 const_cast<ThemeData *>(this)->loadComplete();
280 }
281 auto it = m_textStyleOverrides.find(definitionName);
282 if (it != m_textStyleOverrides.end()) {
283 return it->value(attributeName);
284 }
285 return TextStyleData();
286}
EditorColorRole
Editor color roles, used to paint line numbers, editor background etc.
Definition theme.h:158
@ IndentationLine
Color used to draw vertical indentation levels, typically a line.
Definition theme.h:176
@ CodeFolding
Background colors for code folding regions in the text area, as well as code folding indicators in th...
Definition theme.h:181
@ MarkWarning
Background color for general warning marks.
Definition theme.h:210
@ SpellChecking
Color used to underline spell check errors.
Definition theme.h:174
@ MarkBookmark
Background color for bookmarks.
Definition theme.h:200
@ MarkBreakpointReached
Background color for a reached breakpoint.
Definition theme.h:204
@ SavedLines
Color used to draw a vertical line for marking saved lines.
Definition theme.h:195
@ TemplateFocusedPlaceholder
Background color for the currently active placeholder in text templates.
Definition theme.h:219
@ TemplateReadOnlyPlaceholder
Background color for read-only placeholders in text templates.
Definition theme.h:221
@ MarkError
Background color for general error marks.
Definition theme.h:212
@ CurrentLineNumber
Foreground color for drawing the current line number.
Definition theme.h:187
@ TemplateBackground
Background color for text templates (snippets).
Definition theme.h:214
@ MarkBreakpointDisabled
Background color for inactive (disabled) breakpoints.
Definition theme.h:206
@ IconBorder
Background color for the icon border.
Definition theme.h:178
@ Separator
Line color used to draw separator lines, e.g.
Definition theme.h:198
@ TabMarker
Foreground color for visualizing tabs and trailing spaces.
Definition theme.h:172
@ SearchHighlight
Background color for matching text while searching.
Definition theme.h:166
@ BackgroundColor
Background color for the editing area.
Definition theme.h:160
@ TextSelection
Background color for selected text.
Definition theme.h:162
@ ReplaceHighlight
Background color for replaced text for a search & replace action.
Definition theme.h:168
@ BracketMatching
Background color for matching bracket pairs (including quotes)
Definition theme.h:170
@ MarkBreakpointActive
Background color for active breakpoints.
Definition theme.h:202
@ ModifiedLines
Color used to draw a vertical line for marking changed lines.
Definition theme.h:193
@ LineNumbers
Foreground color for drawing the line numbers.
Definition theme.h:184
@ TemplatePlaceholder
Background color for all editable placeholders in text templates.
Definition theme.h:216
@ MarkExecution
Background color for marking the current execution position.
Definition theme.h:208
@ CurrentLine
Background color for the line of the current text cursor.
Definition theme.h:164
@ WordWrapMarker
Color used in the icon border to indicate dynamically wrapped lines.
Definition theme.h:191
TextStyle
Default styles that can be referenced from syntax definition XML files.
Definition theme.h:75
Q_SCRIPTABLE Q_NOREPLY void start()
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
Syntax highlighting engine for Kate syntax definitions.
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
QByteArray sliced(qsizetype pos) const const
bool isWritable() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
iterator begin()
bool contains(QLatin1StringView key) const const
iterator end()
QJsonValue value(QLatin1StringView key) const const
QString errorString() const const
bool isBool() const const
bool isString() const const
bool toBool(bool defaultValue) const const
int toInt(int defaultValue) const const
QJsonObject toObject() const const
QString toString() const const
QMetaEnum fromType()
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:04 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.