File indexing completed on 2024-04-21 14:54:18

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kcolorschememanager.h"
0009 #include "kcolorschememanager_p.h"
0010 
0011 #include "kcolorschememenu.h"
0012 #include "kcolorschememodel.h"
0013 
0014 #include <KActionMenu>
0015 #include <KConfigGroup>
0016 #include <KLocalizedString>
0017 #include <KSharedConfig>
0018 #include <kcolorscheme.h>
0019 
0020 #include <QApplication>
0021 #include <QDir>
0022 #include <QFileInfo>
0023 #include <QIcon>
0024 #include <QMenu>
0025 #include <QPainter>
0026 #include <QStandardPaths>
0027 
0028 constexpr int defaultSchemeRow = 0;
0029 
0030 void KColorSchemeManagerPrivate::activateSchemeInternal(const QString &colorSchemePath)
0031 {
0032     // hint for plasma-integration to synchronize the color scheme with the window manager/compositor
0033     // The property needs to be set before the palette change because is is checked upon the
0034     // ApplicationPaletteChange event.
0035     qApp->setProperty("KDE_COLOR_SCHEME_PATH", colorSchemePath);
0036     if (colorSchemePath.isEmpty()) {
0037         qApp->setPalette(KColorScheme::createApplicationPalette(KSharedConfig::Ptr(nullptr)));
0038     } else {
0039         qApp->setPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(colorSchemePath)));
0040     }
0041 }
0042 
0043 // The meaning of the Default entry depends on the platform
0044 // On Windows and macOS we automatically apply Breeze/Breeze Dark depending on the system preference
0045 // On other platforms we apply a default KColorScheme
0046 QString KColorSchemeManagerPrivate::automaticColorSchemePath() const
0047 {
0048 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
0049     const QString colorSchemeId = m_colorSchemeWatcher.systemPreference() == KColorSchemeWatcher::PreferDark ? getDarkColorScheme() : getLightColorScheme();
0050     return indexForSchemeId(colorSchemeId).data(KColorSchemeModel::PathRole).toString();
0051 #else
0052     return QString();
0053 #endif
0054 }
0055 
0056 QIcon KColorSchemeManagerPrivate::createPreview(const QString &path)
0057 {
0058     KSharedConfigPtr schemeConfig = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
0059     QIcon result;
0060 
0061     KColorScheme activeWindow(QPalette::Active, KColorScheme::Window, schemeConfig);
0062     KColorScheme activeButton(QPalette::Active, KColorScheme::Button, schemeConfig);
0063     KColorScheme activeView(QPalette::Active, KColorScheme::View, schemeConfig);
0064     KColorScheme activeSelection(QPalette::Active, KColorScheme::Selection, schemeConfig);
0065 
0066     auto pixmap = [&](int size) {
0067         QPixmap pix(size, size);
0068         pix.fill(Qt::black);
0069         QPainter p;
0070         p.begin(&pix);
0071         const int itemSize = size / 2 - 1;
0072         p.fillRect(1, 1, itemSize, itemSize, activeWindow.background());
0073         p.fillRect(1 + itemSize, 1, itemSize, itemSize, activeButton.background());
0074         p.fillRect(1, 1 + itemSize, itemSize, itemSize, activeView.background());
0075         p.fillRect(1 + itemSize, 1 + itemSize, itemSize, itemSize, activeSelection.background());
0076         p.end();
0077         result.addPixmap(pix);
0078     };
0079     // 16x16
0080     pixmap(16);
0081     // 24x24
0082     pixmap(24);
0083 
0084     return result;
0085 }
0086 
0087 KColorSchemeManagerPrivate::KColorSchemeManagerPrivate()
0088     : model(new KColorSchemeModel())
0089 {
0090 }
0091 
0092 KColorSchemeManager::KColorSchemeManager(QObject *parent)
0093     : QObject(parent)
0094     , d(new KColorSchemeManagerPrivate)
0095 {
0096 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
0097     connect(&d->m_colorSchemeWatcher, &KColorSchemeWatcher::systemPreferenceChanged, this, [this]() {
0098         if (!d->m_activatedScheme.isEmpty()) {
0099             // Don't override what has been manually set
0100             return;
0101         }
0102 
0103         d->activateSchemeInternal(d->automaticColorSchemePath());
0104     });
0105 #endif
0106 
0107     KSharedConfigPtr config = KSharedConfig::openConfig();
0108     KConfigGroup cg(config, "UiSettings");
0109     const QString scheme = cg.readEntry("ColorScheme", QString());
0110 
0111     QString schemePath;
0112 
0113     if (scheme.isEmpty() || scheme == QLatin1String("Default")) {
0114         // Color scheme might be already set from a platform theme
0115         // This is used for example by QGnomePlatform that can set color scheme
0116         // matching GNOME settings. This avoids issues where QGnomePlatform sets
0117         // QPalette for dark theme, but end up mixing it also with Breeze light
0118         // that is going to be used as a fallback for apps using KColorScheme.
0119         // BUG: 447029
0120         schemePath = qApp->property("KDE_COLOR_SCHEME_PATH").toString();
0121         if (schemePath.isEmpty()) {
0122             schemePath = d->automaticColorSchemePath();
0123         }
0124     } else {
0125         const auto index = indexForScheme(scheme);
0126         schemePath = index.data(KColorSchemeModel::PathRole).toString();
0127         d->m_activatedScheme = index.data(KColorSchemeModel::IdRole).toString();
0128     }
0129     d->activateSchemeInternal(schemePath);
0130 }
0131 
0132 KColorSchemeManager::~KColorSchemeManager()
0133 {
0134 }
0135 
0136 QAbstractItemModel *KColorSchemeManager::model() const
0137 {
0138     return d->model.get();
0139 }
0140 
0141 QModelIndex KColorSchemeManagerPrivate::indexForSchemeId(const QString &id) const
0142 {
0143     for (int i = 1; i < model->rowCount(); ++i) {
0144         QModelIndex index = model->index(i);
0145         if (index.data(KColorSchemeModel::IdRole).toString() == id) {
0146             return index;
0147         }
0148     }
0149     return QModelIndex();
0150 }
0151 
0152 void KColorSchemeManager::setAutosaveChanges(bool autosaveChanges)
0153 {
0154     d->m_autosaveChanges = autosaveChanges;
0155 }
0156 
0157 QModelIndex KColorSchemeManager::indexForScheme(const QString &name) const
0158 {
0159     // Empty string is mapped to "reset to the system scheme"
0160     if (name.isEmpty()) {
0161         return d->model->index(defaultSchemeRow);
0162     }
0163     for (int i = 1; i < d->model->rowCount(); ++i) {
0164         QModelIndex index = d->model->index(i);
0165         if (index.data(KColorSchemeModel::NameRole).toString() == name) {
0166             return index;
0167         }
0168     }
0169     return QModelIndex();
0170 }
0171 
0172 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 107)
0173 KActionMenu *KColorSchemeManager::createSchemeSelectionMenu(const QIcon &icon, const QString &name, const QString &selectedSchemeName, QObject *parent)
0174 {
0175     auto menu = createSchemeSelectionMenu(name, selectedSchemeName, parent);
0176     menu->setIcon(icon);
0177     return menu;
0178 }
0179 
0180 KActionMenu *KColorSchemeManager::createSchemeSelectionMenu(const QString &text, const QString &selectedSchemeName, QObject *parent)
0181 {
0182     auto menu = createSchemeSelectionMenu(selectedSchemeName, parent);
0183     menu->setText(text);
0184     return menu;
0185 }
0186 
0187 KActionMenu *KColorSchemeManager::createSchemeSelectionMenu(const QString &selectedSchemeName, QObject *parent)
0188 {
0189     auto menu = KColorSchemeMenu::createMenu(this, parent);
0190     auto actions = menu->menu()->actions();
0191     auto it = std::find_if(actions.begin(), actions.end(), [selectedSchemeName](const QAction *action) {
0192         return action->text() == selectedSchemeName;
0193     });
0194     if (it != actions.end()) {
0195         (*it)->setChecked(true);
0196     }
0197     return menu;
0198 }
0199 
0200 KActionMenu *KColorSchemeManager::createSchemeSelectionMenu(QObject *parent)
0201 {
0202     return KColorSchemeMenu::createMenu(this, parent);
0203 }
0204 #endif
0205 
0206 void KColorSchemeManager::activateScheme(const QModelIndex &index)
0207 {
0208     const bool isDefaultEntry = index.data(KColorSchemeModel::PathRole).toString().isEmpty();
0209 
0210     if (index.isValid() && index.model() == d->model.get() && !isDefaultEntry) {
0211         d->activateSchemeInternal(index.data(KColorSchemeModel::PathRole).toString());
0212         d->m_activatedScheme = index.data(KColorSchemeModel::IdRole).toString();
0213         if (d->m_autosaveChanges) {
0214             saveSchemeToConfigFile(index.data(KColorSchemeModel::NameRole).toString());
0215         }
0216     } else {
0217         d->activateSchemeInternal(d->automaticColorSchemePath());
0218         d->m_activatedScheme = QString();
0219         if (d->m_autosaveChanges) {
0220             saveSchemeToConfigFile(QString());
0221         }
0222     }
0223 }
0224 
0225 void KColorSchemeManager::saveSchemeToConfigFile(const QString &schemeName) const
0226 {
0227     KSharedConfigPtr config = KSharedConfig::openConfig();
0228     KConfigGroup cg(config, "UiSettings");
0229     cg.writeEntry("ColorScheme", KLocalizedString::removeAcceleratorMarker(schemeName));
0230     cg.sync();
0231 }
0232 
0233 QString KColorSchemeManager::activeSchemeId() const
0234 {
0235     return d->m_activatedScheme;
0236 }
0237 
0238 #include "moc_kcolorschememanager.cpp"