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"