Warning, file /utilities/konsole/src/colorscheme/ColorScheme.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     This source file is part of Konsole, a terminal emulator.
0003 
0004     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 // Own
0010 #include "ColorScheme.h"
0011 #include "RandomizationRange.h"
0012 #include "hsluv.h"
0013 
0014 // Qt
0015 #include <QMetaEnum>
0016 
0017 // KDE
0018 #include <KConfig>
0019 #include <KConfigGroup>
0020 #include <KLocalizedString>
0021 
0022 // STL
0023 #include <random>
0024 
0025 // Konsole
0026 #include "colorschemedebug.h"
0027 
0028 // define DEBUG_LOADING_TIME
0029 #ifdef DEBUG_LOADING_TIME
0030 #include <QElapsedTimer>
0031 #endif
0032 
0033 namespace
0034 {
0035 const int FGCOLOR_INDEX = 0;
0036 const int BGCOLOR_INDEX = 1;
0037 
0038 const char RandomHueRangeKey[] = "RandomHueRange";
0039 const char RandomSaturationRangeKey[] = "RandomSaturationRange";
0040 const char RandomLightnessRangeKey[] = "RandomLightnessRange";
0041 const char EnableColorRandomizationKey[] = "ColorRandomization";
0042 
0043 const double MaxHue = 360.0;
0044 const double MaxSaturation = 100.0;
0045 const double MaxLightness = 100.0;
0046 }
0047 
0048 using namespace Konsole;
0049 
0050 // The following are almost IBM standard color codes, with some slight
0051 // gamma correction for the dim colors to compensate for bright X screens.
0052 // It contains the 8 ansiterm/xterm colors in 2 intensities.
0053 const QColor ColorScheme::defaultTable[TABLE_COLORS] = {
0054     QColor(0x00, 0x00, 0x00), // Dfore
0055     QColor(0xFF, 0xFF, 0xFF), // Dback
0056     QColor(0x00, 0x00, 0x00), // Black
0057     QColor(0xB2, 0x18, 0x18), // Red
0058     QColor(0x18, 0xB2, 0x18), // Green
0059     QColor(0xB2, 0x68, 0x18), // Yellow
0060     QColor(0x18, 0x18, 0xB2), // Blue
0061     QColor(0xB2, 0x18, 0xB2), // Magenta
0062     QColor(0x18, 0xB2, 0xB2), // Cyan
0063     QColor(0xB2, 0xB2, 0xB2), // White
0064     // intensive versions
0065     QColor(0x00, 0x00, 0x00),
0066     QColor(0xFF, 0xFF, 0xFF),
0067     QColor(0x68, 0x68, 0x68),
0068     QColor(0xFF, 0x54, 0x54),
0069     QColor(0x54, 0xFF, 0x54),
0070     QColor(0xFF, 0xFF, 0x54),
0071     QColor(0x54, 0x54, 0xFF),
0072     QColor(0xFF, 0x54, 0xFF),
0073     QColor(0x54, 0xFF, 0xFF),
0074     QColor(0xFF, 0xFF, 0xFF),
0075     // Here are faint intensities, which may not be good.
0076     // faint versions
0077     QColor(0x00, 0x00, 0x00),
0078     QColor(0xFF, 0xFF, 0xFF),
0079     QColor(0x00, 0x00, 0x00),
0080     QColor(0x65, 0x00, 0x00),
0081     QColor(0x00, 0x65, 0x00),
0082     QColor(0x65, 0x5E, 0x00),
0083     QColor(0x00, 0x00, 0x65),
0084     QColor(0x65, 0x00, 0x65),
0085     QColor(0x00, 0x65, 0x65),
0086     QColor(0x65, 0x65, 0x65),
0087 };
0088 
0089 const char *const ColorScheme::colorNames[TABLE_COLORS] = {
0090     "Foreground",
0091     "Background",
0092     "Color0",
0093     "Color1",
0094     "Color2",
0095     "Color3",
0096     "Color4",
0097     "Color5",
0098     "Color6",
0099     "Color7",
0100     "ForegroundIntense",
0101     "BackgroundIntense",
0102     "Color0Intense",
0103     "Color1Intense",
0104     "Color2Intense",
0105     "Color3Intense",
0106     "Color4Intense",
0107     "Color5Intense",
0108     "Color6Intense",
0109     "Color7Intense",
0110     "ForegroundFaint",
0111     "BackgroundFaint",
0112     "Color0Faint",
0113     "Color1Faint",
0114     "Color2Faint",
0115     "Color3Faint",
0116     "Color4Faint",
0117     "Color5Faint",
0118     "Color6Faint",
0119     "Color7Faint",
0120 };
0121 
0122 const KLazyLocalizedString ColorScheme::translatedColorNames[TABLE_COLORS] = {
0123     kli18nc("@item:intable palette", "Foreground"),
0124     kli18nc("@item:intable palette", "Background"),
0125     kli18nc("@item:intable palette", "Color 1"),
0126     kli18nc("@item:intable palette", "Color 2"),
0127     kli18nc("@item:intable palette", "Color 3"),
0128     kli18nc("@item:intable palette", "Color 4"),
0129     kli18nc("@item:intable palette", "Color 5"),
0130     kli18nc("@item:intable palette", "Color 6"),
0131     kli18nc("@item:intable palette", "Color 7"),
0132     kli18nc("@item:intable palette", "Color 8"),
0133     kli18nc("@item:intable palette", "Foreground (Intense)"),
0134     kli18nc("@item:intable palette", "Background (Intense)"),
0135     kli18nc("@item:intable palette", "Color 1 (Intense)"),
0136     kli18nc("@item:intable palette", "Color 2 (Intense)"),
0137     kli18nc("@item:intable palette", "Color 3 (Intense)"),
0138     kli18nc("@item:intable palette", "Color 4 (Intense)"),
0139     kli18nc("@item:intable palette", "Color 5 (Intense)"),
0140     kli18nc("@item:intable palette", "Color 6 (Intense)"),
0141     kli18nc("@item:intable palette", "Color 7 (Intense)"),
0142     kli18nc("@item:intable palette", "Color 8 (Intense)"),
0143     kli18nc("@item:intable palette", "Foreground (Faint)"),
0144     kli18nc("@item:intable palette", "Background (Faint)"),
0145     kli18nc("@item:intable palette", "Color 1 (Faint)"),
0146     kli18nc("@item:intable palette", "Color 2 (Faint)"),
0147     kli18nc("@item:intable palette", "Color 3 (Faint)"),
0148     kli18nc("@item:intable palette", "Color 4 (Faint)"),
0149     kli18nc("@item:intable palette", "Color 5 (Faint)"),
0150     kli18nc("@item:intable palette", "Color 6 (Faint)"),
0151     kli18nc("@item:intable palette", "Color 7 (Faint)"),
0152     kli18nc("@item:intable palette", "Color 8 (Faint)"),
0153 };
0154 
0155 QString ColorScheme::colorNameForIndex(int index)
0156 {
0157     Q_ASSERT(index >= 0 && index < TABLE_COLORS);
0158 
0159     return QString(QLatin1String(colorNames[index]));
0160 }
0161 
0162 QString ColorScheme::translatedColorNameForIndex(int index)
0163 {
0164     Q_ASSERT(index >= 0 && index < TABLE_COLORS);
0165     return translatedColorNames[index].toString();
0166 }
0167 
0168 ColorScheme::ColorScheme()
0169     : _description(QString())
0170     , _name(QString())
0171     , _table(nullptr)
0172     , _randomTable(nullptr)
0173     , _opacity(1.0)
0174     , _blur(false)
0175     , _colorRandomization(false)
0176     , _wallpaper(nullptr)
0177 {
0178     setWallpaper(QString(), ColorSchemeWallpaper::Tile, QPointF(0.5, 0.5), 1.0, ColorSchemeWallpaper::NoFlip);
0179 }
0180 
0181 ColorScheme::ColorScheme(const ColorScheme &other)
0182     : _description(QString())
0183     , _name(QString())
0184     , _table(nullptr)
0185     , _randomTable(nullptr)
0186     , _opacity(other._opacity)
0187     , _blur(other._blur)
0188     , _colorRandomization(other._colorRandomization)
0189     , _wallpaper(other._wallpaper)
0190 {
0191     setName(other.name());
0192     setDescription(other.description());
0193 
0194     if (other._table != nullptr) {
0195         for (int i = 0; i < TABLE_COLORS; i++) {
0196             setColorTableEntry(i, other._table[i]);
0197         }
0198     }
0199 
0200     if (other._randomTable != nullptr) {
0201         for (int i = 0; i < TABLE_COLORS; i++) {
0202             const RandomizationRange &range = other._randomTable[i];
0203             setRandomizationRange(i, range.hue, range.saturation, range.lightness);
0204         }
0205     }
0206 }
0207 
0208 ColorScheme::~ColorScheme()
0209 {
0210     delete[] _table;
0211     delete[] _randomTable;
0212 }
0213 
0214 void ColorScheme::setDescription(const QString &description)
0215 {
0216     _description = description;
0217 }
0218 
0219 QString ColorScheme::description() const
0220 {
0221     return _description;
0222 }
0223 
0224 void ColorScheme::setName(const QString &name)
0225 {
0226     _name = name;
0227 }
0228 
0229 QString ColorScheme::name() const
0230 {
0231     return _name;
0232 }
0233 
0234 void ColorScheme::setColorTableEntry(int index, const QColor &entry)
0235 {
0236     Q_ASSERT(index >= 0 && index < TABLE_COLORS);
0237 
0238     if (_table == nullptr) {
0239         _table = new QColor[TABLE_COLORS];
0240 
0241         std::copy_n(defaultTable, TABLE_COLORS, _table);
0242     }
0243 
0244     if (entry.isValid()) {
0245         _table[index] = entry;
0246     } else {
0247         _table[index] = defaultTable[index];
0248         qCDebug(ColorSchemeDebug) << "ColorScheme" << name() << "has an invalid color index" << index << ", using default table color";
0249     }
0250 }
0251 
0252 QColor ColorScheme::colorEntry(int index, uint randomSeed) const
0253 {
0254     Q_ASSERT(index >= 0 && index < TABLE_COLORS);
0255 
0256     QColor entry = colorTable()[index];
0257 
0258     if (!_colorRandomization || randomSeed == 0 || _randomTable == nullptr || _randomTable[index].isNull()) {
0259         return entry;
0260     }
0261 
0262     double baseHue;
0263     double baseSaturation;
0264     double baseLightness;
0265     rgb2hsluv(entry.redF(), entry.greenF(), entry.blueF(), &baseHue, &baseSaturation, &baseLightness);
0266 
0267     const RandomizationRange &range = _randomTable[index];
0268 
0269     // 32-bit Mersenne Twister
0270     // Can't use default_random_engine, because in GCC this maps to
0271     // minstd_rand0 which always gives us 0 on the first number.
0272     std::mt19937 randomEngine(randomSeed);
0273 
0274     // Use hues located around base color's hue.
0275     // H=0 [|=      =]    H=128 [   =|=   ]    H=360 [=      =|]
0276     const double minHue = baseHue - range.hue / 2.0;
0277     const double maxHue = baseHue + range.hue / 2.0;
0278     std::uniform_real_distribution<> hueDistribution(minHue, maxHue);
0279     // Hue value is an angle, it wraps after 360°. Adding MAX_HUE
0280     // guarantees that the sum is not negative.
0281     const double hue = fmod(MaxHue + hueDistribution(randomEngine), MaxHue);
0282 
0283     // Saturation is always decreased. With more saturation more
0284     // information about hue is preserved in RGB color space
0285     // (consider red with S=100 and "red" with S=0 which is gray).
0286     // Additionally, I think it can be easier to imagine more
0287     // toned color than more vivid one.
0288     // S=0 [|==      ]    S=50 [  ==|    ]    S=100 [      ==|]
0289     const double minSaturation = qMax(baseSaturation - range.saturation, 0.0);
0290     const double maxSaturation = qMax(range.saturation, baseSaturation);
0291     // Use rising linear distribution as colors with lower
0292     // saturation are less distinguishable.
0293     double saturation;
0294     if (qFuzzyCompare(minSaturation, maxSaturation)) {
0295         saturation = baseSaturation;
0296     } else {
0297         std::piecewise_linear_distribution<> saturationDistribution({minSaturation, maxSaturation}, [](double v) {
0298             return v;
0299         });
0300         saturation = saturationDistribution(randomEngine);
0301     }
0302 
0303     // Lightness range has base value at its center. The base
0304     // value is clamped to prevent the range from shrinking.
0305     // L=0 [=|=        ]    L=50 [    =|=    ]    L=100 [        =|=]
0306     baseLightness = qBound(range.lightness / 2.0, baseLightness, MaxLightness - range.lightness);
0307     const double minLightness = qMax(baseLightness - range.lightness / 2.0, 0.0);
0308     const double maxLightness = qMin(baseLightness + range.lightness / 2.0, MaxLightness);
0309     // Use triangular distribution with peak at L=50.0.
0310     // Dark and very light colors are less distinguishable.
0311     double lightness;
0312     if (qFuzzyCompare(minLightness, maxLightness)) {
0313         lightness = baseLightness;
0314     } else {
0315         static const auto lightnessWeightsFunc = [](double v) {
0316             return 50.0 - qAbs(v - 50.0);
0317         };
0318         std::piecewise_linear_distribution<> lightnessDistribution;
0319         if (minLightness < 50.0 && 50.0 < maxLightness) {
0320             lightnessDistribution = std::piecewise_linear_distribution<>({minLightness, 50.0, maxLightness}, lightnessWeightsFunc);
0321         } else {
0322             lightnessDistribution = std::piecewise_linear_distribution<>({minLightness, maxLightness}, lightnessWeightsFunc);
0323         }
0324         lightness = lightnessDistribution(randomEngine);
0325     }
0326 
0327     double red;
0328     double green;
0329     double blue;
0330     hsluv2rgb(hue, saturation, lightness, &red, &green, &blue);
0331 
0332     return {qRound(red * 255), qRound(green * 255), qRound(blue * 255)};
0333 }
0334 
0335 void ColorScheme::getColorTable(QColor *table, uint randomSeed) const
0336 {
0337     for (int i = 0; i < TABLE_COLORS; i++) {
0338         table[i] = colorEntry(i, randomSeed);
0339     }
0340 }
0341 
0342 bool ColorScheme::isColorRandomizationEnabled() const
0343 {
0344     return (_colorRandomization && _randomTable != nullptr);
0345 }
0346 
0347 void ColorScheme::setColorRandomization(bool randomize)
0348 {
0349     _colorRandomization = randomize;
0350     if (randomize) {
0351         bool hasAnyRandomizationEntries = false;
0352         if (_randomTable != nullptr) {
0353             for (int i = 0; !hasAnyRandomizationEntries && i < TABLE_COLORS; i++) {
0354                 hasAnyRandomizationEntries = !_randomTable[i].isNull();
0355             }
0356         }
0357         // Set default randomization settings
0358         if (!hasAnyRandomizationEntries) {
0359             static const int ColorIndexesForRandomization[] = {
0360                 ColorFgIndex,
0361                 ColorBgIndex,
0362                 ColorFgIntenseIndex,
0363                 ColorBgIntenseIndex,
0364                 ColorFgFaintIndex,
0365                 ColorBgFaintIndex,
0366             };
0367             for (int index : ColorIndexesForRandomization) {
0368                 setRandomizationRange(index, MaxHue, MaxSaturation, 0.0);
0369             }
0370         }
0371     }
0372 }
0373 
0374 void ColorScheme::setRandomizationRange(int index, double hue, double saturation, double lightness)
0375 {
0376     Q_ASSERT(hue <= MaxHue);
0377     Q_ASSERT(index >= 0 && index < TABLE_COLORS);
0378 
0379     if (_randomTable == nullptr) {
0380         _randomTable = new RandomizationRange[TABLE_COLORS];
0381     }
0382 
0383     _randomTable[index].hue = hue;
0384     _randomTable[index].saturation = saturation;
0385     _randomTable[index].lightness = lightness;
0386 }
0387 
0388 const QColor *ColorScheme::colorTable() const
0389 {
0390     if (_table != nullptr) {
0391         return _table;
0392     }
0393     return defaultTable;
0394 }
0395 
0396 QColor ColorScheme::foregroundColor() const
0397 {
0398     return colorTable()[FGCOLOR_INDEX];
0399 }
0400 
0401 QColor ColorScheme::backgroundColor() const
0402 {
0403     return colorTable()[BGCOLOR_INDEX];
0404 }
0405 
0406 bool ColorScheme::hasDarkBackground() const
0407 {
0408     double h;
0409     double s;
0410     double l;
0411     const double r = backgroundColor().redF();
0412     const double g = backgroundColor().greenF();
0413     const double b = backgroundColor().blueF();
0414     rgb2hsluv(r, g, b, &h, &s, &l);
0415     return l < 50;
0416 }
0417 
0418 void ColorScheme::setOpacity(qreal opacity)
0419 {
0420     if (opacity < 0.0 || opacity > 1.0) {
0421         qCDebug(ColorSchemeDebug) << "ColorScheme" << name() << "has an invalid opacity" << opacity << "using 1";
0422         opacity = 1.0;
0423     }
0424     _opacity = opacity;
0425 }
0426 
0427 qreal ColorScheme::opacity() const
0428 {
0429     return _opacity;
0430 }
0431 
0432 void ColorScheme::setBlur(bool blur)
0433 {
0434     _blur = blur;
0435 }
0436 
0437 bool ColorScheme::blur() const
0438 {
0439     return _blur;
0440 }
0441 
0442 void ColorScheme::read(const KConfig &config)
0443 {
0444 #ifdef DEBUG_LOADING_TIME
0445     QElapsedTimer t;
0446     t.start();
0447 #endif
0448 
0449     KConfigGroup configGroup = config.group(QStringLiteral("General"));
0450 
0451     const QString schemeDescription = configGroup.readEntry("Description", i18nc("@item", "Un-named Color Scheme"));
0452 
0453     _description = i18n(schemeDescription.toUtf8().constData());
0454     setOpacity(configGroup.readEntry("Opacity", 1.0));
0455     _blur = configGroup.readEntry("Blur", false);
0456     setWallpaper(configGroup.readEntry("Wallpaper", QString()),
0457                  configGroup.readEntry("FillStyle", QString::fromLatin1("Tile")),
0458                  configGroup.readEntry("Anchor", QPointF(0.5, 0.5)),
0459                  configGroup.readEntry("WallpaperOpacity", 1.0),
0460                  configGroup.readEntry("WallpaperFlipType", QString::fromLatin1("NoFlip")));
0461     _colorRandomization = configGroup.readEntry(EnableColorRandomizationKey, false);
0462 
0463     for (int i = 0; i < TABLE_COLORS; i++) {
0464         readColorEntry(config, i);
0465     }
0466 
0467 #ifdef DEBUG_LOADING_TIME
0468     qDebug() << name() << "loaded in" << t.elapsed() << "ms";
0469 #endif
0470 }
0471 
0472 void ColorScheme::readColorEntry(const KConfig &config, int index)
0473 {
0474     KConfigGroup configGroup = config.group(colorNameForIndex(index));
0475 
0476     if (!configGroup.hasKey("Color") && _table != nullptr) {
0477         setColorTableEntry(index, _table[index % BASE_COLORS]);
0478         return;
0479     }
0480 
0481     QColor entry;
0482 
0483     entry = configGroup.readEntry("Color", QColor());
0484     setColorTableEntry(index, entry);
0485 
0486     const auto readAndCheckConfigEntry = [&](const char *key, double min, double max) -> double {
0487         const double value = configGroup.readEntry(key, min);
0488         if (min > value || value > max) {
0489             qCDebug(ColorSchemeDebug) << QStringLiteral(
0490                                              "Color scheme \"%1\": color index 2 has an invalid value: %3 = %4. "
0491                                              "Allowed value range: %5 - %6. Using %7.")
0492                                              .arg(name())
0493                                              .arg(index)
0494                                              .arg(QLatin1String(key))
0495                                              .arg(value, 0, 'g', 1)
0496                                              .arg(min, 0, 'g', 1)
0497                                              .arg(max, 0, 'g', 1)
0498                                              .arg(min, 0, 'g', 1);
0499             return min;
0500         }
0501         return value;
0502     };
0503 
0504     double hue = readAndCheckConfigEntry(RandomHueRangeKey, 0.0, MaxHue);
0505     double saturation = readAndCheckConfigEntry(RandomSaturationRangeKey, 0.0, MaxSaturation);
0506     double lightness = readAndCheckConfigEntry(RandomLightnessRangeKey, 0.0, MaxLightness);
0507 
0508     if (!qFuzzyIsNull(hue) || !qFuzzyIsNull(saturation) || !qFuzzyIsNull(lightness)) {
0509         setRandomizationRange(index, hue, saturation, lightness);
0510     }
0511 }
0512 
0513 void ColorScheme::write(KConfig &config) const
0514 {
0515     KConfigGroup configGroup = config.group(QStringLiteral("General"));
0516 
0517     configGroup.writeEntry("Description", _description);
0518     configGroup.writeEntry("Opacity", _opacity);
0519     configGroup.writeEntry("Blur", _blur);
0520     configGroup.writeEntry("Wallpaper", _wallpaper->path());
0521     configGroup.writeEntry("FillStyle", QMetaEnum::fromType<ColorSchemeWallpaper::FillStyle>().valueToKey(_wallpaper->style()));
0522     configGroup.writeEntry("WallpaperFlipType", QMetaEnum::fromType<ColorSchemeWallpaper::FlipType>().valueToKey(_wallpaper->flipType()));
0523     configGroup.writeEntry("Anchor", _wallpaper->anchor());
0524     configGroup.writeEntry("WallpaperOpacity", _wallpaper->opacity());
0525     configGroup.writeEntry(EnableColorRandomizationKey, _colorRandomization);
0526 
0527     for (int i = 0; i < TABLE_COLORS; i++) {
0528         writeColorEntry(config, i);
0529     }
0530 }
0531 
0532 void ColorScheme::writeColorEntry(KConfig &config, int index) const
0533 {
0534     KConfigGroup configGroup = config.group(colorNameForIndex(index));
0535 
0536     const QColor &entry = colorTable()[index];
0537 
0538     configGroup.writeEntry("Color", entry);
0539 
0540     // Remove unused keys
0541     static const char *obsoleteKeys[] = {
0542         "Transparent",
0543         "Transparency",
0544         "Bold",
0545         // Uncomment when people stop using Konsole from 2019:
0546         // "MaxRandomHue",
0547         // "MaxRandomValue",
0548         // "MaxRandomSaturation"
0549     };
0550     for (const auto key : obsoleteKeys) {
0551         if (configGroup.hasKey(key)) {
0552             configGroup.deleteEntry(key);
0553         }
0554     }
0555 
0556     RandomizationRange random = _randomTable != nullptr ? _randomTable[index] : RandomizationRange();
0557 
0558     const auto checkAndMaybeSaveValue = [&](const char *key, double value) {
0559         const bool valueIsNull = qFuzzyCompare(value, 0.0);
0560         const bool keyExists = configGroup.hasKey(key);
0561         const bool keyExistsAndHasDifferentValue = !qFuzzyCompare(configGroup.readEntry(key, value), value);
0562         if ((!valueIsNull && !keyExists) || keyExistsAndHasDifferentValue) {
0563             configGroup.writeEntry(key, value);
0564         }
0565     };
0566 
0567     checkAndMaybeSaveValue(RandomHueRangeKey, random.hue);
0568     checkAndMaybeSaveValue(RandomSaturationRangeKey, random.saturation);
0569     checkAndMaybeSaveValue(RandomLightnessRangeKey, random.lightness);
0570 }
0571 
0572 void ColorScheme::setWallpaper(const QString &path,
0573                                const ColorSchemeWallpaper::FillStyle style,
0574                                const QPointF &anchor,
0575                                const qreal &opacity,
0576                                const ColorSchemeWallpaper::FlipType flipType)
0577 {
0578     _wallpaper = new ColorSchemeWallpaper(path, style, anchor, opacity, flipType);
0579 }
0580 
0581 void ColorScheme::setWallpaper(const QString &path, const QString &style, const QPointF &anchor, const qreal &opacity, const QString &flipType)
0582 {
0583     ColorSchemeWallpaper::FillStyle fstyle;
0584     fstyle = static_cast<ColorSchemeWallpaper::FillStyle>(std::max( // keyToValue returns -1 if key was not found, but we should default to 0
0585         QMetaEnum::fromType<ColorSchemeWallpaper::FillStyle>().keyToValue(style.toStdString().c_str()),
0586         0));
0587 
0588     ColorSchemeWallpaper::FlipType ftype;
0589     ftype = static_cast<ColorSchemeWallpaper::FlipType>(std::max( // keyToValue returns -1 if key was not found, but we should default to 0
0590         QMetaEnum::fromType<ColorSchemeWallpaper::FlipType>().keyToValue(flipType.toStdString().c_str()),
0591         0));
0592 
0593     setWallpaper(path, fstyle, anchor, opacity, ftype);
0594 }
0595 
0596 ColorSchemeWallpaper::Ptr ColorScheme::wallpaper() const
0597 {
0598     return _wallpaper;
0599 }