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

source code of krunner/src/abstractrunner.h