File indexing completed on 2024-05-12 17:06:43

0001 /*
0002  * ThemeListModel
0003  * SPDX-FileCopyrightText: 2002 Karol Szwed <gallium@kde.org>
0004  * SPDX-FileCopyrightText: 2002 Daniel Molkentin <molkentin@kde.org>
0005  * SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org>
0006  * SPDX-FileCopyrightText: 2009 Davide Bettio <davide.bettio@kdemail.net>
0007  * SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
0008 
0009  * Portions SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com>
0010  * Portions SPDX-FileCopyrightText: 2007 Ivan Cukic <ivan.cukic+kde@gmail.com>
0011  * Portions SPDX-FileCopyrightText: 2008 Petri Damsten <damu@iki.fi>
0012  * Portions SPDX-FileCopyrightText: 2000 TrollTech AS.
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-only
0015  */
0016 
0017 #include "themelistmodel.h"
0018 
0019 #include <QDir>
0020 #include <QFile>
0021 #include <QPainter>
0022 #include <QStandardPaths>
0023 
0024 #include <KConfigGroup>
0025 #include <KDesktopFile>
0026 #include <KPluginMetaData>
0027 #include <KAboutData>
0028 
0029 #include <KColorScheme>
0030 #include <QDebug>
0031 
0032 ThemeListModel::ThemeListModel(QObject *parent)
0033     : QAbstractListModel(parent)
0034 {
0035     m_roleNames.insert(Qt::DisplayRole, "display");
0036     m_roleNames.insert(PackageNameRole, "packageNameRole");
0037     m_roleNames.insert(PackageDescriptionRole, "packageDescriptionRole");
0038     m_roleNames.insert(PackageAuthorRole, "packageAuthorRole");
0039     m_roleNames.insert(PackageVersionRole, "packageVersionRole");
0040     m_roleNames.insert(PluginNameRole, "pluginNameRole");
0041     m_roleNames.insert(ColorTypeRole, "colorTypeRole");
0042 
0043     reload();
0044 }
0045 
0046 ThemeListModel::~ThemeListModel()
0047 {
0048     clearThemeList();
0049 }
0050 
0051 QHash<int, QByteArray> ThemeListModel::roleNames() const
0052 {
0053     return m_roleNames;
0054 }
0055 
0056 void ThemeListModel::clearThemeList()
0057 {
0058     m_themes.clear();
0059 }
0060 
0061 void ThemeListModel::reload()
0062 {
0063     clearThemeList();
0064 
0065     // get all desktop themes
0066     QStringList themes;
0067     const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "plasma/desktoptheme", QStandardPaths::LocateDirectory);
0068     for (const QString &ppath : packs) {
0069         const QDir cd(ppath);
0070         const QStringList &entries = cd.entryList(QDir::Dirs | QDir::Hidden);
0071         for (const QString &pack : entries) {
0072             const QString prefix = QStringLiteral("%1%2%3%4metadata.").arg(ppath, QDir::separator(), pack, QDir::separator());
0073 
0074             QString _metadata = QStringLiteral("%1json").arg(prefix);
0075             if (QFile::exists(_metadata)) {
0076                 themes << _metadata;
0077                 continue;
0078             }
0079 
0080             _metadata = QStringLiteral("%1desktop").arg(prefix);
0081             if (QFile::exists(_metadata)) {
0082                 themes << _metadata;
0083             }
0084         }
0085     }
0086 
0087     for (const QString &theme : qAsConst(themes)) {
0088         int themeSepIndex = theme.lastIndexOf('/', -1);
0089         QString themeRoot = theme.left(themeSepIndex);
0090         int themeNameSepIndex = themeRoot.lastIndexOf('/', -1);
0091         QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1);
0092 
0093         QString name;
0094         QString comment;
0095         QString author;
0096         QString version;
0097         QString pluginName;
0098 
0099         if (theme.endsWith(QLatin1String(".json"))) {
0100             KPluginMetaData data = KPluginMetaData::fromJsonFile(theme);
0101             name = data.name();
0102             comment = data.description();
0103             pluginName = data.pluginId();
0104 
0105             QList<KAboutPerson> authors = data.authors();
0106             author = authors.isEmpty() ? QString() : authors.first().name();
0107 
0108         } else {
0109             KDesktopFile df(theme);
0110 
0111             if (df.noDisplay()) {
0112                 continue;
0113             }
0114 
0115             name = df.readName();
0116             if (name.isEmpty()) {
0117                 name = packageName;
0118             }
0119             comment = df.readComment();
0120             pluginName = df.desktopGroup().readEntry("X-KDE-PluginInfo-Name", QString());
0121             author = df.desktopGroup().readEntry("X-KDE-PluginInfo-Author", QString());
0122             version = df.desktopGroup().readEntry("X-KDE-PluginInfo-Version", QString());
0123         }
0124 
0125         ThemeInfo info;
0126         info.pluginName = pluginName;
0127 
0128         bool hasPluginName = std::any_of(m_themes.begin(), m_themes.end(), [&](const ThemeInfo &item) {
0129             Q_UNUSED (item)
0130             return info.pluginName == packageName;
0131         });
0132 
0133         if (!hasPluginName) {
0134             // Plasma Theme creates a KColorScheme out of the "color" file and falls back to system colors if there is none
0135             const QString colorsPath = themeRoot + QLatin1String("/colors");
0136             const bool followsSystemColors = !QFileInfo::exists(colorsPath);
0137             ColorType type = FollowsColorTheme;
0138             Q_UNUSED(type);
0139             info.type = FollowsColorTheme;
0140             if (!followsSystemColors) {
0141                 const KSharedConfig::Ptr config = KSharedConfig::openConfig(colorsPath);
0142                 const QPalette palette = KColorScheme::createApplicationPalette(config);
0143                 const int windowBackgroundGray = qGray(palette.window().color().rgb());
0144                 if (windowBackgroundGray < 192) {
0145                     type = DarkTheme;
0146                     info.type = DarkTheme;
0147                 } else {
0148                     type = LightTheme;
0149                     info.type = LightTheme;
0150                 }
0151             }
0152         }
0153 
0154         info.package = packageName;
0155         info.description = comment;
0156         info.author = author;
0157         info.version = version;
0158         info.themeRoot = themeRoot;
0159         m_themes[name] = info;
0160     }
0161 
0162     beginResetModel();
0163     endResetModel();
0164 }
0165 
0166 int ThemeListModel::rowCount(const QModelIndex &) const
0167 {
0168     return m_themes.size();
0169 }
0170 
0171 QVariant ThemeListModel::data(const QModelIndex &index, int role) const
0172 {
0173     if (!index.isValid()) {
0174         return QVariant();
0175     }
0176 
0177     if (index.row() >= m_themes.size()) {
0178         return QVariant();
0179     }
0180 
0181     QMap<QString, ThemeInfo>::const_iterator it = m_themes.constBegin();
0182     for (int i = 0; i < index.row(); ++i) {
0183         ++it;
0184     }
0185 
0186     switch (role) {
0187     case Qt::DisplayRole:
0188         return it.key();
0189     case PackageNameRole:
0190         return (*it).package;
0191     case PackageDescriptionRole:
0192         return (*it).description;
0193     case PackageAuthorRole:
0194         return (*it).author;
0195     case PackageVersionRole:
0196         return (*it).version;
0197     case PluginNameRole:
0198         return (*it).pluginName;
0199     case ColorTypeRole:
0200         return (*it).type;
0201     default:
0202         return QVariant();
0203     }
0204 }
0205 
0206 QVariantMap ThemeListModel::get(int row) const
0207 {
0208     QVariantMap item;
0209 
0210     QModelIndex idx = index(row, 0);
0211 
0212     item["display"] = data(idx, Qt::DisplayRole);
0213     item["pluginNameRole"] = data(idx, PluginNameRole);
0214     item["colorTypeRole"] = data(idx, ColorTypeRole);
0215     item["packageNameRole"] = data(idx, PackageNameRole);
0216     item["packageDescriptionRole"] = data(idx, PackageDescriptionRole);
0217     item["packageAuthorRole"] = data(idx, PackageAuthorRole);
0218     item["packageVersionRole"] = data(idx, PackageVersionRole);
0219 
0220     return item;
0221 }
0222 
0223 QModelIndex ThemeListModel::indexOf(const QString &name) const
0224 {
0225     QMapIterator<QString, ThemeInfo> it(m_themes);
0226     int i = -1;
0227     while (it.hasNext()) {
0228         ++i;
0229         if (it.next().value().package == name) {
0230             return index(i, 0);
0231         }
0232     }
0233 
0234     return {};
0235 }
0236 
0237 #include "moc_themelistmodel.cpp"