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 | |
25 | class KConfigGroup; |
26 | class QMimeData; |
27 | class QRegularExpression; |
28 | class QIcon; |
29 | |
30 | /*! |
31 | * \namespace KRunner |
32 | * \inmodule KRunner |
33 | */ |
34 | namespace KRunner |
35 | { |
36 | class 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 | */ |
50 | class KRUNNER_EXPORT AbstractRunner : public QObject |
51 | { |
52 | Q_OBJECT |
53 | |
54 | public: |
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 ®ex); |
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 | |
203 | Q_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 | |
220 | protected: |
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 | |
302 | private: |
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 | |