File indexing completed on 2024-05-12 16:01:59

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2004-08-02
0007  * Description : theme manager
0008  *
0009  * SPDX-FileCopyrightText: 2006-2011 Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "thememanager.h"
0016 // Qt includes
0017 
0018 #include <QStringList>
0019 #include <QFileInfo>
0020 #include <QFile>
0021 #include <QApplication>
0022 #include <QPalette>
0023 #include <QColor>
0024 #include <QActionGroup>
0025 #include <QBitmap>
0026 #include <QPainter>
0027 #include <QPixmap>
0028 #include <QDate>
0029 #include <QDesktopWidget>
0030 #include <QMenu>
0031 #include <QMenuBar>
0032 #include <QStatusBar>
0033 #include <QDebug>
0034 #include <QAction>
0035 #include <QMessageBox>
0036 
0037 // KDE includes
0038 
0039 #include <klocalizedstring.h>
0040 #include <kcolorscheme.h>
0041 #include <kactioncollection.h>
0042 #include <KoResourcePaths.h>
0043 #include <kactionmenu.h>
0044 #include <kconfig.h>
0045 #include <kconfiggroup.h>
0046 
0047 // Calligra
0048 #include <kis_icon.h>
0049 
0050 #ifdef __APPLE__
0051 #include <QStyle>
0052 #endif
0053 
0054 namespace Digikam
0055 {
0056 
0057 // ---------------------------------------------------------------
0058 
0059 
0060 class ThemeManager::ThemeManagerPriv
0061 {
0062 public:
0063 
0064     ThemeManagerPriv()
0065         : themeMenuActionGroup(0)
0066         , themeMenuAction(0)
0067     {
0068     }
0069 
0070     QString                currentThemeName;
0071     QMap<QString, QString> themeMap;            // map<theme name, theme config path>
0072 
0073     QActionGroup*          themeMenuActionGroup;
0074     KActionMenu*           themeMenuAction;
0075 };
0076 
0077 ThemeManager::ThemeManager(const QString &theme, QObject *parent)
0078     : QObject(parent)
0079     , d(new ThemeManagerPriv)
0080 {
0081     //qDebug() << "Creating theme manager with theme" << theme;
0082     d->currentThemeName = theme;
0083     populateThemeMap();
0084 }
0085 
0086 ThemeManager::~ThemeManager()
0087 {
0088     delete d;
0089 }
0090 
0091 QString ThemeManager::currentThemeName() const
0092 {
0093     //qDebug() << "getting current themename";
0094     QString themeName;
0095     if (d->themeMenuAction && d->themeMenuActionGroup) {
0096 
0097         QAction* action = d->themeMenuActionGroup->checkedAction();
0098         if (action) {
0099             themeName = action->text().remove('&');
0100         }
0101 
0102         //qDebug() << "\tthemename from action" << themeName;
0103     }
0104     else if (!d->currentThemeName.isEmpty()) {
0105 
0106         //qDebug() << "\tcurrent themename" << d->currentThemeName;
0107         themeName = d->currentThemeName;
0108     }
0109     if (themeName.isEmpty()) {
0110         //qDebug() << "\tfallback";
0111         themeName = "Krita dark";
0112     }
0113     //qDebug() << "\tresult" << themeName;
0114     return themeName;
0115 }
0116 
0117 void ThemeManager::setCurrentTheme(const QString& name)
0118 {
0119     //qDebug() << "setCurrentTheme();" << d->currentThemeName << "to" << name;
0120     d->currentThemeName = name;
0121 
0122     if (d->themeMenuAction  && d->themeMenuActionGroup) {
0123         QList<QAction*> list = d->themeMenuActionGroup->actions();
0124         Q_FOREACH (QAction* action, list) {
0125             if (action->text().remove('&') == name) {
0126                 action->setChecked(true);
0127             }
0128         }
0129     }
0130     slotChangePalette();
0131 }
0132 
0133 void ThemeManager::slotChangePalette()
0134 {
0135     //qDebug() << "slotChangePalette" << sender();
0136 
0137     // We must clear the icon cache before the palette is changed. That way
0138     // The widgets can change the icons properly when they receive the
0139     // PaletteChange event if needed.
0140     KisIconUtils::clearIconCache();
0141 
0142     QString theme(currentThemeName());
0143     QString filename        = d->themeMap.value(theme);
0144     KSharedConfigPtr config = KSharedConfig::openConfig(filename);
0145 
0146     QPalette palette               = qApp->palette();
0147     QPalette::ColorGroup states[3] = { QPalette::Active, QPalette::Inactive, QPalette::Disabled };
0148     // TT thinks tooltips shouldn't use active, so we use our active colors for all states
0149     KColorScheme schemeTooltip(QPalette::Active, KColorScheme::Tooltip, config);
0150 
0151     for ( int i = 0; i < 3 ; ++i ) {
0152 
0153         QPalette::ColorGroup state = states[i];
0154         KColorScheme schemeView(state,      KColorScheme::View,      config);
0155         KColorScheme schemeWindow(state,    KColorScheme::Window,    config);
0156         KColorScheme schemeButton(state,    KColorScheme::Button,    config);
0157         KColorScheme schemeSelection(state, KColorScheme::Selection, config);
0158 
0159         palette.setBrush(state, QPalette::WindowText,      schemeWindow.foreground());
0160         palette.setBrush(state, QPalette::Window,          schemeWindow.background());
0161         palette.setBrush(state, QPalette::Base,            schemeView.background());
0162         palette.setBrush(state, QPalette::Text,            schemeView.foreground());
0163         palette.setBrush(state, QPalette::Button,          schemeButton.background());
0164         palette.setBrush(state, QPalette::ButtonText,      schemeButton.foreground());
0165         palette.setBrush(state, QPalette::Highlight,       schemeSelection.background());
0166         palette.setBrush(state, QPalette::HighlightedText, schemeSelection.foreground());
0167         palette.setBrush(state, QPalette::ToolTipBase,     schemeTooltip.background());
0168         palette.setBrush(state, QPalette::ToolTipText,     schemeTooltip.foreground());
0169 
0170         palette.setColor(state, QPalette::Light,           schemeWindow.shade(KColorScheme::LightShade));
0171         palette.setColor(state, QPalette::Midlight,        schemeWindow.shade(KColorScheme::MidlightShade));
0172         palette.setColor(state, QPalette::Mid,             schemeWindow.shade(KColorScheme::MidShade));
0173         palette.setColor(state, QPalette::Dark,            schemeWindow.shade(KColorScheme::DarkShade));
0174         palette.setColor(state, QPalette::Shadow,          schemeWindow.shade(KColorScheme::ShadowShade));
0175 
0176         palette.setBrush(state, QPalette::AlternateBase,   schemeView.background(KColorScheme::AlternateBackground));
0177         palette.setBrush(state, QPalette::Link,            schemeView.foreground(KColorScheme::LinkText));
0178         palette.setBrush(state, QPalette::LinkVisited,     schemeView.foreground(KColorScheme::VisitedText));
0179     }
0180     
0181     //qDebug() << ">>>>>>>>>>>>>>>>>> going to set palette on app" << theme;
0182     // hint for the style to synchronize the color scheme with the window manager/compositor
0183     qApp->setProperty("KDE_COLOR_SCHEME_PATH", filename);
0184     qApp->setPalette(palette);
0185 
0186 #ifdef Q_OS_MACOS
0187     if (theme == "Krita bright" || theme.isEmpty()) {
0188         qApp->setStyle("Macintosh");
0189         qApp->style()->polish(qApp);
0190     } else {
0191         qApp->setStyle("Fusion");
0192         qApp->style()->polish(qApp);
0193     }
0194 #endif
0195 
0196     emit signalThemeChanged();
0197 }
0198 
0199 void ThemeManager::setThemeMenuAction(KActionMenu* const action)
0200 {
0201     d->themeMenuAction = action;
0202     populateThemeMenu();
0203 }
0204 
0205 void ThemeManager::registerThemeActions(KisKActionCollection *actionCollection)
0206 {
0207     if (!d->themeMenuAction) return;
0208     actionCollection->addAction("theme_menu", d->themeMenuAction);
0209 }
0210 
0211 void ThemeManager::populateThemeMenu()
0212 {
0213     if (!d->themeMenuAction) return;
0214 
0215     d->themeMenuAction->menu()->clear();
0216     delete d->themeMenuActionGroup;
0217 
0218     d->themeMenuActionGroup = new QActionGroup(d->themeMenuAction);
0219     connect(d->themeMenuActionGroup, SIGNAL(triggered(QAction*)),
0220             this, SLOT(slotChangePalette()));
0221 
0222     QAction * action;
0223     QStringList schemeFiles = KoResourcePaths::findAllAssets("data", "color-schemes/*.colors");
0224     schemeFiles += KoResourcePaths::findAllAssets("genericdata", "color-schemes/*.colors");
0225 
0226     QMap<QString, QAction*> actionMap;
0227     for (int i = 0; i < schemeFiles.size(); ++i) {
0228         const QString filename  = schemeFiles.at(i);
0229         const QFileInfo info(filename);
0230         KSharedConfigPtr config = KSharedConfig::openConfig(filename);
0231         QIcon icon = createSchemePreviewIcon(config);
0232         KConfigGroup group(config, "General");
0233         const QString name = group.readEntry("Name", info.completeBaseName());
0234         action = new QAction(name, d->themeMenuActionGroup);
0235         action->setIcon(icon);
0236         action->setCheckable(true);
0237         actionMap.insert(name, action);
0238     }
0239 
0240     // sort the list
0241     QStringList actionMapKeys = actionMap.keys();
0242     actionMapKeys.sort();
0243 
0244     Q_FOREACH (const QString& name, actionMapKeys) {
0245         if ( name ==  currentThemeName()) {
0246             actionMap.value(name)->setChecked(true);
0247         }
0248         d->themeMenuAction->addAction(actionMap.value(name));
0249     }
0250 }
0251 
0252 QPixmap ThemeManager::createSchemePreviewIcon(const KSharedConfigPtr& config)
0253 {
0254     // code taken from kdebase/workspace/kcontrol/colors/colorscm.cpp
0255     const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b };
0256     const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff };
0257     const QSize bitsSize(24, 2);
0258     const QBitmap b1    = QBitmap::fromData(bitsSize, bits1);
0259     const QBitmap b2    = QBitmap::fromData(bitsSize, bits2);
0260 
0261     QPixmap pixmap(23, 16);
0262     pixmap.fill(Qt::black); // FIXME use some color other than black for borders?
0263 
0264     KConfigGroup group(config, "WM");
0265     QPainter p(&pixmap);
0266     KColorScheme windowScheme(QPalette::Active, KColorScheme::Window, config);
0267     p.fillRect(1,  1, 7, 7, windowScheme.background());
0268     p.fillRect(2,  2, 5, 2, QBrush(windowScheme.foreground().color(), b1));
0269 
0270     KColorScheme buttonScheme(QPalette::Active, KColorScheme::Button, config);
0271     p.fillRect(8,  1, 7, 7, buttonScheme.background());
0272     p.fillRect(9,  2, 5, 2, QBrush(buttonScheme.foreground().color(), b1));
0273 
0274     p.fillRect(15,  1, 7, 7, group.readEntry("activeBackground", QColor(96, 148, 207)));
0275     p.fillRect(16,  2, 5, 2, QBrush(group.readEntry("activeForeground", QColor(255, 255, 255)), b1));
0276 
0277     KColorScheme viewScheme(QPalette::Active, KColorScheme::View, config);
0278     p.fillRect(1,  8, 7, 7, viewScheme.background());
0279     p.fillRect(2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2));
0280 
0281     KColorScheme selectionScheme(QPalette::Active, KColorScheme::Selection, config);
0282     p.fillRect(8,  8, 7, 7, selectionScheme.background());
0283     p.fillRect(9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2));
0284 
0285     p.fillRect(15,  8, 7, 7, group.readEntry("inactiveBackground", QColor(224, 223, 222)));
0286     p.fillRect(16, 12, 5, 2, QBrush(group.readEntry("inactiveForeground", QColor(20, 19, 18)), b2));
0287 
0288     p.end();
0289     return pixmap;
0290 }
0291 
0292 void ThemeManager::populateThemeMap()
0293 {
0294     QStringList schemeFiles = KoResourcePaths::findAllAssets("data", "color-schemes/*.colors");
0295     schemeFiles += KoResourcePaths::findAllAssets("genericdata", "color-schemes/*.colors");
0296     
0297     for (int i = 0; i < schemeFiles.size(); ++i) {
0298         const QString filename  = schemeFiles.at(i);
0299         const QFileInfo info(filename);
0300         KSharedConfigPtr config = KSharedConfig::openConfig(filename);
0301         KConfigGroup group(config, "General");
0302         const QString name = group.readEntry("Name", info.completeBaseName());
0303         d->themeMap.insert(name, filename);
0304     }
0305 
0306 
0307 }
0308 
0309 }  // namespace Digikam