File indexing completed on 2024-04-28 11:34:17
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com> 0004 SPDX-FileCopyrightText: 2003 Matthias Kretz <kretz@kde.org> 0005 SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 0010 #include "kcmoduleproxy.h" 0011 #include "kcmoduleproxy_p.h" 0012 0013 #include <QApplication> 0014 #include <QLayout> 0015 0016 #include <QDBusConnection> 0017 #include <QDBusConnectionInterface> 0018 #include <QDBusInterface> 0019 #include <QDBusReply> 0020 #include <QDBusServiceWatcher> 0021 #include <QPluginLoader> 0022 0023 #include <KAboutData> 0024 #include <kcmoduleinfo.h> 0025 0026 #include <KLocalizedString> 0027 0028 #include <kcmoduleloader.h> 0029 #include <kcmoduleqml_p.h> 0030 0031 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 82) 0032 #include "ksettingswidgetadaptor.h" 0033 #endif 0034 0035 #include <kcmutils_debug.h> 0036 0037 /* 0038 TODO: 0039 0040 - Resizing horizontally is constrained; minimum size is set somewhere. 0041 It appears to be somehow derived from the module's size. 0042 0043 - Prettify: set icon in KCMultiDialog. 0044 0045 */ 0046 /***************************************************************/ 0047 KCModule *KCModuleProxy::realModule() const 0048 { 0049 Q_D(const KCModuleProxy); 0050 /* 0051 * Note, don't call any function that calls realModule() since 0052 * that leads to an infinite loop. 0053 */ 0054 0055 /* Already loaded */ 0056 if (!d->kcm) { 0057 QApplication::setOverrideCursor(Qt::WaitCursor); 0058 const_cast<KCModuleProxyPrivate *>(d)->loadModule(); 0059 QApplication::restoreOverrideCursor(); 0060 } 0061 return d->kcm; 0062 } 0063 0064 void KCModuleProxyPrivate::loadModule() 0065 { 0066 if (!topLayout) { 0067 topLayout = new QVBoxLayout(parent); 0068 QString name; 0069 if (metaData) { 0070 name = metaData.value().pluginId(); 0071 } 0072 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85) 0073 if (name.isEmpty()) { 0074 name = modInfo.handle(); 0075 } 0076 #endif 0077 name.replace(QLatin1Char('-'), QLatin1Char('_')); // hyphen is not allowed in dbus, only [A-Z][a-z][0-9]_ 0078 name.replace(QLatin1Char('/'), QLatin1Char('_')); // same goes for '/' 0079 name.replace(QLatin1Char(' '), QLatin1Char('_')); // same goes for space characters 0080 dbusService = QLatin1String("org.kde.internal.KSettingsWidget_") + name; 0081 0082 // for dbus path, we also need to convert '.' characters 0083 name.replace(QLatin1Char('.'), QLatin1Char('_')); 0084 dbusPath = QLatin1String("/internal/KSettingsWidget/") + name; 0085 } 0086 0087 const bool canRegisterService = QDBusConnection::sessionBus().registerService(dbusService); 0088 0089 if (!canRegisterService) { 0090 /* We didn't get the name we requested, because it's already taken, */ 0091 /* Figure out the name of where the module is already loaded */ 0092 QDBusInterface proxy(dbusService, dbusPath, QStringLiteral("org.kde.internal.KSettingsWidget")); 0093 QDBusReply<QString> reply = proxy.call(QStringLiteral("applicationName")); 0094 /* If we get a valid application name, the module is already loaded */ 0095 if (reply.isValid()) { 0096 auto *watcher = new QDBusServiceWatcher(parent); 0097 watcher->addWatchedService(dbusService); 0098 watcher->setConnection(QDBusConnection::sessionBus()); 0099 watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange); 0100 QObject::connect(watcher, 0101 &QDBusServiceWatcher::serviceOwnerChanged, 0102 parent, 0103 [this](const QString &serviceName, const QString &oldOwner, const QString &newOwner) { 0104 _k_ownerChanged(serviceName, oldOwner, newOwner); 0105 }); 0106 0107 kcm = KCModuleLoader::reportError(KCModuleLoader::Inline, 0108 i18nc("Argument is application name", "This configuration section is already opened in %1", reply.value()), 0109 QStringLiteral(" "), 0110 parent); 0111 topLayout->addWidget(kcm); 0112 return; 0113 } 0114 } 0115 0116 // qDebug() << "Module not already loaded, loading module " << modInfo.moduleName() << " from library " << modInfo.library() << " using symbol " << modInfo.handle(); 0117 if (metaData) { 0118 kcm = KCModuleLoader::loadModule(metaData.value(), parent, QVariantList(args.cbegin(), args.cend())); 0119 } else { 0120 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88) 0121 kcm = KCModuleLoader::loadModule(modInfo, KCModuleLoader::Inline, parent, args); 0122 #endif 0123 } 0124 0125 QObject::connect(kcm, &KCModule::changed, parent, [this](bool state) { 0126 _k_moduleChanged(state); 0127 }); 0128 QObject::connect(kcm, &KCModule::defaulted, parent, [this](bool state) { 0129 _k_moduleDefaulted(state); 0130 }); 0131 QObject::connect(kcm, &KCModule::destroyed, parent, [this]() { 0132 _k_moduleDestroyed(); 0133 }); 0134 QObject::connect(kcm, &KCModule::quickHelpChanged, parent, &KCModuleProxy::quickHelpChanged); 0135 parent->setWhatsThis(kcm->quickHelp()); 0136 0137 if (kcm->layout()) { 0138 kcm->layout()->setContentsMargins(0, 0, 0, 0); 0139 } 0140 if (qobject_cast<KCModuleQml *>(kcm)) { 0141 topLayout->setContentsMargins(0, 0, 0, 0); 0142 } 0143 0144 topLayout->addWidget(kcm); 0145 0146 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 82) 0147 if (!modInfo.handle().isEmpty()) { 0148 QDBusConnection::sessionBus().registerObject(dbusPath, new KSettingsWidgetAdaptor(parent), QDBusConnection::ExportAllSlots); 0149 } 0150 #endif 0151 } 0152 0153 void KCModuleProxyPrivate::_k_ownerChanged(const QString &service, const QString &oldOwner, const QString &) 0154 { 0155 if (service == dbusService && !oldOwner.isEmpty()) { 0156 // Violence: Get rid of KCMError & CO, so that 0157 // realModule() attempts to reload the module 0158 delete kcm; 0159 kcm = nullptr; 0160 Q_Q(KCModuleProxy); 0161 q->realModule(); 0162 0163 Q_ASSERT(kcm); 0164 kcm->show(); 0165 } 0166 } 0167 0168 void KCModuleProxy::showEvent(QShowEvent *ev) 0169 { 0170 Q_D(KCModuleProxy); 0171 0172 (void)realModule(); 0173 0174 /* We have no kcm, if we're in root mode */ 0175 if (d->kcm) { 0176 d->kcm->showEvent(ev); 0177 } 0178 0179 QWidget::showEvent(ev); 0180 } 0181 0182 KCModuleProxy::~KCModuleProxy() 0183 { 0184 deleteClient(); 0185 if (metaData().isValid()) { 0186 // Do not try to unload static plugins 0187 if (!metaData().isStaticPlugin()) { 0188 QPluginLoader(metaData().fileName()).unload(); 0189 } 0190 } else { 0191 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88) 0192 KCModuleLoader::unloadModule(moduleInfo()); 0193 #endif 0194 } 0195 0196 delete d_ptr; 0197 } 0198 0199 void KCModuleProxy::deleteClient() 0200 { 0201 Q_D(KCModuleProxy); 0202 delete d->kcm; 0203 d->kcm = nullptr; 0204 } 0205 0206 void KCModuleProxyPrivate::_k_moduleChanged(bool c) 0207 { 0208 if (changed == c) { 0209 return; 0210 } 0211 0212 Q_Q(KCModuleProxy); 0213 changed = c; 0214 Q_EMIT q->changed(c); 0215 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87) 0216 Q_EMIT q->changed(q); 0217 #endif 0218 } 0219 0220 void KCModuleProxyPrivate::_k_moduleDefaulted(bool d) 0221 { 0222 if (defaulted == d) { 0223 return; 0224 } 0225 0226 Q_Q(KCModuleProxy); 0227 defaulted = d; 0228 Q_EMIT q->changed(changed); 0229 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87) 0230 Q_EMIT q->changed(q); 0231 #endif 0232 } 0233 0234 void KCModuleProxyPrivate::_k_moduleDestroyed() 0235 { 0236 kcm = nullptr; 0237 } 0238 0239 KCModuleProxy::KCModuleProxy(const KPluginMetaData &metaData, QWidget *parent, const QStringList &args) 0240 : QWidget(parent) 0241 , d_ptr(new KCModuleProxyPrivate(this, metaData, args)) 0242 { 0243 } 0244 0245 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88) 0246 KCModuleProxy::KCModuleProxy(const KService::Ptr &service, QWidget *parent, const QStringList &args) 0247 : QWidget(parent) 0248 , d_ptr(new KCModuleProxyPrivate(this, KCModuleInfo(service), args)) 0249 { 0250 d_ptr->q_ptr = this; 0251 } 0252 0253 KCModuleProxy::KCModuleProxy(const KCModuleInfo &info, QWidget *parent, const QStringList &args) 0254 : QWidget(parent) 0255 , d_ptr(new KCModuleProxyPrivate(this, info, args)) 0256 { 0257 } 0258 0259 KCModuleProxy::KCModuleProxy(const QString &serviceName, QWidget *parent, const QStringList &args) 0260 : QWidget(parent) 0261 , d_ptr(new KCModuleProxyPrivate(this, KCModuleInfo(serviceName), args)) 0262 { 0263 } 0264 #endif 0265 0266 void KCModuleProxy::load() 0267 { 0268 Q_D(KCModuleProxy); 0269 if (realModule()) { 0270 d->kcm->load(); 0271 d->_k_moduleChanged(false); 0272 } 0273 } 0274 0275 void KCModuleProxy::save() 0276 { 0277 Q_D(KCModuleProxy); 0278 if (d->changed && realModule()) { 0279 d->kcm->save(); 0280 d->_k_moduleChanged(false); 0281 } 0282 } 0283 0284 void KCModuleProxy::defaults() 0285 { 0286 Q_D(KCModuleProxy); 0287 if (realModule()) { 0288 d->kcm->defaults(); 0289 } 0290 } 0291 0292 QString KCModuleProxy::quickHelp() const 0293 { 0294 return realModule() ? realModule()->quickHelp() : QString(); 0295 } 0296 0297 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85) 0298 const KAboutData *KCModuleProxy::aboutData() const 0299 { 0300 QT_WARNING_PUSH 0301 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") 0302 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") 0303 return realModule() ? realModule()->aboutData() : nullptr; 0304 QT_WARNING_POP 0305 } 0306 #endif 0307 0308 KCModule::Buttons KCModuleProxy::buttons() const 0309 { 0310 if (realModule()) { 0311 return realModule()->buttons(); 0312 } 0313 return KCModule::Buttons(KCModule::Help | KCModule::Default | KCModule::Apply); 0314 } 0315 0316 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87) 0317 bool KCModuleProxy::changed() const 0318 { 0319 Q_D(const KCModuleProxy); 0320 return d->changed; 0321 } 0322 #endif 0323 0324 bool KCModuleProxy::isChanged() const 0325 { 0326 Q_D(const KCModuleProxy); 0327 return d->changed; 0328 } 0329 0330 bool KCModuleProxy::defaulted() const 0331 { 0332 Q_D(const KCModuleProxy); 0333 return d->defaulted; 0334 } 0335 0336 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88) 0337 KCModuleInfo KCModuleProxy::moduleInfo() const 0338 { 0339 Q_D(const KCModuleProxy); 0340 return d->modInfo; 0341 } 0342 #endif 0343 0344 KPluginMetaData KCModuleProxy::metaData() const 0345 { 0346 Q_D(const KCModuleProxy); 0347 return d->metaData.has_value() ? d->metaData.value() : KPluginMetaData(); 0348 } 0349 0350 QString KCModuleProxy::dbusService() const 0351 { 0352 Q_D(const KCModuleProxy); 0353 return d->dbusService; 0354 } 0355 0356 QString KCModuleProxy::dbusPath() const 0357 { 0358 Q_D(const KCModuleProxy); 0359 return d->dbusPath; 0360 } 0361 0362 QSize KCModuleProxy::minimumSizeHint() const 0363 { 0364 return QWidget::minimumSizeHint(); 0365 } 0366 0367 void KCModuleProxy::setDefaultsIndicatorsVisible(bool show) 0368 { 0369 Q_D(KCModuleProxy); 0370 if (realModule()) { 0371 d->kcm->setDefaultsIndicatorsVisible(show); 0372 } 0373 } 0374 0375 // X void KCModuleProxy::emitQuickHelpChanged() 0376 // X { 0377 // X emit quickHelpChanged(); 0378 // X } 0379 0380 /***************************************************************/ 0381 #include "moc_kcmoduleproxy.cpp"