1/*
2 SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <aseigo@kde.org>
3 SPDX-FileCopyrightText: 2020-2023 Alexander Lohnau<alexander.lohnau@gmx.de>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#ifndef KRUNNER_ABSTRACTRUNNER_H
9#define KRUNNER_ABSTRACTRUNNER_H
10
11#include "krunner_export.h"
12
13#include <QObject>
14#include <QStringList>
15
16#include <KPluginFactory>
17#include <KPluginMetaData>
18
19#include <memory>
20
21#include "querymatch.h"
22#include "runnercontext.h"
23#include "runnersyntax.h"
24
25class KConfigGroup;
26class QMimeData;
27class QRegularExpression;
28class QIcon;
29
30/*!
31 * \namespace KRunner
32 * \inmodule KRunner
33 */
34namespace KRunner
35{
36class AbstractRunnerPrivate;
37
38/*!
39 * \class KRunner::AbstractRunner
40 * \inheaderfile KRunner/AbstractRunner
41 * \inmodule KRunner
42 *
43 * \brief An abstract base class for Plasma Runner plugins.
44 *
45 * Be aware that runners will be moved to their own thread after being instantiated.
46 * This means that except for AbstractRunner::run and the constructor, all methods will be non-blocking
47 * for the UI.
48 * Consider doing heavy resource initialization in the init method instead of the constructor.
49 */
50class KRUNNER_EXPORT AbstractRunner : public QObject
51{
52 Q_OBJECT
53
54public:
55 ~AbstractRunner() override;
56
57 /*!
58 * This is the main query method. It should trigger creation of
59 * QueryMatch instances through RunnerContext::addMatch and
60 * RunnerContext::addMatches.
61 *
62 * If the runner can run precisely the requested term (RunnerContext::query()),
63 * it should create an exact match by setting the type to RunnerContext::ExactMatch.
64 * The first runner that creates a QueryMatch will be the
65 * default runner. Other runner's matches will be suggested in the
66 * interface. Non-exact matches should be offered via RunnerContext::PossibleMatch.
67 *
68 * The match will be activated via run() if the user selects it.
69 *
70 * All matches need to be reported once this method returns. Asynchronous runners therefore need
71 * to make use of a local event loop to wait for all matches.
72 *
73 * It is recommended to use local status data in async runners. The simplest way is
74 * to have a separate class doing all the work like so:
75 *
76 * \code
77 * void MyFancyAsyncRunner::match(RunnerContext &context)
78 * {
79 * QEventLoop loop;
80 * MyAsyncWorker worker(context);
81 * connect(&worker, &MyAsyncWorker::finished, &loop, &MyAsyncWorker::quit);
82 * worker.work();
83 * loop.exec();
84 * }
85 * \endcode
86 *
87 * Here MyAsyncWorker creates all the matches and calls RunnerContext::addMatch
88 * in some internal slot. It emits the finished() signal once done which will
89 * quit the loop and make the match() method return.
90 *
91 * Execution of the correct action should be handled in the run method.
92 *
93 * \warning Returning from this method means to end execution of the runner.
94 *
95 * \sa run(), RunnerContext::addMatch, RunnerContext::addMatches, QueryMatch
96 */
97 virtual void match(KRunner::RunnerContext &context) = 0;
98
99 /*!
100 * Called whenever an exact or possible match associated with this
101 * runner is triggered.
102 *
103 * \a context The context in which the match is triggered, i.e. for which
104 * the match was created.
105 * \a match The actual match to run/execute.
106 */
107 virtual void run(const KRunner::RunnerContext &context, const KRunner::QueryMatch &match);
108
109 /*!
110 * Returns the plugin metadata for this runner that was passed in the constructor
111 */
112 KPluginMetaData metadata() const;
113
114 /*!
115 * Returns the translated name from the runner's metadata
116 */
117 QString name() const;
118
119 /*!
120 * Returns an id from the runner's metadata
121 */
122 QString id() const;
123
124 /*!
125 * Reloads the runner's configuration. This is called when it's KCM in the PluginSelector is applied.
126 *
127 * This function may be used to set for example using setMatchRegex, setMinLetterCount or setTriggerWords.
128 *
129 * Also, syntaxes should be updated when this method is called.
130 *
131 * While reloading the config, matching is suspended.
132 */
133 virtual void reloadConfiguration();
134
135 /*!
136 * Returns the syntaxes the runner has registered that it accepts and understands
137 */
138 QList<RunnerSyntax> syntaxes() const;
139
140 /*!
141 * Returns true if the runner is currently busy with non-interuptable work, signaling that
142 * the RunnerManager may not query it or read it's config properties
143 */
144 bool isMatchingSuspended() const;
145
146 /*!
147 * This is the minimum letter count for the query. If the query is shorter than this value
148 * and KRunner is not in the singleRunnerMode, match method is not called.
149 * This can be set using the X-Plasma-Runner-Min-Letter-Count property or the setMinLetterCount method.
150 * The default value is 0.
151 *
152 * \sa setMinLetterCount
153 * \sa match
154 * \since 5.75
155 */
156 int minLetterCount() const;
157
158 /*!
159 * Set the minLetterCount property
160 *
161 * \a count
162 * \since 5.75
163 */
164 void setMinLetterCount(int count);
165
166 /*!
167 * If this regex is set with a non empty pattern it must match the query in order for match being called.
168 *
169 * Just like the minLetterCount property this check is ignored when the runner is in the singleRunnerMode.
170 *
171 * In case both the regex and the letter count is set the letter count is checked first.
172 *
173 * Returns matchRegex property
174 * \sa hasMatchRegex
175 * \since 5.75
176 */
177 QRegularExpression matchRegex() const;
178
179 /*!
180 * Set the matchRegex property
181 *
182 * \since 5.75
183 */
184 void setMatchRegex(const QRegularExpression &regex);
185
186 /*!
187 * Constructs internally a regex which requires the query to start with the trigger words.
188 * Multiple words are concatenated with or, for instance: "^word1|word2|word3".
189 * The trigger words are internally escaped.
190 * Also the minLetterCount is set to the shortest word in the list.
191 * \since 5.75
192 * \sa matchRegex
193 */
194 void setTriggerWords(const QStringList &triggerWords);
195
196 /*!
197 * If the runner has a valid regex and non empty regex
198 * \internal
199 * \since 5.75
200 */
201 bool hasMatchRegex() const;
202
203Q_SIGNALS:
204 /*!
205 * This signal is emitted when matching is about to commence, giving runners
206 * an opportunity to prepare themselves, e.g. loading data sets or preparing
207 * IPC or network connections. Things that should be loaded once and remain
208 * extant for the lifespan of the AbstractRunner should be done in init().
209 * \sa init()
210 */
211 void prepare();
212
213 /*!
214 * This signal is emitted when a session of matches is complete, giving runners
215 * the opportunity to tear down anything set up as a result of the prepare()
216 * method.
217 */
218 void teardown();
219
220protected:
221 friend class RunnerManager;
222 friend class RunnerManagerPrivate;
223
224 /*!
225 * Constructor for a KRunner plugin
226 *
227 * @note You should connect here to the prepare/teardown signals. However, avoid doing heavy initialization here
228 * in favor of doing it in AbstractRunner::init
229 *
230 * \a parent parent object for this runner
231 *
232 * \a pluginMetaData metadata that was embedded in the runner
233 *
234 * \a args for compatibility with KPluginFactory, since 6.0 this can be omitted
235 * \since 5.72
236 */
237 explicit AbstractRunner(QObject *parent, const KPluginMetaData &pluginMetaData);
238
239 /*!
240 * Sets whether or not the runner is available for match requests. Useful to
241 * prevent queries when the runner is in a busy state.
242 *
243 * \note Do not permanently suspend the runner. This is only intended as a temporary measure to
244 * avoid useless queries being launched or async fetching of config/data being interfered with.
245 */
246 void suspendMatching(bool suspend);
247
248 /*!
249 * Provides access to the runner's configuration object.
250 * This config is saved in the "krunnerrc" file in the [Runners][<pluginId>] config group
251 * Settings should be written in a KDE config module. See https://develop.kde.org/docs/plasma/krunner/#runner-configuration
252 */
253 KConfigGroup config() const;
254
255 /*!
256 * Adds a registered syntax that this runner understands. This is used to
257 * display to the user what this runner can understand and how it can be
258 * used.
259 *
260 * \a syntax the syntax to register
261 */
262 void addSyntax(const RunnerSyntax &syntax);
263
264 /*!
265 * Utility overload for creating a syntax based on the given parameters
266 * \sa RunnerSyntax
267 * \since 5.106
268 */
269 inline void addSyntax(const QString &exampleQuery, const QString &description)
270 {
271 addSyntax(exampleQueries: QStringList(exampleQuery), description);
272 }
273
274 inline void addSyntax(const QStringList &exampleQueries, const QString &description)
275 {
276 addSyntax(syntax: KRunner::RunnerSyntax(exampleQueries, description));
277 }
278
279 /*!
280 * Sets the list of syntaxes; passing in an empty list effectively clears
281 * the syntaxes.
282 *
283 * \a the syntaxes to register for this runner
284 */
285 void setSyntaxes(const QList<RunnerSyntax> &syntaxes);
286
287 /*!
288 * Reimplement this to run any initialization routines on first load.
289 * Because it is executed in the runner's thread, it will not block the UI and is thus preferred.
290 * By default, it calls reloadConfiguration();
291 *
292 * Until the runner is initialized, it will not be queried by the RunnerManager.
293 */
294 virtual void init();
295
296 /*!
297 * Reimplement this if you want your runner to support serialization and drag and drop.
298 * By default, this sets the QMimeData urls to the ones specified in QueryMatch::urls
299 */
300 virtual QMimeData *mimeDataForMatch(const KRunner::QueryMatch &match);
301
302private:
303 std::unique_ptr<AbstractRunnerPrivate> const d;
304 KRUNNER_NO_EXPORT Q_INVOKABLE void matchInternal(KRunner::RunnerContext context);
305 KRUNNER_NO_EXPORT Q_INVOKABLE void reloadConfigurationInternal();
306 KRUNNER_NO_EXPORT Q_SIGNAL void matchInternalFinished(const QString &jobId);
307 KRUNNER_NO_EXPORT Q_SIGNAL void matchingResumed();
308 friend class RunnerManager;
309 friend class RunnerContext;
310 friend class RunnerContextPrivate;
311 friend class QueryMatchPrivate;
312 friend class DBusRunner; // Because it "overrides" matchInternal
313};
314
315} // KRunner namespace
316#endif
317

source code of krunner/src/abstractrunner.h