File indexing completed on 2024-04-28 05:36:11

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
0003  *  SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in>
0004  *  SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  *
0008  */
0009 
0010 #include "kcm.h"
0011 #include "config-kcm.h"
0012 
0013 #include <chrono>
0014 
0015 #include <KConfigGroup>
0016 #include <KLocalizedString>
0017 #include <KPluginFactory>
0018 #include <KSharedConfig>
0019 
0020 #include <KAuth/Action>
0021 #include <KAuth/ExecuteJob>
0022 
0023 #include <KIO/FileCopyJob>
0024 
0025 using namespace std::chrono_literals;
0026 
0027 K_PLUGIN_CLASS_WITH_JSON(KCMPlymouth, "kcm_plymouth.json")
0028 
0029 KCMPlymouth::KCMPlymouth(QObject *parent, const KPluginMetaData &metaData)
0030     : KQuickConfigModule(parent, metaData)
0031     , m_model(new QStandardItemModel(this))
0032 {
0033     qmlRegisterAnonymousType<QStandardItemModel>("KCMPlymouth", 1);
0034     qmlRegisterAnonymousType<KCMPlymouth>("KCMPlymouth", 1);
0035     setButtons(Apply);
0036     setAuthActionName(QStringLiteral("org.kde.kcontrol.kcmplymouth.save"));
0037 
0038     m_model->setItemRoleNames({
0039         {Qt::DisplayRole, QByteArrayLiteral("display")},
0040         {DescriptionRole, QByteArrayLiteral("description")},
0041         {PluginNameRole, QByteArrayLiteral("pluginName")},
0042         {ScreenhotRole, QByteArrayLiteral("screenshot")},
0043         {UninstallableRole, QByteArrayLiteral("uninstallable")},
0044     });
0045 }
0046 
0047 KCMPlymouth::~KCMPlymouth()
0048 {
0049 }
0050 
0051 void KCMPlymouth::reloadModel()
0052 {
0053     m_model->clear();
0054 
0055     QDir dir(QStringLiteral(PLYMOUTH_THEMES_DIR));
0056     if (!dir.exists()) {
0057         return;
0058     }
0059 
0060     KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), QStringLiteral("DownloadedThemes"));
0061 
0062     dir.setFilter(QDir::NoDotAndDotDot | QDir::Dirs);
0063 
0064     const auto list = dir.entryInfoList();
0065     for (const QFileInfo &fileInfo : list) {
0066         const QString pluginName = fileInfo.fileName();
0067         QDir themeDir(fileInfo.filePath());
0068 
0069         KConfig file(themeDir.filePath(pluginName + QLatin1String(".plymouth")), KConfig::SimpleConfig);
0070         KConfigGroup grp = file.group(QStringLiteral("Plymouth Theme"));
0071 
0072         QString displayName = grp.readEntry("Name", QString());
0073         if (displayName.isEmpty()) {
0074             displayName = pluginName;
0075         }
0076 
0077         QStandardItem *row = new QStandardItem(displayName);
0078         row->setData(pluginName, PluginNameRole);
0079         row->setData(grp.readEntry("Description", QString()), DescriptionRole);
0080         row->setData(installedCg.entryMap().contains(fileInfo.fileName()), UninstallableRole);
0081 
0082         // the theme has a preview
0083         if (QFile::exists(themeDir.path() + QStringLiteral("/preview.png"))) {
0084             row->setData(QUrl::fromLocalFile(themeDir.path() + QStringLiteral("/preview.png")), ScreenhotRole);
0085             // fetch it downloaded from kns
0086         } else {
0087             const QString fileName = installedCg.readEntry(fileInfo.fileName(), QString());
0088             if (fileName.isEmpty()) {
0089                 row->setData(QString(), ScreenhotRole);
0090             } else {
0091                 row->setData(QUrl::fromLocalFile(fileName + QStringLiteral(".png")), ScreenhotRole);
0092             }
0093         }
0094 
0095         m_model->appendRow(row);
0096     }
0097 
0098     Q_EMIT selectedPluginIndexChanged();
0099 }
0100 
0101 void KCMPlymouth::onEntryEvent(const KNSCore::Entry &entry)
0102 {
0103     static QStringList alreadyCopiedThumbnails;
0104     if (entry.isValid() && entry.status() == KNSCore::Entry::Installed && !alreadyCopiedThumbnails.contains(entry.uniqueId())) {
0105         alreadyCopiedThumbnails.append(entry.uniqueId());
0106         KIO::file_copy(QUrl(entry.previewUrl(KNSCore::Entry::PreviewBig1)),
0107                        QUrl::fromLocalFile(QString(entry.installedFiles().constFirst() + QStringLiteral(".png"))),
0108                        -1,
0109                        KIO::Overwrite | KIO::HideProgressInfo);
0110     }
0111     reloadModel();
0112 }
0113 
0114 QStandardItemModel *KCMPlymouth::themesModel()
0115 {
0116     return m_model;
0117 }
0118 
0119 QString KCMPlymouth::selectedPlugin() const
0120 {
0121     return m_selectedPlugin;
0122 }
0123 
0124 void KCMPlymouth::setSelectedPlugin(const QString &plugin)
0125 {
0126     if (m_selectedPlugin == plugin) {
0127         return;
0128     }
0129 
0130     m_selectedPlugin = plugin;
0131     Q_EMIT selectedPluginChanged();
0132     Q_EMIT selectedPluginIndexChanged();
0133 
0134     setNeedsSave(true);
0135 }
0136 
0137 bool KCMPlymouth::busy() const
0138 {
0139     return m_busy;
0140 }
0141 
0142 void KCMPlymouth::setBusy(const bool &busy)
0143 {
0144     if (m_busy == busy) {
0145         return;
0146     }
0147 
0148     m_busy = busy;
0149     Q_EMIT busyChanged();
0150 }
0151 
0152 int KCMPlymouth::selectedPluginIndex() const
0153 {
0154     for (int i = 0; i < m_model->rowCount(); ++i) {
0155         if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == m_selectedPlugin) {
0156             return i;
0157         }
0158     }
0159     return -1;
0160 }
0161 
0162 void KCMPlymouth::load()
0163 {
0164     reloadModel();
0165 
0166     KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral(PLYMOUTH_CONFIG_PATH)), QStringLiteral("Daemon"));
0167 
0168     setSelectedPlugin(cg.readEntry("Theme"));
0169 
0170     setNeedsSave(false);
0171 }
0172 
0173 void KCMPlymouth::save()
0174 {
0175     setBusy(true);
0176     QVariantMap helperargs;
0177     helperargs[QStringLiteral("theme")] = m_selectedPlugin;
0178 
0179     KAuth::Action action(authActionName());
0180     action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmplymouth"));
0181     action.setArguments(helperargs);
0182     // We don't know how long this will take. The helper will need to generate N=installed_kernels initrds.
0183     // Be very generous with the timeout! https://bugs.kde.org/show_bug.cgi?id=400641
0184     // NB: there is also a timeout in the helper
0185     action.setTimeout(std::chrono::milliseconds(15min).count());
0186 
0187     KAuth::ExecuteJob *job = action.execute();
0188     bool rc = job->exec();
0189     if (!rc) {
0190         if (job->error() == KAuth::ActionReply::UserCancelledError) {
0191             Q_EMIT showErrorMessage(i18n("Unable to authenticate/execute the action: %1 (%2)", job->error(), job->errorString()));
0192         }
0193         load();
0194     }
0195     setBusy(false);
0196 }
0197 
0198 void KCMPlymouth::uninstall(const QString &plugin)
0199 {
0200     QVariantMap helperargs;
0201     helperargs[QStringLiteral("theme")] = plugin;
0202 
0203     // KAuth::Action action(authActionName());
0204     KAuth::Action action(QStringLiteral("org.kde.kcontrol.kcmplymouth.uninstall"));
0205     action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmplymouth"));
0206     action.setArguments(helperargs);
0207 
0208     KAuth::ExecuteJob *job = action.execute();
0209     bool rc = job->exec();
0210     if (!rc) {
0211         Q_EMIT showErrorMessage(i18n("Unable to authenticate/execute the action: %1 (%2)", job->error(), job->errorString()));
0212     } else {
0213         KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), QStringLiteral("DownloadedThemes"));
0214         installedCg.deleteEntry(plugin);
0215         Q_EMIT showSuccessMessage(i18n("Theme uninstalled successfully."));
0216         load();
0217     }
0218 }
0219 
0220 void KCMPlymouth::defaults()
0221 { /*TODO
0222      if (!) {
0223          return;
0224      }
0225  */
0226 }
0227 
0228 #include "kcm.moc"
0229 
0230 #include "moc_kcm.cpp"