File indexing completed on 2026-07-12 16:17:49

0001 /*
0002     SPDX-FileCopyrightText: 2011 Tamas Krutki <ktamasw@gmail.com>
0003     SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 #include "module.h"
0008 
0009 #include <QDBusConnection>
0010 #include <QDBusMessage>
0011 #include <QDBusPendingCall>
0012 #include <QFileDialog>
0013 #include <QStandardPaths>
0014 #include <QStringList>
0015 
0016 #include <KAboutData>
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 #include <KMessageBox>
0020 #include <KMessageWidget>
0021 #include <KNSWidgets/Button>
0022 #include <KPackage/Package>
0023 #include <KPackage/PackageLoader>
0024 #include <KPackage/PackageStructure>
0025 #include <KPluginFactory>
0026 #include <KSharedConfig>
0027 
0028 #include <KCMultiDialog>
0029 
0030 #include "config-kwin.h"
0031 #include "kwinscriptsdata.h"
0032 
0033 Module::Module(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
0034     : KQuickAddons::ConfigModule(parent, data, args)
0035     , m_kwinScriptsData(new KWinScriptsData(this))
0036     , m_model(new KPluginModel(this))
0037 {
0038     // Hide the help button, because there is no help
0039     setButtons(Apply | Default);
0040     connect(m_model, &KPluginModel::isSaveNeededChanged, this, [this]() {
0041         setNeedsSave(m_model->isSaveNeeded() || !m_pendingDeletions.isEmpty());
0042     });
0043     connect(m_model, &KPluginModel::defaulted, this, [this](bool defaulted) {
0044         setRepresentsDefaults(defaulted);
0045     });
0046     m_model->setConfig(KSharedConfig::openConfig("kwinrc")->group("Plugins"));
0047 }
0048 
0049 void Module::onGHNSEntriesChanged()
0050 {
0051     m_model->clear();
0052     m_model->addPlugins(m_kwinScriptsData->pluginMetaDataList(), QString());
0053 }
0054 
0055 void Module::importScript()
0056 {
0057     QString path = QFileDialog::getOpenFileName(nullptr, i18n("Import KWin Script"), QDir::homePath(),
0058                                                 i18n("*.kwinscript|KWin scripts (*.kwinscript)"));
0059 
0060     if (path.isNull()) {
0061         return;
0062     }
0063 
0064     using namespace KPackage;
0065     PackageStructure *structure = PackageLoader::self()->loadPackageStructure(QStringLiteral("KWin/Script"));
0066     Package package(structure);
0067 
0068     KJob *installJob = package.update(path);
0069     installJob->setProperty("packagePath", path); // so we can retrieve it later for showing the script's name
0070     connect(installJob, &KJob::result, this, &Module::importScriptInstallFinished);
0071 }
0072 
0073 void Module::configure(const KPluginMetaData &data)
0074 {
0075     auto dialog = new KCMultiDialog();
0076     dialog->addModule(data);
0077     dialog->setAttribute(Qt::WA_DeleteOnClose);
0078     dialog->show();
0079 }
0080 
0081 void Module::togglePendingDeletion(const KPluginMetaData &data)
0082 {
0083     if (m_pendingDeletions.contains(data)) {
0084         m_pendingDeletions.removeOne(data);
0085     } else {
0086         m_pendingDeletions.append(data);
0087     }
0088     setNeedsSave(m_model->isSaveNeeded() || !m_pendingDeletions.isEmpty());
0089     Q_EMIT pendingDeletionsChanged();
0090 }
0091 
0092 void Module::importScriptInstallFinished(KJob *job)
0093 {
0094     // if the applet is already installed, just add it to the containment
0095     if (job->error() != KJob::NoError) {
0096         setErrorMessage(i18nc("Placeholder is error message returned from the install service", "Cannot import selected script.\n%1", job->errorString()));
0097         return;
0098     }
0099 
0100     using namespace KPackage;
0101 
0102     // so we can show the name of the package we just imported
0103     PackageStructure *structure = PackageLoader::self()->loadPackageStructure(QStringLiteral("KWin/Script"));
0104     Package package(structure);
0105     package.setPath(job->property("packagePath").toString());
0106     Q_ASSERT(package.isValid());
0107 
0108     m_infoMessage = i18nc("Placeholder is name of the script that was imported", "The script \"%1\" was successfully imported.", package.metadata().name());
0109     m_errorMessage.clear();
0110     Q_EMIT messageChanged();
0111 
0112     m_model->clear();
0113     m_model->addPlugins(m_kwinScriptsData->pluginMetaDataList(), QString());
0114 
0115     setNeedsSave(false);
0116 }
0117 
0118 void Module::defaults()
0119 {
0120     m_model->defaults();
0121     m_pendingDeletions.clear();
0122     Q_EMIT pendingDeletionsChanged();
0123 
0124     setNeedsSave(m_model->isSaveNeeded());
0125 }
0126 
0127 void Module::load()
0128 {
0129     m_model->clear();
0130     m_model->addPlugins(m_kwinScriptsData->pluginMetaDataList(), QString());
0131     m_pendingDeletions.clear();
0132     Q_EMIT pendingDeletionsChanged();
0133 
0134     setNeedsSave(false);
0135 }
0136 
0137 void Module::save()
0138 {
0139     using namespace KPackage;
0140     PackageStructure *structure = PackageLoader::self()->loadPackageStructure(QStringLiteral("KWin/Script"));
0141     for (const KPluginMetaData &info : std::as_const(m_pendingDeletions)) {
0142         // We can get the package root from the entry path
0143         QDir root = QFileInfo(info.metaDataFileName()).dir();
0144         root.cdUp();
0145         KJob *uninstallJob = Package(structure).uninstall(info.pluginId(), root.absolutePath());
0146         connect(uninstallJob, &KJob::result, this, [this, uninstallJob]() {
0147             if (!uninstallJob->errorString().isEmpty()) {
0148                 setErrorMessage(i18n("Error when uninstalling KWin Script: %1", uninstallJob->errorString()));
0149             } else {
0150                 load(); // Make sure to reload the KCM to deleted entries to disappear
0151             }
0152         });
0153     }
0154 
0155     m_infoMessage.clear();
0156     Q_EMIT messageChanged();
0157     m_pendingDeletions.clear();
0158     Q_EMIT pendingDeletionsChanged();
0159 
0160     m_model->save();
0161     QDBusMessage message = QDBusMessage::createMethodCall("org.kde.KWin", "/Scripting", "org.kde.kwin.Scripting", "start");
0162     QDBusConnection::sessionBus().asyncCall(message);
0163 
0164     setNeedsSave(false);
0165 }
0166 
0167 K_PLUGIN_FACTORY_WITH_JSON(KcmKWinScriptsFactory, "kcm_kwin_scripts.json",
0168                            registerPlugin<Module>();
0169                            registerPlugin<KWinScriptsData>();)
0170 
0171 #include "module.moc"