KRunner

runnermodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2011 Aaron Seigo <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "runnermodel.h"
8 
9 #include <QAction>
10 #include <QTimer>
11 
12 #include "krunner_debug.h"
13 
14 #include <KRunner/RunnerManager>
15 
16 RunnerModel::RunnerModel(QObject *parent)
17  : QAbstractListModel(parent)
18  , m_startQueryTimer(new QTimer(this))
19  , m_runningChangedTimeout(new QTimer(this))
20 {
21  m_startQueryTimer->setSingleShot(true);
22  m_startQueryTimer->setInterval(10);
23  connect(m_startQueryTimer, &QTimer::timeout, this, &RunnerModel::startQuery);
24 
25  // FIXME: HACK: some runners stay in a running but finished state, not possible to say if it's actually over
26  m_runningChangedTimeout->setSingleShot(true);
27  connect(m_runningChangedTimeout, &QTimer::timeout, this, &RunnerModel::queryHasFinished);
28 }
29 
30 QHash<int, QByteArray> RunnerModel::roleNames() const
31 {
33  roles.insert(Qt::DisplayRole, "display");
34  roles.insert(Qt::DecorationRole, "decoration");
35  roles.insert(Label, "label");
36  roles.insert(Icon, "icon");
37  roles.insert(Type, "type");
38  roles.insert(Relevance, "relevance");
39  roles.insert(Data, "data");
40  roles.insert(Id, "id");
41  roles.insert(SubText, "description");
42  roles.insert(Enabled, "enabled");
43  roles.insert(RunnerId, "runnerid");
44  roles.insert(RunnerName, "runnerName");
45  roles.insert(Actions, "actions");
46  return roles;
47 }
48 
49 int RunnerModel::rowCount(const QModelIndex &index) const
50 {
51  return index.isValid() ? 0 : m_matches.count();
52 }
53 
54 int RunnerModel::count() const
55 {
56  return m_matches.count();
57 }
58 
59 QStringList RunnerModel::runners() const
60 {
61  return m_manager ? m_manager->allowedRunners() : m_pendingRunnersList;
62 }
63 
64 void RunnerModel::setRunners(const QStringList &allowedRunners)
65 {
66  // use sets to ensure comparison is order-independent
67  const auto runnersIds = this->runners();
68  if (QSet<QString>(allowedRunners.begin(), allowedRunners.end()) == QSet<QString>(runnersIds.begin(), runnersIds.end())) {
69  return;
70  }
71  if (m_manager) {
72  m_manager->setAllowedRunners(allowedRunners);
73 
74  // automagically enter single runner mode if there's only 1 allowed runner
75  m_manager->setSingleMode(allowedRunners.count() == 1);
76  } else {
77  m_pendingRunnersList = allowedRunners;
78  }
79 
80  // to trigger single runner fun!
81  if (allowedRunners.count() == 1) {
82  m_singleRunnerId = allowedRunners.first();
83  scheduleQuery(QString());
84  } else {
85  m_singleRunnerId.clear();
86  }
87  Q_EMIT runnersChanged();
88 }
89 
90 void RunnerModel::run(int index)
91 {
92  if (index >= 0 && index < m_matches.count()) {
93  m_manager->run(m_matches.at(index));
94  }
95 }
96 
97 bool RunnerModel::isRunning() const
98 {
99  return m_running;
100 }
101 
102 QVariant RunnerModel::data(const QModelIndex &index, int role) const
103 {
104  if (!index.isValid() || index.parent().isValid() || index.column() > 0 || index.row() < 0 || index.row() >= m_matches.count()) {
105  // index requested must be valid, but we have no child items!
106  return QVariant();
107  }
108 
109  if (role == Qt::DisplayRole || role == Label) {
110  return m_matches.at(index.row()).text();
111  } else if (role == Qt::DecorationRole || role == Icon) {
112  return m_matches.at(index.row()).icon();
113  } else if (role == Type) {
114  return m_matches.at(index.row()).type();
115  } else if (role == Relevance) {
116  return m_matches.at(index.row()).relevance();
117  } else if (role == Data) {
118  return m_matches.at(index.row()).data();
119  } else if (role == Id) {
120  return m_matches.at(index.row()).id();
121  } else if (role == SubText) {
122  return m_matches.at(index.row()).subtext();
123  } else if (role == Enabled) {
124  return m_matches.at(index.row()).isEnabled();
125  } else if (role == RunnerId) {
126  return m_matches.at(index.row()).runner()->id();
127  } else if (role == RunnerName) {
128  return m_matches.at(index.row()).runner()->name();
129  } else if (role == Actions) {
130  QVariantList actions;
131  Plasma::QueryMatch amatch = m_matches.at(index.row());
132  const QList<QAction *> theactions = m_manager->actionsForMatch(amatch);
133  for (QAction *action : theactions) {
134  actions += QVariant::fromValue<QObject *>(action);
135  }
136  return actions;
137  }
138 
139  return QVariant();
140 }
141 
142 QString RunnerModel::currentQuery() const
143 {
144  return m_manager ? m_manager->query() : QString();
145 }
146 
147 void RunnerModel::scheduleQuery(const QString &query)
148 {
149  m_pendingQuery = query;
150  m_startQueryTimer->start();
151 }
152 
153 void RunnerModel::startQuery()
154 {
155  // avoid creating a manager just so we can run nothing
156  // however, if we have one pending runner, then we'll be in single query mode
157  // and a null query is a valid query
158  if (!m_manager && m_pendingRunnersList.count() != 1 && m_pendingQuery.isEmpty()) {
159  return;
160  }
161 
162  if (createManager() || m_pendingQuery != m_manager->query()) {
163  m_manager->launchQuery(m_pendingQuery, m_singleRunnerId);
164  Q_EMIT queryChanged();
165  m_running = true;
166  Q_EMIT runningChanged(true);
167  }
168 }
169 
170 bool RunnerModel::createManager()
171 {
172  if (!m_manager) {
173  m_manager = new Plasma::RunnerManager(this);
174  connect(m_manager, &Plasma::RunnerManager::matchesChanged, this, &RunnerModel::matchesChanged);
175  connect(m_manager, &Plasma::RunnerManager::queryFinished, this, &RunnerModel::queryHasFinished);
176 
177  if (!m_pendingRunnersList.isEmpty()) {
178  setRunners(m_pendingRunnersList);
179  m_pendingRunnersList.clear();
180  }
181  // connect(m_manager, SIGNAL(queryFinished()), this, SLOT(queryFinished()));
182  return true;
183  }
184 
185  return false;
186 }
187 
188 void RunnerModel::matchesChanged(const QList<Plasma::QueryMatch> &matches)
189 {
190  bool fullReset = false;
191  int oldCount = m_matches.count();
192  int newCount = matches.count();
193  if (newCount > oldCount) {
194  // We received more matches than we had. If all common matches are the
195  // same, we can just append new matches instead of resetting the whole
196  // model
197  for (int row = 0; row < oldCount; ++row) {
198  if (!(m_matches.at(row) == matches.at(row))) {
199  fullReset = true;
200  break;
201  }
202  }
203  if (!fullReset) {
204  // Not a full reset, inserting rows
205  beginInsertRows(QModelIndex(), oldCount, newCount - 1);
206  m_matches = matches;
207  endInsertRows();
208  Q_EMIT countChanged();
209  }
210  } else {
211  fullReset = true;
212  }
213 
214  if (fullReset) {
215  beginResetModel();
216  m_matches = matches;
217  endResetModel();
218  Q_EMIT countChanged();
219  }
220  m_runningChangedTimeout->start(3000);
221 }
222 
223 void RunnerModel::queryHasFinished()
224 {
225  m_running = false;
226  Q_EMIT runningChanged(false);
227 }
228 
229 #include "moc_runnermodel.cpp"
T & first()
std::optional< QSqlQuery > query(const QString &queryStatement)
The RunnerManager class decides what installed runners are runnable, and their ratings....
Definition: runnermanager.h:46
DisplayRole
int count(const T &value) const const
int column() const const
A match returned by an AbstractRunner in response to a given RunnerContext.
Definition: querymatch.h:34
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void matchesChanged(const QList< Plasma::QueryMatch > &matches)
Emitted each time a new match is added to the list.
void queryFinished()
Emitted when the launchQuery finish.
QHash::iterator insert(const Key &key, const T &value)
virtual QHash< int, QByteArray > roleNames() const const
void timeout()
const T & at(int i) const const
bool isValid() const const
int row() const const
QList::iterator begin()
QModelIndex parent() const const
QList::iterator end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Dec 2 2023 03:51:00 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.