File indexing completed on 2024-05-05 09:57:15

0001 /*
0002  *   SPDX-FileCopyrightText: 2009 Ben Cooksley <bcooksley@kde.org>
0003  *   SPDX-FileCopyrightText: 2009 Mathias Soeken <msoeken@informatik.uni-bremen.de>
0004  *   SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
0005  *
0006  *   SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "ModuleView.h"
0010 #include "ExternalAppModule.h"
0011 
0012 #include <QDesktopServices>
0013 #include <QDialogButtonBox>
0014 #include <QKeyEvent>
0015 #include <QLoggingCategory>
0016 #include <QMap>
0017 #include <QPainter>
0018 #include <QPushButton>
0019 #include <QScrollArea>
0020 #include <QStyle>
0021 #include <QVBoxLayout>
0022 #include <QWhatsThis>
0023 
0024 #include <KAboutData>
0025 #include <KAuth/Action>
0026 #include <KAuth/ExecuteJob>
0027 #include <KAuthorized>
0028 #include <KCModule>
0029 #include <KCModuleLoader>
0030 #include <KColorScheme>
0031 #include <KMessageDialog>
0032 #include <KPageWidget>
0033 #include <KSharedConfig>
0034 #include <KStandardGuiItem>
0035 #include <KTitleWidget>
0036 
0037 #include <PlasmaActivities/ResourceInstance>
0038 
0039 #include <cmath>
0040 #include <kpluginmetadata.h>
0041 
0042 #include "MenuItem.h"
0043 
0044 class CustomTitle : public KTitleWidget
0045 {
0046 public:
0047     explicit CustomTitle(QWidget *parent = nullptr);
0048 
0049 protected:
0050     bool event(QEvent *event) override;
0051     void paintEvent(QPaintEvent *event) override;
0052     void colorsChanged();
0053 };
0054 
0055 CustomTitle::CustomTitle(QWidget *parent)
0056     : KTitleWidget(parent)
0057 {
0058     // Use the same left margin as QML titles for consistency (Kirigami/AbstractPageHeader.qml)
0059     // 18px is Standard Kirigami gridUnit for 10pt Noto Sans.
0060     // TODO: make this use a real gridUnit so it will adjust to the user's font,
0061     // once we have a QmlEngine object such that  using it won't risk crashes!
0062     setContentsMargins(18,
0063                        style()->pixelMetric(QStyle::PM_LayoutTopMargin),
0064                        style()->pixelMetric(QStyle::PM_LayoutRightMargin),
0065                        style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
0066 
0067     colorsChanged();
0068 }
0069 
0070 bool CustomTitle::event(QEvent *event)
0071 {
0072     if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::PaletteChange) {
0073         this->colorsChanged();
0074     }
0075     return QWidget::event(event);
0076 }
0077 
0078 void CustomTitle::colorsChanged()
0079 {
0080     auto config = KSharedConfig::openConfig();
0081     auto active = KColorScheme(QPalette::Active, KColorScheme::Header, config);
0082     auto inactive = KColorScheme(QPalette::Inactive, KColorScheme::Header, config);
0083     auto disabled = KColorScheme(QPalette::Disabled, KColorScheme::Header, config);
0084 
0085     QPalette palette = KColorScheme::createApplicationPalette(config);
0086 
0087     palette.setBrush(QPalette::Active, QPalette::Window, active.background());
0088     palette.setBrush(QPalette::Active, QPalette::WindowText, active.foreground());
0089     palette.setBrush(QPalette::Disabled, QPalette::Window, disabled.background());
0090     palette.setBrush(QPalette::Disabled, QPalette::WindowText, disabled.foreground());
0091     palette.setBrush(QPalette::Inactive, QPalette::Window, inactive.background());
0092     palette.setBrush(QPalette::Inactive, QPalette::WindowText, inactive.foreground());
0093 
0094     setPalette(palette);
0095 }
0096 
0097 void CustomTitle::paintEvent(QPaintEvent *event)
0098 {
0099     KTitleWidget::paintEvent(event);
0100 
0101     auto linearlyInterpolateDouble = [](double one, double two, double factor) {
0102         return one + (two - one) * factor;
0103     };
0104 
0105     QPainter p(this);
0106 
0107     const QColor window = palette().color(QPalette::Window);
0108     const QColor text = palette().color(QPalette::Text);
0109     const qreal balance = 0.2;
0110 
0111     const QColor separator = QColor::fromHsv(std::fmod(linearlyInterpolateDouble(window.hue(), text.hue(), balance), 360.0),
0112                                              qBound(0.0, linearlyInterpolateDouble(window.saturation(), text.saturation(), balance), 255.0),
0113                                              qBound(0.0, linearlyInterpolateDouble(window.value(), text.value(), balance), 255.0),
0114                                              qBound(0.0, linearlyInterpolateDouble(window.alpha(), text.alpha(), balance), 255.0));
0115     p.fillRect(event->rect(), window);
0116     p.fillRect(QRect(QPoint(0, height() - 1), QSize(width(), 1)), separator);
0117 }
0118 
0119 class ModuleView::Private
0120 {
0121 public:
0122     Private()
0123     {
0124     }
0125     QMap<KPageWidgetItem *, KCModule *> mPages;
0126     QMap<KPageWidgetItem *, QString> mPagesPluginIdMap;
0127     KPageWidget *mPageWidget = nullptr;
0128     CustomTitle *mCustomHeader = nullptr;
0129     QVBoxLayout *mLayout = nullptr;
0130     QDialogButtonBox *mButtons = nullptr;
0131     QPushButton *mApply = nullptr;
0132     QPushButton *mReset = nullptr;
0133     QPushButton *mDefault = nullptr;
0134     QPushButton *mHelp = nullptr;
0135     QIcon mApplyIcon;
0136     KMessageDialog *mResolvingChangesDialog = nullptr;
0137     bool pageChangeSupressed = false;
0138     bool mSaveStatistics = true;
0139     bool mDefaultsIndicatorsVisible = false;
0140     KCModule::Buttons mButtonMask = ~KCModule::Buttons(KCModule::NoAdditionalButton);
0141     KAuth::Action authAction;
0142 };
0143 
0144 ModuleView::ModuleView(QWidget *parent)
0145     : QWidget(parent)
0146     , d(new Private())
0147 {
0148     auto rootLayout = new QVBoxLayout(this);
0149     rootLayout->setContentsMargins(0, 0, 0, 0);
0150     rootLayout->setSpacing(0);
0151     // Configure a layout first
0152     d->mLayout = new QVBoxLayout();
0153     // Create the Page Widget
0154     d->mPageWidget = new KPageWidget(this);
0155     d->mCustomHeader = new CustomTitle(this);
0156     d->mCustomHeader->setVisible(false);
0157 
0158     rootLayout->addWidget(d->mCustomHeader);
0159     rootLayout->addItem(d->mLayout);
0160     // d->mPageWidget->setPageHeader(d->mCustomHeader);
0161     d->mPageWidget->layout()->setContentsMargins(0, 0, 0, 0);
0162 
0163     // Zero out only the horizontal spacing (the vertical spacing is fine)
0164     auto gridLayout = qobject_cast<QGridLayout *>(d->mPageWidget->layout());
0165 
0166     gridLayout->setHorizontalSpacing(0);
0167 
0168     d->mLayout->addWidget(d->mPageWidget);
0169     // Create the dialog
0170     d->mButtons = new QDialogButtonBox(Qt::Horizontal, this);
0171     d->mLayout->addWidget(d->mButtons);
0172 
0173     // Create the buttons in it
0174     d->mApply = d->mButtons->addButton(QDialogButtonBox::Apply);
0175     d->mApplyIcon = d->mApply->icon();
0176     KGuiItem::assign(d->mApply, KStandardGuiItem::apply());
0177     d->mDefault = d->mButtons->addButton(QDialogButtonBox::RestoreDefaults);
0178     KGuiItem::assign(d->mDefault, KStandardGuiItem::defaults());
0179     d->mReset = d->mButtons->addButton(QDialogButtonBox::Reset);
0180     KGuiItem::assign(d->mReset, KStandardGuiItem::reset());
0181     d->mHelp = d->mButtons->addButton(QDialogButtonBox::Help);
0182     KGuiItem::assign(d->mHelp, KStandardGuiItem::help());
0183     // Set some more sensible tooltips
0184     d->mReset->setToolTip(i18n("Reset all current changes to previous values"));
0185     // Set Auto-Default mode ( KDE Bug #211187 )
0186     d->mApply->setAutoDefault(true);
0187     d->mDefault->setAutoDefault(true);
0188     d->mReset->setAutoDefault(true);
0189     d->mHelp->setAutoDefault(true);
0190     // Prevent the buttons from being used
0191     d->mApply->setEnabled(false);
0192     d->mDefault->setEnabled(false);
0193     d->mReset->setEnabled(false);
0194     d->mHelp->setEnabled(false);
0195     // Connect up the buttons
0196     connect(d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()));
0197     connect(d->mReset, &QAbstractButton::clicked, this, &ModuleView::moduleLoad);
0198     connect(d->mHelp, &QAbstractButton::clicked, this, &ModuleView::moduleHelp);
0199     connect(d->mDefault, &QAbstractButton::clicked, this, &ModuleView::moduleDefaults);
0200     // clang-format off
0201     connect(d->mPageWidget, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)),
0202              this, SLOT(activeModuleChanged(KPageWidgetItem*,KPageWidgetItem*)));
0203     // clang-format on
0204 
0205     connect(d->mApply, &QPushButton::clicked, this, [this] {
0206         if (d->authAction.isValid()) {
0207             KAuth::ExecuteJob *job = d->authAction.execute(KAuth::Action::AuthorizeOnlyMode);
0208             connect(job, &KAuth::ExecuteJob::statusChanged, this, [this](KAuth::Action::AuthStatus status) {
0209                 authStatusChanged(status);
0210             });
0211         }
0212     });
0213 }
0214 
0215 ModuleView::~ModuleView()
0216 {
0217     delete d;
0218 }
0219 
0220 QString ModuleView::activeModuleName() const
0221 {
0222     return d->mPageWidget->currentPage() ? d->mPageWidget->currentPage()->name() : QString();
0223 }
0224 
0225 void ModuleView::loadModule(const QModelIndex &menuItem, const QStringList &args)
0226 {
0227     if (!menuItem.isValid()) {
0228         return;
0229     }
0230 
0231     auto item = menuItem.data(Qt::UserRole).value<MenuItem *>();
0232 
0233     // if module has a main page (like in Appearance > Global Theme) we'll load that
0234     if (item->isLibrary() || item->isExternalAppModule()) {
0235         addModule(item, args);
0236     }
0237     // if module doesn't have a main page, we'll load the first subpage
0238     else if (menuItem.model()->rowCount(menuItem) > 0) {
0239         auto subpageItem = menuItem.model()->index(0, 0, menuItem).data(Qt::UserRole).value<MenuItem *>();
0240         addModule(subpageItem, args);
0241     }
0242 }
0243 
0244 void ModuleView::addModule(MenuItem *item, const QStringList &args)
0245 {
0246     const KPluginMetaData data = item->metaData();
0247     if (!KAuthorized::authorizeControlModule(data.pluginId())) {
0248         qWarning() << "Not authorised to load module";
0249         return;
0250     }
0251     if (data.isHidden()) {
0252         return;
0253     }
0254 
0255     if (KPageWidgetItem *page = d->mPagesPluginIdMap.key(data.pluginId())) {
0256         activeModuleChanged(page, d->mPageWidget->currentPage());
0257         return;
0258     }
0259 
0260     // Create the scroller
0261     auto moduleScroll = new QScrollArea(this);
0262     // Prepare the scroll area
0263     moduleScroll->setWidgetResizable(true);
0264     moduleScroll->setFrameStyle(QFrame::NoFrame);
0265     moduleScroll->viewport()->setAutoFillBackground(false);
0266     // Create the page
0267     auto page = new KPageWidgetItem(moduleScroll, data.name());
0268     // Provide information to the users
0269 
0270     if (item->isExternalAppModule()) {
0271         auto externalWidget = new ExternalAppModule(KService::Ptr(new KService(item->metaData().fileName())));
0272         moduleScroll->setWidget(externalWidget);
0273         d->mCustomHeader->setText(item->metaData().name()); // We have to set this manually, BUG: 448672
0274         page->setName(QString());
0275     } else { // It must be a normal module then
0276         auto kcm = KCModuleLoader::loadModule(data, moduleScroll, QVariantList(args.begin(), args.end()));
0277         moduleScroll->setWidget(kcm->widget());
0278         kcm->widget()->setAutoFillBackground(false);
0279         kcm->load();
0280         connect(kcm, &KCModule::needsSaveChanged, this, &ModuleView::stateChanged);
0281         connect(kcm, &KCModule::representsDefaultsChanged, this, [this, kcm]() {
0282             if (kcm == d->mPages.value(d->mPageWidget->currentPage()) && kcm->buttons() & d->mButtonMask & KCModule::Default) {
0283                 d->mDefault->setEnabled(!kcm->representsDefaults());
0284             }
0285         });
0286 
0287         d->mPages.insert(page, kcm);
0288     }
0289 
0290     d->mPagesPluginIdMap.insert(page, data.pluginId());
0291     updatePageIconHeader(page);
0292     // Add the new page
0293     d->mPageWidget->addPage(page);
0294 }
0295 
0296 void ModuleView::updatePageIconHeader(KPageWidgetItem *page)
0297 {
0298     if (!page) {
0299         // Page is invalid. Probably means we have a race condition during closure of everyone so do nothing
0300         return;
0301     }
0302 
0303     KCModule *kcm = d->mPages.value(page);
0304     if (!kcm) {
0305         // Seems like we have some form of a race condition going on here...
0306         return;
0307     }
0308 
0309     const bool isQml = kcm->inherits("KCModuleQml");
0310     const bool isSidebar = faceType() == KPageView::Plain;
0311 
0312     if (!kcm->metaData().isValid()) {
0313         // KCModule was (incorrectly) created with a constructor that didn't store metadata
0314         // Never use the custom header here because we don't know the module name
0315         page->setHeaderVisible(!isQml);
0316         if (d->mPageWidget->currentPage() == page) {
0317             d->mCustomHeader->hide();
0318         }
0319         return;
0320     }
0321 
0322     const QString &moduleName = kcm->metaData().name();
0323     page->setHeader(moduleName);
0324     page->setIcon(QIcon::fromTheme(kcm->metaData().iconName()));
0325 
0326     // Use the module's header only for QWidgets KCMs on Icons mode
0327     page->setHeaderVisible(!isQml && !isSidebar);
0328 
0329     // Use the custom header only for QWidgets KCMs on Sidebar mode
0330     // Only affect visibility if it's the current page
0331     if (d->mPageWidget->currentPage() == page) {
0332         if (!isQml && isSidebar) {
0333             d->mCustomHeader->setText(moduleName); // also includes show()
0334         } else {
0335             d->mCustomHeader->hide();
0336         }
0337     }
0338 }
0339 
0340 bool ModuleView::resolveChanges()
0341 {
0342     KCModule *kcm = d->mPages.value(d->mPageWidget->currentPage());
0343     return resolveChanges(kcm);
0344 }
0345 
0346 bool ModuleView::resolveChanges(KCModule *kcm)
0347 {
0348     if (!kcm || !kcm->needsSave()) {
0349         return true;
0350     }
0351 
0352     // if we are already resolving changes handle it like a cancel
0353     if (d->mResolvingChangesDialog) {
0354         d->mResolvingChangesDialog->reject();
0355     }
0356 
0357     // Let the user decide
0358     d->mResolvingChangesDialog = new KMessageDialog(KMessageDialog::WarningTwoActionsCancel,
0359                                                     i18n("The settings of the current module have changed.\n"
0360                                                          "Do you want to apply the changes or discard them?"),
0361                                                     this);
0362     d->mResolvingChangesDialog->setAttribute(Qt::WA_DeleteOnClose);
0363     d->mResolvingChangesDialog->setButtons(KStandardGuiItem::apply(), KStandardGuiItem::discard(), KStandardGuiItem::cancel());
0364     d->mResolvingChangesDialog->setCaption(i18n("Apply Settings"));
0365     d->mResolvingChangesDialog->setIcon(QIcon()); // Use default message box warning icon.
0366     int result = d->mResolvingChangesDialog->exec();
0367     d->mResolvingChangesDialog = nullptr;
0368 
0369     switch (result) {
0370     case KMessageDialog::PrimaryAction:
0371         return moduleSave(kcm);
0372     case KMessageDialog::SecondaryAction:
0373         kcm->load();
0374         return true;
0375     case KMessageDialog::Cancel:
0376         return false;
0377     default:
0378         Q_ASSERT(false);
0379         return false;
0380     }
0381 }
0382 
0383 void ModuleView::closeModules()
0384 {
0385     d->pageChangeSupressed = true;
0386     d->authAction = KAuth::Action();
0387     for (auto page = d->mPagesPluginIdMap.cbegin(); page != d->mPagesPluginIdMap.cend(); ++page) {
0388         // Delete the KCM first, because e.g. the KFontInst KCM accesses it's widgets in the destructor
0389         delete d->mPages.value(page.key());
0390         d->mPageWidget->removePage(page.key());
0391     }
0392 
0393     d->mPages.clear();
0394     d->mPagesPluginIdMap.clear();
0395     d->pageChangeSupressed = false;
0396 }
0397 
0398 bool ModuleView::moduleSave()
0399 {
0400     KCModule *moduleProxy = d->mPages.value(d->mPageWidget->currentPage());
0401     return moduleSave(moduleProxy);
0402 }
0403 
0404 bool ModuleView::moduleSave(KCModule *module)
0405 {
0406     if (!module) {
0407         return false;
0408     }
0409 
0410     module->save();
0411     Q_EMIT moduleSaved();
0412     return true;
0413 }
0414 
0415 void ModuleView::moduleLoad()
0416 {
0417     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0418     if (activeModule) {
0419         activeModule->load();
0420     }
0421 }
0422 
0423 void ModuleView::moduleDefaults()
0424 {
0425     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0426     if (activeModule) {
0427         activeModule->defaults();
0428     }
0429 }
0430 
0431 void ModuleView::moduleHelp()
0432 {
0433     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0434     if (!activeModule) {
0435         return;
0436     }
0437 
0438     const QString docPath = activeModule->metaData().value(QStringLiteral("X-DocPath"));
0439     if (docPath.isEmpty()) {
0440         return;
0441     }
0442 
0443     // UrlHandler from KGUIAddons sets a handler for help:/ urls, which opens khelpcenter
0444     // if it's available or falls back to opening the relevant page at docs.kde.org
0445     QDesktopServices::openUrl(QUrl(QStringLiteral("help:/") + docPath));
0446 }
0447 
0448 void ModuleView::activeModuleChanged(KPageWidgetItem *current, KPageWidgetItem *previous)
0449 {
0450     d->mPageWidget->blockSignals(true);
0451     d->mPageWidget->setCurrentPage(previous);
0452     KCModule *previousModule = d->mPages.value(previous);
0453     if (resolveChanges(previousModule)) {
0454         d->mPageWidget->setCurrentPage(current);
0455     }
0456     d->mPageWidget->blockSignals(false);
0457     if (d->pageChangeSupressed) {
0458         return;
0459     }
0460 
0461     // We need to get the state of the now active module
0462     stateChanged();
0463 
0464     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0465     if (!activeModule) {
0466         return;
0467     }
0468 
0469     // TODO: if we'll ever need statistics for kinfocenter modules, save them with an URL like "kinfo:"
0470     if (d->mSaveStatistics && activeModule->metaData().pluginId() != QStringLiteral("kcm_landingpage")) {
0471         KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("kcm:") + activeModule->metaData().pluginId()),
0472                                                       QStringLiteral("org.kde.systemsettings"));
0473     }
0474 
0475     d->mLayout->setContentsMargins(0, 0, 0, 0);
0476     d->mLayout->setSpacing(0);
0477     d->mButtons->setContentsMargins(style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
0478                                     style()->pixelMetric(QStyle::PM_LayoutTopMargin),
0479                                     style()->pixelMetric(QStyle::PM_LayoutRightMargin),
0480                                     style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
0481     d->mButtons->setProperty("_breeze_force_frame", true);
0482     d->mPageWidget->layout()->setSpacing(0);
0483     if (auto titleWidget = qobject_cast<KTitleWidget *>(d->mPageWidget->pageHeader())) {
0484         // 18px is Standard Kirigami gridUnit for 10pt Noto Sans.
0485         // TODO: make this use a real gridUnit so it will adjust to the user's font,
0486         // once we have a QmlEngine object such that  using it won't risk crashes!
0487         titleWidget->layout()->setContentsMargins(18,
0488                                                   style()->pixelMetric(QStyle::PM_LayoutRightMargin),
0489                                                   style()->pixelMetric(QStyle::PM_LayoutRightMargin),
0490                                                   style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
0491     }
0492 
0493     updatePageIconHeader(current);
0494     moduleShowDefaultsIndicators(d->mDefaultsIndicatorsVisible);
0495 }
0496 
0497 void ModuleView::stateChanged()
0498 {
0499     updatePageIconHeader(d->mPageWidget->currentPage());
0500     updateButtons();
0501 
0502     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0503     Q_EMIT moduleChanged(activeModule && activeModule->needsSave());
0504 }
0505 
0506 void ModuleView::updateButtons()
0507 {
0508     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0509     bool change = false;
0510     bool defaulted = false;
0511     KCModule::Buttons buttons = KCModule::NoAdditionalButton;
0512 
0513     if (activeModule) {
0514         buttons = activeModule->buttons() & d->mButtonMask;
0515         change = activeModule->needsSave();
0516         defaulted = activeModule->representsDefaults();
0517 
0518         d->authAction = KAuth::Action(activeModule->authActionName());
0519         authStatusChanged(d->authAction.status());
0520 
0521         // Do not display Help button if there is no docPath available
0522         if (activeModule->metaData().value(QStringLiteral("X-DocPath")).isEmpty()) {
0523             buttons &= ~KCModule::Help;
0524         }
0525 
0526         disconnect(d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()));
0527         connect(d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()));
0528     } else {
0529         d->authAction = KAuth::Action();
0530     }
0531 
0532     d->mDefault->setEnabled(!defaulted);
0533     d->mDefault->setVisible(buttons & KCModule::Default);
0534     d->mApply->setEnabled(change);
0535     d->mApply->setVisible(buttons & KCModule::Apply);
0536     d->mReset->setEnabled(change);
0537     d->mReset->setVisible(buttons & KCModule::Apply);
0538     d->mHelp->setEnabled(buttons & KCModule::Help);
0539     d->mHelp->setVisible(buttons & KCModule::Help);
0540 
0541     d->mButtons->setVisible(buttons != KCModule::NoAdditionalButton);
0542 }
0543 
0544 void ModuleView::keyPressEvent(QKeyEvent *event)
0545 {
0546     if (event->key() == Qt::Key_F1 && d->mHelp->isVisible() && d->mHelp->isEnabled()) {
0547         d->mHelp->animateClick();
0548         event->accept();
0549         return;
0550     } else if (event->key() == Qt::Key_Escape) {
0551         event->accept();
0552         Q_EMIT closeRequest();
0553         return;
0554     } else if (event->key() == Qt::Key_F1 && event->modifiers() == Qt::ShiftModifier) {
0555         QWhatsThis::enterWhatsThisMode();
0556         event->accept();
0557         return;
0558     }
0559 
0560     QWidget::keyPressEvent(event);
0561 }
0562 
0563 void ModuleView::setFaceType(KPageView::FaceType type)
0564 {
0565     d->mPageWidget->setFaceType(type);
0566 }
0567 
0568 KPageView::FaceType ModuleView::faceType() const
0569 {
0570     return d->mPageWidget->faceType();
0571 }
0572 
0573 void ModuleView::setSaveStatistics(bool save)
0574 {
0575     d->mSaveStatistics = save;
0576 }
0577 
0578 bool ModuleView::saveStatistics() const
0579 {
0580     return d->mSaveStatistics;
0581 }
0582 
0583 void ModuleView::setApplyVisible(bool visible)
0584 {
0585     d->mButtonMask.setFlag(KCModule::Apply, visible);
0586     updateButtons();
0587 }
0588 
0589 bool ModuleView::isApplyVisible() const
0590 {
0591     return d->mApply->isVisible();
0592 }
0593 
0594 void ModuleView::setDefaultsVisible(bool visible)
0595 {
0596     d->mButtonMask.setFlag(KCModule::Default, visible);
0597     updateButtons();
0598 }
0599 
0600 bool ModuleView::isDefaultsVisible() const
0601 {
0602     return d->mDefault->isVisible();
0603 }
0604 
0605 bool ModuleView::isResetVisible() const
0606 {
0607     return d->mReset->isVisible();
0608 }
0609 
0610 void ModuleView::moduleShowDefaultsIndicators(bool show)
0611 {
0612     d->mDefaultsIndicatorsVisible = show;
0613     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0614     if (activeModule) {
0615         activeModule->setDefaultsIndicatorsVisible(show);
0616     }
0617 }
0618 
0619 void ModuleView::setHeaderHeight(qreal height)
0620 {
0621     if (height == d->mCustomHeader->minimumHeight()) {
0622         return;
0623     }
0624 
0625     d->mCustomHeader->setMinimumHeight(height);
0626 }
0627 
0628 qreal ModuleView::headerHeight() const
0629 {
0630     return d->mCustomHeader->minimumHeight();
0631 }
0632 
0633 void ModuleView::setActiveModule(const QString &pluginId)
0634 {
0635     const auto pageList = d->mPagesPluginIdMap.keys();
0636     for (const auto page : pageList) {
0637         if (d->mPagesPluginIdMap.value(page) == pluginId) {
0638             d->mPageWidget->setCurrentPage(page);
0639             break;
0640         }
0641     }
0642 }
0643 
0644 void ModuleView::requestActivation(const QVariantList &args)
0645 {
0646     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0647     if (activeModule) {
0648         Q_EMIT activeModule->activationRequested(args);
0649     }
0650 }
0651 
0652 KPluginMetaData ModuleView::activeModuleMetadata() const
0653 {
0654     KCModule *activeModule = d->mPages.value(d->mPageWidget->currentPage());
0655     if (!activeModule) {
0656         return {};
0657     }
0658     return activeModule->metaData();
0659 }
0660 
0661 void ModuleView::authStatusChanged(KAuth::Action::AuthStatus status)
0662 {
0663     switch (status) {
0664     case KAuth::Action::AuthorizedStatus:
0665         d->mApply->setEnabled(true);
0666         d->mApply->setIcon(d->mApplyIcon);
0667         break;
0668     case KAuth::Action::AuthRequiredStatus:
0669         d->mApply->setEnabled(true);
0670         d->mApply->setIcon(QIcon::fromTheme(QStringLiteral("dialog-password")));
0671         break;
0672     default:
0673         d->mApply->setEnabled(false);
0674         d->mApply->setIcon(d->mApplyIcon);
0675     }
0676 }
0677 
0678 #include "moc_ModuleView.cpp"