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 QFile loadFile(filePath);
81 if (!loadFile.open(QIODevice::ReadOnly)) {
82 return false;
83 }
84 const QByteArray jsonData = loadFile.readAll();
85 // look for metadata object
86 int metaDataStart = jsonData.indexOf("\"metadata\"");
87 int start = jsonData.indexOf('{', metaDataStart);
88 int end = jsonData.indexOf("}", metaDataStart);
89 if (start < 0 || end < 0) {
90 qCWarning(Log) << "Failed to parse theme file" << filePath << ":"
91 << "no metadata object found";
92 return false;
93 }
94
95 QJsonParseError parseError;
96 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData.mid(start, (end + 1) - start), &parseError);
97 if (parseError.error != QJsonParseError::NoError) {
98 qCWarning(Log) << "Failed to parse theme file" << filePath << ":" << parseError.errorString();
99 return false;
100 }
101
102 m_filePath = filePath;
103
104 // read metadata
105 QJsonObject metadata = jsonDoc.object();
106 m_name = metadata.value(QLatin1String("name")).toString();
107 m_revision = metadata.value(QLatin1String("revision")).toInt();
108 return true;
109}
110
111void ThemeData::loadComplete()
112{
113 if (m_completelyLoaded) {
114 return;
115 }
116 m_completelyLoaded = true;
117
118 QFile loadFile(m_filePath);
119 if (!loadFile.open(QIODevice::ReadOnly)) {
120 return;
121 }
122 const QByteArray jsonData = loadFile.readAll();
123
124 QJsonParseError parseError;
125 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
126 if (parseError.error != QJsonParseError::NoError) {
127 qCWarning(Log) << "Failed to parse theme file" << m_filePath << ":" << parseError.errorString();
128 return;
129 }
130
131 QJsonObject obj = jsonDoc.object();
132 // read text styles
133 const auto metaEnumStyle = QMetaEnum::fromType<Theme::TextStyle>();
134 const QJsonObject textStyles = obj.value(QLatin1String("text-styles")).toObject();
135 for (int i = 0; i < metaEnumStyle.keyCount(); ++i) {
136 Q_ASSERT(i == metaEnumStyle.value(i));
137 m_textStyles[i] = readThemeData(textStyles.value(QLatin1String(metaEnumStyle.key(i))).toObject());
138 }
139
140 // read editor colors
141 const auto metaEnumColor = QMetaEnum::fromType<Theme::EditorColorRole>();
142 const QJsonObject editorColors = obj.value(QLatin1String("editor-colors")).toObject();
143 for (int i = 0; i < metaEnumColor.keyCount(); ++i) {
144 Q_ASSERT(i == metaEnumColor.value(i));
145 m_editorColors[i] = readColor(editorColors.value(QLatin1String(metaEnumColor.key(i))));
146 }
147
148 // if we have no new key around for Theme::BackgroundColor => use old variants to be compatible
149 if (!editorColors.contains(QLatin1String(metaEnumColor.key(Theme::BackgroundColor)))) {
150 m_editorColors[Theme::BackgroundColor] = readColor(editorColors.value(QLatin1String("background-color")));
151 m_editorColors[Theme::TextSelection] = readColor(editorColors.value(QLatin1String("selection")));
152 m_editorColors[Theme::CurrentLine] = readColor(editorColors.value(QLatin1String("current-line")));
153 m_editorColors[Theme::SearchHighlight] = readColor(editorColors.value(QLatin1String("search-highlight")));
154 m_editorColors[Theme::ReplaceHighlight] = readColor(editorColors.value(QLatin1String("replace-highlight")));
155 m_editorColors[Theme::BracketMatching] = readColor(editorColors.value(QLatin1String("bracket-matching")));
156 m_editorColors[Theme::TabMarker] = readColor(editorColors.value(QLatin1String("tab-marker")));
157 m_editorColors[Theme::SpellChecking] = readColor(editorColors.value(QLatin1String("spell-checking")));
158 m_editorColors[Theme::IndentationLine] = readColor(editorColors.value(QLatin1String("indentation-line")));
159 m_editorColors[Theme::IconBorder] = readColor(editorColors.value(QLatin1String("icon-border")));
160 m_editorColors[Theme::CodeFolding] = readColor(editorColors.value(QLatin1String("code-folding")));
161 m_editorColors[Theme::LineNumbers] = readColor(editorColors.value(QLatin1String("line-numbers")));
162 m_editorColors[Theme::CurrentLineNumber] = readColor(editorColors.value(QLatin1String("current-line-number")));
163 m_editorColors[Theme::WordWrapMarker] = readColor(editorColors.value(QLatin1String("word-wrap-marker")));
164 m_editorColors[Theme::ModifiedLines] = readColor(editorColors.value(QLatin1String("modified-lines")));
165 m_editorColors[Theme::SavedLines] = readColor(editorColors.value(QLatin1String("saved-lines")));
166 m_editorColors[Theme::Separator] = readColor(editorColors.value(QLatin1String("separator")));
167 m_editorColors[Theme::MarkBookmark] = readColor(editorColors.value(QLatin1String("mark-bookmark")));
168 m_editorColors[Theme::MarkBreakpointActive] = readColor(editorColors.value(QLatin1String("mark-breakpoint-active")));
169 m_editorColors[Theme::MarkBreakpointReached] = readColor(editorColors.value(QLatin1String("mark-breakpoint-reached")));
170 m_editorColors[Theme::MarkBreakpointDisabled] = readColor(editorColors.value(QLatin1String("mark-breakpoint-disabled")));
171 m_editorColors[Theme::MarkExecution] = readColor(editorColors.value(QLatin1String("mark-execution")));
172 m_editorColors[Theme::MarkWarning] = readColor(editorColors.value(QLatin1String("mark-warning")));
173 m_editorColors[Theme::MarkError] = readColor(editorColors.value(QLatin1String("mark-error")));
174 m_editorColors[Theme::TemplateBackground] = readColor(editorColors.value(QLatin1String("template-background")));
175 m_editorColors[Theme::TemplatePlaceholder] = readColor(editorColors.value(QLatin1String("template-placeholder")));
176 m_editorColors[Theme::TemplateFocusedPlaceholder] = readColor(editorColors.value(QLatin1String("template-focused-placeholder")));
177 m_editorColors[Theme::TemplateReadOnlyPlaceholder] = readColor(editorColors.value(QLatin1String("template-read-only-placeholder")));
178 }
179
180 // read per-definition style overrides
181 const auto customStyles = obj.value(QLatin1String("custom-styles")).toObject();
182 for (auto it = customStyles.begin(); it != customStyles.end(); ++it) {
183 const auto obj = it.value().toObject();
184 auto &overrideStyle = m_textStyleOverrides[it.key()];
185 for (auto it2 = obj.begin(); it2 != obj.end(); ++it2) {
186 overrideStyle.insert(it2.key(), readThemeData(it2.value().toObject()));
187 }
188 }
189
190 return;
191}
192
193QString ThemeData::name() const
194{
195 return m_name;
196}
197
198int ThemeData::revision() const
199{
200 return m_revision;
201}
202
203bool ThemeData::isReadOnly() const
204{
205 return !QFileInfo(m_filePath).isWritable();
206}
207
208QString ThemeData::filePath() const
209{
210 return m_filePath;
211}
212
213TextStyleData ThemeData::textStyle(Theme::TextStyle style) const
214{
215 if (!m_completelyLoaded) {
216 const_cast<ThemeData *>(this)->loadComplete();
217 }
218 return m_textStyles[style];
219}
220
221QRgb ThemeData::textColor(Theme::TextStyle style) const
222{
223 return textStyle(style).textColor;
224}
225
226QRgb ThemeData::selectedTextColor(Theme::TextStyle style) const
227{
228 return textStyle(style).selectedTextColor;
229}
230
231QRgb ThemeData::backgroundColor(Theme::TextStyle style) const
232{
233 return textStyle(style).backgroundColor;
234}
235
236QRgb ThemeData::selectedBackgroundColor(Theme::TextStyle style) const
237{
238 return textStyle(style).selectedBackgroundColor;
239}
240
241bool ThemeData::isBold(Theme::TextStyle style) const
242{
243 return textStyle(style).bold;
244}
245
246bool ThemeData::isItalic(Theme::TextStyle style) const
247{
248 return textStyle(style).italic;
249}
250
251bool ThemeData::isUnderline(Theme::TextStyle style) const
252{
253 return textStyle(style).underline;
254}
255
256bool ThemeData::isStrikeThrough(Theme::TextStyle style) const
257{
258 return textStyle(style).strikeThrough;
259}
260
261QRgb ThemeData::editorColor(Theme::EditorColorRole role) const
262{
263 if (!m_completelyLoaded) {
264 const_cast<ThemeData *>(this)->loadComplete();
265 }
266 Q_ASSERT(static_cast<int>(role) >= 0 && static_cast<int>(role) <= static_cast<int>(Theme::TemplateReadOnlyPlaceholder));
267 return m_editorColors[role];
268}
269
270TextStyleData ThemeData::textStyleOverride(const QString &definitionName, const QString &attributeName) const
271{
272 if (!m_completelyLoaded) {
273 const_cast<ThemeData *>(this)->loadComplete();
274 }
275 auto it = m_textStyleOverrides.find(definitionName);
276 if (it != m_textStyleOverrides.end()) {
277 return it->value(attributeName);
278 }
279 return TextStyleData();
280}
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()
const QList< QKeySequence > & end()
Syntax highlighting engine for Kate syntax definitions.
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
QByteArray mid(qsizetype pos, qsizetype len) 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()
iterator insert(QLatin1StringView key, const QJsonValue &value)
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
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:29 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.