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"