File indexing completed on 2024-12-22 05:15:22

0001 /*
0002     SPDX-FileCopyrightText: 2012 Aurélien Gâteau <agateau@kde.org>
0003     SPDX-FileCopyrightText: 2014 Eike Hein <hein@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "runnermodel.h"
0009 #include "runnermatchesmodel.h"
0010 
0011 #include <QSet>
0012 
0013 #include <KConfigGroup>
0014 #include <KLocalizedString>
0015 #include <KRunner/AbstractRunner>
0016 #include <KRunner/RunnerManager>
0017 #include <chrono>
0018 #include <optional>
0019 
0020 using namespace std::chrono_literals;
0021 
0022 RunnerModel::RunnerModel(QObject *parent)
0023     : QAbstractListModel(parent)
0024     , m_favoritesModel(nullptr)
0025     , m_appletInterface(nullptr)
0026     , m_mergeResults(false)
0027     , m_krunnerConfig(KSharedConfig::openConfig(QStringLiteral("krunnerrc")))
0028 {
0029     m_queryTimer.setSingleShot(true);
0030     m_queryTimer.setInterval(10ms);
0031     connect(&m_queryTimer, &QTimer::timeout, this, &RunnerModel::startQuery);
0032     const auto readFavorites = [this]() {
0033         m_favoritePluginIds = m_krunnerConfig
0034                                   ->group(QStringLiteral("Plugins")) //
0035                                   .group(QStringLiteral("Favorites"))
0036                                   .readEntry("plugins", QStringList(QStringLiteral("krunner_services")));
0037         if (m_mergeResults && !m_models.isEmpty()) {
0038             m_models.constFirst()->setFavoriteIds(m_favoritePluginIds);
0039         }
0040     };
0041     m_configWatcher = KConfigWatcher::create(m_krunnerConfig);
0042     connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, readFavorites);
0043     readFavorites();
0044 }
0045 
0046 RunnerModel::~RunnerModel()
0047 {
0048 }
0049 
0050 QHash<int, QByteArray> RunnerModel::roleNames() const
0051 {
0052     return {{Qt::DisplayRole, "display"}};
0053 }
0054 
0055 AbstractModel *RunnerModel::favoritesModel() const
0056 {
0057     return m_favoritesModel;
0058 }
0059 
0060 void RunnerModel::setFavoritesModel(AbstractModel *model)
0061 {
0062     if (m_favoritesModel != model) {
0063         m_favoritesModel = model;
0064 
0065         clear();
0066 
0067         for (auto *model : std::as_const(m_models)) {
0068             model->setFavoritesModel(m_favoritesModel);
0069         }
0070 
0071         if (!m_query.isEmpty()) {
0072             m_queryTimer.start();
0073         }
0074 
0075         Q_EMIT favoritesModelChanged();
0076     }
0077 }
0078 
0079 QObject *RunnerModel::appletInterface() const
0080 {
0081     return m_appletInterface;
0082 }
0083 
0084 void RunnerModel::setAppletInterface(QObject *appletInterface)
0085 {
0086     if (m_appletInterface != appletInterface) {
0087         m_appletInterface = appletInterface;
0088 
0089         clear();
0090 
0091         if (!m_query.isEmpty()) {
0092             m_queryTimer.start();
0093         }
0094 
0095         Q_EMIT appletInterfaceChanged();
0096     }
0097 }
0098 
0099 bool RunnerModel::mergeResults() const
0100 {
0101     return m_mergeResults;
0102 }
0103 
0104 void RunnerModel::setMergeResults(bool merge)
0105 {
0106     if (m_mergeResults != merge) {
0107         m_mergeResults = merge;
0108         Q_EMIT mergeResultsChanged();
0109 
0110         // If we haven't lazy-initialzed our models, we do not need to re-create them
0111         if (!m_models.isEmpty()) {
0112             qDeleteAll(m_models);
0113             m_models.clear();
0114             // Just re-create all models,
0115             initializeModels();
0116         }
0117     }
0118 }
0119 
0120 QVariant RunnerModel::data(const QModelIndex &index, int role) const
0121 {
0122     if (!index.isValid() || index.row() >= m_models.count()) {
0123         return QVariant();
0124     }
0125 
0126     if (role == Qt::DisplayRole) {
0127         return m_models.at(index.row())->name();
0128     }
0129 
0130     return QVariant();
0131 }
0132 
0133 int RunnerModel::rowCount(const QModelIndex &parent) const
0134 {
0135     return parent.isValid() ? 0 : m_models.count();
0136 }
0137 
0138 int RunnerModel::count() const
0139 {
0140     return rowCount();
0141 }
0142 
0143 RunnerMatchesModel *RunnerModel::modelForRow(int row)
0144 {
0145     if (row < 0 || row >= m_models.count()) {
0146         return nullptr;
0147     }
0148 
0149     return m_models.at(row);
0150 }
0151 
0152 QStringList RunnerModel::runners() const
0153 {
0154     return m_runners;
0155 }
0156 
0157 void RunnerModel::setRunners(const QStringList &runners)
0158 {
0159     if (runners == m_runners) {
0160         return;
0161     }
0162 
0163     m_runners = runners;
0164     Q_EMIT runnersChanged();
0165 
0166     // Update the existing models only, if we have intialized the models
0167     if (!m_models.isEmpty()) {
0168         if (m_mergeResults) {
0169             Q_ASSERT(m_models.length() == 1);
0170             m_models.constFirst()->runnerManager()->setAllowedRunners(runners);
0171         } else {
0172             // Just re-create all the models, it is an edge-case anyway
0173             qDeleteAll(m_models);
0174             m_models.clear();
0175             initializeModels();
0176         }
0177     }
0178 }
0179 
0180 QString RunnerModel::query() const
0181 {
0182     return m_query;
0183 }
0184 
0185 void RunnerModel::setQuery(const QString &query)
0186 {
0187     if (m_query == query) {
0188         return; // do not init models if the query doesn't change. particularly important during startup!
0189     }
0190     if (m_models.isEmpty()) {
0191         initializeModels();
0192     }
0193     m_query = query;
0194     m_queryTimer.start();
0195     Q_EMIT queryChanged();
0196 }
0197 
0198 void RunnerModel::startQuery()
0199 {
0200     if (m_query.isEmpty()) {
0201         clear();
0202         QTimer::singleShot(0, this, &RunnerModel::queryFinished);
0203     } else {
0204         m_queryingModels = m_models.size();
0205         for (KRunner::ResultsModel *model : std::as_const(m_models)) {
0206             model->setQueryString(m_query);
0207         }
0208     }
0209 }
0210 
0211 void RunnerModel::clear()
0212 {
0213     for (KRunner::ResultsModel *model : std::as_const(m_models)) {
0214         model->clear();
0215     }
0216 }
0217 
0218 void RunnerModel::initializeModels()
0219 {
0220     beginResetModel();
0221     if (m_mergeResults) {
0222         auto model = new RunnerMatchesModel(QString(), i18n("Search results"), this);
0223         model->runnerManager()->setAllowedRunners(m_runners);
0224         model->setFavoritesModel(m_favoritesModel);
0225         model->setFavoriteIds(m_favoritePluginIds);
0226         m_models.append(model);
0227     } else {
0228         for (const QString &runnerId : std::as_const(m_runners)) {
0229             auto *model = new RunnerMatchesModel(runnerId, std::nullopt, this);
0230             model->setFavoritesModel(m_favoritesModel);
0231             m_models.append(model);
0232         }
0233     }
0234     for (auto model : std::as_const(m_models)) {
0235         connect(model->runnerManager(), &KRunner::RunnerManager::queryFinished, this, [this]() {
0236             if (--m_queryingModels == 0) {
0237                 Q_EMIT queryFinished();
0238             }
0239         });
0240     }
0241     endResetModel();
0242     Q_EMIT countChanged();
0243 }