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 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 82)
32 #include "ksettingswidgetadaptor.h"
33 #endif
34 
35 #include <kcmutils_debug.h>
36 
37 /*
38  TODO:
39 
40  - Resizing horizontally is constrained; minimum size is set somewhere.
41  It appears to be somehow derived from the module's size.
42 
43  - Prettify: set icon in KCMultiDialog.
44 
45  */
46 /***************************************************************/
48 {
49  Q_D(const KCModuleProxy);
50  /*
51  * Note, don't call any function that calls realModule() since
52  * that leads to an infinite loop.
53  */
54 
55  /* Already loaded */
56  if (!d->kcm) {
58  const_cast<KCModuleProxyPrivate *>(d)->loadModule();
60  }
61  return d->kcm;
62 }
63 
64 void KCModuleProxyPrivate::loadModule()
65 {
66  if (!topLayout) {
67  topLayout = new QVBoxLayout(parent);
68  QString name;
69  if (metaData) {
70  name = metaData.value().pluginId();
71  }
72 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
73  if (name.isEmpty()) {
74  name = modInfo.handle();
75  }
76 #endif
77  name.replace(QLatin1Char('-'), QLatin1Char('_')); // hyphen is not allowed in dbus, only [A-Z][a-z][0-9]_
78  name.replace(QLatin1Char('/'), QLatin1Char('_')); // same goes for '/'
79  name.replace(QLatin1Char(' '), QLatin1Char('_')); // same goes for space characters
80  dbusService = QLatin1String("org.kde.internal.KSettingsWidget_") + name;
81 
82  // for dbus path, we also need to convert '.' characters
83  name.replace(QLatin1Char('.'), QLatin1Char('_'));
84  dbusPath = QLatin1String("/internal/KSettingsWidget/") + name;
85  }
86 
87  const bool canRegisterService = QDBusConnection::sessionBus().registerService(dbusService);
88 
89  if (!canRegisterService) {
90  /* We didn't get the name we requested, because it's already taken, */
91  /* Figure out the name of where the module is already loaded */
92  QDBusInterface proxy(dbusService, dbusPath, QStringLiteral("org.kde.internal.KSettingsWidget"));
93  QDBusReply<QString> reply = proxy.call(QStringLiteral("applicationName"));
94  /* If we get a valid application name, the module is already loaded */
95  if (reply.isValid()) {
96  auto *watcher = new QDBusServiceWatcher(parent);
97  watcher->addWatchedService(dbusService);
98  watcher->setConnection(QDBusConnection::sessionBus());
99  watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
100  QObject::connect(watcher,
102  parent,
103  [this](const QString &serviceName, const QString &oldOwner, const QString &newOwner) {
104  _k_ownerChanged(serviceName, oldOwner, newOwner);
105  });
106 
108  i18nc("Argument is application name", "This configuration section is already opened in %1", reply.value()),
109  QStringLiteral(" "),
110  parent);
111  topLayout->addWidget(kcm);
112  return;
113  }
114  }
115 
116  // qDebug() << "Module not already loaded, loading module " << modInfo.moduleName() << " from library " << modInfo.library() << " using symbol " << modInfo.handle();
117  if (metaData) {
118  kcm = KCModuleLoader::loadModule(metaData.value(), parent, QVariantList(args.cbegin(), args.cend()));
119  } else {
120 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
121  kcm = KCModuleLoader::loadModule(modInfo, KCModuleLoader::Inline, parent, args);
122 #endif
123  }
124 
125  QObject::connect(kcm, &KCModule::changed, parent, [this](bool state) {
126  _k_moduleChanged(state);
127  });
128  QObject::connect(kcm, &KCModule::defaulted, parent, [this](bool state) {
129  _k_moduleDefaulted(state);
130  });
131  QObject::connect(kcm, &KCModule::destroyed, parent, [this]() {
132  _k_moduleDestroyed();
133  });
134  QObject::connect(kcm, &KCModule::quickHelpChanged, parent, &KCModuleProxy::quickHelpChanged);
135  parent->setWhatsThis(kcm->quickHelp());
136 
137  if (kcm->layout()) {
138  kcm->layout()->setContentsMargins(0, 0, 0, 0);
139  }
140  if (qobject_cast<KCModuleQml *>(kcm)) {
141  topLayout->setContentsMargins(0, 0, 0, 0);
142  }
143 
144  topLayout->addWidget(kcm);
145 
146 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 82)
147  if (!modInfo.handle().isEmpty()) {
148  QDBusConnection::sessionBus().registerObject(dbusPath, new KSettingsWidgetAdaptor(parent), QDBusConnection::ExportAllSlots);
149  }
150 #endif
151 }
152 
153 void KCModuleProxyPrivate::_k_ownerChanged(const QString &service, const QString &oldOwner, const QString &)
154 {
155  if (service == dbusService && !oldOwner.isEmpty()) {
156  // Violence: Get rid of KCMError & CO, so that
157  // realModule() attempts to reload the module
158  delete kcm;
159  kcm = nullptr;
160  Q_Q(KCModuleProxy);
161  q->realModule();
162 
163  Q_ASSERT(kcm);
164  kcm->show();
165  }
166 }
167 
169 {
171 
172  (void)realModule();
173 
174  /* We have no kcm, if we're in root mode */
175  if (d->kcm) {
176  d->kcm->showEvent(ev);
177  }
178 
179  QWidget::showEvent(ev);
180 }
181 
183 {
184  deleteClient();
185  if (metaData().isValid()) {
186  // Do not try to unload static plugins
187  if (!metaData().isStaticPlugin()) {
188  QPluginLoader(metaData().fileName()).unload();
189  }
190  } else {
191 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
193 #endif
194  }
195 
196  delete d_ptr;
197 }
198 
200 {
202  delete d->kcm;
203  d->kcm = nullptr;
204 }
205 
206 void KCModuleProxyPrivate::_k_moduleChanged(bool c)
207 {
208  if (changed == c) {
209  return;
210  }
211 
212  Q_Q(KCModuleProxy);
213  changed = c;
214  Q_EMIT q->changed(c);
215 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87)
216  Q_EMIT q->changed(q);
217 #endif
218 }
219 
220 void KCModuleProxyPrivate::_k_moduleDefaulted(bool d)
221 {
222  if (defaulted == d) {
223  return;
224  }
225 
226  Q_Q(KCModuleProxy);
227  defaulted = d;
228  Q_EMIT q->changed(changed);
229 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87)
230  Q_EMIT q->changed(q);
231 #endif
232 }
233 
234 void KCModuleProxyPrivate::_k_moduleDestroyed()
235 {
236  kcm = nullptr;
237 }
238 
239 KCModuleProxy::KCModuleProxy(const KPluginMetaData &metaData, QWidget *parent, const QStringList &args)
240  : QWidget(parent)
241  , d_ptr(new KCModuleProxyPrivate(this, metaData, args))
242 {
243 }
244 
245 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
246 KCModuleProxy::KCModuleProxy(const KService::Ptr &service, QWidget *parent, const QStringList &args)
247  : QWidget(parent)
248  , d_ptr(new KCModuleProxyPrivate(this, KCModuleInfo(service), args))
249 {
250  d_ptr->q_ptr = this;
251 }
252 
254  : QWidget(parent)
255  , d_ptr(new KCModuleProxyPrivate(this, info, args))
256 {
257 }
258 
259 KCModuleProxy::KCModuleProxy(const QString &serviceName, QWidget *parent, const QStringList &args)
260  : QWidget(parent)
261  , d_ptr(new KCModuleProxyPrivate(this, KCModuleInfo(serviceName), args))
262 {
263 }
264 #endif
265 
267 {
269  if (realModule()) {
270  d->kcm->load();
271  d->_k_moduleChanged(false);
272  }
273 }
274 
276 {
278  if (d->changed && realModule()) {
279  d->kcm->save();
280  d->_k_moduleChanged(false);
281  }
282 }
283 
285 {
287  if (realModule()) {
288  d->kcm->defaults();
289  }
290 }
291 
293 {
294  return realModule() ? realModule()->quickHelp() : QString();
295 }
296 
297 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
299 {
300  QT_WARNING_PUSH
301  QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
302  QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
303  return realModule() ? realModule()->aboutData() : nullptr;
304  QT_WARNING_POP
305 }
306 #endif
307 
309 {
310  if (realModule()) {
311  return realModule()->buttons();
312  }
313  return KCModule::Buttons(KCModule::Help | KCModule::Default | KCModule::Apply);
314 }
315 
316 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 87)
318 {
319  Q_D(const KCModuleProxy);
320  return d->changed;
321 }
322 #endif
323 
325 {
326  Q_D(const KCModuleProxy);
327  return d->changed;
328 }
329 
331 {
332  Q_D(const KCModuleProxy);
333  return d->defaulted;
334 }
335 
336 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
338 {
339  Q_D(const KCModuleProxy);
340  return d->modInfo;
341 }
342 #endif
343 
345 {
346  Q_D(const KCModuleProxy);
347  return d->metaData.has_value() ? d->metaData.value() : KPluginMetaData();
348 }
349 
351 {
352  Q_D(const KCModuleProxy);
353  return d->dbusService;
354 }
355 
357 {
358  Q_D(const KCModuleProxy);
359  return d->dbusPath;
360 }
361 
363 {
364  return QWidget::minimumSizeHint();
365 }
366 
368 {
370  if (realModule()) {
371  d->kcm->setDefaultsIndicatorsVisible(show);
372  }
373 }
374 
375 // X void KCModuleProxy::emitQuickHelpChanged()
376 // X {
377 // X emit quickHelpChanged();
378 // X }
379 
380 /***************************************************************/
381 #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-2023 The KDE developers.
Generated on Mon May 8 2023 04:05:07 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.