1 /*
2 SPDX-FileCopyrightText: 2000 Matthias Elter <elter@kde.org>
3 SPDX-FileCopyrightText: 2003 Daniel Molkentin <molkentin@kde.org>
4 SPDX-FileCopyrightText: 2003, 2006 Matthias Kretz <kretz@kde.org>
5 SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com>
6 SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
7 SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11
12 #include "kcmultidialog.h"
13 #include "kcmultidialog_p.h"
14
15 #include "kcmoduleproxy.h"
16 #include "kcmoduleqml_p.h"
17 #include <kcmutils_debug.h>
18
19 #include <QApplication>
20 #include <QDesktopServices>
21 #include <QDesktopWidget>
22 #include <QJsonArray>
23 #include <QLayout>
24 #include <QProcess>
25 #include <QPushButton>
26 #include <QScreen>
27 #include <QStringList>
28 #include <QStyle>
29 #include <QUrl>
30
31 #ifndef KCONFIGWIDGETS_NO_KAUTH
32 #include <KAuthAction>
33 #include <KAuthObjectDecorator>
34 #endif
35 #include <KGuiItem>
36 #include <KIconUtils>
37 #include <KLocalizedString>
38 #include <KMessageBox>
39 #include <KPageWidgetModel>
40 #include <KPluginInfo>
41 #include <KPluginMetaData>
42
resolveChanges(KCModuleProxy * currentProxy)43 bool KCMultiDialogPrivate::resolveChanges(KCModuleProxy *currentProxy)
44 {
45 Q_Q(KCMultiDialog);
46 if (!currentProxy || !currentProxy->isChanged()) {
47 return true;
48 }
49
50 // Let the user decide
51 const int queryUser = KMessageBox::warningYesNoCancel(q,
52 i18n("The settings of the current module have changed.\n"
53 "Do you want to apply the changes or discard them?"),
54 i18n("Apply Settings"),
55 KStandardGuiItem::apply(),
56 KStandardGuiItem::discard(),
57 KStandardGuiItem::cancel());
58
59 switch (queryUser) {
60 case KMessageBox::Yes:
61 return moduleSave(currentProxy);
62
63 case KMessageBox::No:
64 currentProxy->load();
65 return true;
66
67 case KMessageBox::Cancel:
68 return false;
69
70 default:
71 Q_ASSERT(false);
72 return false;
73 }
74 }
75
_k_slotCurrentPageChanged(KPageWidgetItem * current,KPageWidgetItem * previous)76 void KCMultiDialogPrivate::_k_slotCurrentPageChanged(KPageWidgetItem *current, KPageWidgetItem *previous)
77 {
78 Q_Q(KCMultiDialog);
79
80 KCModuleProxy *previousModule = nullptr;
81 for (int i = 0; i < modules.count(); ++i) {
82 if (modules[i].item == previous) {
83 previousModule = modules[i].kcm;
84 }
85 }
86
87 // Delete global margins and spacing, since we want the contents to
88 // be able to touch the edges of the window
89 q->layout()->setContentsMargins(0, 0, 0, 0);
90
91 const KPageWidget *pageWidget = q->pageWidget();
92 pageWidget->layout()->setSpacing(0);
93
94 // Then, we set the margins for the title header and the buttonBox footer
95 const QStyle *style = q->style();
96 const QMargins layoutMargins = QMargins(style->pixelMetric(QStyle::PM_LayoutLeftMargin),
97 style->pixelMetric(QStyle::PM_LayoutTopMargin),
98 style->pixelMetric(QStyle::PM_LayoutRightMargin),
99 style->pixelMetric(QStyle::PM_LayoutBottomMargin));
100
101 if (pageWidget->pageHeader()) {
102 pageWidget->pageHeader()->setContentsMargins(layoutMargins);
103 }
104
105 // Do not set buttonBox's top margin as that space will be covered by the content's bottom margin
106 q->buttonBox()->setContentsMargins(layoutMargins.left(), 0, layoutMargins.right(), layoutMargins.bottom());
107
108 q->blockSignals(true);
109 q->setCurrentPage(previous);
110
111 if (resolveChanges(previousModule)) {
112 q->setCurrentPage(current);
113 }
114 q->blockSignals(false);
115
116 // We need to get the state of the now active module
117 _k_clientChanged();
118 }
119
_k_clientChanged()120 void KCMultiDialogPrivate::_k_clientChanged()
121 {
122 Q_Q(KCMultiDialog);
123 // qDebug();
124 // Get the current module
125 KCModuleProxy *activeModule = nullptr;
126 for (int i = 0; i < modules.count(); ++i) {
127 if (modules[i].item == q->currentPage()) {
128 activeModule = modules[i].kcm;
129 break;
130 }
131 }
132
133 bool change = false;
134 bool defaulted = false;
135 if (activeModule) {
136 change = activeModule->isChanged();
137 defaulted = activeModule->defaulted();
138
139 QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply);
140 if (applyButton) {
141 q->disconnect(applyButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked);
142 #ifndef KCONFIGWIDGETS_NO_KAUTH
143 delete applyButton->findChild<KAuth::ObjectDecorator *>();
144 #endif
145 }
146
147 QPushButton *okButton = q->buttonBox()->button(QDialogButtonBox::Ok);
148 if (okButton) {
149 q->disconnect(okButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked);
150 #ifndef KCONFIGWIDGETS_NO_KAUTH
151 delete okButton->findChild<KAuth::ObjectDecorator *>();
152 #endif
153 }
154
155 #ifndef KCONFIGWIDGETS_NO_KAUTH
156 if (activeModule->realModule()->needsAuthorization()) {
157 if (applyButton) {
158 KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(applyButton);
159 decorator->setAuthAction(activeModule->realModule()->authAction());
160 activeModule->realModule()->authAction().setParentWidget(activeModule->realModule());
161 q->connect(decorator, &KAuth::ObjectDecorator::authorized, q, &KCMultiDialog::slotApplyClicked);
162 }
163
164 if (okButton) {
165 KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(okButton);
166 decorator->setAuthAction(activeModule->realModule()->authAction());
167 activeModule->realModule()->authAction().setParentWidget(activeModule->realModule());
168 q->connect(decorator, &KAuth::ObjectDecorator::authorized, q, &KCMultiDialog::slotOkClicked);
169 }
170 } else {
171 if (applyButton) {
172 q->connect(applyButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked);
173 delete applyButton->findChild<KAuth::ObjectDecorator *>();
174 }
175
176 if (okButton) {
177 q->connect(okButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked);
178 delete okButton->findChild<KAuth::ObjectDecorator *>();
179 }
180 }
181 #endif
182 }
183
184 auto buttons = activeModule ? activeModule->buttons() : KCModule::NoAdditionalButton;
185
186 QPushButton *resetButton = q->buttonBox()->button(QDialogButtonBox::Reset);
187 if (resetButton) {
188 resetButton->setVisible(buttons & KCModule::Apply);
189 resetButton->setEnabled(change);
190 }
191
192 QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply);
193 if (applyButton) {
194 applyButton->setVisible(buttons & KCModule::Apply);
195 applyButton->setEnabled(change);
196 }
197
198 QPushButton *cancelButton = q->buttonBox()->button(QDialogButtonBox::Cancel);
199 if (cancelButton) {
200 cancelButton->setVisible(buttons & KCModule::Apply);
201 }
202
203 QPushButton *okButton = q->buttonBox()->button(QDialogButtonBox::Ok);
204 if (okButton) {
205 okButton->setVisible(buttons & KCModule::Apply);
206 }
207
208 QPushButton *closeButton = q->buttonBox()->button(QDialogButtonBox::Close);
209 if (closeButton) {
210 closeButton->setHidden(buttons & KCModule::Apply);
211 }
212
213 QPushButton *helpButton = q->buttonBox()->button(QDialogButtonBox::Help);
214 if (helpButton) {
215 helpButton->setVisible(buttons & KCModule::Help);
216 }
217
218 QPushButton *defaultButton = q->buttonBox()->button(QDialogButtonBox::RestoreDefaults);
219 if (defaultButton) {
220 defaultButton->setVisible(buttons & KCModule::Default);
221 defaultButton->setEnabled(!defaulted);
222 }
223 }
224
_k_updateHeader(bool use,const QString & message)225 void KCMultiDialogPrivate::_k_updateHeader(bool use, const QString &message)
226 {
227 Q_Q(KCMultiDialog);
228 KPageWidgetItem *item = q->currentPage();
229 KCModuleProxy *kcm = qobject_cast<KCModuleProxy *>(item->widget());
230
231 QString moduleName;
232 QString icon;
233
234 if (kcm->metaData().isValid()) {
235 moduleName = kcm->metaData().name();
236 icon = kcm->metaData().iconName();
237 }
238
239 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
240 if (kcm->moduleInfo().isValid()) {
241 moduleName = kcm->moduleInfo().moduleName();
242 icon = kcm->moduleInfo().icon();
243 }
244 #endif
245
246 if (use) {
247 item->setHeader(QStringLiteral("<b>") + moduleName + QStringLiteral("</b><br><i>") + message + QStringLiteral("</i>"));
248 item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(icon), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner));
249 } else {
250 item->setHeader(moduleName);
251 item->setIcon(QIcon::fromTheme(icon));
252 }
253 }
254
init()255 void KCMultiDialogPrivate::init()
256 {
257 Q_Q(KCMultiDialog);
258 q->setFaceType(KPageDialog::Auto);
259 q->setWindowTitle(i18n("Configure"));
260 q->setModal(false);
261
262 QDialogButtonBox *buttonBox = new QDialogButtonBox(q);
263 buttonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Cancel | QDialogButtonBox::Apply
264 | QDialogButtonBox::Close | QDialogButtonBox::Ok | QDialogButtonBox::Reset);
265 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
266 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
267 KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults());
268 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply());
269 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Close), KStandardGuiItem::close());
270 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Reset), KStandardGuiItem::reset());
271 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Help), KStandardGuiItem::help());
272 buttonBox->button(QDialogButtonBox::Close)->setVisible(false);
273 buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false);
274 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
275
276 q->connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked);
277 q->connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked);
278 q->connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, &KCMultiDialog::slotDefaultClicked);
279 q->connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &KCMultiDialog::slotHelpClicked);
280 q->connect(buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, q, &KCMultiDialog::slotUser1Clicked);
281
282 q->setButtonBox(buttonBox);
283 q->connect(q, &KPageDialog::currentPageChanged, q, [this](KPageWidgetItem *current, KPageWidgetItem *before) {
284 _k_slotCurrentPageChanged(current, before);
285 });
286 }
287
KCMultiDialog(QWidget * parent)288 KCMultiDialog::KCMultiDialog(QWidget *parent)
289 : KPageDialog(parent)
290 , d_ptr(new KCMultiDialogPrivate(this))
291 {
292 d_func()->init();
293 }
294
KCMultiDialog(KPageWidget * pageWidget,QWidget * parent,Qt::WindowFlags flags)295 KCMultiDialog::KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
296 : KPageDialog(pageWidget, parent, flags)
297 , d_ptr(new KCMultiDialogPrivate(this))
298 {
299 d_func()->init();
300 }
301
KCMultiDialog(KCMultiDialogPrivate & dd,KPageWidget * pageWidget,QWidget * parent,Qt::WindowFlags flags)302 KCMultiDialog::KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
303 : KPageDialog(pageWidget, parent, flags)
304 , d_ptr(&dd)
305 {
306 d_func()->init();
307 }
308
~KCMultiDialog()309 KCMultiDialog::~KCMultiDialog()
310 {
311 delete d_ptr;
312 }
313
showEvent(QShowEvent * ev)314 void KCMultiDialog::showEvent(QShowEvent *ev)
315 {
316 KPageDialog::showEvent(ev);
317 adjustSize();
318 /**
319 * adjustSize() relies on sizeHint but is limited to 2/3 of the desktop size
320 * Workaround for https://bugreports.qt.io/browse/QTBUG-3459
321 *
322 * We adjust the size after passing the show event
323 * because otherwise window pos is set to (0,0)
324 */
325 QScreen *screen = QApplication::screenAt(pos());
326 if (screen) {
327 const QSize maxSize = screen->availableGeometry().size();
328 resize(qMin(sizeHint().width(), maxSize.width()), qMin(sizeHint().height(), maxSize.height()));
329 }
330 }
331
slotDefaultClicked()332 void KCMultiDialog::slotDefaultClicked()
333 {
334 Q_D(KCMultiDialog);
335 const KPageWidgetItem *item = currentPage();
336 if (!item) {
337 return;
338 }
339
340 for (int i = 0; i < d->modules.count(); ++i) {
341 if (d->modules[i].item == item) {
342 d->modules[i].kcm->defaults();
343 d->_k_clientChanged();
344 return;
345 }
346 }
347 }
348
slotUser1Clicked()349 void KCMultiDialog::slotUser1Clicked()
350 {
351 const KPageWidgetItem *item = currentPage();
352 if (!item) {
353 return;
354 }
355
356 Q_D(KCMultiDialog);
357 for (int i = 0; i < d->modules.count(); ++i) {
358 if (d->modules[i].item == item) {
359 d->modules[i].kcm->load();
360 d->_k_clientChanged();
361 return;
362 }
363 }
364 }
365
moduleSave(KCModuleProxy * module)366 bool KCMultiDialogPrivate::moduleSave(KCModuleProxy *module)
367 {
368 if (!module) {
369 return false;
370 }
371
372 module->save();
373 return true;
374 }
375
apply()376 void KCMultiDialogPrivate::apply()
377 {
378 Q_Q(KCMultiDialog);
379 QStringList updatedComponents;
380
381 for (const CreatedModule &module : std::as_const(modules)) {
382 KCModuleProxy *proxy = module.kcm;
383
384 if (proxy->isChanged()) {
385 proxy->save();
386 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
387 /**
388 * Add name of the components the kcm belongs to the list
389 * of updated components.
390 */
391 const QStringList componentNames = module.componentNames;
392 for (const QString &componentName : componentNames) {
393 if (!updatedComponents.contains(componentName)) {
394 updatedComponents.append(componentName);
395 }
396 }
397 #endif
398 }
399 }
400
401 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
402 // Send the configCommitted signal for every updated component.
403 for (const QString &name : std::as_const(updatedComponents)) {
404 Q_EMIT q->configCommitted(name.toLatin1());
405 }
406 #endif
407
408 Q_EMIT q->configCommitted();
409 }
410
slotApplyClicked()411 void KCMultiDialog::slotApplyClicked()
412 {
413 QPushButton *applyButton = buttonBox()->button(QDialogButtonBox::Apply);
414 applyButton->setFocus();
415
416 d_func()->apply();
417 }
418
slotOkClicked()419 void KCMultiDialog::slotOkClicked()
420 {
421 QPushButton *okButton = buttonBox()->button(QDialogButtonBox::Ok);
422 okButton->setFocus();
423
424 d_func()->apply();
425 accept();
426 }
427
slotHelpClicked()428 void KCMultiDialog::slotHelpClicked()
429 {
430 const KPageWidgetItem *item = currentPage();
431 if (!item) {
432 return;
433 }
434
435 Q_D(KCMultiDialog);
436 QString docPath;
437 for (int i = 0; i < d->modules.count(); ++i) {
438 if (d->modules[i].item == item) {
439 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
440 docPath = d->modules[i].kcm->moduleInfo().docPath();
441 #endif
442 if (docPath.isEmpty()) {
443 docPath = d->modules[i].kcm->metaData().value(QStringLiteral("X-DocPath"));
444 }
445 break;
446 }
447 }
448
449 const QUrl docUrl = QUrl(QStringLiteral("help:/")).resolved(QUrl(docPath)); // same code as in KHelpClient::invokeHelp
450 const QString docUrlScheme = docUrl.scheme();
451 if (docUrlScheme == QLatin1String("man") || docUrlScheme == QLatin1String("info")) {
452 QProcess::startDetached(QStringLiteral("khelpcenter"), QStringList() << docUrl.toString());
453 } else {
454 QDesktopServices::openUrl(docUrl);
455 }
456 }
457
closeEvent(QCloseEvent * event)458 void KCMultiDialog::closeEvent(QCloseEvent *event)
459 {
460 Q_D(KCMultiDialog);
461 KPageDialog::closeEvent(event);
462
463 /**
464 * If we don't delete them, the DBUS registration stays, and trying to load the KCMs
465 * in other situations will lead to "module already loaded in Foo," while to the user
466 * doesn't appear so(the dialog is hidden)
467 */
468 for (auto &proxy : std::as_const(d->modules)) {
469 proxy.kcm->deleteClient();
470 }
471 }
472
addModule(const KPluginMetaData & metaData)473 KPageWidgetItem *KCMultiDialog::addModule(const KPluginMetaData &metaData)
474 {
475 return addModule(metaData, QStringList());
476 }
477
addModule(const KPluginMetaData & metaData,const QStringList & args)478 KPageWidgetItem *KCMultiDialog::addModule(const KPluginMetaData &metaData, const QStringList &args)
479 {
480 Q_D(KCMultiDialog);
481 // Create the scroller
482 auto *moduleScroll = new UnboundScrollArea(this);
483 // Prepare the scroll area
484 moduleScroll->setWidgetResizable(true);
485 moduleScroll->setFrameStyle(QFrame::NoFrame);
486 moduleScroll->viewport()->setAutoFillBackground(false);
487
488 KCModuleProxy *kcm = new KCModuleProxy(metaData, moduleScroll, args);
489 moduleScroll->setWidget(kcm);
490
491 KPageWidgetItem *item = new KPageWidgetItem(moduleScroll, metaData.name());
492
493 KCMultiDialogPrivate::CreatedModule createdModule;
494 createdModule.kcm = kcm;
495 createdModule.item = item;
496 d->modules.append(createdModule);
497
498 if (qobject_cast<KCModuleQml *>(kcm->realModule())) {
499 item->setHeaderVisible(false);
500 }
501
502 if (kcm->realModule() && kcm->realModule()->useRootOnlyMessage()) {
503 item->setHeader(QStringLiteral("<b>%1</b><br><i>%2</i>").arg(metaData.name(), kcm->realModule()->rootOnlyMessage()));
504 item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(metaData.iconName()), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner));
505 } else {
506 item->setHeader(metaData.name());
507 item->setIcon(QIcon::fromTheme(metaData.iconName()));
508 }
509 const int weight = metaData.rawData().value(QStringLiteral("X-KDE-Weight")).toInt();
510 item->setProperty("_k_weight", weight);
511
512 bool updateCurrentPage = false;
513 const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(pageWidget()->model());
514 Q_ASSERT(model);
515 const int siblingCount = model->rowCount();
516 int row = 0;
517 for (; row < siblingCount; ++row) {
518 KPageWidgetItem *siblingItem = model->item(model->index(row, 0));
519 if (siblingItem->property("_k_weight").toInt() > weight) {
520 // the item we found is heavier than the new module
521 // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name();
522 insertPage(siblingItem, item);
523 if (siblingItem == currentPage()) {
524 updateCurrentPage = true;
525 }
526
527 break;
528 }
529 }
530 if (row == siblingCount) {
531 // the new module is either the first or the heaviest item
532 // qDebug() << "adding KCM " << item->name() << " at the top level";
533 addPage(item);
534 }
535
536 QObject::connect(kcm, qOverload<bool>(&KCModuleProxy::changed), this, [d]() {
537 d->_k_clientChanged();
538 });
539
540 QObject::connect(kcm->realModule(), &KCModule::rootOnlyMessageChanged, this, [d](bool use, const QString &message) {
541 d->_k_updateHeader(use, message);
542 });
543
544 if (d->modules.count() == 1 || updateCurrentPage) {
545 setCurrentPage(item);
546 d->_k_clientChanged();
547 }
548 return item;
549 }
550
551 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
addModule(const QString & path,const QStringList & args)552 KPageWidgetItem *KCMultiDialog::addModule(const QString &path, const QStringList &args)
553 {
554 QString complete = path;
555
556 if (!path.endsWith(QLatin1String(".desktop"))) {
557 complete += QStringLiteral(".desktop");
558 }
559
560 KService::Ptr service = KService::serviceByStorageId(complete);
561
562 return addModule(KCModuleInfo(service), nullptr, args);
563 }
564 #endif
565
566 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
addModule(const KCModuleInfo & moduleInfo,KPageWidgetItem * parentItem,const QStringList & args)567 KPageWidgetItem *KCMultiDialog::addModule(const KCModuleInfo &moduleInfo, KPageWidgetItem *parentItem, const QStringList &args)
568 {
569 Q_D(KCMultiDialog);
570 if (!moduleInfo.isValid()) {
571 return nullptr;
572 }
573
574 // KAuthorized::authorizeControlModule( moduleInfo.service()->menuId() ) is
575 // checked in noDisplay already
576 if (moduleInfo.service() && moduleInfo.service()->noDisplay()) {
577 return nullptr;
578 }
579
580 // Create the scroller
581 auto *moduleScroll = new UnboundScrollArea(this);
582 // Prepare the scroll area
583 moduleScroll->setWidgetResizable(true);
584 moduleScroll->setFrameStyle(QFrame::NoFrame);
585 moduleScroll->viewport()->setAutoFillBackground(false);
586
587 KCModuleProxy *kcm = new KCModuleProxy(moduleInfo, moduleScroll, args);
588 moduleScroll->setWidget(kcm);
589
590 // qDebug() << moduleInfo.moduleName();
591 KPageWidgetItem *item = new KPageWidgetItem(moduleScroll, moduleInfo.moduleName());
592
593 KCMultiDialogPrivate::CreatedModule cm;
594 cm.kcm = kcm;
595 cm.item = item;
596 cm.componentNames = moduleInfo.property(QStringLiteral("X-KDE-ParentComponents")).toStringList();
597 d->modules.append(cm);
598
599 if (qobject_cast<KCModuleQml *>(kcm->realModule())) {
600 item->setHeaderVisible(false);
601 }
602
603 if (kcm->realModule() && kcm->realModule()->useRootOnlyMessage()) {
604 item->setHeader(QStringLiteral("<b>") + moduleInfo.moduleName() + QStringLiteral("</b><br><i>") + kcm->realModule()->rootOnlyMessage()
605 + QStringLiteral("</i>"));
606 item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(moduleInfo.icon()), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner));
607 } else {
608 item->setHeader(moduleInfo.moduleName());
609 item->setIcon(QIcon::fromTheme(moduleInfo.icon()));
610 }
611 item->setProperty("_k_weight", moduleInfo.weight());
612
613 bool updateCurrentPage = false;
614 const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(pageWidget()->model());
615 Q_ASSERT(model);
616 if (parentItem) {
617 const QModelIndex parentIndex = model->index(parentItem);
618 const int siblingCount = model->rowCount(parentIndex);
619 int row = 0;
620 for (; row < siblingCount; ++row) {
621 KPageWidgetItem *siblingItem = model->item(model->index(row, 0, parentIndex));
622 if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
623 // the item we found is heavier than the new module
624 // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name();
625 insertPage(siblingItem, item);
626 break;
627 }
628 }
629 if (row >= siblingCount) {
630 // the new module is either the first or the heaviest item
631 // qDebug() << "adding KCM " << item->name() << " with parent " << parentItem->name();
632 addSubPage(parentItem, item);
633 }
634 } else {
635 const int siblingCount = model->rowCount();
636 int row = 0;
637 for (; row < siblingCount; ++row) {
638 KPageWidgetItem *siblingItem = model->item(model->index(row, 0));
639 if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
640 // the item we found is heavier than the new module
641 // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name();
642 insertPage(siblingItem, item);
643 if (siblingItem == currentPage()) {
644 updateCurrentPage = true;
645 }
646
647 break;
648 }
649 }
650 if (row == siblingCount) {
651 // the new module is either the first or the heaviest item
652 // qDebug() << "adding KCM " << item->name() << " at the top level";
653 addPage(item);
654 }
655 }
656
657 QObject::connect(kcm, qOverload<bool>(&KCModuleProxy::changed), this, [d]() {
658 d->_k_clientChanged();
659 });
660
661 QObject::connect(kcm->realModule(), &KCModule::rootOnlyMessageChanged, this, [d](bool use, QString message) {
662 d->_k_updateHeader(use, message);
663 });
664
665 if (d->modules.count() == 1 || updateCurrentPage) {
666 setCurrentPage(item);
667 d->_k_clientChanged();
668 }
669 return item;
670 }
671 #endif
672
clear()673 void KCMultiDialog::clear()
674 {
675 Q_D(KCMultiDialog);
676 // qDebug() ;
677
678 for (int i = 0; i < d->modules.count(); ++i) {
679 removePage(d->modules[i].item);
680 delete d->modules[i].kcm;
681 }
682
683 d->modules.clear();
684
685 d->_k_clientChanged();
686 }
687
688 #include "moc_kcmultidialog.cpp"
689