File indexing completed on 2024-04-14 14:18:23

0001 /*
0002     SPDX-FileCopyrightText: 2000 Matthias Elter <elter@kde.org>
0003     SPDX-FileCopyrightText: 2003 Daniel Molkentin <molkentin@kde.org>
0004     SPDX-FileCopyrightText: 2003, 2006 Matthias Kretz <kretz@kde.org>
0005     SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com>
0006     SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
0007     SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
0008 
0009     SPDX-License-Identifier: LGPL-2.0-or-later
0010 */
0011 
0012 #include "kcmultidialog.h"
0013 #include "kcmultidialog_p.h"
0014 
0015 #include "kcmoduleproxy.h"
0016 #include "kcmoduleqml_p.h"
0017 #include <kcmutils_debug.h>
0018 
0019 #include <QApplication>
0020 #include <QDesktopServices>
0021 #include <QJsonArray>
0022 #include <QLayout>
0023 #include <QProcess>
0024 #include <QPushButton>
0025 #include <QScreen>
0026 #include <QStandardPaths>
0027 #include <QStringList>
0028 #include <QStyle>
0029 #include <QUrl>
0030 
0031 #if KCONFIGWIDGETS_WITH_KAUTH
0032 #include <KAuth/Action>
0033 #include <KAuth/ObjectDecorator>
0034 #endif
0035 #include <KGuiItem>
0036 #include <KIconUtils>
0037 #include <KLocalizedString>
0038 #include <KMessageBox>
0039 #include <KPageWidgetModel>
0040 
0041 bool KCMultiDialogPrivate::resolveChanges(KCModuleProxy *currentProxy)
0042 {
0043     Q_Q(KCMultiDialog);
0044     if (!currentProxy || !currentProxy->isChanged()) {
0045         return true;
0046     }
0047 
0048     // Let the user decide
0049     const int queryUser = KMessageBox::warningTwoActionsCancel(q,
0050                                                                i18n("The settings of the current module have changed.\n"
0051                                                                     "Do you want to apply the changes or discard them?"),
0052                                                                i18n("Apply Settings"),
0053                                                                KStandardGuiItem::apply(),
0054                                                                KStandardGuiItem::discard(),
0055                                                                KStandardGuiItem::cancel());
0056 
0057     switch (queryUser) {
0058     case KMessageBox::PrimaryAction:
0059         return moduleSave(currentProxy);
0060 
0061     case KMessageBox::SecondaryAction:
0062         currentProxy->load();
0063         return true;
0064 
0065     case KMessageBox::Cancel:
0066         return false;
0067 
0068     default:
0069         Q_ASSERT(false);
0070         return false;
0071     }
0072 }
0073 
0074 void KCMultiDialogPrivate::_k_slotCurrentPageChanged(KPageWidgetItem *current, KPageWidgetItem *previous)
0075 {
0076     Q_Q(KCMultiDialog);
0077 
0078     KCModuleProxy *previousModule = nullptr;
0079     for (int i = 0; i < modules.count(); ++i) {
0080         if (modules[i].item == previous) {
0081             previousModule = modules[i].kcm;
0082         }
0083     }
0084 
0085     // Delete global margins and spacing, since we want the contents to
0086     // be able to touch the edges of the window
0087     q->layout()->setContentsMargins(0, 0, 0, 0);
0088 
0089     const KPageWidget *pageWidget = q->pageWidget();
0090     pageWidget->layout()->setSpacing(0);
0091 
0092     // Then, we set the margins for the title header and the buttonBox footer
0093     const QStyle *style = q->style();
0094     const QMargins layoutMargins = QMargins(style->pixelMetric(QStyle::PM_LayoutLeftMargin),
0095                                             style->pixelMetric(QStyle::PM_LayoutTopMargin),
0096                                             style->pixelMetric(QStyle::PM_LayoutRightMargin),
0097                                             style->pixelMetric(QStyle::PM_LayoutBottomMargin));
0098 
0099     if (pageWidget->pageHeader()) {
0100         pageWidget->pageHeader()->setContentsMargins(layoutMargins);
0101     }
0102 
0103     // Do not set buttonBox's top margin as that space will be covered by the content's bottom margin
0104     q->buttonBox()->setContentsMargins(layoutMargins.left(), 0, layoutMargins.right(), layoutMargins.bottom());
0105 
0106     q->blockSignals(true);
0107     q->setCurrentPage(previous);
0108 
0109     if (resolveChanges(previousModule)) {
0110         q->setCurrentPage(current);
0111     }
0112     q->blockSignals(false);
0113 
0114     // We need to get the state of the now active module
0115     _k_clientChanged();
0116 }
0117 
0118 void KCMultiDialogPrivate::_k_clientChanged()
0119 {
0120     Q_Q(KCMultiDialog);
0121     // qDebug();
0122     // Get the current module
0123     KCModuleProxy *activeModule = nullptr;
0124     for (int i = 0; i < modules.count(); ++i) {
0125         if (modules[i].item == q->currentPage()) {
0126             activeModule = modules[i].kcm;
0127             break;
0128         }
0129     }
0130 
0131     bool change = false;
0132     bool defaulted = false;
0133     if (activeModule) {
0134         change = activeModule->isChanged();
0135         defaulted = activeModule->defaulted();
0136 
0137         QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply);
0138         if (applyButton) {
0139             q->disconnect(applyButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked);
0140 #if KCONFIGWIDGETS_WITH_KAUTH
0141             delete applyButton->findChild<KAuth::ObjectDecorator *>();
0142 #endif
0143         }
0144 
0145         QPushButton *okButton = q->buttonBox()->button(QDialogButtonBox::Ok);
0146         if (okButton) {
0147             q->disconnect(okButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked);
0148 #if KCONFIGWIDGETS_WITH_KAUTH
0149             delete okButton->findChild<KAuth::ObjectDecorator *>();
0150 #endif
0151         }
0152 
0153 #if KCONFIGWIDGETS_WITH_KAUTH
0154         if (activeModule->realModule()->needsAuthorization()) {
0155             if (applyButton) {
0156                 KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(applyButton);
0157                 decorator->setAuthAction(activeModule->realModule()->authAction());
0158                 activeModule->realModule()->authAction().setParentWidget(activeModule->realModule());
0159                 q->connect(decorator, &KAuth::ObjectDecorator::authorized, q, &KCMultiDialog::slotApplyClicked);
0160             }
0161 
0162             if (okButton) {
0163                 KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(okButton);
0164                 decorator->setAuthAction(activeModule->realModule()->authAction());
0165                 activeModule->realModule()->authAction().setParentWidget(activeModule->realModule());
0166                 q->connect(decorator, &KAuth::ObjectDecorator::authorized, q, &KCMultiDialog::slotOkClicked);
0167             }
0168         } else {
0169             if (applyButton) {
0170                 q->connect(applyButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked);
0171                 delete applyButton->findChild<KAuth::ObjectDecorator *>();
0172             }
0173 
0174             if (okButton) {
0175                 q->connect(okButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked);
0176                 delete okButton->findChild<KAuth::ObjectDecorator *>();
0177             }
0178         }
0179 #endif
0180     }
0181 
0182     auto buttons = activeModule ? activeModule->buttons() : KCModule::NoAdditionalButton;
0183 
0184     QPushButton *resetButton = q->buttonBox()->button(QDialogButtonBox::Reset);
0185     if (resetButton) {
0186         resetButton->setVisible(buttons & KCModule::Apply);
0187         resetButton->setEnabled(change);
0188     }
0189 
0190     QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply);
0191     if (applyButton) {
0192         applyButton->setVisible(buttons & KCModule::Apply);
0193         applyButton->setEnabled(change);
0194     }
0195 
0196     QPushButton *cancelButton = q->buttonBox()->button(QDialogButtonBox::Cancel);
0197     if (cancelButton) {
0198         cancelButton->setVisible(buttons & KCModule::Apply);
0199     }
0200 
0201     QPushButton *okButton = q->buttonBox()->button(QDialogButtonBox::Ok);
0202     if (okButton) {
0203         okButton->setVisible(buttons & KCModule::Apply);
0204     }
0205 
0206     QPushButton *closeButton = q->buttonBox()->button(QDialogButtonBox::Close);
0207     if (closeButton) {
0208         closeButton->setHidden(buttons & KCModule::Apply);
0209     }
0210 
0211     QPushButton *helpButton = q->buttonBox()->button(QDialogButtonBox::Help);
0212     if (helpButton) {
0213         helpButton->setVisible(buttons & KCModule::Help);
0214     }
0215 
0216     QPushButton *defaultButton = q->buttonBox()->button(QDialogButtonBox::RestoreDefaults);
0217     if (defaultButton) {
0218         defaultButton->setVisible(buttons & KCModule::Default);
0219         defaultButton->setEnabled(!defaulted);
0220     }
0221 }
0222 
0223 void KCMultiDialogPrivate::_k_updateHeader(bool use, const QString &message)
0224 {
0225     Q_Q(KCMultiDialog);
0226     KPageWidgetItem *item = q->currentPage();
0227     KCModuleProxy *kcm = qobject_cast<KCModuleProxy *>(item->widget());
0228 
0229     QString moduleName;
0230     QString icon;
0231 
0232     if (kcm->metaData().isValid()) {
0233         moduleName = kcm->metaData().name();
0234         icon = kcm->metaData().iconName();
0235     }
0236 
0237 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
0238     if (kcm->moduleInfo().isValid()) {
0239         moduleName = kcm->moduleInfo().moduleName();
0240         icon = kcm->moduleInfo().icon();
0241     }
0242 #endif
0243 
0244     if (use) {
0245         item->setHeader(QStringLiteral("<b>") + moduleName + QStringLiteral("</b><br><i>") + message + QStringLiteral("</i>"));
0246         item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(icon), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner));
0247     } else {
0248         item->setHeader(moduleName);
0249         item->setIcon(QIcon::fromTheme(icon));
0250     }
0251 }
0252 
0253 void KCMultiDialogPrivate::init()
0254 {
0255     Q_Q(KCMultiDialog);
0256     q->setFaceType(KPageDialog::Auto);
0257     q->setWindowTitle(i18n("Configure"));
0258     q->setModal(false);
0259 
0260     QDialogButtonBox *buttonBox = new QDialogButtonBox(q);
0261     buttonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Cancel | QDialogButtonBox::Apply
0262                                   | QDialogButtonBox::Close | QDialogButtonBox::Ok | QDialogButtonBox::Reset);
0263     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
0264     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
0265     KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults());
0266     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply());
0267     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Close), KStandardGuiItem::close());
0268     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Reset), KStandardGuiItem::reset());
0269     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Help), KStandardGuiItem::help());
0270     buttonBox->button(QDialogButtonBox::Close)->setVisible(false);
0271     buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false);
0272     buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
0273 
0274     q->connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked);
0275     q->connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked);
0276     q->connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, &KCMultiDialog::slotDefaultClicked);
0277     q->connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &KCMultiDialog::slotHelpClicked);
0278     q->connect(buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, q, &KCMultiDialog::slotUser1Clicked);
0279 
0280     q->setButtonBox(buttonBox);
0281     q->connect(q, &KPageDialog::currentPageChanged, q, [this](KPageWidgetItem *current, KPageWidgetItem *before) {
0282         _k_slotCurrentPageChanged(current, before);
0283     });
0284 }
0285 
0286 KCMultiDialog::KCMultiDialog(QWidget *parent)
0287     : KPageDialog(parent)
0288     , d_ptr(new KCMultiDialogPrivate(this))
0289 {
0290     d_func()->init();
0291 }
0292 
0293 KCMultiDialog::KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
0294     : KPageDialog(pageWidget, parent, flags)
0295     , d_ptr(new KCMultiDialogPrivate(this))
0296 {
0297     d_func()->init();
0298 }
0299 
0300 KCMultiDialog::KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
0301     : KPageDialog(pageWidget, parent, flags)
0302     , d_ptr(&dd)
0303 {
0304     d_func()->init();
0305 }
0306 
0307 KCMultiDialog::~KCMultiDialog()
0308 {
0309     delete d_ptr;
0310 }
0311 
0312 void KCMultiDialog::showEvent(QShowEvent *ev)
0313 {
0314     KPageDialog::showEvent(ev);
0315     adjustSize();
0316     /**
0317      * adjustSize() relies on sizeHint but is limited to 2/3 of the desktop size
0318      * Workaround for https://bugreports.qt.io/browse/QTBUG-3459
0319      *
0320      * We adjust the size after passing the show event
0321      * because otherwise window pos is set to (0,0)
0322      */
0323 
0324     const QSize maxSize = screen()->availableGeometry().size();
0325     resize(qMin(sizeHint().width(), maxSize.width()), qMin(sizeHint().height(), maxSize.height()));
0326 }
0327 
0328 void KCMultiDialog::slotDefaultClicked()
0329 {
0330     Q_D(KCMultiDialog);
0331     const KPageWidgetItem *item = currentPage();
0332     if (!item) {
0333         return;
0334     }
0335 
0336     for (int i = 0; i < d->modules.count(); ++i) {
0337         if (d->modules[i].item == item) {
0338             d->modules[i].kcm->defaults();
0339             d->_k_clientChanged();
0340             return;
0341         }
0342     }
0343 }
0344 
0345 void KCMultiDialog::slotUser1Clicked()
0346 {
0347     const KPageWidgetItem *item = currentPage();
0348     if (!item) {
0349         return;
0350     }
0351 
0352     Q_D(KCMultiDialog);
0353     for (int i = 0; i < d->modules.count(); ++i) {
0354         if (d->modules[i].item == item) {
0355             d->modules[i].kcm->load();
0356             d->_k_clientChanged();
0357             return;
0358         }
0359     }
0360 }
0361 
0362 bool KCMultiDialogPrivate::moduleSave(KCModuleProxy *module)
0363 {
0364     if (!module) {
0365         return false;
0366     }
0367 
0368     module->save();
0369     return true;
0370 }
0371 
0372 void KCMultiDialogPrivate::apply()
0373 {
0374     Q_Q(KCMultiDialog);
0375     QStringList updatedComponents;
0376 
0377     for (const CreatedModule &module : std::as_const(modules)) {
0378         KCModuleProxy *proxy = module.kcm;
0379 
0380         if (proxy->isChanged()) {
0381             proxy->save();
0382 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
0383             /**
0384              * Add name of the components the kcm belongs to the list
0385              * of updated components.
0386              */
0387             const QStringList componentNames = module.componentNames;
0388             for (const QString &componentName : componentNames) {
0389                 if (!updatedComponents.contains(componentName)) {
0390                     updatedComponents.append(componentName);
0391                 }
0392             }
0393 #endif
0394         }
0395     }
0396 
0397 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
0398     // Send the configCommitted signal for every updated component.
0399     for (const QString &name : std::as_const(updatedComponents)) {
0400         Q_EMIT q->configCommitted(name.toLatin1());
0401     }
0402 #endif
0403 
0404     Q_EMIT q->configCommitted();
0405 }
0406 
0407 void KCMultiDialog::slotApplyClicked()
0408 {
0409     QPushButton *applyButton = buttonBox()->button(QDialogButtonBox::Apply);
0410     applyButton->setFocus();
0411 
0412     d_func()->apply();
0413 }
0414 
0415 void KCMultiDialog::slotOkClicked()
0416 {
0417     QPushButton *okButton = buttonBox()->button(QDialogButtonBox::Ok);
0418     okButton->setFocus();
0419 
0420     d_func()->apply();
0421     accept();
0422 }
0423 
0424 void KCMultiDialog::slotHelpClicked()
0425 {
0426     const KPageWidgetItem *item = currentPage();
0427     if (!item) {
0428         return;
0429     }
0430 
0431     Q_D(KCMultiDialog);
0432     QString docPath;
0433     for (int i = 0; i < d->modules.count(); ++i) {
0434         if (d->modules[i].item == item) {
0435 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
0436             docPath = d->modules[i].kcm->moduleInfo().docPath();
0437 #endif
0438             if (docPath.isEmpty()) {
0439                 docPath = d->modules[i].kcm->metaData().value(QStringLiteral("X-DocPath"));
0440             }
0441             break;
0442         }
0443     }
0444 
0445     const QUrl docUrl = QUrl(QStringLiteral("help:/")).resolved(QUrl(docPath)); // same code as in KHelpClient::invokeHelp
0446     const QString docUrlScheme = docUrl.scheme();
0447     const QString helpExec = QStandardPaths::findExecutable(QStringLiteral("khelpcenter"));
0448     const bool foundExec = !helpExec.isEmpty();
0449     if (!foundExec) {
0450         qCDebug(KCMUTILS_LOG) << "Couldn't find khelpcenter executable in PATH.";
0451     }
0452     if (foundExec && (docUrlScheme == QLatin1String("man") || docUrlScheme == QLatin1String("info"))) {
0453         QProcess::startDetached(helpExec, QStringList() << docUrl.toString());
0454     } else {
0455         QDesktopServices::openUrl(docUrl);
0456     }
0457 }
0458 
0459 void KCMultiDialog::closeEvent(QCloseEvent *event)
0460 {
0461     Q_D(KCMultiDialog);
0462     KPageDialog::closeEvent(event);
0463 
0464     /**
0465      * If we don't delete them, the DBUS registration stays, and trying to load the KCMs
0466      * in other situations will lead to "module already loaded in Foo," while to the user
0467      * doesn't appear so(the dialog is hidden)
0468      */
0469     for (auto &proxy : std::as_const(d->modules)) {
0470         proxy.kcm->deleteClient();
0471     }
0472 }
0473 
0474 KPageWidgetItem *KCMultiDialog::addModule(const KPluginMetaData &metaData)
0475 {
0476     return addModule(metaData, QStringList());
0477 }
0478 
0479 KPageWidgetItem *KCMultiDialog::addModule(const KPluginMetaData &metaData, const QStringList &args)
0480 {
0481     Q_D(KCMultiDialog);
0482     // Create the scroller
0483     auto *moduleScroll = new UnboundScrollArea(this);
0484     // Prepare the scroll area
0485     moduleScroll->setWidgetResizable(true);
0486     moduleScroll->setFrameStyle(QFrame::NoFrame);
0487     moduleScroll->viewport()->setAutoFillBackground(false);
0488 
0489     KCModuleProxy *kcm = new KCModuleProxy(metaData, moduleScroll, args);
0490     moduleScroll->setWidget(kcm);
0491 
0492     KPageWidgetItem *item = new KPageWidgetItem(moduleScroll, metaData.name());
0493 
0494     KCMultiDialogPrivate::CreatedModule createdModule;
0495     createdModule.kcm = kcm;
0496     createdModule.item = item;
0497     d->modules.append(createdModule);
0498 
0499     if (qobject_cast<KCModuleQml *>(kcm->realModule())) {
0500         item->setHeaderVisible(false);
0501     }
0502 
0503     if (kcm->realModule() && kcm->realModule()->useRootOnlyMessage()) {
0504         item->setHeader(QStringLiteral("<b>%1</b><br><i>%2</i>").arg(metaData.name(), kcm->realModule()->rootOnlyMessage()));
0505         item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(metaData.iconName()), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner));
0506     } else {
0507         item->setHeader(metaData.name());
0508         item->setIcon(QIcon::fromTheme(metaData.iconName()));
0509     }
0510     const int weight = metaData.rawData().value(QStringLiteral("X-KDE-Weight")).toInt();
0511     item->setProperty("_k_weight", weight);
0512 
0513     bool updateCurrentPage = false;
0514     const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(pageWidget()->model());
0515     Q_ASSERT(model);
0516     const int siblingCount = model->rowCount();
0517     int row = 0;
0518     for (; row < siblingCount; ++row) {
0519         KPageWidgetItem *siblingItem = model->item(model->index(row, 0));
0520         if (siblingItem->property("_k_weight").toInt() > weight) {
0521             // the item we found is heavier than the new module
0522             // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name();
0523             insertPage(siblingItem, item);
0524             if (siblingItem == currentPage()) {
0525                 updateCurrentPage = true;
0526             }
0527 
0528             break;
0529         }
0530     }
0531     if (row == siblingCount) {
0532         // the new module is either the first or the heaviest item
0533         // qDebug() << "adding KCM " << item->name() << " at the top level";
0534         addPage(item);
0535     }
0536 
0537     QObject::connect(kcm, qOverload<bool>(&KCModuleProxy::changed), this, [d]() {
0538         d->_k_clientChanged();
0539     });
0540 
0541     QObject::connect(kcm->realModule(), &KCModule::rootOnlyMessageChanged, this, [d](bool use, const QString &message) {
0542         d->_k_updateHeader(use, message);
0543     });
0544 
0545     if (d->modules.count() == 1 || updateCurrentPage) {
0546         setCurrentPage(item);
0547         d->_k_clientChanged();
0548     }
0549     return item;
0550 }
0551 
0552 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
0553 KPageWidgetItem *KCMultiDialog::addModule(const QString &path, const QStringList &args)
0554 {
0555     QString complete = path;
0556 
0557     if (!path.endsWith(QLatin1String(".desktop"))) {
0558         complete += QStringLiteral(".desktop");
0559     }
0560 
0561     KService::Ptr service = KService::serviceByStorageId(complete);
0562 
0563     return addModule(KCModuleInfo(service), nullptr, args);
0564 }
0565 #endif
0566 
0567 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 85)
0568 KPageWidgetItem *KCMultiDialog::addModule(const KCModuleInfo &moduleInfo, KPageWidgetItem *parentItem, const QStringList &args)
0569 {
0570     Q_D(KCMultiDialog);
0571     if (!moduleInfo.isValid()) {
0572         return nullptr;
0573     }
0574 
0575     // KAuthorized::authorizeControlModule( moduleInfo.service()->menuId() ) is
0576     // checked in noDisplay already
0577     if (moduleInfo.service() && moduleInfo.service()->noDisplay()) {
0578         return nullptr;
0579     }
0580 
0581     // Create the scroller
0582     auto *moduleScroll = new UnboundScrollArea(this);
0583     // Prepare the scroll area
0584     moduleScroll->setWidgetResizable(true);
0585     moduleScroll->setFrameStyle(QFrame::NoFrame);
0586     moduleScroll->viewport()->setAutoFillBackground(false);
0587 
0588     KCModuleProxy *kcm = new KCModuleProxy(moduleInfo, moduleScroll, args);
0589     moduleScroll->setWidget(kcm);
0590 
0591     // qDebug() << moduleInfo.moduleName();
0592     KPageWidgetItem *item = new KPageWidgetItem(moduleScroll, moduleInfo.moduleName());
0593 
0594     KCMultiDialogPrivate::CreatedModule cm;
0595     cm.kcm = kcm;
0596     cm.item = item;
0597     cm.componentNames = moduleInfo.property(QStringLiteral("X-KDE-ParentComponents")).toStringList();
0598     d->modules.append(cm);
0599 
0600     if (qobject_cast<KCModuleQml *>(kcm->realModule())) {
0601         item->setHeaderVisible(false);
0602     }
0603 
0604     if (kcm->realModule() && kcm->realModule()->useRootOnlyMessage()) {
0605         item->setHeader(QStringLiteral("<b>") + moduleInfo.moduleName() + QStringLiteral("</b><br><i>") + kcm->realModule()->rootOnlyMessage()
0606                         + QStringLiteral("</i>"));
0607         item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(moduleInfo.icon()), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner));
0608     } else {
0609         item->setHeader(moduleInfo.moduleName());
0610         item->setIcon(QIcon::fromTheme(moduleInfo.icon()));
0611     }
0612     item->setProperty("_k_weight", moduleInfo.weight());
0613 
0614     bool updateCurrentPage = false;
0615     const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(pageWidget()->model());
0616     Q_ASSERT(model);
0617     if (parentItem) {
0618         const QModelIndex parentIndex = model->index(parentItem);
0619         const int siblingCount = model->rowCount(parentIndex);
0620         int row = 0;
0621         for (; row < siblingCount; ++row) {
0622             KPageWidgetItem *siblingItem = model->item(model->index(row, 0, parentIndex));
0623             if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
0624                 // the item we found is heavier than the new module
0625                 // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name();
0626                 insertPage(siblingItem, item);
0627                 break;
0628             }
0629         }
0630         if (row >= siblingCount) {
0631             // the new module is either the first or the heaviest item
0632             // qDebug() << "adding KCM " << item->name() << " with parent " << parentItem->name();
0633             addSubPage(parentItem, item);
0634         }
0635     } else {
0636         const int siblingCount = model->rowCount();
0637         int row = 0;
0638         for (; row < siblingCount; ++row) {
0639             KPageWidgetItem *siblingItem = model->item(model->index(row, 0));
0640             if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
0641                 // the item we found is heavier than the new module
0642                 // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name();
0643                 insertPage(siblingItem, item);
0644                 if (siblingItem == currentPage()) {
0645                     updateCurrentPage = true;
0646                 }
0647 
0648                 break;
0649             }
0650         }
0651         if (row == siblingCount) {
0652             // the new module is either the first or the heaviest item
0653             // qDebug() << "adding KCM " << item->name() << " at the top level";
0654             addPage(item);
0655         }
0656     }
0657 
0658     QObject::connect(kcm, qOverload<bool>(&KCModuleProxy::changed), this, [d]() {
0659         d->_k_clientChanged();
0660     });
0661 
0662     QObject::connect(kcm->realModule(), &KCModule::rootOnlyMessageChanged, this, [d](bool use, QString message) {
0663         d->_k_updateHeader(use, message);
0664     });
0665 
0666     if (d->modules.count() == 1 || updateCurrentPage) {
0667         setCurrentPage(item);
0668         d->_k_clientChanged();
0669     }
0670     return item;
0671 }
0672 #endif
0673 
0674 void KCMultiDialog::clear()
0675 {
0676     Q_D(KCMultiDialog);
0677     // qDebug() ;
0678 
0679     for (int i = 0; i < d->modules.count(); ++i) {
0680         removePage(d->modules[i].item);
0681     }
0682 
0683     d->modules.clear();
0684 
0685     d->_k_clientChanged();
0686 }
0687 
0688 #include "moc_kcmultidialog.cpp"