KCMUtils

kcmoduleproxy.cpp
1 /*
2  This file is part of the KDE project
3  SPDX-FileCopyrightText: 2004 Frans Englich <[email protected]>
4  SPDX-FileCopyrightText: 2003 Matthias Kretz <[email protected]>
5  SPDX-FileCopyrightText: 2021 Alexander Lohnau <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-only
8 */
9 
10 #include "kcmoduleproxy.h"
11 #include "kcmoduleproxy_p.h"
12 
13 #include <QApplication>
14 #include <QLayout>
15 
16 #include <QDBusConnection>
17 #include <QDBusConnectionInterface>
18 #include <QDBusInterface>
19 #include <QDBusReply>
20 #include <QDBusServiceWatcher>
21 #include <QPluginLoader>
22 
23 #include <KAboutData>
24 #include <kcmoduleinfo.h>
25 
26 #include <KLocalizedString>
27 
28 #include <kcmoduleloader.h>
29 #include <kcmoduleqml_p.h>
30 
31 #include <KColorScheme>
32 
33 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 82)
34 #include "ksettingswidgetadaptor.h"
35 #endif
36 
37 #include <kcmutils_debug.h>
38 
39 /*
40  TODO:
41 
42  - Resizing horizontally is constrained; minimum size is set somewhere.
43  It appears to be somehow derived from the module's size.
44 
45  - Prettify: set icon in KCMultiDialog.
46 
47  */
48 /***************************************************************/
50 {
51  Q_D(const KCModuleProxy);
52  /*
53  * Note, don't call any function that calls realModule() since
54  * that leads to an infinite loop.
55  */
56 
57  /* Already loaded */
58  if (!d->kcm) {
60  const_cast<KCModuleProxyPrivate *>(d)->loadModule();
62  }
63  return d->kcm;
64 }
65 
66 void KCModuleProxyPrivate::loadModule()
67 {
68  if (!topLayout) {
69  topLayout = new QVBoxLayout(parent);
70  QString name;
71  if (metaData) {
72  name = metaData.value().pluginId();
73  }
74 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
75  if (name.isEmpty()) {
76  name = modInfo.handle();
77  }
78 #endif
79  name.replace(QLatin1Char('-'), QLatin1Char('_')); // hyphen is not allowed in dbus, only [A-Z][a-z][0-9]_
80  name.replace(QLatin1Char('/'), QLatin1Char('_')); // same goes for '/'
81  name.replace(QLatin1Char(' '), QLatin1Char('_')); // same goes for space characters
82  dbusService = QLatin1String("org.kde.internal.KSettingsWidget_") + name;
83 
84  // for dbus path, we also need to convert '.' characters
85  name.replace(QLatin1Char('.'), QLatin1Char('_'));
86  dbusPath = QLatin1String("/internal/KSettingsWidget/") + name;
87  }
88 
89  const bool canRegisterService = QDBusConnection::sessionBus().registerService(dbusService);
90 
91  if (!canRegisterService) {
92  /* We didn't get the name we requested, because it's already taken, */
93  /* Figure out the name of where the module is already loaded */
94  QDBusInterface proxy(dbusService, dbusPath, QStringLiteral("org.kde.internal.KSettingsWidget"));
95  QDBusReply<QString> reply = proxy.call(QStringLiteral("applicationName"));
96  /* If we get a valid application name, the module is already loaded */
97  if (reply.isValid()) {
98  auto *watcher = new QDBusServiceWatcher(parent);
99  watcher->addWatchedService(dbusService);
100  watcher->setConnection(QDBusConnection::sessionBus());
101  watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
102  QObject::connect(watcher,
104  parent,
105  [this](const QString &serviceName, const QString &oldOwner, const QString &newOwner) {
106  _k_ownerChanged(serviceName, oldOwner, newOwner);
107  });
108 
110  i18nc("Argument is application name", "This configuration section is already opened in %1", reply.value()),
111  QStringLiteral(" "),
112  parent);
113  topLayout->addWidget(kcm);
114  return;
115  }
116  }
117 
118  // qDebug() << "Module not already loaded, loading module " << modInfo.moduleName() << " from library " << modInfo.library() << " using symbol " << modInfo.handle();
119  if (metaData) {
120  kcm = KCModuleLoader::loadModule(metaData.value(), parent, QVariantList(args.cbegin(), args.cend()));
121  } else {
122 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
123  kcm = KCModuleLoader::loadModule(modInfo, KCModuleLoader::Inline, parent, args);
124 #endif
125  }
126 
127  QObject::connect(kcm, &KCModule::changed, parent, [this](bool state) {
128  _k_moduleChanged(state);
129  });
130  QObject::connect(kcm, &KCModule::defaulted, parent, [this](bool state) {
131  _k_moduleDefaulted(state);
132  });
133  QObject::connect(kcm, &KCModule::destroyed, parent, [this]() {
134  _k_moduleDestroyed();
135  });
136  QObject::connect(kcm, &KCModule::quickHelpChanged, parent, &KCModuleProxy::quickHelpChanged);
137  parent->setWhatsThis(kcm->quickHelp());
138 
139  if (kcm->layout()) {
140  kcm->layout()->setContentsMargins(0, 0, 0, 0);
141  }
142  if (qobject_cast<KCModuleQml *>(kcm)) {
143  topLayout->setContentsMargins(0, 0, 0, 0);
144  }
145 
146  topLayout->addWidget(kcm);
147 
148 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 82)
149  if (!modInfo.handle().isEmpty()) {
150  QDBusConnection::sessionBus().registerObject(dbusPath, new KSettingsWidgetAdaptor(parent), QDBusConnection::ExportAllSlots);
151  }
152 #endif
153 }
154 
155 void KCModuleProxyPrivate::_k_ownerChanged(const QString &service, const QString &oldOwner, const QString &)
156 {
157  if (service == dbusService && !oldOwner.isEmpty()) {
158  // Violence: Get rid of KCMError & CO, so that
159  // realModule() attempts to reload the module
160  delete kcm;
161  kcm = nullptr;
162  Q_Q(KCModuleProxy);
163  q->realModule();
164 
165  Q_ASSERT(kcm);
166  kcm->show();
167  }
168 }
169 
171 {
173 
174  (void)realModule();
175 
176  /* We have no kcm, if we're in root mode */
177  if (d->kcm) {
178  d->kcm->showEvent(ev);
179  }
180 
181  QWidget::showEvent(ev);
182 }
183 
185 {
186  deleteClient();
187  if (metaData().isValid()) {
188  // Do not try to unload static plugins
189  if (!metaData().isStaticPlugin()) {
190  QPluginLoader(metaData().fileName()).unload();
191  }
192  } else {
193 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
195 #endif
196  }
197 
198  delete d_ptr;
199 }
200 
202 {
204  delete d->kcm;
205  d->kcm = nullptr;
206 }
207 
208 void KCModuleProxyPrivate::_k_moduleChanged(bool c)
209 {
210  if (changed == c) {
211  return;
212  }
213 
214  Q_Q(KCModuleProxy);
215  changed = c;
216  Q_EMIT q->changed(c);
217 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87)
218  Q_EMIT q->changed(q);
219 #endif
220 }
221 
222 void KCModuleProxyPrivate::_k_moduleDefaulted(bool d)
223 {
224  if (defaulted == d) {
225  return;
226  }
227 
228  Q_Q(KCModuleProxy);
229  defaulted = d;
230  Q_EMIT q->changed(changed);
231 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87)
232  Q_EMIT q->changed(q);
233 #endif
234 }
235 
236 void KCModuleProxyPrivate::_k_moduleDestroyed()
237 {
238  kcm = nullptr;
239 }
240 
241 KCModuleProxy::KCModuleProxy(const KPluginMetaData &metaData, QWidget *parent, const QStringList &args)
242  : QWidget(parent)
243  , d_ptr(new KCModuleProxyPrivate(this, metaData, args))
244 {
245 }
246 
247 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
248 KCModuleProxy::KCModuleProxy(const KService::Ptr &service, QWidget *parent, const QStringList &args)
249  : QWidget(parent)
250  , d_ptr(new KCModuleProxyPrivate(this, KCModuleInfo(service), args))
251 {
252  d_ptr->q_ptr = this;
253 }
254 
256  : QWidget(parent)
257  , d_ptr(new KCModuleProxyPrivate(this, info, args))
258 {
259 }
260 
261 KCModuleProxy::KCModuleProxy(const QString &serviceName, QWidget *parent, const QStringList &args)
262  : QWidget(parent)
263  , d_ptr(new KCModuleProxyPrivate(this, KCModuleInfo(serviceName), args))
264 {
265 }
266 #endif
267 
269 {
271  if (realModule()) {
272  d->kcm->load();
273  d->_k_moduleChanged(false);
274  }
275 }
276 
278 {
280  if (d->changed && realModule()) {
281  d->kcm->save();
282  d->_k_moduleChanged(false);
283  }
284 }
285 
287 {
289  if (realModule()) {
290  d->kcm->defaults();
291  }
292 }
293 
295 {
296  return realModule() ? realModule()->quickHelp() : QString();
297 }
298 
299 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
301 {
302  QT_WARNING_PUSH
303  QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
304  QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
305  return realModule() ? realModule()->aboutData() : nullptr;
306  QT_WARNING_POP
307 }
308 #endif
309 
311 {
312  if (realModule()) {
313  return realModule()->buttons();
314  }
315  return KCModule::Buttons(KCModule::Help | KCModule::Default | KCModule::Apply);
316 }
317 
318 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87)
320 {
321  Q_D(const KCModuleProxy);
322  return d->changed;
323 }
324 #endif
325 
327 {
328  Q_D(const KCModuleProxy);
329  return d->changed;
330 }
331 
333 {
334  Q_D(const KCModuleProxy);
335  return d->defaulted;
336 }
337 
338 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
340 {
341  Q_D(const KCModuleProxy);
342  return d->modInfo;
343 }
344 #endif
345 
347 {
348  Q_D(const KCModuleProxy);
349  return d->metaData.has_value() ? d->metaData.value() : KPluginMetaData();
350 }
351 
353 {
354  Q_D(const KCModuleProxy);
355  return d->dbusService;
356 }
357 
359 {
360  Q_D(const KCModuleProxy);
361  return d->dbusPath;
362 }
363 
365 {
366  return QWidget::minimumSizeHint();
367 }
368 
370 {
372  if (realModule()) {
373  d->kcm->setDefaultsIndicatorsVisible(show);
374  }
375 }
376 
377 // X void KCModuleProxy::emitQuickHelpChanged()
378 // X {
379 // X emit quickHelpChanged();
380 // X }
381 
382 /***************************************************************/
383 #include "moc_kcmoduleproxy.cpp"
bool defaulted() const
KCMUTILS_EXPORT void unloadModule(const KCModuleInfo &mod)
Unloads the module's library.
Encapsulates a KCModule for embedding.
Definition: kcmoduleproxy.h:55
void load()
Calling it will cause the contained module to run its load() routine.
void defaulted(bool state)
bool isValid() const const
bool isChanged() const
const KAboutData * aboutData() const
virtual const KAboutData * aboutData() const
QSize minimumSizeHint() const override
Returns the recommended minimum size for the widget.
KCModule::Buttons buttons() const
bool registerObject(const QString &path, QObject *object, QDBusConnection::RegisterOptions options)
KCModuleProxy(const KCModuleInfo &info, QWidget *parent=nullptr, const QStringList &args=QStringList())
Constructs a KCModuleProxy from a KCModuleInfo class.
@ Inline
the error report is shown instead of the KCModule that should have * been loaded
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString quickHelp() const
KCMUTILS_EXPORT KCModule * loadModule(const KCModuleInfo &module, ErrorReporting report, QWidget *parent=nullptr, const QStringList &args=QStringList())
Loads a KCModule.
void destroyed(QObject *obj)
void save()
Calling it will cause the contained module to run its save() routine.
bool changed() const
QDBusConnection sessionBus()
bool registerService(const QString &serviceName)
virtual void showEvent(QShowEvent *event)
bool isEmpty() const const
WaitCursor
void showEvent(QShowEvent *) override
Reimplemented for internal purposes.
void deleteClient()
Calling this, results in deleting the contained module, and unregistering from DCOP.
KPluginMetaData metaData() const
Returns the KPluginMetaData used to load the KCM.
QString dbusPath() const
Returns the D-Bus Path.
QString & replace(int position, int n, QChar after)
void show()
QString dbusService() const
Returns the D-Bus Service name.
void quickHelpChanged()
virtual QString quickHelp() const
void setOverrideCursor(const QCursor &cursor)
void setDefaultsIndicatorsVisible(bool show)
Show or hide an indicator when settings have changed from their default value.
Buttons buttons() const
QString name(StandardShortcut id)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
~KCModuleProxy() override
Default destructor.
void restoreOverrideCursor()
A class that provides information about a KCModule.
Definition: kcmoduleinfo.h:36
QFlags< Button > Buttons
void defaults()
Calling it will cause the contained module to load its default values.
void changed()
void serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
QDBusReply::Type value() const const
Q_D(Todo)
KCModule * realModule() const
Access to the actual module.
KCModuleInfo moduleInfo() const
KCMUTILS_EXPORT KCModule * reportError(ErrorReporting report, const QString &text, const QString &details, QWidget *parent)
Returns a KCModule containing the messages report and text.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:17:34 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.