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 | namespace KRunner |
31 | { |
32 | class 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 | */ |
44 | class KRUNNER_EXPORT AbstractRunner : public QObject |
45 | { |
46 | Q_OBJECT |
47 | |
48 | public: |
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 ®ex); |
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 | |
190 | Q_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 | |
207 | protected: |
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 | |
285 | private: |
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 | |