1 /*****************************************************************************
2  * Copyright (C) 2000-2002 Shie Erlich <erlich@users.sourceforge.net>        *
3  * Copyright (C) 2000-2002 Rafi Yanai <yanai@users.sourceforge.net>          *
4  * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org]              *
5  *                                                                           *
6  * This file is part of Krusader [https://krusader.org].                     *
7  *                                                                           *
8  * Krusader is free software: you can redistribute it and/or modify          *
9  * it under the terms of the GNU General Public License as published by      *
10  * the Free Software Foundation, either version 2 of the License, or         *
11  * (at your option) any later version.                                       *
12  *                                                                           *
13  * Krusader is distributed in the hope that it will be useful,               *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
16  * GNU General Public License for more details.                              *
17  *                                                                           *
18  * You should have received a copy of the GNU General Public License         *
19  * along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
20  *****************************************************************************/
21 
22 #include "krcolorcache.h"
23 #include "../krglobal.h"
24 #include "../defaults.h"
25 
26 // QtCore
27 #include <QDebug>
28 #include <QFile>
29 #include <QList>
30 // QtGui
31 #include <QPixmapCache>
32 // QtWidgets
33 #include <QApplication>
34 
35 #include <KConfigCore/KSharedConfig>
36 
37 // Macro: set target = col, if col is valid
38 #define SETCOLOR(target, col) { if (col.isValid()) target = col; }
39 
40 // Static class, which lists all allowed keywords for a quick access.
41 // Just call a method to initialize it.
42 class KrColorSettingNames
43 {
44     static QMap<QString, bool> s_colorNames;
45     static QMap<QString, bool> s_numNames;
46     static QMap<QString, bool> s_boolNames;
47     static void initialize();
48 public:
49     static QStringList getColorNames();
50     static bool isColorNameValid(const QString & settingName);
51     static QStringList getNumNames();
52     static bool isNumNameValid(const QString & settingName);
53     static QStringList getBoolNames();
54     static bool isBoolNameValid(const QString & settingName);
55 } krColorSettingNames;
56 
57 QMap<QString, bool> KrColorSettingNames::s_colorNames;
58 QMap<QString, bool> KrColorSettingNames::s_numNames;
59 QMap<QString, bool> KrColorSettingNames::s_boolNames;
60 
initialize()61 void KrColorSettingNames::initialize()
62 {
63     if (!s_colorNames.empty())
64         return;
65     s_colorNames["Foreground"] = true;
66     s_colorNames["Inactive Foreground"] = true;
67     s_colorNames["Directory Foreground"] = true;
68     s_colorNames["Inactive Directory Foreground"] = true;
69     s_colorNames["Executable Foreground"] = true;
70     s_colorNames["Inactive Executable Foreground"] = true;
71     s_colorNames["Symlink Foreground"] = true;
72     s_colorNames["Inactive Symlink Foreground"] = true;
73     s_colorNames["Invalid Symlink Foreground"] = true;
74     s_colorNames["Inactive Invalid Symlink Foreground"] = true;
75     s_colorNames["Marked Foreground"] = true;
76     s_colorNames["Inactive Marked Foreground"] = true;
77     s_colorNames["Marked Background"] = true;
78     s_colorNames["Inactive Marked Background"] = true;
79     s_colorNames["Current Foreground"] = true;
80     s_colorNames["Inactive Current Foreground"] = true;
81     s_colorNames["Current Background"] = true;
82     s_colorNames["Inactive Current Background"] = true;
83     s_colorNames["Marked Current Foreground"] = true;
84     s_colorNames["Inactive Marked Current Foreground"] = true;
85     s_colorNames["Alternate Background"] = true;
86     s_colorNames["Inactive Alternate Background"] = true;
87     s_colorNames["Background"] = true;
88     s_colorNames["Inactive Background"] = true;
89     s_colorNames["Alternate Marked Background"] = true;
90     s_colorNames["Inactive Alternate Marked Background"] = true;
91     s_colorNames["Dim Target Color"] = true;
92 
93     s_numNames["Dim Factor"] = true;
94 
95     s_boolNames["KDE Default"] = true;
96     s_boolNames["Enable Alternate Background"] = true;
97     s_boolNames["Show Current Item Always"] = true;
98     s_boolNames["Dim Inactive Colors"] = true;
99 }
100 
getColorNames()101 QStringList KrColorSettingNames::getColorNames()
102 {
103     initialize();
104     return s_colorNames.keys();
105 }
106 
isColorNameValid(const QString & settingName)107 bool KrColorSettingNames::isColorNameValid(const QString & settingName)
108 {
109     initialize();
110     return s_colorNames.contains(settingName);
111 }
112 
getNumNames()113 QStringList KrColorSettingNames::getNumNames()
114 {
115     initialize();
116     return s_numNames.keys();
117 }
118 
isNumNameValid(const QString & settingName)119 bool KrColorSettingNames::isNumNameValid(const QString & settingName)
120 {
121     initialize();
122     return s_numNames.contains(settingName);
123 }
124 
getBoolNames()125 QStringList KrColorSettingNames::getBoolNames()
126 {
127     initialize();
128     return s_boolNames.keys();
129 }
130 
isBoolNameValid(const QString & settingName)131 bool KrColorSettingNames::isBoolNameValid(const QString & settingName)
132 {
133     initialize();
134     return s_boolNames.contains(settingName);
135 }
136 
137 
138 
139 /*
140 KrColorSettings implementation. Contains all properties in QMaps. loadFromConfig initializes them from krConfig.
141 */
142 class KrColorSettingsImpl
143 {
144     friend class KrColorSettings;
145     QMap<QString, QString> m_colorTextValues;
146     QMap<QString, QColor> m_colorValues;
147     QMap<QString, int> m_numValues;
148     QMap<QString, bool> m_boolValues;
149     void loadFromConfig();
150 };
151 
loadFromConfig()152 void KrColorSettingsImpl::loadFromConfig()
153 {
154     KConfigGroup group(krConfig, "Colors");
155     QStringList names = KrColorSettingNames::getColorNames();
156     for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) {
157         m_colorTextValues[*it] = group.readEntry(*it, QString());
158         if (m_colorTextValues[*it].count(',') == 2)
159             m_colorValues[*it] = group.readEntry(*it, QColor());
160         else
161             m_colorValues[*it] = QColor();
162     }
163     names = KrColorSettingNames::getNumNames();
164     for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) {
165         if (!group.readEntry(*it, QString()).isEmpty()) {
166             m_numValues[*it] = group.readEntry(*it, (long long)0);
167         }
168     }
169     names = KrColorSettingNames::getBoolNames();
170     for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) {
171         if (!group.readEntry(*it, QString()).isEmpty()) {
172             m_boolValues[*it] = group.readEntry(*it, false);
173         }
174     }
175 }
176 
KrColorSettings()177 KrColorSettings::KrColorSettings()
178 {
179     m_impl = new KrColorSettingsImpl();
180     m_impl->loadFromConfig();
181 }
182 
KrColorSettings(const KrColorSettings & src)183 KrColorSettings::KrColorSettings(const KrColorSettings & src)
184 {
185     m_impl = new KrColorSettingsImpl();
186     operator =(src);
187 }
188 
~KrColorSettings()189 KrColorSettings::~KrColorSettings()
190 {
191     delete m_impl;
192 }
193 
operator =(const KrColorSettings & src)194 const KrColorSettings & KrColorSettings::operator= (const KrColorSettings & src)
195 {
196     if (this == & src)
197         return * this;
198     QStringList names = KrColorSettingNames::getColorNames();
199     for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) {
200         m_impl->m_colorTextValues[*it] = src.m_impl->m_colorTextValues[*it];
201         m_impl->m_colorValues[*it] = src.m_impl->m_colorValues[*it];
202     }
203     for (QMap<QString, int>::Iterator it = src.m_impl->m_numValues.begin(); it != src.m_impl->m_numValues.end(); ++it) {
204         m_impl->m_numValues[it.key()] = it.value();
205     }
206     for (QMap<QString, bool>::Iterator it = src.m_impl->m_boolValues.begin(); it != src.m_impl->m_boolValues.end(); ++it) {
207         m_impl->m_boolValues[it.key()] = it.value();
208     }
209     return * this;
210 }
211 
getColorNames()212 QList<QString> KrColorSettings::getColorNames()
213 {
214     return KrColorSettingNames::getColorNames();
215 }
216 
isColorNameValid(const QString & settingName)217 bool KrColorSettings::isColorNameValid(const QString & settingName)
218 {
219     return KrColorSettingNames::isColorNameValid(settingName);
220 }
221 
setColorValue(const QString & settingName,const QColor & color)222 bool KrColorSettings::setColorValue(const QString & settingName, const QColor & color)
223 {
224     if (!isColorNameValid(settingName)) {
225         qWarning() << "Invalid color setting name: " << settingName;
226         return false;
227     }
228     m_impl->m_colorValues[settingName] = color;
229     return true;
230 }
231 
getColorValue(const QString & settingName) const232 QColor KrColorSettings::getColorValue(const QString & settingName) const
233 {
234     if (!isColorNameValid(settingName)) {
235         qWarning() << "Invalid color setting name: " << settingName;
236         return QColor();
237     }
238     return m_impl->m_colorValues[settingName];
239 }
240 
setColorTextValue(const QString & settingName,const QString & colorText)241 bool KrColorSettings::setColorTextValue(const QString & settingName, const QString & colorText)
242 {
243     if (!isColorNameValid(settingName)) {
244         qWarning() << "Invalid color setting name: " << settingName;
245         return false;
246     }
247     m_impl->m_colorTextValues[settingName] = colorText;
248     return true;
249 }
250 
getColorTextValue(const QString & settingName) const251 QString KrColorSettings::getColorTextValue(const QString & settingName) const
252 {
253     if (!isColorNameValid(settingName)) {
254         qWarning() << "Invalid color setting name: " << settingName;
255         return QString();
256     }
257     return m_impl->m_colorTextValues[settingName];
258 }
259 
getNumNames()260 QList<QString> KrColorSettings::getNumNames()
261 {
262     return KrColorSettingNames::getNumNames();
263 }
264 
isNumNameValid(const QString & settingName)265 bool KrColorSettings::isNumNameValid(const QString & settingName)
266 {
267     return KrColorSettingNames::isNumNameValid(settingName);
268 }
269 
setNumValue(const QString & settingName,int value)270 bool KrColorSettings::setNumValue(const QString & settingName, int value)
271 {
272     if (!isNumNameValid(settingName)) {
273         qWarning() << "Invalid number setting name: " << settingName;
274         return false;
275     }
276     m_impl->m_numValues[settingName] = value;
277     return true;
278 }
279 
getNumValue(const QString & settingName,int defaultValue) const280 int KrColorSettings::getNumValue(const QString & settingName, int defaultValue) const
281 {
282     if (!isNumNameValid(settingName)) {
283         qWarning() << "Invalid number setting name: " << settingName;
284         return 0;
285     }
286     if (!m_impl->m_numValues.contains(settingName))
287         return defaultValue;
288     return m_impl->m_numValues[settingName];
289 }
290 
getBoolNames()291 QList<QString> KrColorSettings::getBoolNames()
292 {
293     return KrColorSettingNames::getBoolNames();
294 }
295 
isBoolNameValid(const QString & settingName)296 bool KrColorSettings::isBoolNameValid(const QString & settingName)
297 {
298     return KrColorSettingNames::isBoolNameValid(settingName);
299 }
300 
setBoolValue(const QString & settingName,bool value)301 bool KrColorSettings::setBoolValue(const QString & settingName, bool value)
302 {
303     if (!isBoolNameValid(settingName)) {
304         qWarning() << "Invalid bool setting name: " << settingName;
305         return false;
306     }
307     m_impl->m_boolValues[settingName] = value;
308     return true;
309 }
310 
getBoolValue(const QString & settingName,bool defaultValue) const311 int KrColorSettings::getBoolValue(const QString & settingName, bool defaultValue) const
312 {
313     if (!isBoolNameValid(settingName)) {
314         qWarning() << "Invalid bool setting name: " << settingName;
315         return false;
316     }
317     if (!m_impl->m_boolValues.contains(settingName))
318         return defaultValue;
319     return m_impl->m_boolValues[settingName];
320 }
321 
322 
323 
KrColorItemType()324 KrColorItemType::KrColorItemType()
325 {
326     m_fileType = File;
327     m_alternateBackgroundColor = false;
328     m_activePanel = false;
329     m_currentItem = false;
330     m_selectedItem = false;
331 }
332 
KrColorItemType(FileType type,bool alternateBackgroundColor,bool activePanel,bool currentItem,bool selectedItem)333 KrColorItemType::KrColorItemType(FileType type, bool alternateBackgroundColor, bool activePanel, bool currentItem, bool selectedItem)
334 {
335     m_fileType = type;
336     m_alternateBackgroundColor = alternateBackgroundColor;
337     m_activePanel = activePanel;
338     m_currentItem = currentItem;
339     m_selectedItem = selectedItem;
340 }
341 
KrColorItemType(const KrColorItemType & src)342 KrColorItemType::KrColorItemType(const KrColorItemType & src)
343 {
344     operator= (src);
345 }
346 
operator =(const KrColorItemType & src)347 const KrColorItemType & KrColorItemType::operator= (const KrColorItemType & src)
348 {
349     if (this == & src)
350         return * this;
351     m_fileType = src.m_fileType;
352     m_alternateBackgroundColor = src.m_alternateBackgroundColor;
353     m_activePanel = src.m_activePanel;
354     m_currentItem = src.m_currentItem;
355     m_selectedItem = src.m_selectedItem;
356     return * this;
357 }
358 
359 
360 
361 
362 /*
363 KrColorCache implementation. Contains the KrColorSettings used for teh calculation and the cache for the results.
364 getColors is the only method to call. All other are taken from the previous versions.
365 */
366 class KrColorCacheImpl
367 {
368     friend class KrColorCache;
369     QMap<QString, KrColorGroup> m_cachedColors;
370     KrColorSettings m_colorSettings;
371 
372     KrColorGroup getColors(const KrColorItemType & type) const;
373     static const QColor & setColorIfContrastIsSufficient(const QColor & background, const QColor & color1, const QColor & color2);
374     QColor getForegroundColor(bool isActive) const;
375     QColor getSpecialForegroundColor(const QString & type, bool isActive) const;
376     QColor getBackgroundColor(bool isActive) const;
377     QColor getAlternateBackgroundColor(bool isActive) const;
378     QColor getMarkedForegroundColor(bool isActive) const;
379     QColor getMarkedBackgroundColor(bool isActive) const;
380     QColor getAlternateMarkedBackgroundColor(bool isActive) const;
381     QColor getCurrentForegroundColor(bool isActive) const;
382     QColor getCurrentBackgroundColor(bool isActive) const;
383     QColor getCurrentMarkedForegroundColor(bool isActive) const;
384     QColor dimColor(QColor color, bool isBackgroundColor) const;
385 };
386 
getColors(const KrColorItemType & type) const387 KrColorGroup KrColorCacheImpl::getColors(const KrColorItemType & type) const
388 {
389     KrColorGroup result;
390     if (m_colorSettings.getBoolValue("KDE Default", _KDEDefaultColors)) {
391         bool enableAlternateBackground = m_colorSettings.getBoolValue("Enable Alternate Background", _AlternateBackground);
392 
393         QPalette p = QGuiApplication::palette();
394         QColor background = enableAlternateBackground && type.m_alternateBackgroundColor ?
395                     p.color(QPalette::Active, QPalette::Base)
396                   :  p.color(QPalette::Active, QPalette::AlternateBase);
397         result.setBackground(background);
398         result.setText(p.color(QPalette::Active, QPalette::Text));
399         result.setHighlight(p.color(QPalette::Active, QPalette::Highlight));
400         result.setHighlightedText(p.color(QPalette::Active, QPalette::HighlightedText));
401 
402         /*  if (type.m_currentItem && type.m_activePanel)
403           {
404            QColor currentBackground = KColorScheme(QPalette::Active).background(KColorScheme::ActiveBackground).color();
405            // set the background
406            result.setColor(QColorGroup::Highlight, currentBackground);
407            result.setColor(QColorGroup::Base, currentBackground);
408            result.setColor(QColorGroup::Background, currentBackground);
409           }*/
410         return result;
411     }
412     bool markCurrentAlways = m_colorSettings.getBoolValue("Show Current Item Always", _ShowCurrentItemAlways);
413     bool dimBackground = m_colorSettings.getBoolValue("Dim Inactive Colors", false);
414 
415     // cache m_activePanel flag. If color dimming is turned on, it is set to true, as the inactive colors
416     // are calculated from the active ones at the end.
417     bool isActive = type.m_activePanel;
418     if (dimBackground)
419         isActive = true;
420 
421     // First calculate fore- and background.
422     QColor background = type.m_alternateBackgroundColor ?
423                         getAlternateBackgroundColor(isActive)
424                         : getBackgroundColor(isActive);
425     QColor foreground;
426     switch (type.m_fileType) {
427     case KrColorItemType::Directory :
428         foreground = getSpecialForegroundColor("Directory", isActive);
429         break;
430     case KrColorItemType::Executable :
431         foreground = getSpecialForegroundColor("Executable", isActive);
432         break;
433     case KrColorItemType::InvalidSymlink :
434         foreground = getSpecialForegroundColor("Invalid Symlink", isActive);
435         break;
436     case KrColorItemType::Symlink :
437         foreground = getSpecialForegroundColor("Symlink", isActive);
438         break;
439     default:
440         foreground = getForegroundColor(isActive);
441     }
442 
443     // set the background color
444     result.setBackground(background);
445 
446     // set the foreground color
447     result.setText(foreground);
448 
449     // now the color of a marked item
450     QColor markedBackground = type.m_alternateBackgroundColor ?
451                               getAlternateMarkedBackgroundColor(isActive)
452                               : getMarkedBackgroundColor(isActive);
453     QColor markedForeground = getMarkedForegroundColor(isActive);
454     if (!markedForeground.isValid()) // transparent
455         // choose fore- or background, depending on its contrast compared to markedBackground
456         markedForeground = setColorIfContrastIsSufficient(markedBackground, foreground, background);
457 
458     // set it in the color group (different group color than normal foreground!)
459     result.setHighlightedText(markedForeground);
460     result.setHighlight(markedBackground);
461 
462     // In case the current item is a selected one, set the fore- and background colors for the contrast calculation below
463     if (type.m_selectedItem) {
464         background = markedBackground;
465         foreground = markedForeground;
466     }
467 
468     // finally the current item
469     if (type.m_currentItem && (markCurrentAlways || isActive)) {
470         // if this is the current item AND the panels has the focus OR the current should be marked always
471         QColor currentBackground = getCurrentBackgroundColor(isActive);
472 
473         if (!currentBackground.isValid()) // transparent
474             currentBackground = background;
475 
476         // set the background
477         result.setHighlight(currentBackground);
478         result.setBackground(currentBackground);
479 
480         QColor color;
481         if (type.m_selectedItem)
482             color = getCurrentMarkedForegroundColor(isActive);
483         if (!color.isValid()) { // not used
484             color = getCurrentForegroundColor(isActive);
485             if (!color.isValid()) // transparent
486                 // choose fore- or background, depending on its contrast compared to markedBackground
487                 color = setColorIfContrastIsSufficient(currentBackground, foreground, background);
488         }
489 
490         // set the foreground
491         result.setText(color);
492         result.setHighlightedText(color);
493     }
494 
495     if (dimBackground && !type.m_activePanel) {
496         // if color dimming is chosen, dim the colors for the inactive panel
497         result.setBackground(dimColor(result.background(), true));
498         result.setText(dimColor(result.text(), false));
499         result.setHighlightedText(dimColor(result.highlightedText(), false));
500         result.setHighlight(dimColor(result.highlight(), true));
501     }
502     return result;
503 }
504 
setColorIfContrastIsSufficient(const QColor & background,const QColor & color1,const QColor & color2)505 const QColor & KrColorCacheImpl::setColorIfContrastIsSufficient(const QColor & background, const QColor & color1, const QColor & color2)
506 {
507 #define sqr(x) ((x)*(x))
508     int contrast = sqr(color1.red() - background.red()) + sqr(color1.green() - background.green()) + sqr(color1.blue() - background.blue());
509 
510     // if the contrast between background and color1 is too small, take color2 instead.
511     if (contrast < 1000)
512         return color2;
513     return color1;
514 }
515 
getForegroundColor(bool isActive) const516 QColor KrColorCacheImpl::getForegroundColor(bool isActive) const
517 {
518     QPalette p = QGuiApplication::palette();
519     QColor color = p.color(QPalette::Active, QPalette::Text);
520     SETCOLOR(color, m_colorSettings.getColorValue("Foreground"));
521     if (!isActive) SETCOLOR(color, m_colorSettings.getColorValue("Inactive Foreground"));
522     return color;
523 }
524 
getSpecialForegroundColor(const QString & type,bool isActive) const525 QColor KrColorCacheImpl::getSpecialForegroundColor(const QString & type, bool isActive) const
526 {
527     QString colorName = "Inactive " + type + " Foreground";
528     if (!isActive && m_colorSettings.getColorTextValue(colorName) == "Inactive Foreground")
529         return getForegroundColor(false);
530     QColor color = m_colorSettings.getColorValue(type + " Foreground");
531     if (!isActive) SETCOLOR(color, m_colorSettings.getColorValue(colorName));
532     if (!color.isValid())
533         return getForegroundColor(isActive);
534     return color;
535 }
536 
getBackgroundColor(bool isActive) const537 QColor KrColorCacheImpl::getBackgroundColor(bool isActive) const
538 {
539     QColor color = QGuiApplication::palette().color(QPalette::Active, QPalette::Base);
540     SETCOLOR(color, m_colorSettings.getColorValue("Background"));
541     if (!isActive) SETCOLOR(color, m_colorSettings.getColorValue("Inactive Background"));
542     return color;
543 }
544 
getAlternateBackgroundColor(bool isActive) const545 QColor KrColorCacheImpl::getAlternateBackgroundColor(bool isActive) const
546 {
547     QPalette p = QGuiApplication::palette();
548     if (isActive && m_colorSettings.getColorTextValue("Alternate Background") == "Background")
549         return getBackgroundColor(true);
550     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Background").isEmpty())
551         return getAlternateBackgroundColor(true);
552     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Background") == "Inactive Background")
553         return getBackgroundColor(false);
554     QColor color = isActive ?
555                    m_colorSettings.getColorValue("Alternate Background")
556                    : m_colorSettings.getColorValue("Inactive Alternate Background");
557     if (!color.isValid())
558         color = p.color(QPalette::Active, QPalette::AlternateBase);
559     if (!color.isValid())
560         color = p.color(QPalette::Active, QPalette::Base);
561     return color;
562 }
563 
getMarkedForegroundColor(bool isActive) const564 QColor KrColorCacheImpl::getMarkedForegroundColor(bool isActive) const
565 {
566     QString colorName = isActive ? "Marked Foreground" : "Inactive Marked Foreground";
567     if (m_colorSettings.getColorTextValue(colorName) == "transparent")
568         return QColor();
569     if (isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
570         return QGuiApplication::palette().color(QPalette::Active, QPalette::HighlightedText);
571     if (!isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
572         return getMarkedForegroundColor(true);
573     return m_colorSettings.getColorValue(colorName);
574 }
575 
getMarkedBackgroundColor(bool isActive) const576 QColor KrColorCacheImpl::getMarkedBackgroundColor(bool isActive) const
577 {
578     if (isActive && m_colorSettings.getColorTextValue("Marked Background").isEmpty())
579         return QGuiApplication::palette().color(QPalette::Active, QPalette::Highlight);
580     if (isActive && m_colorSettings.getColorTextValue("Marked Background") == "Background")
581         return getBackgroundColor(true);
582     if (!isActive && m_colorSettings.getColorTextValue("Inactive Marked Background").isEmpty())
583         return getMarkedBackgroundColor(true);
584     if (!isActive && m_colorSettings.getColorTextValue("Inactive Marked Background") == "Inactive Background")
585         return getBackgroundColor(false);
586     return isActive ?
587            m_colorSettings.getColorValue("Marked Background")
588            : m_colorSettings.getColorValue("Inactive Marked Background");
589 }
590 
getAlternateMarkedBackgroundColor(bool isActive) const591 QColor KrColorCacheImpl::getAlternateMarkedBackgroundColor(bool isActive) const
592 {
593     if (isActive && m_colorSettings.getColorTextValue("Alternate Marked Background") == "Alternate Background")
594         return getAlternateBackgroundColor(true);
595     if (isActive && m_colorSettings.getColorTextValue("Alternate Marked Background").isEmpty())
596         return getMarkedBackgroundColor(true);
597     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Marked Background").isEmpty())
598         return getAlternateMarkedBackgroundColor(true);
599     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Marked Background") == "Inactive Alternate Background")
600         return getAlternateBackgroundColor(false);
601     if (!isActive && m_colorSettings.getColorTextValue("Inactive Alternate Marked Background") == "Inactive Marked Background")
602         return getMarkedBackgroundColor(false);
603     return isActive ?
604            m_colorSettings.getColorValue("Alternate Marked Background")
605            : m_colorSettings.getColorValue("Inactive Alternate Marked Background");
606 }
607 
getCurrentForegroundColor(bool isActive) const608 QColor KrColorCacheImpl::getCurrentForegroundColor(bool isActive) const
609 {
610     QColor color = m_colorSettings.getColorValue("Current Foreground");
611     if (!isActive) SETCOLOR(color, m_colorSettings.getColorValue("Inactive Current Foreground"));
612     return color;
613 }
614 
getCurrentBackgroundColor(bool isActive) const615 QColor KrColorCacheImpl::getCurrentBackgroundColor(bool isActive) const
616 {
617     if (isActive && m_colorSettings.getColorTextValue("Current Background").isEmpty())
618         return QColor();
619     if (isActive && m_colorSettings.getColorTextValue("Current Background") == "Background")
620         return getBackgroundColor(true);
621     if (!isActive && m_colorSettings.getColorTextValue("Inactive Current Background").isEmpty())
622         return getCurrentBackgroundColor(true);
623     if (!isActive && m_colorSettings.getColorTextValue("Inactive Current Background") == "Inactive Background")
624         return getBackgroundColor(false);
625     return isActive ?
626            m_colorSettings.getColorValue("Current Background")
627            : m_colorSettings.getColorValue("Inactive Current Background");
628 }
629 
getCurrentMarkedForegroundColor(bool isActive) const630 QColor KrColorCacheImpl::getCurrentMarkedForegroundColor(bool isActive) const
631 {
632     QString colorName = isActive ? "Marked Current Foreground" : "Inactive Marked Current Foreground";
633     if (isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
634         return QColor();
635     if (isActive && m_colorSettings.getColorTextValue(colorName) == "Marked Foreground")
636         return getMarkedForegroundColor(true);
637     if (!isActive && m_colorSettings.getColorTextValue(colorName).isEmpty())
638         return getCurrentMarkedForegroundColor(true);
639     if (!isActive && m_colorSettings.getColorTextValue(colorName) == "Inactive Marked Foreground")
640         return getMarkedForegroundColor(false);
641     return m_colorSettings.getColorValue(colorName);
642 }
643 
dimColor(QColor color,bool) const644 QColor KrColorCacheImpl::dimColor(QColor color, bool /* isBackgroundColor */) const
645 {
646     int dimFactor = m_colorSettings.getNumValue("Dim Factor", 100);
647     QColor targetColor = m_colorSettings.getColorValue("Dim Target Color");
648     if (!targetColor.isValid())
649         targetColor = QColor(255, 255, 255);
650     bool dimBackground = m_colorSettings.getBoolValue("Dim Inactive Colors", false);
651     bool dim = dimFactor >= 0 && dimFactor < 100 && dimBackground;
652     if (dim)
653         color = KrColorCache::dimColor(color, dimFactor, targetColor);
654     return color;
655 }
656 
657 
658 
659 
660 
661 
662 KrColorCache * KrColorCache::m_instance = 0;
663 
KrColorCache()664 KrColorCache::KrColorCache()
665 {
666     m_impl = new KrColorCacheImpl;
667 }
668 
~KrColorCache()669 KrColorCache::~KrColorCache()
670 {
671     delete m_impl;
672 }
673 
getColorCache()674 KrColorCache & KrColorCache::getColorCache()
675 {
676     if (!m_instance) {
677         m_instance = new KrColorCache;
678         m_instance->refreshColors();
679     }
680     return * m_instance;
681 }
682 
getColors(KrColorGroup & result,const KrColorItemType & type) const683 void KrColorCache::getColors(KrColorGroup  & result, const KrColorItemType & type) const
684 {
685     // for the cache lookup: calculate a unique key from the type
686     char hashKey[128];
687     switch (type.m_fileType) {
688     case KrColorItemType::Directory :
689         strcpy(hashKey, "Directory");
690         break;
691     case KrColorItemType::Executable :
692         strcpy(hashKey, "Executable");
693         break;
694     case KrColorItemType::InvalidSymlink :
695         strcpy(hashKey, "InvalidSymlink");
696         break;
697     case KrColorItemType::Symlink :
698         strcpy(hashKey, "Symlink");
699         break;
700     default:
701         strcpy(hashKey, "File");
702     }
703     if (type.m_activePanel)
704         strcat(hashKey, "-Active");
705     if (type.m_alternateBackgroundColor)
706         strcat(hashKey, "-Alternate");
707     if (type.m_currentItem)
708         strcat(hashKey, "-Current");
709     if (type.m_selectedItem)
710         strcat(hashKey, "-Selected");
711 
712     // lookup in cache
713     if (!m_impl->m_cachedColors.contains(hashKey))
714         // not found: calculate color group and store it in cache
715         m_impl->m_cachedColors[hashKey] = m_impl->getColors(type);
716 
717     // get color group from cache
718     const KrColorGroup & col = m_impl->m_cachedColors[hashKey];
719 
720     // copy colors in question to result color group
721     result.setBackground(col.background());
722     result.setText(col.text());
723     result.setHighlightedText(col.highlightedText());
724     result.setHighlight(col.highlight());
725 }
726 
getDimSettings(QColor & dimColor,int & dimFactor)727 bool KrColorCache::getDimSettings(QColor & dimColor, int & dimFactor)
728 {
729     if (m_impl->m_colorSettings.getBoolValue("Dim Inactive Colors", false)) {
730         dimFactor = m_impl->m_colorSettings.getNumValue("Dim Factor", 100);
731         dimColor = m_impl->m_colorSettings.getColorValue("Dim Target Color");
732         if (!dimColor.isValid())
733             dimColor.setRgb(255, 255, 255);
734         return dimFactor >= 0 && dimFactor < 100;
735     }
736     return false;
737 }
738 
dimColor(const QColor & color,int dim,const QColor & targetColor)739 QColor KrColorCache::dimColor(const QColor & color, int dim, const QColor & targetColor)
740 {
741     return QColor((targetColor.red() * (100 - dim) + color.red() * dim) / 100,
742                   (targetColor.green() * (100 - dim) + color.green() * dim) / 100,
743                   (targetColor.blue() * (100 - dim) + color.blue() * dim) / 100);
744 }
745 
refreshColors()746 void KrColorCache::refreshColors()
747 {
748     m_impl->m_cachedColors.clear();
749     m_impl->m_colorSettings = KrColorSettings();
750     QPixmapCache::clear(); // dimmed icons are cached
751     colorsRefreshed();
752 }
753 
setColors(const KrColorSettings & colorSettings)754 void KrColorCache::setColors(const KrColorSettings & colorSettings)
755 {
756     m_impl->m_cachedColors.clear();
757     m_impl->m_colorSettings = colorSettings;
758     QPixmapCache::clear(); // dimmed icons are cached
759     colorsRefreshed();
760 }
761 
762