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