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