File indexing completed on 2024-04-28 17:06:20

0001 /*
0002     SPDX-FileCopyrightText: 2000-2002 Shie Erlich <erlich@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2000-2002 Rafi Yanai <yanai@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "krcolorcache.h"
0010 #include "../defaults.h"
0011 #include "../krglobal.h"
0012 
0013 // QtCore
0014 #include <QDebug>
0015 #include <QFile>
0016 #include <QList>
0017 // QtGui
0018 #include <QPixmapCache>
0019 // QtWidgets
0020 #include <QApplication>
0021 
0022 #include <KConfigCore/KSharedConfig>
0023 
0024 // Macro: set target = col, if col is valid
0025 #define SETCOLOR(target, col)                                                                                                                                  \
0026     {                                                                                                                                                          \
0027         if (col.isValid())                                                                                                                                     \
0028             target = col;                                                                                                                                      \
0029     }
0030 
0031 // Static class, which lists all allowed keywords for a quick access.
0032 // Just call a method to initialize it.
0033 class KrColorSettingNames
0034 {
0035     static QMap<QString, bool> s_colorNames;
0036     static QMap<QString, bool> s_numNames;
0037     static QMap<QString, bool> s_boolNames;
0038     static void initialize();
0039 
0040 public:
0041     static QStringList getColorNames();
0042     static bool isColorNameValid(const QString &settingName);
0043     static QStringList getNumNames();
0044     static bool isNumNameValid(const QString &settingName);
0045     static QStringList getBoolNames();
0046     static bool isBoolNameValid(const QString &settingName);
0047 } krColorSettingNames;
0048 
0049 QMap<QString, bool> KrColorSettingNames::s_colorNames;
0050 QMap<QString, bool> KrColorSettingNames::s_numNames;
0051 QMap<QString, bool> KrColorSettingNames::s_boolNames;
0052 
0053 void KrColorSettingNames::initialize()
0054 {
0055     if (!s_colorNames.empty())
0056         return;
0057     s_colorNames["Foreground"] = true;
0058     s_colorNames["Inactive Foreground"] = true;
0059     s_colorNames["Rename Foreground"] = true;
0060     s_colorNames["Directory Foreground"] = true;
0061     s_colorNames["Inactive Directory Foreground"] = true;
0062     s_colorNames["Executable Foreground"] = true;
0063     s_colorNames["Inactive Executable Foreground"] = true;
0064     s_colorNames["Symlink Foreground"] = true;
0065     s_colorNames["Inactive Symlink Foreground"] = true;
0066     s_colorNames["Invalid Symlink Foreground"] = true;
0067     s_colorNames["Inactive Invalid Symlink Foreground"] = true;
0068     s_colorNames["Marked Foreground"] = true;
0069     s_colorNames["Inactive Marked Foreground"] = true;
0070     s_colorNames["Marked Background"] = true;
0071     s_colorNames["Inactive Marked Background"] = true;
0072     s_colorNames["Current Foreground"] = true;
0073     s_colorNames["Inactive Current Foreground"] = true;
0074     s_colorNames["Current Background"] = true;
0075     s_colorNames["Inactive Current Background"] = true;
0076     s_colorNames["Marked Current Foreground"] = true;
0077     s_colorNames["Inactive Marked Current Foreground"] = true;
0078     s_colorNames["Alternate Background"] = true;
0079     s_colorNames["Inactive Alternate Background"] = true;
0080     s_colorNames["Background"] = true;
0081     s_colorNames["Inactive Background"] = true;
0082     s_colorNames["Rename Background"] = true;
0083     s_colorNames["Alternate Marked Background"] = true;
0084     s_colorNames["Inactive Alternate Marked Background"] = true;
0085     s_colorNames["Dim Target Color"] = true;
0086 
0087     s_numNames["Dim Factor"] = true;
0088 
0089     s_boolNames["KDE Default"] = true;
0090     s_boolNames["Enable Alternate Background"] = true;
0091     s_boolNames["Show Current Item Always"] = true;
0092     s_boolNames["Dim Inactive Colors"] = true;
0093 }
0094 
0095 QStringList KrColorSettingNames::getColorNames()
0096 {
0097     initialize();
0098     return s_colorNames.keys();
0099 }
0100 
0101 bool KrColorSettingNames::isColorNameValid(const QString &settingName)
0102 {
0103     initialize();
0104     return s_colorNames.contains(settingName);
0105 }
0106 
0107 QStringList KrColorSettingNames::getNumNames()
0108 {
0109     initialize();
0110     return s_numNames.keys();
0111 }
0112 
0113 bool KrColorSettingNames::isNumNameValid(const QString &settingName)
0114 {
0115     initialize();
0116     return s_numNames.contains(settingName);
0117 }
0118 
0119 QStringList KrColorSettingNames::getBoolNames()
0120 {
0121     initialize();
0122     return s_boolNames.keys();
0123 }
0124 
0125 bool KrColorSettingNames::isBoolNameValid(const QString &settingName)
0126 {
0127     initialize();
0128     return s_boolNames.contains(settingName);
0129 }
0130 
0131 /*
0132 KrColorSettings implementation. Contains all properties in QMaps. loadFromConfig initializes them from krConfig.
0133 */
0134 class KrColorSettingsImpl
0135 {
0136     friend class KrColorSettings;
0137     QMap<QString, QString> m_colorTextValues;
0138     QMap<QString, QColor> m_colorValues;
0139     QMap<QString, int> m_numValues;
0140     QMap<QString, bool> m_boolValues;
0141     void loadFromConfig();
0142 };
0143 
0144 void KrColorSettingsImpl::loadFromConfig()
0145 {
0146     KConfigGroup group(krConfig, "Colors");
0147     QStringList names = KrColorSettingNames::getColorNames();
0148     for (auto &name : names) {
0149         m_colorTextValues[name] = group.readEntry(name, QString());
0150         if (m_colorTextValues[name].count(',') == 2)
0151             m_colorValues[name] = group.readEntry(name, QColor());
0152         else
0153             m_colorValues[name] = QColor();
0154     }
0155     names = KrColorSettingNames::getNumNames();
0156     for (auto &name : names) {
0157         if (!group.readEntry(name, QString()).isEmpty()) {
0158             m_numValues[name] = group.readEntry(name, 0);
0159         }
0160     }
0161     names = KrColorSettingNames::getBoolNames();
0162     for (auto &name : names) {
0163         if (!group.readEntry(name, QString()).isEmpty()) {
0164             m_boolValues[name] = group.readEntry(name, false);
0165         }
0166     }
0167 }
0168 
0169 KrColorSettings::KrColorSettings()
0170 {
0171     m_impl = new KrColorSettingsImpl();
0172     m_impl->loadFromConfig();
0173 }
0174 
0175 KrColorSettings::KrColorSettings(const KrColorSettings &src)
0176 {
0177     m_impl = new KrColorSettingsImpl();
0178     operator=(src);
0179 }
0180 
0181 KrColorSettings::~KrColorSettings()
0182 {
0183     delete m_impl;
0184 }
0185 
0186 const KrColorSettings &KrColorSettings::operator=(const KrColorSettings &src)
0187 {
0188     if (this == &src)
0189         return *this;
0190     QStringList names = KrColorSettingNames::getColorNames();
0191     for (auto &name : names) {
0192         m_impl->m_colorTextValues[name] = src.m_impl->m_colorTextValues[name];
0193         m_impl->m_colorValues[name] = src.m_impl->m_colorValues[name];
0194     }
0195     for (QMap<QString, int>::Iterator it = src.m_impl->m_numValues.begin(); it != src.m_impl->m_numValues.end(); ++it) {
0196         m_impl->m_numValues[it.key()] = it.value();
0197     }
0198     for (QMap<QString, bool>::Iterator it = src.m_impl->m_boolValues.begin(); it != src.m_impl->m_boolValues.end(); ++it) {
0199         m_impl->m_boolValues[it.key()] = it.value();
0200     }
0201     return *this;
0202 }
0203 
0204 QList<QString> KrColorSettings::getColorNames()
0205 {
0206     return KrColorSettingNames::getColorNames();
0207 }
0208 
0209 bool KrColorSettings::isColorNameValid(const QString &settingName)
0210 {
0211     return KrColorSettingNames::isColorNameValid(settingName);
0212 }
0213 
0214 bool KrColorSettings::setColorValue(const QString &settingName, const QColor &color)
0215 {
0216     if (!isColorNameValid(settingName)) {
0217         qWarning() << "Invalid color setting name: " << settingName;
0218         return false;
0219     }
0220     m_impl->m_colorValues[settingName] = color;
0221     return true;
0222 }
0223 
0224 QColor KrColorSettings::getColorValue(const QString &settingName) const
0225 {
0226     if (!isColorNameValid(settingName)) {
0227         qWarning() << "Invalid color setting name: " << settingName;
0228         return QColor();
0229     }
0230     return m_impl->m_colorValues[settingName];
0231 }
0232 
0233 bool KrColorSettings::setColorTextValue(const QString &settingName, const QString &colorText)
0234 {
0235     if (!isColorNameValid(settingName)) {
0236         qWarning() << "Invalid color setting name: " << settingName;
0237         return false;
0238     }
0239     m_impl->m_colorTextValues[settingName] = colorText;
0240     return true;
0241 }
0242 
0243 QString KrColorSettings::getColorTextValue(const QString &settingName) const
0244 {
0245     if (!isColorNameValid(settingName)) {
0246         qWarning() << "Invalid color setting name: " << settingName;
0247         return QString();
0248     }
0249     return m_impl->m_colorTextValues[settingName];
0250 }
0251 
0252 QList<QString> KrColorSettings::getNumNames()
0253 {
0254     return KrColorSettingNames::getNumNames();
0255 }
0256 
0257 bool KrColorSettings::isNumNameValid(const QString &settingName)
0258 {
0259     return KrColorSettingNames::isNumNameValid(settingName);
0260 }
0261 
0262 bool KrColorSettings::setNumValue(const QString &settingName, int value)
0263 {
0264     if (!isNumNameValid(settingName)) {
0265         qWarning() << "Invalid number setting name: " << settingName;
0266         return false;
0267     }
0268     m_impl->m_numValues[settingName] = value;
0269     return true;
0270 }
0271 
0272 int KrColorSettings::getNumValue(const QString &settingName, int defaultValue) const
0273 {
0274     if (!isNumNameValid(settingName)) {
0275         qWarning() << "Invalid number setting name: " << settingName;
0276         return 0;
0277     }
0278     if (!m_impl->m_numValues.contains(settingName))
0279         return defaultValue;
0280     return m_impl->m_numValues[settingName];
0281 }
0282 
0283 QList<QString> KrColorSettings::getBoolNames()
0284 {
0285     return KrColorSettingNames::getBoolNames();
0286 }
0287 
0288 bool KrColorSettings::isBoolNameValid(const QString &settingName)
0289 {
0290     return KrColorSettingNames::isBoolNameValid(settingName);
0291 }
0292 
0293 bool KrColorSettings::setBoolValue(const QString &settingName, bool value)
0294 {
0295     if (!isBoolNameValid(settingName)) {
0296         qWarning() << "Invalid bool setting name: " << settingName;
0297         return false;
0298     }
0299     m_impl->m_boolValues[settingName] = value;
0300     return true;
0301 }
0302 
0303 int KrColorSettings::getBoolValue(const QString &settingName, bool defaultValue) const
0304 {
0305     if (!isBoolNameValid(settingName)) {
0306         qWarning() << "Invalid bool setting name: " << settingName;
0307         return false;
0308     }
0309     if (!m_impl->m_boolValues.contains(settingName))
0310         return defaultValue;
0311     return m_impl->m_boolValues[settingName];
0312 }
0313 
0314 KrColorItemType::KrColorItemType()
0315 {
0316     m_fileType = File;
0317     m_alternateBackgroundColor = false;
0318     m_activePanel = false;
0319     m_currentItem = false;
0320     m_selectedItem = false;
0321 }
0322 
0323 KrColorItemType::KrColorItemType(FileType type, bool alternateBackgroundColor, bool activePanel, bool currentItem, bool selectedItem)
0324 {
0325     m_fileType = type;
0326     m_alternateBackgroundColor = alternateBackgroundColor;
0327     m_activePanel = activePanel;
0328     m_currentItem = currentItem;
0329     m_selectedItem = selectedItem;
0330 }
0331 
0332 KrColorItemType::KrColorItemType(const KrColorItemType &src)
0333 {
0334     operator=(src);
0335 }
0336 
0337 const KrColorItemType &KrColorItemType::operator=(const KrColorItemType &src)
0338 {
0339     if (this == &src)
0340         return *this;
0341     m_fileType = src.m_fileType;
0342     m_alternateBackgroundColor = src.m_alternateBackgroundColor;
0343     m_activePanel = src.m_activePanel;
0344     m_currentItem = src.m_currentItem;
0345     m_selectedItem = src.m_selectedItem;
0346     return *this;
0347 }
0348 
0349 /*
0350 KrColorCache implementation. Contains the KrColorSettings used for teh calculation and the cache for the results.
0351 getColors is the only method to call. All other are taken from the previous versions.
0352 */
0353 class KrColorCacheImpl
0354 {
0355     friend class KrColorCache;
0356     QMap<QString, KrColorGroup> m_cachedColors;
0357     KrColorSettings m_colorSettings;
0358 
0359     KrColorGroup getColors(const KrColorItemType &type) const;
0360     static const QColor &setColorIfContrastIsSufficient(const QColor &background, const QColor &color1, const QColor &color2);
0361     QColor getForegroundColor(bool isActive) const;
0362     QColor getSpecialForegroundColor(const QString &type, bool isActive) const;
0363     QColor getBackgroundColor(bool isActive) const;
0364     QColor getAlternateBackgroundColor(bool isActive) const;
0365     QColor getMarkedForegroundColor(bool isActive) const;
0366     QColor getMarkedBackgroundColor(bool isActive) const;
0367     QColor getAlternateMarkedBackgroundColor(bool isActive) const;
0368     QColor getCurrentForegroundColor(bool isActive) const;
0369     QColor getCurrentBackgroundColor(bool isActive) const;
0370     QColor getCurrentMarkedForegroundColor(bool isActive) const;
0371     QColor dimColor(QColor color, bool isBackgroundColor) const;
0372 };
0373 
0374 KrColorGroup KrColorCacheImpl::getColors(const KrColorItemType &type) const
0375 {
0376     KrColorGroup result;
0377     if (m_colorSettings.getBoolValue("KDE Default", _KDEDefaultColors)) {
0378         bool enableAlternateBackground = m_colorSettings.getBoolValue("Enable Alternate Background", _AlternateBackground);
0379 
0380         QPalette p = QGuiApplication::palette();
0381         QColor background = enableAlternateBackground && type.m_alternateBackgroundColor ? p.color(QPalette::Active, QPalette::Base)
0382                                                                                          : p.color(QPalette::Active, QPalette::AlternateBase);
0383         result.setBackground(background);
0384         result.setText(p.color(QPalette::Active, QPalette::Text));
0385         result.setHighlight(p.color(QPalette::Active, QPalette::Highlight));
0386         result.setHighlightedText(p.color(QPalette::Active, QPalette::HighlightedText));
0387 
0388         /*  if (type.m_currentItem && type.m_activePanel)
0389           {
0390            QColor currentBackground = KColorScheme(QPalette::Active).background(KColorScheme::ActiveBackground).color();
0391            // set the background
0392            result.setColor(QColorGroup::Highlight, currentBackground);
0393            result.setColor(QColorGroup::Base, currentBackground);
0394            result.setColor(QColorGroup::Background, currentBackground);
0395           }*/
0396         return result;
0397     }
0398     bool markCurrentAlways = m_colorSettings.getBoolValue("Show Current Item Always", _ShowCurrentItemAlways);
0399     bool dimBackground = m_colorSettings.getBoolValue("Dim Inactive Colors", false);
0400 
0401     // cache m_activePanel flag. If color dimming is turned on, it is set to true, as the inactive colors
0402     // are calculated from the active ones at the end.
0403     bool isActive = type.m_activePanel;
0404     if (dimBackground)
0405         isActive = true;
0406 
0407     // First calculate fore- and background.
0408     QColor background = type.m_alternateBackgroundColor ? getAlternateBackgroundColor(isActive) : getBackgroundColor(isActive);
0409     QColor foreground;
0410     switch (type.m_fileType) {
0411     case KrColorItemType::Directory:
0412         foreground = getSpecialForegroundColor("Directory", isActive);
0413         break;
0414     case KrColorItemType::Executable:
0415         foreground = getSpecialForegroundColor("Executable", isActive);
0416         break;
0417     case KrColorItemType::InvalidSymlink:
0418         foreground = getSpecialForegroundColor("Invalid Symlink", isActive);
0419         break;
0420     case KrColorItemType::Symlink:
0421         foreground = getSpecialForegroundColor("Symlink", isActive);
0422         break;
0423     default:
0424         foreground = getForegroundColor(isActive);
0425     }
0426 
0427     // set the background color
0428     result.setBackground(background);
0429 
0430     // set the foreground color
0431     result.setText(foreground);
0432 
0433     // now the color of a marked item
0434     QColor markedBackground = type.m_alternateBackgroundColor ? getAlternateMarkedBackgroundColor(isActive) : getMarkedBackgroundColor(isActive);
0435     QColor markedForeground = getMarkedForegroundColor(isActive);
0436     if (!markedForeground.isValid()) // transparent
0437         // choose fore- or background, depending on its contrast compared to markedBackground
0438         markedForeground = setColorIfContrastIsSufficient(markedBackground, foreground, background);
0439 
0440     // set it in the color group (different group color than normal foreground!)
0441     result.setHighlightedText(markedForeground);
0442     result.setHighlight(markedBackground);
0443 
0444     // In case the current item is a selected one, set the fore- and background colors for the contrast calculation below
0445     if (type.m_selectedItem) {
0446         background = markedBackground;
0447         foreground = markedForeground;
0448     }
0449 
0450     // finally the current item
0451     if (type.m_currentItem && (markCurrentAlways || isActive)) {
0452         // if this is the current item AND the panels has the focus OR the current should be marked always
0453         QColor currentBackground = getCurrentBackgroundColor(isActive);
0454 
0455         if (!currentBackground.isValid()) // transparent
0456             currentBackground = background;
0457 
0458         // set the background
0459         result.setHighlight(currentBackground);
0460         result.setBackground(currentBackground);
0461 
0462         QColor color;
0463         if (type.m_selectedItem)
0464             color = getCurrentMarkedForegroundColor(isActive);
0465         if (!color.isValid()) { // not used
0466             color = getCurrentForegroundColor(isActive);
0467             if (!color.isValid()) // transparent
0468                 // choose fore- or background, depending on its contrast compared to markedBackground
0469                 color = setColorIfContrastIsSufficient(currentBackground, foreground, background);
0470         }
0471 
0472         // set the foreground
0473         result.setText(color);
0474         result.setHighlightedText(color);
0475     }
0476 
0477     if (dimBackground && !type.m_activePanel) {
0478         // if color dimming is chosen, dim the colors for the inactive panel
0479         result.setBackground(dimColor(result.background(), true));
0480         result.setText(dimColor(result.text(), false));
0481         result.setHighlightedText(dimColor(result.highlightedText(), false));
0482         result.setHighlight(dimColor(result.highlight(), true));
0483     }
0484     return result;
0485 }
0486 
0487 const QColor &KrColorCacheImpl::setColorIfContrastIsSufficient(const QColor &background, const QColor &color1, const QColor &color2)
0488 {
0489 #define sqr(x) ((x) * (x))
0490     int contrast = sqr(color1.red() - background.red()) + sqr(color1.green() - background.green()) + sqr(color1.blue() - background.blue());
0491 
0492     // if the contrast between background and color1 is too small, take color2 instead.
0493     if (contrast < 1000)
0494         return color2;
0495     return color1;
0496 }
0497 
0498 QColor KrColorCacheImpl::getForegroundColor(bool isActive) const
0499 {
0500     QPalette p = QGuiApplication::palette();
0501     QColor color = p.color(QPalette::Active, QPalette::Text);
0502     SETCOLOR(color, m_colorSettings.getColorValue("Foreground"));
0503     if (!isActive)
0504         SETCOLOR(color, m_colorSettings.getColorValue("Inactive Foreground"));
0505     return color;
0506 }
0507 
0508 QColor KrColorCacheImpl::getSpecialForegroundColor(const QString &type, bool isActive) const
0509 {
0510     QString colorName = "Inactive " + type + " Foreground";
0511     if (!isActive && m_colorSettings.getColorTextValue(colorName) == "Inactive Foreground")
0512         return getForegroundColor(false);
0513     QColor color = m_colorSettings.getColorValue(type + " Foreground");
0514     if (!isActive)
0515         SETCOLOR(color, m_colorSettings.getColorValue(colorName));
0516     if (!color.isValid())
0517         return getForegroundColor(isActive);
0518     return color;
0519 }
0520 
0521 QColor KrColorCacheImpl::getBackgroundColor(bool isActive) const
0522 {
0523     QColor color = QGuiApplication::palette().color(QPalette::Active, QPalette::Base);
0524     SETCOLOR(color, m_colorSettings.getColorValue("Background"));
0525     if (!isActive)
0526         SETCOLOR(color, m_colorSettings.getColorValue("Inactive Background"));
0527     return color;
0528 }
0529 
0530 QColor KrColorCacheImpl::getAlternateBackgroundColor(bool isActive) const
0531 {
0532     QPalette p = QGuiApplication::palette();
0533     if (isActive && m_colorSettings.getColorTextValue("Alternate Background") == "Background")
0534         return getBackgroundColor(true);
0535     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Background").isEmpty())
0536         return getAlternateBackgroundColor(true);
0537     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Background") == "Inactive Background")
0538         return getBackgroundColor(false);
0539     QColor color = isActive ? m_colorSettings.getColorValue("Alternate Background") : m_colorSettings.getColorValue("Inactive Alternate Background");
0540     if (!color.isValid())
0541         color = p.color(QPalette::Active, QPalette::AlternateBase);
0542     if (!color.isValid())
0543         color = p.color(QPalette::Active, QPalette::Base);
0544     return color;
0545 }
0546 
0547 QColor KrColorCacheImpl::getMarkedForegroundColor(bool isActive) const
0548 {
0549     QString colorName = isActive ? "Marked Foreground" : "Inactive Marked Foreground";
0550     if (m_colorSettings.getColorTextValue(colorName) == "transparent")
0551         return QColor();
0552     if (isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
0553         return QGuiApplication::palette().color(QPalette::Active, QPalette::HighlightedText);
0554     if (!isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
0555         return getMarkedForegroundColor(true);
0556     return m_colorSettings.getColorValue(colorName);
0557 }
0558 
0559 QColor KrColorCacheImpl::getMarkedBackgroundColor(bool isActive) const
0560 {
0561     if (isActive && m_colorSettings.getColorTextValue("Marked Background").isEmpty())
0562         return QGuiApplication::palette().color(QPalette::Active, QPalette::Highlight);
0563     if (isActive && m_colorSettings.getColorTextValue("Marked Background") == "Background")
0564         return getBackgroundColor(true);
0565     if (!isActive && m_colorSettings.getColorTextValue("Inactive Marked Background").isEmpty())
0566         return getMarkedBackgroundColor(true);
0567     if (!isActive && m_colorSettings.getColorTextValue("Inactive Marked Background") == "Inactive Background")
0568         return getBackgroundColor(false);
0569     return isActive ? m_colorSettings.getColorValue("Marked Background") : m_colorSettings.getColorValue("Inactive Marked Background");
0570 }
0571 
0572 QColor KrColorCacheImpl::getAlternateMarkedBackgroundColor(bool isActive) const
0573 {
0574     if (isActive && m_colorSettings.getColorTextValue("Alternate Marked Background") == "Alternate Background")
0575         return getAlternateBackgroundColor(true);
0576     if (isActive && m_colorSettings.getColorTextValue("Alternate Marked Background").isEmpty())
0577         return getMarkedBackgroundColor(true);
0578     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Marked Background").isEmpty())
0579         return getAlternateMarkedBackgroundColor(true);
0580     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Marked Background") == "Inactive Alternate Background")
0581         return getAlternateBackgroundColor(false);
0582     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Marked Background") == "Inactive Marked Background")
0583         return getMarkedBackgroundColor(false);
0584     return isActive ? m_colorSettings.getColorValue("Alternate Marked Background") : m_colorSettings.getColorValue("Inactive Alternate Marked Background");
0585 }
0586 
0587 QColor KrColorCacheImpl::getCurrentForegroundColor(bool isActive) const
0588 {
0589     QColor color = m_colorSettings.getColorValue("Current Foreground");
0590     if (!isActive)
0591         SETCOLOR(color, m_colorSettings.getColorValue("Inactive Current Foreground"));
0592     return color;
0593 }
0594 
0595 QColor KrColorCacheImpl::getCurrentBackgroundColor(bool isActive) const
0596 {
0597     if (isActive && m_colorSettings.getColorTextValue("Current Background").isEmpty())
0598         return QColor();
0599     if (isActive && m_colorSettings.getColorTextValue("Current Background") == "Background")
0600         return getBackgroundColor(true);
0601     if (!isActive && m_colorSettings.getColorTextValue("Inactive Current Background").isEmpty())
0602         return getCurrentBackgroundColor(true);
0603     if (!isActive && m_colorSettings.getColorTextValue("Inactive Current Background") == "Inactive Background")
0604         return getBackgroundColor(false);
0605     return isActive ? m_colorSettings.getColorValue("Current Background") : m_colorSettings.getColorValue("Inactive Current Background");
0606 }
0607 
0608 QColor KrColorCacheImpl::getCurrentMarkedForegroundColor(bool isActive) const
0609 {
0610     QString colorName = isActive ? "Marked Current Foreground" : "Inactive Marked Current Foreground";
0611     if (isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
0612         return QColor();
0613     if (isActive && m_colorSettings.getColorTextValue(colorName) == "Marked Foreground")
0614         return getMarkedForegroundColor(true);
0615     if (!isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
0616         return getCurrentMarkedForegroundColor(true);
0617     if (!isActive && m_colorSettings.getColorTextValue(colorName) == "Inactive Marked Foreground")
0618         return getMarkedForegroundColor(false);
0619     return m_colorSettings.getColorValue(colorName);
0620 }
0621 
0622 QColor KrColorCacheImpl::dimColor(QColor color, bool /* isBackgroundColor */) const
0623 {
0624     int dimFactor = m_colorSettings.getNumValue("Dim Factor", 100);
0625     QColor targetColor = m_colorSettings.getColorValue("Dim Target Color");
0626     if (!targetColor.isValid())
0627         targetColor = QColor(255, 255, 255);
0628     bool dimBackground = m_colorSettings.getBoolValue("Dim Inactive Colors", false);
0629     bool dim = dimFactor >= 0 && dimFactor < 100 && dimBackground;
0630     if (dim)
0631         color = KrColorCache::dimColor(color, dimFactor, targetColor);
0632     return color;
0633 }
0634 
0635 KrColorCache *KrColorCache::m_instance = nullptr;
0636 
0637 KrColorCache::KrColorCache()
0638 {
0639     m_impl = new KrColorCacheImpl;
0640 }
0641 
0642 KrColorCache::~KrColorCache()
0643 {
0644     delete m_impl;
0645 }
0646 
0647 KrColorCache &KrColorCache::getColorCache()
0648 {
0649     if (!m_instance) {
0650         m_instance = new KrColorCache;
0651         m_instance->refreshColors();
0652     }
0653     return *m_instance;
0654 }
0655 
0656 void KrColorCache::getColors(KrColorGroup &result, const KrColorItemType &type) const
0657 {
0658     // for the cache lookup: calculate a unique key from the type
0659     char hashKey[128];
0660     switch (type.m_fileType) {
0661     case KrColorItemType::Directory:
0662         strcpy(hashKey, "Directory");
0663         break;
0664     case KrColorItemType::Executable:
0665         strcpy(hashKey, "Executable");
0666         break;
0667     case KrColorItemType::InvalidSymlink:
0668         strcpy(hashKey, "InvalidSymlink");
0669         break;
0670     case KrColorItemType::Symlink:
0671         strcpy(hashKey, "Symlink");
0672         break;
0673     default:
0674         strcpy(hashKey, "File");
0675     }
0676     if (type.m_activePanel)
0677         strcat(hashKey, "-Active");
0678     if (type.m_alternateBackgroundColor)
0679         strcat(hashKey, "-Alternate");
0680     if (type.m_currentItem)
0681         strcat(hashKey, "-Current");
0682     if (type.m_selectedItem)
0683         strcat(hashKey, "-Selected");
0684 
0685     // lookup in cache
0686     if (!m_impl->m_cachedColors.contains(hashKey))
0687         // not found: calculate color group and store it in cache
0688         m_impl->m_cachedColors[hashKey] = m_impl->getColors(type);
0689 
0690     // get color group from cache
0691     const KrColorGroup &col = m_impl->m_cachedColors[hashKey];
0692 
0693     // copy colors in question to result color group
0694     result.setBackground(col.background());
0695     result.setText(col.text());
0696     result.setHighlightedText(col.highlightedText());
0697     result.setHighlight(col.highlight());
0698 }
0699 
0700 bool KrColorCache::getDimSettings(QColor &dimColor, int &dimFactor)
0701 {
0702     if (m_impl->m_colorSettings.getBoolValue("Dim Inactive Colors", false)) {
0703         dimFactor = m_impl->m_colorSettings.getNumValue("Dim Factor", 100);
0704         dimColor = m_impl->m_colorSettings.getColorValue("Dim Target Color");
0705         if (!dimColor.isValid())
0706             dimColor.setRgb(255, 255, 255);
0707         return dimFactor >= 0 && dimFactor < 100;
0708     }
0709     return false;
0710 }
0711 
0712 QColor KrColorCache::dimColor(const QColor &color, int dim, const QColor &targetColor)
0713 {
0714     return QColor((targetColor.red() * (100 - dim) + color.red() * dim) / 100,
0715                   (targetColor.green() * (100 - dim) + color.green() * dim) / 100,
0716                   (targetColor.blue() * (100 - dim) + color.blue() * dim) / 100);
0717 }
0718 
0719 void KrColorCache::refreshColors()
0720 {
0721     m_impl->m_cachedColors.clear();
0722     m_impl->m_colorSettings = KrColorSettings();
0723     QPixmapCache::clear(); // dimmed icons are cached
0724     emit colorsRefreshed();
0725 }
0726 
0727 void KrColorCache::setColors(const KrColorSettings &colorSettings)
0728 {
0729     m_impl->m_cachedColors.clear();
0730     m_impl->m_colorSettings = colorSettings;
0731     QPixmapCache::clear(); // dimmed icons are cached
0732     emit colorsRefreshed();
0733 }