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"