File indexing completed on 2024-05-12 04:02:18

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
0004 
0005     SPDX-License-Identifier: MIT
0006 */
0007 
0008 #include "format.h"
0009 #include "definition.h"
0010 #include "definitionref_p.h"
0011 #include "format_p.h"
0012 #include "textstyledata_p.h"
0013 #include "themedata_p.h"
0014 #include "xml_p.h"
0015 
0016 #include <QColor>
0017 #include <QMetaEnum>
0018 #include <QXmlStreamReader>
0019 
0020 using namespace KSyntaxHighlighting;
0021 
0022 static Theme::TextStyle stringToDefaultFormat(QStringView str)
0023 {
0024     if (!str.startsWith(QLatin1String("ds"))) {
0025         return Theme::Normal;
0026     }
0027 
0028     const auto metaEnum = QMetaEnum::fromType<Theme::TextStyle>();
0029 
0030     bool ok = false;
0031     const auto value = metaEnum.keyToValue(str.mid(2).toLatin1().constData(), &ok);
0032     if (!ok || value < 0) {
0033         return Theme::Normal;
0034     }
0035     return static_cast<Theme::TextStyle>(value);
0036 }
0037 
0038 FormatPrivate *FormatPrivate::detachAndGet(Format &format)
0039 {
0040     format.d.detach();
0041     return format.d.data();
0042 }
0043 
0044 TextStyleData FormatPrivate::styleOverride(const Theme &theme) const
0045 {
0046     return ThemeData::get(theme)->textStyleOverride(definitionName, name);
0047 }
0048 
0049 QColor FormatPrivate::color(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const
0050 {
0051     const auto overrideStyle = styleOverride(theme);
0052     if (overrideStyle.*styleColor) {
0053         return QColor::fromRgb(overrideStyle.*styleColor);
0054     }
0055     // use QColor::fromRgba for QRgb => QColor conversion to avoid unset colors == black!
0056     return QColor::fromRgba(style.*styleColor ? style.*styleColor : (theme.*themeColor)(defaultStyle));
0057 }
0058 
0059 bool FormatPrivate::hasColor(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const
0060 {
0061     // use QColor::fromRgba for background QRgb => QColor conversion to avoid unset colors == black!
0062     return color(theme, styleColor, themeColor) != QColor::fromRgba((theme.*themeColor)(Theme::Normal)) && (style.*styleColor || (theme.*themeColor)(defaultStyle) || styleOverride(theme).*styleColor);
0063 }
0064 
0065 static QExplicitlySharedDataPointer<FormatPrivate> &sharedDefaultPrivate()
0066 {
0067     static QExplicitlySharedDataPointer<FormatPrivate> def(new FormatPrivate);
0068     return def;
0069 }
0070 
0071 Format::Format()
0072     : d(sharedDefaultPrivate())
0073 {
0074 }
0075 
0076 Format::Format(const Format &other)
0077     : d(other.d)
0078 {
0079 }
0080 
0081 Format::~Format()
0082 {
0083 }
0084 
0085 Format &Format::operator=(const Format &other)
0086 {
0087     d = other.d;
0088     return *this;
0089 }
0090 
0091 bool Format::isValid() const
0092 {
0093     return !d->name.isEmpty();
0094 }
0095 
0096 QString Format::name() const
0097 {
0098     return d->name;
0099 }
0100 
0101 int Format::id() const
0102 {
0103     return d->id;
0104 }
0105 
0106 Theme::TextStyle Format::textStyle() const
0107 {
0108     return d->defaultStyle;
0109 }
0110 
0111 bool Format::isDefaultTextStyle(const Theme &theme) const
0112 {
0113     // use QColor::fromRgba for background QRgb => QColor conversion to avoid unset colors == black!
0114     return (!hasTextColor(theme)) && (!hasBackgroundColor(theme)) && (selectedTextColor(theme).rgba() == theme.selectedTextColor(Theme::Normal))
0115         && (selectedBackgroundColor(theme).rgba() == (theme.selectedBackgroundColor(Theme::Normal))) && (isBold(theme) == theme.isBold(Theme::Normal))
0116         && (isItalic(theme) == theme.isItalic(Theme::Normal)) && (isUnderline(theme) == theme.isUnderline(Theme::Normal))
0117         && (isStrikeThrough(theme) == theme.isStrikeThrough(Theme::Normal));
0118 }
0119 
0120 bool Format::hasTextColor(const Theme &theme) const
0121 {
0122     return d->hasColor(theme, &TextStyleData::textColor, &Theme::textColor);
0123 }
0124 
0125 QColor Format::textColor(const Theme &theme) const
0126 {
0127     return d->color(theme, &TextStyleData::textColor, &Theme::textColor);
0128 }
0129 
0130 QColor Format::selectedTextColor(const Theme &theme) const
0131 {
0132     return d->color(theme, &TextStyleData::selectedTextColor, &Theme::selectedTextColor);
0133 }
0134 
0135 bool Format::hasBackgroundColor(const Theme &theme) const
0136 {
0137     return d->hasColor(theme, &TextStyleData::backgroundColor, &Theme::backgroundColor);
0138 }
0139 
0140 QColor Format::backgroundColor(const Theme &theme) const
0141 {
0142     return d->color(theme, &TextStyleData::backgroundColor, &Theme::backgroundColor);
0143 }
0144 
0145 QColor Format::selectedBackgroundColor(const Theme &theme) const
0146 {
0147     return d->color(theme, &TextStyleData::selectedBackgroundColor, &Theme::selectedBackgroundColor);
0148 }
0149 
0150 bool Format::isBold(const Theme &theme) const
0151 {
0152     const auto overrideStyle = d->styleOverride(theme);
0153     if (overrideStyle.hasBold) {
0154         return overrideStyle.bold;
0155     }
0156     return d->style.hasBold ? d->style.bold : theme.isBold(d->defaultStyle);
0157 }
0158 
0159 bool Format::isItalic(const Theme &theme) const
0160 {
0161     const auto overrideStyle = d->styleOverride(theme);
0162     if (overrideStyle.hasItalic) {
0163         return overrideStyle.italic;
0164     }
0165     return d->style.hasItalic ? d->style.italic : theme.isItalic(d->defaultStyle);
0166 }
0167 
0168 bool Format::isUnderline(const Theme &theme) const
0169 {
0170     const auto overrideStyle = d->styleOverride(theme);
0171     if (overrideStyle.hasUnderline) {
0172         return overrideStyle.underline;
0173     }
0174     return d->style.hasUnderline ? d->style.underline : theme.isUnderline(d->defaultStyle);
0175 }
0176 
0177 bool Format::isStrikeThrough(const Theme &theme) const
0178 {
0179     const auto overrideStyle = d->styleOverride(theme);
0180     if (overrideStyle.hasStrikeThrough) {
0181         return overrideStyle.strikeThrough;
0182     }
0183     return d->style.hasStrikeThrough ? d->style.strikeThrough : theme.isStrikeThrough(d->defaultStyle);
0184 }
0185 
0186 bool Format::spellCheck() const
0187 {
0188     return d->spellCheck;
0189 }
0190 
0191 bool Format::hasBoldOverride() const
0192 {
0193     return d->style.hasBold;
0194 }
0195 
0196 bool Format::hasItalicOverride() const
0197 {
0198     return d->style.hasItalic;
0199 }
0200 
0201 bool Format::hasUnderlineOverride() const
0202 {
0203     return d->style.hasUnderline;
0204 }
0205 
0206 bool Format::hasStrikeThroughOverride() const
0207 {
0208     return d->style.hasStrikeThrough;
0209 }
0210 
0211 bool Format::hasTextColorOverride() const
0212 {
0213     return d->style.textColor;
0214 }
0215 
0216 bool Format::hasBackgroundColorOverride() const
0217 {
0218     return d->style.backgroundColor;
0219 }
0220 
0221 bool Format::hasSelectedTextColorOverride() const
0222 {
0223     return d->style.selectedTextColor;
0224 }
0225 
0226 bool Format::hasSelectedBackgroundColorOverride() const
0227 {
0228     return d->style.selectedBackgroundColor;
0229 }
0230 
0231 void FormatPrivate::load(QXmlStreamReader &reader)
0232 {
0233     name = reader.attributes().value(QLatin1String("name")).toString();
0234     defaultStyle = stringToDefaultFormat(reader.attributes().value(QLatin1String("defStyleNum")));
0235 
0236     QStringView attribute = reader.attributes().value(QLatin1String("color"));
0237     if (!attribute.isEmpty()) {
0238         style.textColor = QColor(attribute).rgba();
0239     }
0240 
0241     attribute = reader.attributes().value(QLatin1String("selColor"));
0242     if (!attribute.isEmpty()) {
0243         style.selectedTextColor = QColor(attribute).rgba();
0244     }
0245 
0246     attribute = reader.attributes().value(QLatin1String("backgroundColor"));
0247     if (!attribute.isEmpty()) {
0248         style.backgroundColor = QColor(attribute).rgba();
0249     }
0250 
0251     attribute = reader.attributes().value(QLatin1String("selBackgroundColor"));
0252     if (!attribute.isEmpty()) {
0253         style.selectedBackgroundColor = QColor(attribute).rgba();
0254     }
0255 
0256     attribute = reader.attributes().value(QLatin1String("italic"));
0257     if (!attribute.isEmpty()) {
0258         style.hasItalic = true;
0259         style.italic = Xml::attrToBool(attribute);
0260     }
0261 
0262     attribute = reader.attributes().value(QLatin1String("bold"));
0263     if (!attribute.isEmpty()) {
0264         style.hasBold = true;
0265         style.bold = Xml::attrToBool(attribute);
0266     }
0267 
0268     attribute = reader.attributes().value(QLatin1String("underline"));
0269     if (!attribute.isEmpty()) {
0270         style.hasUnderline = true;
0271         style.underline = Xml::attrToBool(attribute);
0272     }
0273 
0274     attribute = reader.attributes().value(QLatin1String("strikeOut"));
0275     if (!attribute.isEmpty()) {
0276         style.hasStrikeThrough = true;
0277         style.strikeThrough = Xml::attrToBool(attribute);
0278     }
0279 
0280     attribute = reader.attributes().value(QLatin1String("spellChecking"));
0281     if (!attribute.isEmpty()) {
0282         spellCheck = Xml::attrToBool(attribute);
0283     }
0284 }