File indexing completed on 2024-04-21 14:53:52

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2007, 2006 Rafael Fernández López <ereslibre@kde.org>
0004     SPDX-FileCopyrightText: 2002-2003 Matthias Kretz <kretz@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kpluginselector.h"
0010 
0011 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 90)
0012 
0013 #include "kpluginselector_p.h"
0014 
0015 #include <kcmutils_debug.h>
0016 
0017 #include <QApplication>
0018 #include <QCheckBox>
0019 #include <QDialog>
0020 #include <QDialogButtonBox>
0021 #include <QDir>
0022 #include <QDirIterator>
0023 #include <QLabel>
0024 #include <QLineEdit>
0025 #include <QPainter>
0026 #include <QPushButton>
0027 #include <QStandardPaths>
0028 #include <QStyle>
0029 #include <QStyleOptionViewItem>
0030 #include <QVBoxLayout>
0031 
0032 #include <KAboutPluginDialog>
0033 #include <KCategorizedSortFilterProxyModel>
0034 #include <KCategorizedView>
0035 #include <KCategoryDrawer>
0036 #include <KLocalizedString>
0037 #include <KMessageBox>
0038 #include <KPluginMetaData>
0039 #include <KStandardGuiItem>
0040 #include <KUrlLabel>
0041 #include <kcmoduleinfo.h>
0042 #include <kcmoduleproxy.h>
0043 
0044 #define MARGIN 5
0045 
0046 QT_WARNING_PUSH
0047 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0048 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0049 
0050 KPluginSelector::Private::Private(KPluginSelector *parent)
0051     : QObject(parent)
0052     , parent(parent)
0053     , listView(nullptr)
0054     , categoryDrawer(nullptr)
0055     , pluginDelegate(nullptr)
0056     , showIcons(false)
0057     , showDefaultIndicator(false)
0058 {
0059 }
0060 
0061 KPluginSelector::Private::~Private()
0062 {
0063 }
0064 
0065 void KPluginSelector::Private::updateDependencies(PluginEntry *pluginEntry, bool added)
0066 {
0067     if (added) {
0068         QStringList dependencyList = pluginEntry->pluginInfo.dependencies();
0069 
0070         if (dependencyList.isEmpty()) {
0071             return;
0072         }
0073 
0074         for (int i = 0; i < pluginModel->rowCount(); i++) {
0075             const QModelIndex index = pluginModel->index(i, 0);
0076             PluginEntry *pe = static_cast<PluginEntry *>(index.internalPointer());
0077 
0078             if ((pe->pluginInfo.pluginName() != pluginEntry->pluginInfo.pluginName()) && dependencyList.contains(pe->pluginInfo.pluginName()) && !pe->checked) {
0079                 dependenciesWidget->addDependency(pe->pluginInfo.name(), pluginEntry->pluginInfo.name(), added);
0080                 const_cast<QAbstractItemModel *>(index.model())->setData(index, added, Qt::CheckStateRole);
0081                 updateDependencies(pe, added);
0082             }
0083         }
0084     } else {
0085         for (int i = 0; i < pluginModel->rowCount(); i++) {
0086             const QModelIndex index = pluginModel->index(i, 0);
0087             PluginEntry *pe = static_cast<PluginEntry *>(index.internalPointer());
0088 
0089             if ((pe->pluginInfo.pluginName() != pluginEntry->pluginInfo.pluginName())
0090                 && pe->pluginInfo.dependencies().contains(pluginEntry->pluginInfo.pluginName()) && pe->checked) {
0091                 dependenciesWidget->addDependency(pe->pluginInfo.name(), pluginEntry->pluginInfo.name(), added);
0092                 const_cast<QAbstractItemModel *>(index.model())->setData(index, added, Qt::CheckStateRole);
0093                 updateDependencies(pe, added);
0094             }
0095         }
0096     }
0097 }
0098 
0099 int KPluginSelector::Private::dependantLayoutValue(int value, int width, int totalWidth) const
0100 {
0101     if (listView->layoutDirection() == Qt::LeftToRight) {
0102         return value;
0103     }
0104 
0105     return totalWidth - width - value;
0106 }
0107 
0108 KPluginSelector::Private::DependenciesWidget::DependenciesWidget(QWidget *parent)
0109     : QWidget(parent)
0110     , addedByDependencies(0)
0111     , removedByDependencies(0)
0112 {
0113     setVisible(false);
0114 
0115     details = new QLabel();
0116 
0117     QHBoxLayout *layout = new QHBoxLayout(this);
0118 
0119     QVBoxLayout *dataLayout = new QVBoxLayout;
0120     dataLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
0121     layout->setAlignment(Qt::AlignLeft);
0122     QLabel *label = new QLabel();
0123     label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
0124     label->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(style()->pixelMetric(QStyle::PM_MessageBoxIconSize)));
0125     label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0126     layout->addWidget(label);
0127     KUrlLabel *link = new KUrlLabel();
0128     link->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
0129     link->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0130     link->setGlowEnabled(false);
0131     link->setUnderline(false);
0132     link->setFloatEnabled(true);
0133     link->setUseCursor(true);
0134     link->setHighlightedColor(palette().color(QPalette::Link));
0135     link->setSelectedColor(palette().color(QPalette::Link));
0136     link->setText(i18n("Automatic changes have been performed due to plugin dependencies. Click here for further information"));
0137     dataLayout->addWidget(link);
0138     dataLayout->addWidget(details);
0139     layout->addLayout(dataLayout);
0140 
0141     QObject::connect(link, &KUrlLabel::leftClickedUrl, this, &KPluginSelector::Private::DependenciesWidget::showDependencyDetails);
0142 }
0143 
0144 KPluginSelector::Private::DependenciesWidget::~DependenciesWidget()
0145 {
0146 }
0147 
0148 void KPluginSelector::Private::DependenciesWidget::addDependency(const QString &dependency, const QString &pluginCausant, bool added)
0149 {
0150     if (!isVisible()) {
0151         setVisible(true);
0152     }
0153 
0154     struct FurtherInfo furtherInfo;
0155     furtherInfo.added = added;
0156     furtherInfo.pluginCausant = pluginCausant;
0157 
0158     if (dependencyMap.contains(dependency)) { // The dependency moved from added to removed or vice-versa
0159         if (added && removedByDependencies) {
0160             removedByDependencies--;
0161         } else if (addedByDependencies) {
0162             addedByDependencies--;
0163         }
0164 
0165         dependencyMap[dependency] = furtherInfo;
0166     } else {
0167         dependencyMap.insert(dependency, furtherInfo);
0168     }
0169 
0170     if (added) {
0171         addedByDependencies++;
0172     } else {
0173         removedByDependencies++;
0174     }
0175 
0176     updateDetails();
0177 }
0178 
0179 void KPluginSelector::Private::DependenciesWidget::userOverrideDependency(const QString &dependency)
0180 {
0181     if (dependencyMap.contains(dependency)) {
0182         if (addedByDependencies && dependencyMap[dependency].added) {
0183             addedByDependencies--;
0184         } else if (removedByDependencies) {
0185             removedByDependencies--;
0186         }
0187 
0188         dependencyMap.remove(dependency);
0189     }
0190 
0191     updateDetails();
0192 }
0193 
0194 void KPluginSelector::Private::DependenciesWidget::clearDependencies()
0195 {
0196     addedByDependencies = 0;
0197     removedByDependencies = 0;
0198     dependencyMap.clear();
0199     updateDetails();
0200 }
0201 
0202 void KPluginSelector::Private::DependenciesWidget::showDependencyDetails()
0203 {
0204     QString message = i18n("Automatic changes have been performed in order to satisfy plugin dependencies:\n");
0205 
0206     for (auto it = dependencyMap.cbegin(); it != dependencyMap.cend(); ++it) {
0207         const QString &dependency = it.key();
0208         const FurtherInfo &info = it.value();
0209         if (info.added) {
0210             message += i18n("\n    %1 plugin has been automatically checked because of the dependency of %2 plugin", dependency, info.pluginCausant);
0211         } else {
0212             message += i18n("\n    %1 plugin has been automatically unchecked because of its dependency on %2 plugin", dependency, info.pluginCausant);
0213         }
0214     }
0215 
0216     KMessageBox::information(this, message, i18n("Dependency Check"));
0217 
0218     addedByDependencies = 0;
0219     removedByDependencies = 0;
0220     updateDetails();
0221 }
0222 
0223 void KPluginSelector::Private::DependenciesWidget::updateDetails()
0224 {
0225     if (dependencyMap.isEmpty()) {
0226         setVisible(false);
0227         return;
0228     }
0229 
0230     QString message;
0231 
0232     if (addedByDependencies) {
0233         message +=
0234             i18np("%1 plugin automatically added due to plugin dependencies", "%1 plugins automatically added due to plugin dependencies", addedByDependencies);
0235     }
0236 
0237     if (removedByDependencies && !message.isEmpty()) {
0238         message += i18n(", ");
0239     }
0240 
0241     if (removedByDependencies) {
0242         message += i18np("%1 plugin automatically removed due to plugin dependencies",
0243                          "%1 plugins automatically removed due to plugin dependencies",
0244                          removedByDependencies);
0245     }
0246 
0247     if (message.isEmpty()) {
0248         details->setVisible(false);
0249     } else {
0250         details->setVisible(true);
0251         details->setText(message);
0252     }
0253 }
0254 
0255 KPluginSelector::KPluginSelector(QWidget *parent)
0256     : QWidget(parent)
0257     , d(new Private(this))
0258 {
0259     QVBoxLayout *layout = new QVBoxLayout(this);
0260     layout->setContentsMargins(0, 0, 0, 0);
0261 
0262     d->lineEdit = new QLineEdit(this);
0263     d->lineEdit->setClearButtonEnabled(true);
0264     d->lineEdit->setPlaceholderText(i18n("Search..."));
0265     d->listView = new KCategorizedView(this);
0266     d->categoryDrawer = new KCategoryDrawer(d->listView);
0267     d->listView->setVerticalScrollMode(QListView::ScrollPerPixel);
0268     d->listView->setAlternatingRowColors(true);
0269     d->listView->setCategoryDrawer(d->categoryDrawer);
0270     d->dependenciesWidget = new Private::DependenciesWidget(this);
0271 
0272     d->pluginModel = new Private::PluginModel(d, this);
0273     d->proxyModel = new Private::ProxyModel(d, this);
0274     d->proxyModel->setCategorizedModel(true);
0275     d->proxyModel->setSourceModel(d->pluginModel);
0276     d->listView->setModel(d->proxyModel);
0277     d->listView->setAlternatingRowColors(true);
0278 
0279     Private::PluginDelegate *pluginDelegate = new Private::PluginDelegate(d, this);
0280     d->listView->setItemDelegate(pluginDelegate);
0281 
0282     d->listView->setMouseTracking(true);
0283     d->listView->viewport()->setAttribute(Qt::WA_Hover);
0284 
0285     connect(d->lineEdit, &QLineEdit::textChanged, d->proxyModel, &QSortFilterProxyModel::invalidate);
0286     connect(pluginDelegate, &Private::PluginDelegate::changed, this, &KPluginSelector::changed);
0287     connect(pluginDelegate, &Private::PluginDelegate::configCommitted, this, &KPluginSelector::configCommitted);
0288     connect(this, &KPluginSelector::defaultsIndicatorsVisible, pluginDelegate, &Private::PluginDelegate::slotResetModel);
0289 
0290     connect(this, &KPluginSelector::changed, [this] {
0291         Q_EMIT defaulted(isDefault());
0292     });
0293 
0294     layout->addWidget(d->lineEdit);
0295     layout->addWidget(d->listView);
0296     layout->addWidget(d->dependenciesWidget);
0297 
0298     // When a KPluginSelector instance gets focus,
0299     // it should pass over the focus to its child searchbar.
0300     setFocusProxy(d->lineEdit);
0301 }
0302 
0303 KPluginSelector::~KPluginSelector()
0304 {
0305     delete d->listView->itemDelegate();
0306     delete d->listView; // depends on some other things in d, make sure this dies first.
0307     delete d;
0308 }
0309 
0310 void KPluginSelector::addPlugins(const QString &componentName, const QString &categoryName, const QString &categoryKey, KSharedConfig::Ptr config)
0311 {
0312     QStringList desktopFileNames;
0313     const QStringList dirs =
0314         QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, componentName + QStringLiteral("/kpartplugins"), QStandardPaths::LocateDirectory);
0315     for (const QString &dir : dirs) {
0316         QDirIterator it(dir, QStringList() << QStringLiteral("*.desktop"), QDir::NoFilter, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
0317         while (it.hasNext()) {
0318             desktopFileNames.append(it.next());
0319         }
0320     }
0321 
0322     QList<KPluginInfo> pluginInfoList = KPluginInfo::fromFiles(desktopFileNames);
0323 
0324     if (pluginInfoList.isEmpty()) {
0325         return;
0326     }
0327 
0328     if (!config) {
0329         config = KSharedConfig::openConfig(componentName + QStringLiteral("rc"));
0330     }
0331     Q_ASSERT(config);
0332 
0333     KConfigGroup cfgGroup(config, "KParts Plugins");
0334     // qDebug() << "cfgGroup = " << &cfgGroup;
0335 
0336     d->pluginModel->addPlugins(pluginInfoList, categoryName, categoryKey, cfgGroup);
0337     d->proxyModel->sort(0);
0338 }
0339 
0340 void KPluginSelector::addPlugins(const QList<KPluginInfo> &pluginInfoList,
0341                                  PluginLoadMethod pluginLoadMethod,
0342                                  const QString &categoryName,
0343                                  const QString &categoryKey,
0344                                  const KSharedConfig::Ptr &config)
0345 {
0346     if (pluginInfoList.isEmpty()) {
0347         return;
0348     }
0349 
0350     KConfigGroup cfgGroup(config ? config : KSharedConfig::openConfig(), "Plugins");
0351     // qDebug() << "cfgGroup = " << &cfgGroup;
0352 
0353     d->pluginModel->addPlugins(pluginInfoList, categoryName, categoryKey, cfgGroup, pluginLoadMethod, true /* manually added */);
0354     d->proxyModel->sort(0);
0355 }
0356 
0357 void KPluginSelector::clearPlugins()
0358 {
0359     d->pluginModel->clear();
0360 }
0361 
0362 void KPluginSelector::load()
0363 {
0364     for (int i = 0; i < d->pluginModel->rowCount(); i++) {
0365         const QModelIndex index = d->pluginModel->index(i, 0);
0366         PluginEntry *pluginEntry = static_cast<PluginEntry *>(index.internalPointer());
0367         pluginEntry->pluginInfo.load(pluginEntry->cfgGroup);
0368         d->pluginModel->setData(index, pluginEntry->pluginInfo.isPluginEnabled(), Qt::CheckStateRole);
0369     }
0370 
0371     static_cast<KPluginSelector::Private::PluginDelegate *>(d->listView->itemDelegate())->clearChangedEntries();
0372     Q_EMIT changed(false);
0373 }
0374 
0375 void KPluginSelector::save()
0376 {
0377     for (int i = 0; i < d->pluginModel->rowCount(); i++) {
0378         const QModelIndex index = d->pluginModel->index(i, 0);
0379         PluginEntry *pluginEntry = static_cast<PluginEntry *>(index.internalPointer());
0380         pluginEntry->pluginInfo.setPluginEnabled(pluginEntry->checked);
0381         pluginEntry->pluginInfo.save(pluginEntry->cfgGroup);
0382         pluginEntry->cfgGroup.sync();
0383     }
0384 
0385     static_cast<KPluginSelector::Private::PluginDelegate *>(d->listView->itemDelegate())->clearChangedEntries();
0386     Q_EMIT changed(false);
0387 }
0388 
0389 bool KPluginSelector::isSaveNeeded() const
0390 {
0391     for (int i = 0; i < d->pluginModel->rowCount(); i++) {
0392         const QModelIndex index = d->pluginModel->index(i, 0);
0393         PluginEntry *pluginEntry = static_cast<PluginEntry *>(index.internalPointer());
0394         if (d->pluginModel->data(index, Qt::CheckStateRole).toBool() != pluginEntry->pluginInfo.isPluginEnabled()) {
0395             return true;
0396         }
0397     }
0398 
0399     return false;
0400 }
0401 
0402 void KPluginSelector::defaults()
0403 {
0404     bool isChanged = false;
0405     auto delegate = static_cast<KPluginSelector::Private::PluginDelegate *>(d->listView->itemDelegate());
0406     delegate->clearChangedEntries();
0407     for (int i = 0; i < d->pluginModel->rowCount(); i++) {
0408         const QModelIndex index = d->pluginModel->index(i, 0);
0409         PluginEntry *pluginEntry = static_cast<PluginEntry *>(index.internalPointer());
0410         bool entryChanged = pluginEntry->pluginInfo.isPluginEnabled() != pluginEntry->pluginInfo.isPluginEnabledByDefault();
0411         isChanged |= entryChanged;
0412         d->pluginModel->setData(index, pluginEntry->pluginInfo.isPluginEnabledByDefault(), Qt::CheckStateRole);
0413         if (entryChanged) {
0414             delegate->addChangedEntry(pluginEntry);
0415         }
0416     }
0417 
0418     Q_EMIT changed(isChanged);
0419 }
0420 
0421 bool KPluginSelector::isDefault() const
0422 {
0423     for (int i = 0; i < d->pluginModel->rowCount(); i++) {
0424         const QModelIndex index = d->pluginModel->index(i, 0);
0425         PluginEntry *pluginEntry = static_cast<PluginEntry *>(index.internalPointer());
0426         if (d->pluginModel->data(index, Qt::CheckStateRole).toBool() != pluginEntry->pluginInfo.isPluginEnabledByDefault()) {
0427             return false;
0428         }
0429     }
0430 
0431     return true;
0432 }
0433 
0434 void KPluginSelector::updatePluginsState()
0435 {
0436     static_cast<KPluginSelector::Private::PluginDelegate *>(d->listView->itemDelegate())->clearChangedEntries();
0437     for (int i = 0; i < d->pluginModel->rowCount(); i++) {
0438         const QModelIndex index = d->pluginModel->index(i, 0);
0439         PluginEntry *pluginEntry = static_cast<PluginEntry *>(index.internalPointer());
0440         if (pluginEntry->manuallyAdded) {
0441             pluginEntry->pluginInfo.setPluginEnabled(pluginEntry->checked);
0442         }
0443     }
0444 }
0445 
0446 void KPluginSelector::setConfigurationArguments(const QStringList &arguments)
0447 {
0448     d->kcmArguments = arguments;
0449 }
0450 
0451 QStringList KPluginSelector::configurationArguments() const
0452 {
0453     return d->kcmArguments;
0454 }
0455 
0456 void KPluginSelector::showConfiguration(const QString &componentName)
0457 {
0458     QModelIndex idx;
0459     for (int i = 0, c = d->proxyModel->rowCount(); i < c; ++i) {
0460         const auto currentIndex = d->proxyModel->index(i, 0);
0461         const auto entry = currentIndex.data(KPluginSelector::Private::PluginEntryRole).value<PluginEntry *>();
0462         if (entry->pluginInfo.pluginName() == componentName) {
0463             idx = currentIndex;
0464             break;
0465         }
0466     }
0467 
0468     if (idx.isValid()) {
0469         auto delegate = static_cast<KPluginSelector::Private::PluginDelegate *>(d->listView->itemDelegate());
0470         delegate->configure(idx);
0471     } else {
0472         qCWarning(KCMUTILS_LOG) << "Could not find plugin" << componentName;
0473     }
0474 }
0475 
0476 void KPluginSelector::setAdditionalButtonHandler(std::function<QPushButton *(const KPluginInfo &)> handler)
0477 {
0478     static_cast<Private::PluginDelegate *>(d->listView->itemDelegate())->setHandler(handler);
0479 }
0480 
0481 void KPluginSelector::setDefaultsIndicatorsVisible(bool isVisible)
0482 {
0483     if (isVisible != d->showDefaultIndicator) {
0484         d->showDefaultIndicator = isVisible;
0485         Q_EMIT defaultsIndicatorsVisible();
0486     }
0487 }
0488 
0489 KPluginSelector::Private::PluginModel::PluginModel(KPluginSelector::Private *pluginSelector_d, QObject *parent)
0490     : QAbstractListModel(parent)
0491     , pluginSelector_d(pluginSelector_d)
0492 {
0493 }
0494 
0495 KPluginSelector::Private::PluginModel::~PluginModel()
0496 {
0497 }
0498 
0499 static bool hasServiceNoDisplaySet(const KPluginInfo &pluginInfo)
0500 {
0501     QT_WARNING_PUSH
0502     QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0503     QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0504     return pluginInfo.service() && pluginInfo.service()->noDisplay();
0505     QT_WARNING_POP
0506 }
0507 
0508 void KPluginSelector::Private::PluginModel::addPlugins(const QList<KPluginInfo> &pluginList,
0509                                                        const QString &categoryName,
0510                                                        const QString &categoryKey,
0511                                                        const KConfigGroup &cfgGroup,
0512                                                        PluginLoadMethod pluginLoadMethod,
0513                                                        bool manuallyAdded)
0514 {
0515     QList<PluginEntry> listToAdd;
0516 
0517     for (const KPluginInfo &pluginInfo : pluginList) {
0518         PluginEntry pluginEntry;
0519         pluginEntry.category = categoryName;
0520         pluginEntry.pluginInfo = pluginInfo;
0521         if (pluginLoadMethod == ReadConfigFile) {
0522             pluginEntry.pluginInfo.load(cfgGroup);
0523         }
0524         pluginEntry.checked = pluginInfo.isPluginEnabled();
0525         pluginEntry.manuallyAdded = manuallyAdded;
0526         if (cfgGroup.isValid()) {
0527             pluginEntry.cfgGroup = cfgGroup;
0528         } else {
0529             pluginEntry.cfgGroup = pluginInfo.config();
0530         }
0531 
0532         // this is where kiosk will set if a plugin is checkable or not (pluginName + "Enabled")
0533         pluginEntry.isCheckable = !pluginInfo.isValid() || !pluginEntry.cfgGroup.isEntryImmutable(pluginInfo.pluginName() + QLatin1String("Enabled"));
0534 
0535         if (!pluginEntryList.contains(pluginEntry) && !listToAdd.contains(pluginEntry)
0536             && (categoryKey.isEmpty() || !pluginInfo.category().compare(categoryKey, Qt::CaseInsensitive)) && (!hasServiceNoDisplaySet(pluginInfo))) {
0537             listToAdd << pluginEntry;
0538 
0539             if (!pluginSelector_d->showIcons && !pluginInfo.icon().isEmpty()) {
0540                 pluginSelector_d->showIcons = true;
0541             }
0542         }
0543     }
0544 
0545     if (!listToAdd.isEmpty()) {
0546         beginInsertRows(QModelIndex(), pluginEntryList.count(), pluginEntryList.count() + listToAdd.count() - 1);
0547         pluginEntryList << listToAdd;
0548         endInsertRows();
0549     }
0550 }
0551 
0552 void KPluginSelector::Private::PluginModel::clear()
0553 {
0554     beginResetModel();
0555     pluginEntryList.clear();
0556     endResetModel();
0557 }
0558 
0559 QModelIndex KPluginSelector::Private::PluginModel::index(int row, int column, const QModelIndex &parent) const
0560 {
0561     Q_UNUSED(parent)
0562 
0563     return createIndex(row, column, (row < pluginEntryList.count()) ? (void *)&pluginEntryList.at(row) : nullptr);
0564 }
0565 
0566 QVariant KPluginSelector::Private::PluginModel::data(const QModelIndex &index, int role) const
0567 {
0568     if (!index.isValid() || !index.internalPointer()) {
0569         return QVariant();
0570     }
0571 
0572     PluginEntry *pluginEntry = static_cast<PluginEntry *>(index.internalPointer());
0573 
0574     switch (role) {
0575     case Qt::DisplayRole:
0576         return pluginEntry->pluginInfo.name();
0577     case PluginEntryRole:
0578         return QVariant::fromValue(pluginEntry);
0579     case ServicesCountRole:
0580         return pluginEntry->pluginInfo.kcmServices().count() +
0581             // if we have a X-KDE-ConfigModule key, we know that we have a config module
0582             (pluginEntry->pluginInfo.property(QStringLiteral("X-KDE-ConfigModule")).toString().isEmpty() ? 0 : 1);
0583     case NameRole:
0584         return pluginEntry->pluginInfo.name();
0585     case CommentRole:
0586         return pluginEntry->pluginInfo.comment();
0587     case AuthorRole:
0588         return pluginEntry->pluginInfo.author();
0589     case EmailRole:
0590         return pluginEntry->pluginInfo.email();
0591     case WebsiteRole:
0592         return pluginEntry->pluginInfo.website();
0593     case VersionRole:
0594         return pluginEntry->pluginInfo.version();
0595     case LicenseRole:
0596         return pluginEntry->pluginInfo.license();
0597     case DependenciesRole:
0598         return pluginEntry->pluginInfo.dependencies();
0599     case IsCheckableRole:
0600         return pluginEntry->isCheckable;
0601     case Qt::DecorationRole:
0602         return pluginEntry->pluginInfo.icon();
0603     case Qt::CheckStateRole:
0604         return pluginEntry->checked;
0605     case KCategorizedSortFilterProxyModel::CategoryDisplayRole: // fall through
0606     case KCategorizedSortFilterProxyModel::CategorySortRole:
0607         return pluginEntry->category;
0608     default:
0609         return QVariant();
0610     }
0611 }
0612 
0613 bool KPluginSelector::Private::PluginModel::setData(const QModelIndex &index, const QVariant &value, int role)
0614 {
0615     if (!index.isValid()) {
0616         return false;
0617     }
0618 
0619     bool ret = false;
0620 
0621     if (role == Qt::CheckStateRole) {
0622         static_cast<PluginEntry *>(index.internalPointer())->checked = value.toBool();
0623         ret = true;
0624     }
0625 
0626     if (ret) {
0627         Q_EMIT dataChanged(index, index);
0628     }
0629 
0630     return ret;
0631 }
0632 
0633 int KPluginSelector::Private::PluginModel::rowCount(const QModelIndex &parent) const
0634 {
0635     if (parent.isValid()) {
0636         return 0;
0637     }
0638 
0639     return pluginEntryList.count();
0640 }
0641 
0642 KPluginSelector::Private::ProxyModel::ProxyModel(KPluginSelector::Private *pluginSelector_d, QObject *parent)
0643     : KCategorizedSortFilterProxyModel(parent)
0644     , pluginSelector_d(pluginSelector_d)
0645 {
0646     sort(0);
0647 }
0648 
0649 KPluginSelector::Private::ProxyModel::~ProxyModel()
0650 {
0651 }
0652 
0653 bool KPluginSelector::Private::ProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
0654 {
0655     Q_UNUSED(sourceParent)
0656 
0657     if (!pluginSelector_d->lineEdit->text().isEmpty()) {
0658         const QModelIndex index = sourceModel()->index(sourceRow, 0);
0659         const KPluginInfo pluginInfo = static_cast<PluginEntry *>(index.internalPointer())->pluginInfo;
0660         return pluginInfo.name().contains(pluginSelector_d->lineEdit->text(), Qt::CaseInsensitive)
0661             || pluginInfo.comment().contains(pluginSelector_d->lineEdit->text(), Qt::CaseInsensitive);
0662     }
0663 
0664     return true;
0665 }
0666 
0667 bool KPluginSelector::Private::ProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const
0668 {
0669     return static_cast<PluginEntry *>(left.internalPointer())
0670                ->pluginInfo.name()
0671                .compare(static_cast<PluginEntry *>(right.internalPointer())->pluginInfo.name(), Qt::CaseInsensitive)
0672         < 0;
0673 }
0674 
0675 KPluginSelector::Private::PluginDelegate::PluginDelegate(KPluginSelector::Private *pluginSelector_d, QObject *parent)
0676     : KWidgetItemDelegate(pluginSelector_d->listView, parent)
0677     , checkBox(new QCheckBox)
0678     , pushButton(new QPushButton)
0679     , pluginSelector_d(pluginSelector_d)
0680 {
0681     pushButton->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); // only for getting size matters
0682 }
0683 
0684 KPluginSelector::Private::PluginDelegate::~PluginDelegate()
0685 {
0686     delete checkBox;
0687     delete pushButton;
0688 }
0689 
0690 void KPluginSelector::Private::PluginDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0691 {
0692     if (!index.isValid()) {
0693         return;
0694     }
0695 
0696     int xOffset = checkBox->sizeHint().width();
0697     bool disabled = !index.model()->data(index, IsCheckableRole).toBool();
0698 
0699     painter->save();
0700 
0701     QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr);
0702 
0703     int iconSize = option.rect.height() - MARGIN * 2;
0704     if (pluginSelector_d->showIcons) {
0705         QIcon icon = QIcon::fromTheme(index.model()->data(index, Qt::DecorationRole).toString());
0706         icon.paint(painter,
0707                    QRect(pluginSelector_d->dependantLayoutValue(MARGIN + option.rect.left() + xOffset, iconSize, option.rect.width()),
0708                          MARGIN + option.rect.top(),
0709                          iconSize,
0710                          iconSize));
0711     } else {
0712         iconSize = -MARGIN;
0713     }
0714 
0715     QRect contentsRect(pluginSelector_d->dependantLayoutValue(MARGIN * 2 + iconSize + option.rect.left() + xOffset,
0716                                                               option.rect.width() - MARGIN * 3 - iconSize - xOffset,
0717                                                               option.rect.width()),
0718                        MARGIN + option.rect.top(),
0719                        option.rect.width() - MARGIN * 3 - iconSize - xOffset,
0720                        option.rect.height() - MARGIN * 2);
0721 
0722     int lessHorizontalSpace = MARGIN * 2 + pushButton->sizeHint().width();
0723     if (index.model()->data(index, ServicesCountRole).toBool()) {
0724         lessHorizontalSpace += MARGIN + pushButton->sizeHint().width();
0725     }
0726     // Reserve space for extra button
0727     if (handler) {
0728         lessHorizontalSpace += MARGIN + pushButton->sizeHint().width();
0729     }
0730 
0731     contentsRect.setWidth(contentsRect.width() - lessHorizontalSpace);
0732 
0733     if (option.state & QStyle::State_Selected) {
0734         painter->setPen(option.palette.highlightedText().color());
0735     }
0736 
0737     if (pluginSelector_d->listView->layoutDirection() == Qt::RightToLeft) {
0738         contentsRect.translate(lessHorizontalSpace, 0);
0739     }
0740 
0741     painter->save();
0742     if (disabled) {
0743         QPalette pal(option.palette);
0744         pal.setCurrentColorGroup(QPalette::Disabled);
0745         painter->setPen(pal.text().color());
0746     }
0747 
0748     painter->save();
0749     QFont font = titleFont(option.font);
0750     QFontMetrics fmTitle(font);
0751     painter->setFont(font);
0752     painter->drawText(contentsRect,
0753                       Qt::AlignLeft | Qt::AlignTop,
0754                       fmTitle.elidedText(index.model()->data(index, Qt::DisplayRole).toString(), Qt::ElideRight, contentsRect.width()));
0755     painter->restore();
0756 
0757     painter->drawText(contentsRect,
0758                       Qt::AlignLeft | Qt::AlignBottom,
0759                       option.fontMetrics.elidedText(index.model()->data(index, CommentRole).toString(), Qt::ElideRight, contentsRect.width()));
0760 
0761     painter->restore();
0762     painter->restore();
0763 }
0764 
0765 QSize KPluginSelector::Private::PluginDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0766 {
0767     int i = 5;
0768     int j = 1;
0769     if (index.model()->data(index, ServicesCountRole).toBool()) {
0770         i = 6;
0771         j = 2;
0772     }
0773     // Reserve space for extra button
0774     if (handler) {
0775         ++j;
0776     }
0777 
0778     if (!pluginSelector_d->showIcons) {
0779         i--;
0780     }
0781 
0782     const QFont font = titleFont(option.font);
0783     const QFontMetrics fmTitle(font);
0784     const QString text = index.model()->data(index, Qt::DisplayRole).toString();
0785     const QString comment = index.model()->data(index, CommentRole).toString();
0786     const int maxTextWidth = qMax(fmTitle.boundingRect(text).width(), option.fontMetrics.boundingRect(comment).width());
0787 
0788     const auto iconSize = pluginSelector_d->listView->style()->pixelMetric(QStyle::PM_IconViewIconSize);
0789     return QSize(maxTextWidth + (pluginSelector_d->showIcons ? iconSize : 0) + MARGIN * i + pushButton->sizeHint().width() * j,
0790                  qMax(iconSize + MARGIN * 2, fmTitle.height() + option.fontMetrics.height() + MARGIN * 2));
0791 }
0792 
0793 QList<QWidget *> KPluginSelector::Private::PluginDelegate::createItemWidgets(const QModelIndex &index) const
0794 {
0795     Q_UNUSED(index);
0796     QList<QWidget *> widgetList;
0797 
0798     QCheckBox *enabledCheckBox = new QCheckBox;
0799     connect(enabledCheckBox, &QAbstractButton::clicked, this, &PluginDelegate::slotStateChanged);
0800     connect(enabledCheckBox, &QAbstractButton::clicked, this, &PluginDelegate::emitChanged);
0801 
0802     QPushButton *aboutPushButton = new QPushButton;
0803     aboutPushButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
0804     aboutPushButton->setToolTip(i18n("About"));
0805     connect(aboutPushButton, &QAbstractButton::clicked, this, &PluginDelegate::slotAboutClicked);
0806 
0807     QPushButton *configurePushButton = new QPushButton;
0808     configurePushButton->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
0809     configurePushButton->setToolTip(i18n("Configure"));
0810     connect(configurePushButton, &QAbstractButton::clicked, this, &PluginDelegate::slotConfigureClicked);
0811 
0812     setBlockedEventTypes(enabledCheckBox,
0813                          QList<QEvent::Type>() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick << QEvent::KeyPress
0814                                                << QEvent::KeyRelease);
0815 
0816     setBlockedEventTypes(aboutPushButton,
0817                          QList<QEvent::Type>() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick << QEvent::KeyPress
0818                                                << QEvent::KeyRelease);
0819 
0820     setBlockedEventTypes(configurePushButton,
0821                          QList<QEvent::Type>() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick << QEvent::KeyPress
0822                                                << QEvent::KeyRelease);
0823 
0824     widgetList << enabledCheckBox << configurePushButton << aboutPushButton;
0825     if (handler) {
0826         QPushButton *btn = handler(pluginSelector_d->pluginModel->pluginEntryList.at(index.row()).pluginInfo);
0827         if (btn) {
0828             widgetList << btn;
0829         }
0830     }
0831 
0832     return widgetList;
0833 }
0834 
0835 void KPluginSelector::Private::PluginDelegate::updateItemWidgets(const QList<QWidget *> widgets,
0836                                                                  const QStyleOptionViewItem &option,
0837                                                                  const QPersistentModelIndex &index) const
0838 {
0839     int extraButtonWidth = 0;
0840     QPushButton *extraButton = nullptr;
0841     if (widgets.count() == 4) {
0842         extraButton = static_cast<QPushButton *>(widgets[3]);
0843         extraButtonWidth = extraButton->sizeHint().width() + MARGIN;
0844     }
0845     QCheckBox *checkBox = static_cast<QCheckBox *>(widgets[0]);
0846     checkBox->resize(checkBox->sizeHint());
0847     checkBox->move(pluginSelector_d->dependantLayoutValue(MARGIN, checkBox->sizeHint().width(), option.rect.width()),
0848                    option.rect.height() / 2 - checkBox->sizeHint().height() / 2);
0849 
0850     QPushButton *aboutPushButton = static_cast<QPushButton *>(widgets[2]);
0851     QSize aboutPushButtonSizeHint = aboutPushButton->sizeHint();
0852     aboutPushButton->resize(aboutPushButtonSizeHint);
0853     aboutPushButton->move(pluginSelector_d->dependantLayoutValue(option.rect.width() - MARGIN - aboutPushButtonSizeHint.width() - extraButtonWidth,
0854                                                                  aboutPushButtonSizeHint.width(),
0855                                                                  option.rect.width()),
0856                           option.rect.height() / 2 - aboutPushButtonSizeHint.height() / 2);
0857 
0858     QPushButton *configurePushButton = static_cast<QPushButton *>(widgets[1]);
0859     QSize configurePushButtonSizeHint = configurePushButton->sizeHint();
0860     configurePushButton->resize(configurePushButtonSizeHint);
0861     configurePushButton->move(pluginSelector_d->dependantLayoutValue(option.rect.width() - MARGIN * 2 - configurePushButtonSizeHint.width()
0862                                                                          - aboutPushButtonSizeHint.width() - extraButtonWidth,
0863                                                                      configurePushButtonSizeHint.width(),
0864                                                                      option.rect.width()),
0865                               option.rect.height() / 2 - configurePushButtonSizeHint.height() / 2);
0866     if (extraButton) {
0867         QSize extraPushButtonSizeHint = extraButton->sizeHint();
0868         extraButton->resize(extraPushButtonSizeHint);
0869         extraButton->move(pluginSelector_d->dependantLayoutValue(option.rect.width() - extraButtonWidth, extraPushButtonSizeHint.width(), option.rect.width()),
0870                           option.rect.height() / 2 - extraPushButtonSizeHint.height() / 2);
0871     }
0872 
0873     if (!index.isValid() || !index.internalPointer()) {
0874         checkBox->setVisible(false);
0875         aboutPushButton->setVisible(false);
0876         configurePushButton->setVisible(false);
0877         if (extraButton) {
0878             extraButton->setVisible(false);
0879         }
0880     } else {
0881         PluginEntry *pluginEntry = index.model()->data(index, PluginEntryRole).value<PluginEntry *>();
0882         bool isDefault = pluginEntry->pluginInfo.isPluginEnabledByDefault() == index.model()->data(index, Qt::CheckStateRole).toBool();
0883         checkBox->setProperty("_kde_highlight_neutral", pluginSelector_d->showDefaultIndicator && !isDefault);
0884 
0885         checkBox->setChecked(index.model()->data(index, Qt::CheckStateRole).toBool());
0886         checkBox->setEnabled(index.model()->data(index, IsCheckableRole).toBool());
0887         configurePushButton->setVisible(index.model()->data(index, ServicesCountRole).toBool());
0888         configurePushButton->setEnabled(index.model()->data(index, Qt::CheckStateRole).toBool());
0889     }
0890 }
0891 
0892 void KPluginSelector::Private::PluginDelegate::slotStateChanged(bool state)
0893 {
0894     if (!focusedIndex().isValid()) {
0895         return;
0896     }
0897 
0898     const QModelIndex index = focusedIndex();
0899 
0900     pluginSelector_d->dependenciesWidget->clearDependencies();
0901 
0902     PluginEntry *pluginEntry = index.model()->data(index, PluginEntryRole).value<PluginEntry *>();
0903     pluginSelector_d->updateDependencies(pluginEntry, state);
0904 
0905     const_cast<QAbstractItemModel *>(index.model())->setData(index, state, Qt::CheckStateRole);
0906 }
0907 
0908 void KPluginSelector::Private::PluginDelegate::emitChanged(bool state)
0909 {
0910     const QModelIndex index = focusedIndex();
0911     PluginEntry *pluginEntry = index.model()->data(index, PluginEntryRole).value<PluginEntry *>();
0912 
0913     if (pluginEntry->pluginInfo.isPluginEnabled() != state) {
0914         changedEntries << pluginEntry;
0915     } else {
0916         changedEntries.remove(pluginEntry);
0917     }
0918     Q_EMIT changed(!changedEntries.isEmpty());
0919 }
0920 
0921 void KPluginSelector::Private::PluginDelegate::slotAboutClicked()
0922 {
0923     const QModelIndex index = focusedIndex();
0924     const QAbstractItemModel *model = index.model();
0925 
0926     PluginEntry *pluginEntry = model->data(index, PluginEntryRole).value<PluginEntry *>();
0927     KPluginMetaData pluginMetaData = pluginEntry->pluginInfo.toMetaData();
0928 
0929     auto *aboutPlugin = new KAboutPluginDialog(pluginMetaData, itemView());
0930     aboutPlugin->setAttribute(Qt::WA_DeleteOnClose);
0931     aboutPlugin->show();
0932 }
0933 
0934 void KPluginSelector::Private::PluginDelegate::slotConfigureClicked()
0935 {
0936     configure(focusedIndex());
0937 }
0938 
0939 void KPluginSelector::Private::PluginDelegate::configure(const QModelIndex &index)
0940 {
0941     const QAbstractItemModel *model = index.model();
0942 
0943     PluginEntry *pluginEntry = model->data(index, PluginEntryRole).value<PluginEntry *>();
0944     KPluginInfo pluginInfo = pluginEntry->pluginInfo;
0945 
0946     QDialog configDialog(itemView());
0947     configDialog.setWindowTitle(model->data(index, NameRole).toString());
0948     // The number of KCModuleProxies in use determines whether to use a tabwidget
0949     QTabWidget *newTabWidget = nullptr;
0950     // Widget to use for the setting dialog's main widget,
0951     // either a QTabWidget or a KCModuleProxy
0952     QWidget *mainWidget = nullptr;
0953     // Widget to use as the KCModuleProxy's parent.
0954     // The first proxy is owned by the dialog itself
0955     QWidget *moduleProxyParentWidget = &configDialog;
0956 
0957     QVector<KPluginMetaData> metaDataList;
0958     const auto lstServices = KPluginInfo::fromServices(pluginInfo.kcmServices());
0959     for (const KPluginInfo &info : lstServices) {
0960         metaDataList << info.toMetaData();
0961     }
0962     const QString configModule = pluginInfo.property(QStringLiteral("X-KDE-ConfigModule")).toString();
0963     if (!configModule.isEmpty()) {
0964         const QString absoluteKCMPath = QPluginLoader(configModule).fileName();
0965         // If we have a static plugin the filename does not exist
0966         if (absoluteKCMPath.isEmpty()) {
0967             const int idx = configModule.lastIndexOf(QLatin1Char('/'));
0968             const QString pluginNamespace = configModule.left(idx);
0969             const QString pluginId = configModule.mid(idx + 1);
0970             metaDataList = {KPluginMetaData::findPluginById(pluginNamespace, pluginId)}; // Clear the list to avoid old desktop files to appear twice
0971         } else {
0972             // the KCMs don't need any metadata themselves, just use the one from the KPluginInfo object
0973             // this way for example a KPackage plugin can specify plugin keyword
0974             KPluginMetaData data(pluginInfo.toMetaData().rawData(), absoluteKCMPath);
0975             metaDataList = {data}; // Clear the list to avoid old desktop files to appear twice
0976         }
0977     }
0978     for (const KPluginMetaData &data : std::as_const(metaDataList)) {
0979         if (!data.rawData().value(QStringLiteral("NoDisplay")).toBool()) {
0980             KCModuleProxy *currentModuleProxy = new KCModuleProxy(data, moduleProxyParentWidget, pluginSelector_d->kcmArguments);
0981             if (currentModuleProxy->realModule()) {
0982                 moduleProxyList << currentModuleProxy;
0983                 if (mainWidget && !newTabWidget) {
0984                     // we already created one KCModuleProxy, so we need a tab widget.
0985                     // Move the first proxy into the tab widget and ensure this and subsequent
0986                     // proxies are in the tab widget
0987                     newTabWidget = new QTabWidget(&configDialog);
0988                     moduleProxyParentWidget = newTabWidget;
0989                     mainWidget->setParent(newTabWidget);
0990                     KCModuleProxy *moduleProxy = qobject_cast<KCModuleProxy *>(mainWidget);
0991                     if (moduleProxy) {
0992                         newTabWidget->addTab(mainWidget, data.name());
0993                         mainWidget = newTabWidget;
0994                     } else {
0995                         delete newTabWidget;
0996                         newTabWidget = nullptr;
0997                         moduleProxyParentWidget = &configDialog;
0998                         mainWidget->setParent(nullptr);
0999                     }
1000                 }
1001 
1002                 if (newTabWidget) {
1003                     newTabWidget->addTab(currentModuleProxy, pluginInfo.name());
1004                 } else {
1005                     mainWidget = currentModuleProxy;
1006                 }
1007             } else {
1008                 delete currentModuleProxy;
1009             }
1010         }
1011     }
1012 
1013     // it could happen that we had services to show, but none of them were real modules.
1014     if (!moduleProxyList.isEmpty()) {
1015         QVBoxLayout *layout = new QVBoxLayout(&configDialog);
1016         layout->addWidget(mainWidget);
1017 
1018         QDialogButtonBox *buttonBox = new QDialogButtonBox(&configDialog);
1019         buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults);
1020         KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
1021         KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
1022         KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults());
1023         connect(buttonBox, &QDialogButtonBox::accepted, &configDialog, &QDialog::accept);
1024         connect(buttonBox, &QDialogButtonBox::rejected, &configDialog, &QDialog::reject);
1025         connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, this, &PluginDelegate::slotDefaultClicked);
1026         layout->addWidget(buttonBox);
1027 
1028         if (configDialog.exec() == QDialog::Accepted) {
1029             for (KCModuleProxy *moduleProxy : std::as_const(moduleProxyList)) {
1030                 QStringList parentComponents;
1031 #if KCMUTILS_BUILD_DEPRECATED_SINCE(5, 88)
1032                 if (moduleProxy->moduleInfo().isValid()) {
1033                     parentComponents = moduleProxy->moduleInfo().property(QStringLiteral("X-KDE-ParentComponents")).toStringList();
1034                 }
1035 #else
1036                 if (moduleProxy->metaData().isValid()) {
1037                     parentComponents = moduleProxy->metaData().rawData().value(QStringLiteral("X-KDE-ParentComponents")).toVariant().toStringList();
1038                 }
1039 #endif
1040                 moduleProxy->save();
1041                 for (const QString &parentComponent : std::as_const(parentComponents)) {
1042                     Q_EMIT configCommitted(parentComponent.toLatin1());
1043                 }
1044             }
1045         } else {
1046             for (KCModuleProxy *moduleProxy : std::as_const(moduleProxyList)) {
1047                 moduleProxy->load();
1048             }
1049         }
1050 
1051         qDeleteAll(moduleProxyList);
1052         moduleProxyList.clear();
1053     }
1054 }
1055 
1056 void KPluginSelector::Private::PluginDelegate::slotDefaultClicked()
1057 {
1058     for (KCModuleProxy *moduleProxy : std::as_const(moduleProxyList)) {
1059         moduleProxy->defaults();
1060     }
1061 }
1062 
1063 void KPluginSelector::Private::PluginDelegate::slotResetModel()
1064 {
1065     resetModel();
1066 }
1067 
1068 QFont KPluginSelector::Private::PluginDelegate::titleFont(const QFont &baseFont) const
1069 {
1070     QFont retFont(baseFont);
1071     retFont.setBold(true);
1072 
1073     return retFont;
1074 }
1075 void KPluginSelector::Private::PluginDelegate::setHandler(std::function<QPushButton *(const KPluginInfo &)> handler)
1076 {
1077     this->handler = handler;
1078 }
1079 
1080 #include "moc_kpluginselector.cpp"
1081 #include "moc_kpluginselector_p.cpp"
1082 #endif