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