File indexing completed on 2024-04-21 05:36:33

0001 /*
0002     SPDX-FileCopyrightText: 2013 Reza Fatahilah Shah <rshah0385@kireihana.com>
0003     SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #include "themesmodel.h"
0008 #include "config.h"
0009 #include "thememetadata.h"
0010 
0011 #include <QDir>
0012 #include <QStandardPaths>
0013 #include <QString>
0014 #include <QUrl>
0015 
0016 #include <KConfig>
0017 #include <KConfigGroup>
0018 #include <KSharedConfig>
0019 #include <QDebug>
0020 
0021 ThemesModel::ThemesModel(QObject *parent)
0022     : QAbstractListModel(parent)
0023 {
0024     populate();
0025     m_customInstalledThemes = KSharedConfig::openConfig(QStringLiteral("sddmthemeinstallerrc"))->group("DownloadedThemes").entryMap().values();
0026 }
0027 
0028 ThemesModel::~ThemesModel()
0029 {
0030 }
0031 
0032 int ThemesModel::rowCount(const QModelIndex &parent) const
0033 {
0034     Q_UNUSED(parent);
0035 
0036     return mThemeList.size();
0037 }
0038 
0039 QHash<int, QByteArray> ThemesModel::roleNames() const
0040 {
0041     return {
0042         {Qt::DisplayRole, QByteArrayLiteral("display")},
0043         {IdRole, QByteArrayLiteral("id")},
0044         {AuthorRole, QByteArrayLiteral("author")},
0045         {DescriptionRole, QByteArrayLiteral("description")},
0046         {LicenseRole, QByteArrayLiteral("license")},
0047         {EmailRole, QByteArrayLiteral("email")},
0048         {WebsiteRole, QByteArrayLiteral("website")},
0049         {CopyrightRole, QByteArrayLiteral("copyright")},
0050         {VersionRole, QByteArrayLiteral("version")},
0051         {ThemeApiRole, QByteArrayLiteral("themeApi")},
0052         {PreviewRole, QByteArrayLiteral("preview")},
0053         {PathRole, QByteArrayLiteral("path")},
0054         {ConfigFileRole, QByteArrayLiteral("configFile")},
0055         {CurrentBackgroundRole, QByteArrayLiteral("currentBackground")},
0056         {DeletableRole, QByteArrayLiteral("deletable")},
0057     };
0058 }
0059 
0060 QVariant ThemesModel::data(const QModelIndex &index, int role) const
0061 {
0062     if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
0063         return QVariant();
0064     }
0065 
0066     const ThemeMetadata metadata = mThemeList[index.row()];
0067 
0068     switch (role) {
0069     case Qt::DisplayRole:
0070         return metadata.name();
0071     case ThemesModel::IdRole:
0072         return metadata.themeid();
0073     case ThemesModel::AuthorRole:
0074         return metadata.author();
0075     case ThemesModel::DescriptionRole:
0076         return metadata.description();
0077     case ThemesModel::LicenseRole:
0078         return metadata.license();
0079     case ThemesModel::EmailRole:
0080         return metadata.email();
0081     case ThemesModel::WebsiteRole:
0082         return metadata.website();
0083     case ThemesModel::CopyrightRole:
0084         return metadata.copyright();
0085     case ThemesModel::VersionRole:
0086         return metadata.version();
0087     case ThemesModel::ThemeApiRole:
0088         return metadata.themeapi();
0089     case ThemesModel::PreviewRole:
0090         return QUrl::fromLocalFile(metadata.path() + metadata.screenshot());
0091     case ThemesModel::PathRole:
0092         return metadata.path();
0093     case ThemesModel::ConfigFileRole:
0094         return metadata.configfile();
0095     case ThemesModel::CurrentBackgroundRole:
0096         if (metadata.supportsBackground()) {
0097             return m_currentWallpapers[metadata.themeid()];
0098         }
0099         break;
0100     case DeletableRole:
0101         return m_customInstalledThemes.contains(metadata.path().chopped(1)); // Chop the trailing /
0102     }
0103 
0104     return QVariant();
0105 }
0106 
0107 bool ThemesModel::setData(const QModelIndex &index, const QVariant &value, int role)
0108 {
0109     if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
0110         return false;
0111     }
0112     if (role != ThemesModel::CurrentBackgroundRole) {
0113         return false;
0114     }
0115     m_currentWallpapers[mThemeList[index.row()].themeid()] = value.toString();
0116     Q_EMIT dataChanged(index, index, {ThemesModel::CurrentBackgroundRole});
0117     return true;
0118 }
0119 
0120 void ThemesModel::populate()
0121 {
0122     if (!mThemeList.isEmpty()) {
0123         beginResetModel();
0124         mThemeList.clear();
0125         endResetModel();
0126     }
0127 
0128     const QString themesBaseDir = KSharedConfig::openConfig(QStringLiteral(SDDM_CONFIG_FILE), KConfig::SimpleConfig)->group("Theme").readEntry("ThemeDir");
0129     QStringList themesBaseDirs;
0130     if (!themesBaseDir.isEmpty()) {
0131         themesBaseDirs.append(themesBaseDir);
0132     } else {
0133         themesBaseDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("sddm/themes"), QStandardPaths::LocateDirectory);
0134     }
0135 
0136     auto alreadyHave = [this](const QString &theme) {
0137         return std::any_of(mThemeList.cbegin(), mThemeList.cend(), [&theme](const ThemeMetadata &data) {
0138             return data.themeid() == theme;
0139         });
0140     };
0141     for (const auto &folder : themesBaseDirs) {
0142         QDir dir(folder);
0143         if (!dir.exists()) {
0144             return;
0145         }
0146         for (const QString &theme : dir.entryList(QDir::AllDirs | QDir::Readable | QDir::NoDotAndDotDot)) {
0147             QString path = folder + QLatin1Char('/') + theme;
0148             if (!alreadyHave(theme) && QFile::exists(path + QStringLiteral("/metadata.desktop"))) {
0149                 add(theme, path);
0150             }
0151         }
0152     }
0153 }
0154 
0155 void ThemesModel::add(const QString &id, const QString &path)
0156 {
0157     beginInsertRows(QModelIndex(), mThemeList.count(), mThemeList.count());
0158 
0159     const auto data = ThemeMetadata(id, path);
0160     mThemeList.append(data);
0161     if (data.supportsBackground()) {
0162         const QString themeConfigPath = data.path() + data.configfile();
0163         auto themeConfig = KSharedConfig::openConfig(themeConfigPath + QStringLiteral(".user"), KConfig::CascadeConfig);
0164         themeConfig->addConfigSources({themeConfigPath});
0165         const QString backgroundPath = themeConfig->group("General").readEntry("background");
0166         if (backgroundPath.startsWith(QStringLiteral("/"))) {
0167             m_currentWallpapers.insert(data.themeid(), backgroundPath);
0168         } else {
0169             m_currentWallpapers.insert(data.themeid(), data.path() + backgroundPath);
0170         }
0171     }
0172     endInsertRows();
0173 }
0174 
0175 void ThemesModel::dump(const QString &id, const QString &path)
0176 {
0177     Q_UNUSED(id)
0178 
0179     ThemeMetadata metadata(path);
0180 
0181     qDebug() << "Theme Path:" << metadata.path();
0182     qDebug() << "Name: " << metadata.name();
0183     qDebug() << "Version: " << metadata.version();
0184     qDebug() << "Author: " << metadata.author();
0185     qDebug() << "Description: " << metadata.description();
0186     qDebug() << "Email: " << metadata.email();
0187     qDebug() << "License: " << metadata.license();
0188     qDebug() << "Copyright: " << metadata.copyright();
0189     qDebug() << "Screenshot: " << metadata.screenshot();
0190 }
0191 
0192 int ThemesModel::currentIndex() const
0193 {
0194     return m_currentIndex;
0195 }
0196 
0197 QString ThemesModel::currentTheme() const
0198 {
0199     return mThemeList[m_currentIndex].themeid();
0200 }
0201 
0202 void ThemesModel::setCurrentTheme(const QString &theme)
0203 {
0204     auto it = std::find_if(mThemeList.cbegin(), mThemeList.cend(), [&theme](const ThemeMetadata &themeData) {
0205         return themeData.themeid() == theme;
0206     });
0207     const int index = it - mThemeList.cbegin();
0208     if (it == mThemeList.cend() || index == m_currentIndex) {
0209         return;
0210     }
0211     m_currentIndex = index;
0212     Q_EMIT currentIndexChanged();
0213 }