diff --git a/src/kcmoduleloader.cpp b/src/kcmoduleloader.cpp index 197023e..6658d38 100644 --- a/src/kcmoduleloader.cpp +++ b/src/kcmoduleloader.cpp @@ -1,182 +1,187 @@ /* Copyright (c) 1999 Matthias Hoelzer-Kluepfel Copyright (c) 2000 Matthias Elter Copyright (c) 2003,2004,2006 Matthias Kretz Copyright (c) 2004 Frans Englich This file is part of the KDE project This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2, as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcmoduleloader.h" #include "kcmoduleqml_p.h" #include #include #include #include #include #include #include #include #include #include using namespace KCModuleLoader; /***************************************************************/ /** * When something goes wrong in loading the module, this one * jumps in as a "dummy" module. */ class KCMError : public KCModule { public: KCMError(const QString &msg, const QString &details, QWidget *parent) : KCModule(parent) { QVBoxLayout *topLayout = new QVBoxLayout(this); QLabel *lab = new QLabel(msg, this); lab->setWordWrap(true); topLayout->addWidget(lab); lab = new QLabel(details, this); lab->setWordWrap(true); topLayout->addWidget(lab); } }; /***************************************************************/ KCModule *KCModuleLoader::loadModule(const QString &module, ErrorReporting report, QWidget *parent, const QStringList &args) { return loadModule(KCModuleInfo(module), report, parent, args); } KCModule *KCModuleLoader::loadModule(const KCModuleInfo &mod, ErrorReporting report, QWidget *parent, const QStringList &args) { /* * Simple libraries as modules are the easiest case: * We just have to load the library and get the module * from the factory. */ - if (!mod.pluginInfo().isValid()) + if (!mod.service()) { return reportError(report, - i18n("The module %1 could not be found.", - mod.moduleName()), i18n("

The diagnosis is:
The desktop file %1 could not be found.

", mod.fileName()), parent); - if (mod.service() && mod.service()->noDisplay()) - return reportError(report, i18n("The module %1 is disabled.", mod.moduleName()), - i18n("

Either the hardware/software the module configures is not available or the module has been disabled by the administrator.

"), + i18n("The module %1 could not be found.", mod.moduleName()), + i18n("

The diagnosis is:
The desktop file %1 could not be found.

", mod.fileName()), parent); + } + + if (mod.service()->noDisplay()) { + return reportError(report, + i18n("The module %1 is disabled.", mod.moduleName()), + i18n("

Either the hardware/software the module configures is not available or" + " the module has been disabled by the administrator.

"), + parent); + } if (!mod.library().isEmpty()) { QString error; QVariantList args2; args2.reserve(args.count()); for (const QString &arg : args) { args2 << arg; } KCModule *module = nullptr; KPluginLoader loader(KPluginLoader::findPlugin(QLatin1String("kcms/") + mod.library())); KPluginFactory* factory = loader.factory(); if (!factory) { // KF6 TODO: make this a warning, and remove mention of fallback qDebug() << "Couldn't load plugin" << QLatin1String("kcms/") + mod.library() << ":" << loader.errorString() << " -- falling back to old-style loading from desktop file"; } else { std::unique_ptr cm(factory->create(nullptr, args2)); if (!cm) { qWarning() << "Error creating object from plugin" << loader.fileName(); } else { if (!cm->mainUi()) { return reportError(report, i18n("Error loading QML file."), cm->errorString(), parent); } module = new KCModuleQml(std::move(cm), parent, args2); return module; } } // KF6 TODO: remove this compat block - if (mod.service()) { - module = mod.service()->createInstance(parent, args2, &error); - } + module = mod.service()->createInstance(parent, args2, &error); + if (module) { return module; } else //#ifndef NDEBUG { // KF6 TODO: remove this old compat block // get the create_ function QLibrary lib(KPluginLoader::findPlugin(mod.library())); if (lib.load()) { KCModule *(*create)(QWidget *, const char *); QByteArray factorymethod("create_"); factorymethod += mod.handle().toLatin1(); create = reinterpret_cast(lib.resolve(factorymethod.constData())); if (create) { return create(parent, mod.handle().toLatin1().constData()); } else { qWarning() << "This module has no valid entry symbol at all. The reason could be that it's still using K_EXPORT_COMPONENT_FACTORY with a custom X-KDE-FactoryName which is not supported anymore"; } lib.unload(); } } //#endif // NDEBUG return reportError(report, error, QString(), parent); } /* * Ok, we could not load the library. * Try to run it as an executable. * This must not be done when calling from kcmshell, or you'll * have infinite recursion * (startService calls kcmshell which calls modloader which calls startService...) * */ return reportError(report, i18n("The module %1 is not a valid configuration module.", mod.moduleName()), i18n("The diagnosis is:
The desktop file %1 does not specify a library.
", mod.fileName()), parent); } void KCModuleLoader::unloadModule(const KCModuleInfo &mod) { // get the library loader instance KPluginLoader loader(mod.library()); loader.unload(); } KCModule *KCModuleLoader::reportError(ErrorReporting report, const QString &text, const QString &details, QWidget *parent) { QString realDetails = details; if (realDetails.isNull()) { realDetails = i18n("

Possible reasons:

  • An error occurred during your last " "system upgrade, leaving an orphaned control module behind
  • You have old third party " "modules lying around.

Check these points carefully and try to remove " "the module mentioned in the error message. If this fails, consider contacting " "your distributor or packager.

"); } if (report & KCModuleLoader::Dialog) { KMessageBox::detailedError(parent, text, realDetails); } if (report & KCModuleLoader::Inline) { return new KCMError(text, realDetails, parent); } return nullptr; } diff --git a/src/kcmultidialog.cpp b/src/kcmultidialog.cpp index 82d0fb1..07e8e46 100644 --- a/src/kcmultidialog.cpp +++ b/src/kcmultidialog.cpp @@ -1,582 +1,582 @@ /* Copyright (c) 2000 Matthias Elter Copyright (c) 2003 Daniel Molkentin Copyright (c) 2003,2006 Matthias Kretz Copyright (c) 2004 Frans Englich Copyright (c) 2006 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcmultidialog.h" #include "kcmultidialog_p.h" #include "kcmoduleqml_p.h" #include "kcmoduleproxy.h" #include #include #include #include #include #include #include #include #include #include #include #ifndef KCONFIGWIDGETS_NO_KAUTH #include #include #endif #include #include #include #include #include #include bool KCMultiDialogPrivate::resolveChanges(KCModuleProxy *currentProxy) { Q_Q(KCMultiDialog); if (!currentProxy || !currentProxy->changed()) { return true; } // Let the user decide const int queryUser = KMessageBox::warningYesNoCancel( q, i18n("The settings of the current module have changed.\n" "Do you want to apply the changes or discard them?"), i18n("Apply Settings"), KStandardGuiItem::apply(), KStandardGuiItem::discard(), KStandardGuiItem::cancel()); switch (queryUser) { case KMessageBox::Yes: return moduleSave(currentProxy); case KMessageBox::No: currentProxy->load(); return true; case KMessageBox::Cancel: return false; default: Q_ASSERT(false); return false; } } void KCMultiDialogPrivate::_k_slotCurrentPageChanged(KPageWidgetItem *current, KPageWidgetItem *previous) { Q_Q(KCMultiDialog); KCModuleProxy *previousModule = nullptr; for (int i = 0; i < modules.count(); ++i) { if (modules[i].item == previous) { previousModule = modules[i].kcm; } } // Remove margins for the buttonbox; KPageDialog handles that already q->buttonBox()->setContentsMargins(0, 0, 0, 0); q->blockSignals(true); q->setCurrentPage(previous); if (resolveChanges(previousModule)) { q->setCurrentPage(current); } q->blockSignals(false); // We need to get the state of the now active module _k_clientChanged(); } void KCMultiDialogPrivate::_k_clientChanged() { Q_Q(KCMultiDialog); // qDebug(); // Get the current module KCModuleProxy *activeModule = nullptr; for (int i = 0; i < modules.count(); ++i) { if (modules[ i ].item == q->currentPage()) { activeModule = modules[ i ].kcm; break; } } bool change = false; bool defaulted = false; if (activeModule) { change = activeModule->changed(); defaulted = activeModule->defaulted(); QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply); if (applyButton) { q->disconnect(applyButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked); #ifndef KCONFIGWIDGETS_NO_KAUTH delete applyButton->findChild(); #endif } QPushButton *okButton = q->buttonBox()->button(QDialogButtonBox::Ok); if (okButton) { q->disconnect(okButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked); #ifndef KCONFIGWIDGETS_NO_KAUTH delete okButton->findChild(); #endif } #ifndef KCONFIGWIDGETS_NO_KAUTH if (activeModule->realModule()->needsAuthorization()) { if (applyButton) { KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(applyButton); decorator->setAuthAction(activeModule->realModule()->authAction()); activeModule->realModule()->authAction().setParentWidget(activeModule->realModule()); q->connect(decorator, &KAuth::ObjectDecorator::authorized, q, &KCMultiDialog::slotApplyClicked); } if (okButton) { KAuth::ObjectDecorator *decorator = new KAuth::ObjectDecorator(okButton); decorator->setAuthAction(activeModule->realModule()->authAction()); activeModule->realModule()->authAction().setParentWidget(activeModule->realModule()); q->connect(decorator, &KAuth::ObjectDecorator::authorized, q, &KCMultiDialog::slotOkClicked); } } else { if (applyButton) { q->connect(applyButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked); delete applyButton->findChild(); } if (okButton) { q->connect(okButton, &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked); delete okButton->findChild(); } } #endif } auto buttons = activeModule ? activeModule->buttons() : KCModule::NoAdditionalButton; QPushButton *resetButton = q->buttonBox()->button(QDialogButtonBox::Reset); if (resetButton) { resetButton->setVisible(buttons & KCModule::Apply); resetButton->setEnabled(change); } QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply); if (applyButton) { applyButton->setVisible(buttons & KCModule::Apply); applyButton->setEnabled(change); } QPushButton *cancelButton = q->buttonBox()->button(QDialogButtonBox::Cancel); if (cancelButton) { cancelButton->setVisible(buttons & KCModule::Apply); } QPushButton *okButton = q->buttonBox()->button(QDialogButtonBox::Ok); if (okButton) { okButton->setVisible(buttons & KCModule::Apply); } QPushButton *closeButton = q->buttonBox()->button(QDialogButtonBox::Close); if (closeButton) { closeButton->setHidden(buttons & KCModule::Apply); } QPushButton *helpButton = q->buttonBox()->button(QDialogButtonBox::Help); if (helpButton) { helpButton->setVisible(buttons & KCModule::Help); } QPushButton *defaultButton = q->buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (defaultButton) { defaultButton->setVisible(buttons & KCModule::Default); defaultButton->setEnabled(!defaulted); } } void KCMultiDialogPrivate::_k_updateHeader(bool use, const QString &message) { Q_Q(KCMultiDialog); KPageWidgetItem *item = q->currentPage(); KCModuleProxy *kcm = qobject_cast(item->widget()); if (use) { item->setHeader(QStringLiteral("") + kcm->moduleInfo().moduleName() + QStringLiteral("
") + message + QStringLiteral("")); item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(kcm->moduleInfo().icon()), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner)); } else { item->setHeader(kcm->moduleInfo().moduleName()); item->setIcon(QIcon::fromTheme(kcm->moduleInfo().icon())); } } void KCMultiDialogPrivate::init() { Q_Q(KCMultiDialog); q->setFaceType(KPageDialog::Auto); q->setWindowTitle(i18n("Configure")); q->setModal(false); QDialogButtonBox *buttonBox = new QDialogButtonBox(q); buttonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Close | QDialogButtonBox::Ok | QDialogButtonBox::Reset); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Close), KStandardGuiItem::close()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Reset), KStandardGuiItem::reset()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Help), KStandardGuiItem::help()); buttonBox->button(QDialogButtonBox::Close)->setVisible(false); buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false); buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); q->connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, &KCMultiDialog::slotApplyClicked); q->connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, q, &KCMultiDialog::slotOkClicked); q->connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, &KCMultiDialog::slotDefaultClicked); q->connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &KCMultiDialog::slotHelpClicked); q->connect(buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, q, &KCMultiDialog::slotUser1Clicked); q->setButtonBox(buttonBox); q->connect(q, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), SLOT(_k_slotCurrentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); } KCMultiDialog::KCMultiDialog(QWidget *parent) : KPageDialog(parent) , d_ptr(new KCMultiDialogPrivate(this)) { d_func()->init(); } KCMultiDialog::KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags) : KPageDialog(pageWidget, parent, flags) , d_ptr(new KCMultiDialogPrivate(this)) { d_func()->init(); } KCMultiDialog::KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags) : KPageDialog(pageWidget, parent, flags) , d_ptr(&dd) { d_func()->init(); } KCMultiDialog::~KCMultiDialog() { delete d_ptr; } void KCMultiDialog::showEvent(QShowEvent *ev) { KPageDialog::showEvent(ev); adjustSize(); /** * adjustSize() relies on sizeHint but is limited to 2/3 of the desktop size * Workaround for https://bugreports.qt.io/browse/QTBUG-3459 * * We adjust the size after passing the show event * because otherwise window pos is set to (0,0) */ QScreen *screen = QApplication::screenAt(pos()); if (screen) { const QSize maxSize = screen->availableGeometry().size(); resize(qMin(sizeHint().width(), maxSize.width()), qMin(sizeHint().height(), maxSize.height())); } } void KCMultiDialog::slotDefaultClicked() { Q_D(KCMultiDialog); const KPageWidgetItem *item = currentPage(); if (!item) { return; } for (int i = 0; i < d->modules.count(); ++i) { if (d->modules[ i ].item == item) { d->modules[ i ].kcm->defaults(); d->_k_clientChanged(); return; } } } void KCMultiDialog::slotUser1Clicked() { const KPageWidgetItem *item = currentPage(); if (!item) { return; } Q_D(KCMultiDialog); for (int i = 0; i < d->modules.count(); ++i) { if (d->modules[ i ].item == item) { d->modules[ i ].kcm->load(); d->_k_clientChanged(); return; } } } bool KCMultiDialogPrivate::moduleSave(KCModuleProxy *module) { if (!module) { return false; } module->save(); return true; } void KCMultiDialogPrivate::apply() { Q_Q(KCMultiDialog); QStringList updatedComponents; for (const CreatedModule &module : qAsConst(modules)) { KCModuleProxy *proxy = module.kcm; if (proxy->changed()) { proxy->save(); /** * Add name of the components the kcm belongs to the list * of updated components. */ const QStringList componentNames = module.componentNames; for (const QString &componentName : componentNames) { if (!updatedComponents.contains(componentName)) { updatedComponents.append(componentName); } } } } // Send the configCommitted signal for every updated component. for (const QString &name : qAsConst(updatedComponents)) { emit q->configCommitted(name.toLatin1()); } emit q->configCommitted(); } void KCMultiDialog::slotApplyClicked() { QPushButton *applyButton = buttonBox()->button(QDialogButtonBox::Apply); applyButton->setFocus(); d_func()->apply(); } void KCMultiDialog::slotOkClicked() { QPushButton *okButton = buttonBox()->button(QDialogButtonBox::Ok); okButton->setFocus(); d_func()->apply(); accept(); } void KCMultiDialog::slotHelpClicked() { const KPageWidgetItem *item = currentPage(); if (!item) { return; } Q_D(KCMultiDialog); QString docPath; for (int i = 0; i < d->modules.count(); ++i) { if (d->modules[ i ].item == item) { docPath = d->modules[ i ].kcm->moduleInfo().docPath(); break; } } const QUrl docUrl = QUrl(QStringLiteral("help:/")).resolved(QUrl(docPath)); // same code as in KHelpClient::invokeHelp const QString docUrlScheme = docUrl.scheme(); if (docUrlScheme == QLatin1String("help") || docUrlScheme == QLatin1String("man") || docUrlScheme == QLatin1String("info")) { QProcess::startDetached(QStringLiteral("khelpcenter"), QStringList() << docUrl.toString()); } else { QDesktopServices::openUrl(docUrl); } } void KCMultiDialog::closeEvent(QCloseEvent *event) { Q_D(KCMultiDialog); KPageDialog::closeEvent(event); /** * If we don't delete them, the DBUS registration stays, and trying to load the KCMs * in other situations will lead to "module already loaded in Foo," while to the user * doesn't appear so(the dialog is hidden) */ for(auto &proxy : qAsConst(d->modules)) { proxy.kcm->deleteClient(); } } KPageWidgetItem *KCMultiDialog::addModule(const QString &path, const QStringList &args) { QString complete = path; if (!path.endsWith(QLatin1String(".desktop"))) { complete += QStringLiteral(".desktop"); } KService::Ptr service = KService::serviceByStorageId(complete); return addModule(KCModuleInfo(service), nullptr, args); } KPageWidgetItem *KCMultiDialog::addModule(const KCModuleInfo &moduleInfo, KPageWidgetItem *parentItem, const QStringList &args) { Q_D(KCMultiDialog); - if (!moduleInfo.pluginInfo().isValid()) { + if (!moduleInfo.service()) { return nullptr; } //KAuthorized::authorizeControlModule( moduleInfo.service()->menuId() ) is //checked in noDisplay already if (moduleInfo.service() && moduleInfo.service()->noDisplay()) { return nullptr; } // Create the scroller auto *moduleScroll = new UnboundScrollArea(this); // Prepare the scroll area moduleScroll->setWidgetResizable(true); moduleScroll->setFrameStyle(QFrame::NoFrame); moduleScroll->viewport()->setAutoFillBackground(false); KCModuleProxy *kcm = new KCModuleProxy(moduleInfo, moduleScroll, args); moduleScroll->setWidget(kcm); // qDebug() << moduleInfo.moduleName(); KPageWidgetItem *item = new KPageWidgetItem(moduleScroll, moduleInfo.moduleName()); KCMultiDialogPrivate::CreatedModule cm; cm.kcm = kcm; cm.item = item; - cm.componentNames = moduleInfo.pluginInfo().property(QStringLiteral("X-KDE-ParentComponents")).toStringList(); + cm.componentNames = moduleInfo.service()->property(QStringLiteral("X-KDE-ParentComponents")).toStringList(); d->modules.append(cm); if (qobject_cast(kcm->realModule())) { item->setHeaderVisible(false); } if (kcm->realModule() && kcm->realModule()->useRootOnlyMessage()) { item->setHeader(QStringLiteral("") + moduleInfo.moduleName() + QStringLiteral("
") + kcm->realModule()->rootOnlyMessage() + QStringLiteral("")); item->setIcon(KIconUtils::addOverlay(QIcon::fromTheme(moduleInfo.icon()), QIcon::fromTheme(QStringLiteral("dialog-warning")), Qt::BottomRightCorner)); } else { item->setHeader(moduleInfo.moduleName()); item->setIcon(QIcon::fromTheme(moduleInfo.icon())); } item->setProperty("_k_weight", moduleInfo.weight()); bool updateCurrentPage = false; const KPageWidgetModel *model = qobject_cast(pageWidget()->model()); Q_ASSERT(model); if (parentItem) { const QModelIndex parentIndex = model->index(parentItem); const int siblingCount = model->rowCount(parentIndex); int row = 0; for (; row < siblingCount; ++row) { KPageWidgetItem *siblingItem = model->item(model->index(row, 0, parentIndex)); if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) { // the item we found is heavier than the new module // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name(); insertPage(siblingItem, item); break; } } if (row >= siblingCount) { // the new module is either the first or the heaviest item // qDebug() << "adding KCM " << item->name() << " with parent " << parentItem->name(); addSubPage(parentItem, item); } } else { const int siblingCount = model->rowCount(); int row = 0; for (; row < siblingCount; ++row) { KPageWidgetItem *siblingItem = model->item(model->index(row, 0)); if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) { // the item we found is heavier than the new module // qDebug() << "adding KCM " << item->name() << " before " << siblingItem->name(); insertPage(siblingItem, item); if (siblingItem == currentPage()) { updateCurrentPage = true; } break; } } if (row == siblingCount) { // the new module is either the first or the heaviest item // qDebug() << "adding KCM " << item->name() << " at the top level"; addPage(item); } } connect(kcm, SIGNAL(changed(bool)), this, SLOT(_k_clientChanged())); connect(kcm->realModule(), SIGNAL(rootOnlyMessageChanged(bool,QString)), this, SLOT(_k_updateHeader(bool,QString))); if (d->modules.count() == 1 || updateCurrentPage) { setCurrentPage(item); d->_k_clientChanged(); } return item; } void KCMultiDialog::clear() { Q_D(KCMultiDialog); // qDebug() ; for (int i = 0; i < d->modules.count(); ++i) { removePage(d->modules[ i ].item); delete d->modules[ i ].kcm; } d->modules.clear(); d->_k_clientChanged(); } #include "moc_kcmultidialog.cpp"