File indexing completed on 2024-05-19 11:44:10

0001 /*
0002     SPDX-FileCopyrightText: 2011 Aaron Seigo <aseigo@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "runnermodel.h"
0008 
0009 #include <QAction>
0010 #include <QTimer>
0011 
0012 #include "krunner_debug.h"
0013 
0014 #include <KRunner/RunnerManager>
0015 
0016 RunnerModel::RunnerModel(QObject *parent)
0017     : QAbstractListModel(parent)
0018     , m_startQueryTimer(new QTimer(this))
0019     , m_runningChangedTimeout(new QTimer(this))
0020 {
0021     m_startQueryTimer->setSingleShot(true);
0022     m_startQueryTimer->setInterval(10);
0023     connect(m_startQueryTimer, &QTimer::timeout, this, &RunnerModel::startQuery);
0024 
0025     // FIXME: HACK: some runners stay in a running but finished state, not possible to say if it's actually over
0026     m_runningChangedTimeout->setSingleShot(true);
0027     connect(m_runningChangedTimeout, &QTimer::timeout, this, &RunnerModel::queryHasFinished);
0028 }
0029 
0030 QHash<int, QByteArray> RunnerModel::roleNames() const
0031 {
0032     QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
0033     roles.insert(Qt::DisplayRole, "display");
0034     roles.insert(Qt::DecorationRole, "decoration");
0035     roles.insert(Label, "label");
0036     roles.insert(Icon, "icon");
0037     roles.insert(Type, "type");
0038     roles.insert(Relevance, "relevance");
0039     roles.insert(Data, "data");
0040     roles.insert(Id, "id");
0041     roles.insert(SubText, "description");
0042     roles.insert(Enabled, "enabled");
0043     roles.insert(RunnerId, "runnerid");
0044     roles.insert(RunnerName, "runnerName");
0045     roles.insert(Actions, "actions");
0046     return roles;
0047 }
0048 
0049 int RunnerModel::rowCount(const QModelIndex &index) const
0050 {
0051     return index.isValid() ? 0 : m_matches.count();
0052 }
0053 
0054 int RunnerModel::count() const
0055 {
0056     return m_matches.count();
0057 }
0058 
0059 QStringList RunnerModel::runners() const
0060 {
0061     return m_manager ? m_manager->allowedRunners() : m_pendingRunnersList;
0062 }
0063 
0064 void RunnerModel::setRunners(const QStringList &allowedRunners)
0065 {
0066     // use sets to ensure comparison is order-independent
0067     const auto runnersIds = this->runners();
0068     if (QSet<QString>(allowedRunners.begin(), allowedRunners.end()) == QSet<QString>(runnersIds.begin(), runnersIds.end())) {
0069         return;
0070     }
0071     if (m_manager) {
0072         m_manager->setAllowedRunners(allowedRunners);
0073 
0074         // automagically enter single runner mode if there's only 1 allowed runner
0075         m_manager->setSingleMode(allowedRunners.count() == 1);
0076     } else {
0077         m_pendingRunnersList = allowedRunners;
0078     }
0079 
0080     // to trigger single runner fun!
0081     if (allowedRunners.count() == 1) {
0082         m_singleRunnerId = allowedRunners.first();
0083         scheduleQuery(QString());
0084     } else {
0085         m_singleRunnerId.clear();
0086     }
0087     Q_EMIT runnersChanged();
0088 }
0089 
0090 void RunnerModel::run(int index)
0091 {
0092     if (index >= 0 && index < m_matches.count()) {
0093         m_manager->run(m_matches.at(index));
0094     }
0095 }
0096 
0097 bool RunnerModel::isRunning() const
0098 {
0099     return m_running;
0100 }
0101 
0102 QVariant RunnerModel::data(const QModelIndex &index, int role) const
0103 {
0104     if (!index.isValid() || index.parent().isValid() || index.column() > 0 || index.row() < 0 || index.row() >= m_matches.count()) {
0105         // index requested must be valid, but we have no child items!
0106         return QVariant();
0107     }
0108 
0109     if (role == Qt::DisplayRole || role == Label) {
0110         return m_matches.at(index.row()).text();
0111     } else if (role == Qt::DecorationRole || role == Icon) {
0112         return m_matches.at(index.row()).icon();
0113     } else if (role == Type) {
0114         return m_matches.at(index.row()).type();
0115     } else if (role == Relevance) {
0116         return m_matches.at(index.row()).relevance();
0117     } else if (role == Data) {
0118         return m_matches.at(index.row()).data();
0119     } else if (role == Id) {
0120         return m_matches.at(index.row()).id();
0121     } else if (role == SubText) {
0122         return m_matches.at(index.row()).subtext();
0123     } else if (role == Enabled) {
0124         return m_matches.at(index.row()).isEnabled();
0125     } else if (role == RunnerId) {
0126         return m_matches.at(index.row()).runner()->id();
0127     } else if (role == RunnerName) {
0128         return m_matches.at(index.row()).runner()->name();
0129     } else if (role == Actions) {
0130         QVariantList actions;
0131         Plasma::QueryMatch amatch = m_matches.at(index.row());
0132         const QList<QAction *> theactions = m_manager->actionsForMatch(amatch);
0133         for (QAction *action : theactions) {
0134             actions += QVariant::fromValue<QObject *>(action);
0135         }
0136         return actions;
0137     }
0138 
0139     return QVariant();
0140 }
0141 
0142 QString RunnerModel::currentQuery() const
0143 {
0144     return m_manager ? m_manager->query() : QString();
0145 }
0146 
0147 void RunnerModel::scheduleQuery(const QString &query)
0148 {
0149     m_pendingQuery = query;
0150     m_startQueryTimer->start();
0151 }
0152 
0153 void RunnerModel::startQuery()
0154 {
0155     // avoid creating a manager just so we can run nothing
0156     // however, if we have one pending runner, then we'll be in single query mode
0157     // and a null query is a valid query
0158     if (!m_manager && m_pendingRunnersList.count() != 1 && m_pendingQuery.isEmpty()) {
0159         return;
0160     }
0161 
0162     if (createManager() || m_pendingQuery != m_manager->query()) {
0163         m_manager->launchQuery(m_pendingQuery, m_singleRunnerId);
0164         Q_EMIT queryChanged();
0165         m_running = true;
0166         Q_EMIT runningChanged(true);
0167     }
0168 }
0169 
0170 bool RunnerModel::createManager()
0171 {
0172     if (!m_manager) {
0173         m_manager = new Plasma::RunnerManager(this);
0174         connect(m_manager, &Plasma::RunnerManager::matchesChanged, this, &RunnerModel::matchesChanged);
0175         connect(m_manager, &Plasma::RunnerManager::queryFinished, this, &RunnerModel::queryHasFinished);
0176 
0177         if (!m_pendingRunnersList.isEmpty()) {
0178             setRunners(m_pendingRunnersList);
0179             m_pendingRunnersList.clear();
0180         }
0181         // connect(m_manager, SIGNAL(queryFinished()), this, SLOT(queryFinished()));
0182         return true;
0183     }
0184 
0185     return false;
0186 }
0187 
0188 void RunnerModel::matchesChanged(const QList<Plasma::QueryMatch> &matches)
0189 {
0190     bool fullReset = false;
0191     int oldCount = m_matches.count();
0192     int newCount = matches.count();
0193     if (newCount > oldCount) {
0194         // We received more matches than we had. If all common matches are the
0195         // same, we can just append new matches instead of resetting the whole
0196         // model
0197         for (int row = 0; row < oldCount; ++row) {
0198             if (!(m_matches.at(row) == matches.at(row))) {
0199                 fullReset = true;
0200                 break;
0201             }
0202         }
0203         if (!fullReset) {
0204             // Not a full reset, inserting rows
0205             beginInsertRows(QModelIndex(), oldCount, newCount - 1);
0206             m_matches = matches;
0207             endInsertRows();
0208             Q_EMIT countChanged();
0209         }
0210     } else {
0211         fullReset = true;
0212     }
0213 
0214     if (fullReset) {
0215         beginResetModel();
0216         m_matches = matches;
0217         endResetModel();
0218         Q_EMIT countChanged();
0219     }
0220     m_runningChangedTimeout->start(3000);
0221 }
0222 
0223 void RunnerModel::queryHasFinished()
0224 {
0225     m_running = false;
0226     Q_EMIT runningChanged(false);
0227 }
0228 
0229 #include "moc_runnermodel.cpp"