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 *
214 * @note You should connect here to the prepare/teardown signals. However, avoid doing heavy initialization here
215 * in favor of doing it in AbstractRunner::init
216 *
217 * @param parent parent object for this runner
218 * @param pluginMetaData metadata that was embedded in the runner
219 * @param args for compatibility with KPluginFactory, since 6.0 this can be omitted
220 * @since 5.72
221 */
222 explicit AbstractRunner(QObject *parent, const KPluginMetaData &pluginMetaData);
223
224 /**
225 * Sets whether or not the runner is available for match requests. Useful to
226 * prevent queries when the runner is in a busy state.
227 * @note Do not permanently suspend the runner. This is only intended as a temporary measure to
228 * avoid useless queries being launched or async fetching of config/data being interfered with.
229 */
230 void suspendMatching(bool suspend);
231
232 /**
233 * Provides access to the runner's configuration object.
234 */
235 KConfigGroup config() const;
236
237 /**
238 * Adds a registered syntax that this runner understands. This is used to
239 * display to the user what this runner can understand and how it can be
240 * used.
241 *
242 * @param syntax the syntax to register
243 */
244 void addSyntax(const RunnerSyntax &syntax);
245
246 /**
247 * Utility overload for creating a syntax based on the given parameters
248 * @see RunnerSyntax
249 * @since 5.106
250 */
251 inline void addSyntax(const QString &exampleQuery, const QString &description)
252 {
253 addSyntax(exampleQueries: QStringList(exampleQuery), description);
254 }
255
256 /// @copydoc addSyntax(const QString &exampleQuery, const QString &description)
257 inline void addSyntax(const QStringList &exampleQueries, const QString &description)
258 {
259 addSyntax(syntax: KRunner::RunnerSyntax(exampleQueries, description));
260 }
261
262 /**
263 * Sets the list of syntaxes; passing in an empty list effectively clears
264 * the syntaxes.
265 *
266 * @param the syntaxes to register for this runner
267 */
268 void setSyntaxes(const QList<RunnerSyntax> &syntaxes);
269
270 /**
271 * Reimplement this to run any initialization routines on first load.
272 * Because it is executed in the runner's thread, it will not block the UI and is thus preferred.
273 * By default, it calls reloadConfiguration();
274 *
275 * Until the runner is initialized, it will not be queried by the RunnerManager.
276 */
277 virtual void init();
278
279 /**
280 * Reimplement this if you want your runner to support serialization and drag and drop.
281 * By default, this sets the QMimeData urls to the ones specified in @ref QueryMatch::urls
282 */
283 virtual QMimeData *mimeDataForMatch(const KRunner::QueryMatch &match);
284
285private:
286 std::unique_ptr<AbstractRunnerPrivate> const d;
287 KRUNNER_NO_EXPORT Q_INVOKABLE void matchInternal(KRunner::RunnerContext context);
288 KRUNNER_NO_EXPORT Q_INVOKABLE void reloadConfigurationInternal();
289 KRUNNER_NO_EXPORT Q_SIGNAL void matchInternalFinished(const QString &jobId);
290 KRUNNER_NO_EXPORT Q_SIGNAL void matchingResumed();
291 friend class RunnerManager;
292 friend class RunnerContext;
293 friend class RunnerContextPrivate;
294 friend class QueryMatchPrivate;
295 friend class DBusRunner; // Because it "overrides" matchInternal
296};
297
298} // KRunner namespace
299#endif
300

source code of krunner/src/abstractrunner.h