KRunner

abstractrunner.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "abstractrunner.h"
8 
9 #ifndef KSERVICE_BUILD_DEPRECATED_SINCE
10 #define KSERVICE_BUILD_DEPRECATED_SINCE(a, b) 0
11 #endif
12 
13 #include "abstractrunner_p.h"
14 
15 #include <QAction>
16 #include <QElapsedTimer>
17 #include <QHash>
18 #include <QMimeData>
19 #include <QMutex>
20 
21 #include <KConfigGroup>
22 #include <KLocalizedString>
23 #include <KSharedConfig>
24 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 65)
25 #include <Plasma/Package>
26 #endif
27 
28 #include "krunner_debug.h"
29 
30 namespace Plasma
31 {
32 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 77)
34  : QObject(parent)
35  , d(new AbstractRunnerPrivate(this))
36 {
37  d->init(path);
38 }
39 #endif
40 
41 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 86)
42 AbstractRunner::AbstractRunner(const KPluginMetaData &pluginMetaData, QObject *parent)
43  : QObject(parent)
44  , d(new AbstractRunnerPrivate(this))
45 {
46  d->init(pluginMetaData);
47 }
48 #endif
49 
50 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
52  : QObject(parent)
53  , d(new AbstractRunnerPrivate(this))
54 {
55  d->init(service);
56 }
57 #endif
58 
59 AbstractRunner::AbstractRunner(QObject *parent, const KPluginMetaData &pluginMetaData, const QVariantList &args)
60  : QObject(parent)
61  , d(new AbstractRunnerPrivate(this))
62 {
63  Q_UNUSED(args)
64 
65  d->init(pluginMetaData);
66 }
67 
68 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 77)
69 AbstractRunner::AbstractRunner(QObject *parent, const QVariantList &args)
70  : QObject(parent)
71  , d(new AbstractRunnerPrivate(this))
72 {
73  if (!args.isEmpty()) {
74 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
75  // backward-compatible support has metadata only as second argument
76  // which we prefer of course
77  if (args.size() > 1) {
78  const KPluginMetaData metaData = args[1].value<KPluginMetaData>();
79 #else
80  const KPluginMetaData metaData = args[0].value<KPluginMetaData>();
81 #endif
82  if (metaData.isValid()) {
83  d->init(metaData);
84  return;
85  }
86 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
87  }
88 
89  KService::Ptr service = KService::serviceByStorageId(args[0].toString());
90  if (service) {
91  d->init(service);
92  }
93 #endif
94  }
95 }
96 #endif
97 
98 AbstractRunner::~AbstractRunner() = default;
99 
101 {
102  QString group = id();
103  if (group.isEmpty()) {
104  group = QStringLiteral("UnnamedRunner");
105  }
106 
107  KConfigGroup runners(KSharedConfig::openConfig(QStringLiteral("krunnerrc")), "Runners");
108  return KConfigGroup(&runners, group);
109 }
110 
112 {
113 }
114 
116 {
117  d->syntaxes.append(syntax);
118 }
119 
120 bool AbstractRunner::hasUniqueResults()
121 {
122  return d->hasUniqueResults;
123 }
124 
125 bool AbstractRunner::hasWeakResults()
126 {
127  return d->hasWeakResults;
128 }
129 
130 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
132 {
133  d->syntaxes.append(syntax);
134  d->defaultSyntax = &(d->syntaxes.last());
135 }
136 #endif
137 
139 {
140  d->syntaxes = syntaxes;
141 }
142 
144 {
145  return d->syntaxes;
146 }
147 
148 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
150 {
151  return d->defaultSyntax;
152 }
153 #endif
154 
155 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
157 {
158  static const int reasonableRunTime = 1500;
159  static const int fastEnoughTime = 250;
160 
161  QElapsedTimer time;
162  time.start();
163 
164  // The local copy is already obtained in the job
165  match(localContext);
166 
167  // automatically rate limit runners that become slooow
168  const int runtime = time.elapsed();
169  bool slowed = speed() == SlowSpeed;
170 
171  if (!slowed && runtime > reasonableRunTime) {
172  // we punish runners that return too slowly, even if they don't bring
173  // back matches
174  d->fastRuns = 0;
175  setSpeed(SlowSpeed);
176  }
177 
178  if (slowed && runtime < fastEnoughTime && localContext.query().size() > 2) {
179  ++d->fastRuns;
180 
181  if (d->fastRuns > 2) {
182  // we reward slowed runners who bring back matches fast enough
183  // 3 times in a row
184  setSpeed(NormalSpeed);
185  }
186  }
187 }
188 #endif
189 
191 {
192  return match.isValid() ? match.actions() : QList<QAction *>();
193 }
194 
195 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 86)
196 QAction *AbstractRunner::addAction(const QString &id, const QIcon &icon, const QString &text)
197 {
198  QAction *a = new QAction(icon, text, this);
199  d->actions.insert(id, a);
200  return a;
201 }
202 
203 void AbstractRunner::addAction(const QString &id, QAction *action)
204 {
205  d->actions.insert(id, action);
206 }
207 
209 {
210  QAction *a = d->actions.take(id);
211  delete a;
212 }
213 
215 {
216  return d->actions.value(id);
217 }
218 
220 {
221  return d->actions;
222 }
223 
225 {
226  qDeleteAll(d->actions);
227  d->actions.clear();
228 }
229 #endif
230 
232 {
233  if (match.urls().isEmpty()) {
234  return nullptr;
235  }
236  QMimeData *result = new QMimeData();
237  result->setUrls(match.urls());
238  return result;
239 }
240 
241 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 71)
243 {
244  return d->hasRunOptions;
245 }
246 #endif
247 
248 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 71)
249 void AbstractRunner::setHasRunOptions(bool hasRunOptions)
250 {
251  d->hasRunOptions = hasRunOptions;
252 }
253 #endif
254 
255 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 71)
257 {
258  Q_UNUSED(parent)
259 }
260 #endif
261 
262 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
264 {
265  // the only time the read lock will fail is if we were slow are going to speed up
266  // or if we were fast and are going to slow down; so don't wait in this case, just
267  // say we're slow. we either will be soon or were just a moment ago and it doesn't
268  // hurt to do one more run the slow way
269  if (!d->speedLock.tryLockForRead()) {
270  return SlowSpeed;
271  }
272  Speed s = d->speed;
273  d->speedLock.unlock();
274  return s;
275 }
276 #endif
277 
278 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
280 {
281  d->speedLock.lockForWrite();
282  d->speed = speed;
283  d->speedLock.unlock();
284 }
285 #endif
286 
288 {
289  return d->priority;
290 }
291 
293 {
294  d->priority = priority;
295 }
296 
297 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
299 {
300  return d->blackListed;
301 }
302 #endif
303 
304 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
306 {
307  d->blackListed = types;
308 }
309 #endif
310 
312 {
313  Q_UNUSED(search);
314  Q_UNUSED(action);
315 }
316 
317 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
319 {
320  return QStringList() << name();
321 }
322 #endif
323 
324 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
326 {
327  return icon();
328 }
329 #endif
330 
332 {
333 }
334 
335 QString AbstractRunner::name() const
336 {
337  if (d->runnerDescription.isValid()) {
338  return d->runnerDescription.name();
339  }
340 
341  return objectName();
342 }
343 
344 QIcon AbstractRunner::icon() const
345 {
346  if (d->runnerDescription.isValid()) {
347  return QIcon::fromTheme(d->runnerDescription.iconName());
348  }
349 
350  return QIcon();
351 }
352 
353 QString AbstractRunner::id() const
354 {
355  if (d->runnerDescription.isValid()) {
356  return d->runnerDescription.pluginId();
357  }
358 
359  return objectName();
360 }
361 
362 QString AbstractRunner::description() const
363 {
364  if (d->runnerDescription.isValid()) {
365  return d->runnerDescription.description();
366  }
367 
368  return objectName();
369 }
370 
371 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72)
373 {
374  QT_WARNING_PUSH
375  QT_WARNING_DISABLE_DEPRECATED
376  return KPluginInfo::fromMetaData(d->runnerDescription);
377  QT_WARNING_POP
378 }
379 #endif
380 
381 KPluginMetaData AbstractRunner::metadata(RunnerReturnPluginMetaDataConstant) const
382 {
383  return d->runnerDescription;
384 }
385 
386 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 65)
388 {
389  QT_WARNING_PUSH
390  QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
391  QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
392  return Package();
393  QT_WARNING_POP
394 }
395 #endif
396 
398 {
400 }
401 
402 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 73)
404 {
405  return d->dataEngine(name);
406 }
407 #endif
408 
410 {
411  return d->suspendMatching;
412 }
413 
415 {
416  if (d->suspendMatching == suspend) {
417  return;
418  }
419 
420  d->suspendMatching = suspend;
421  Q_EMIT matchingSuspended(suspend);
422 }
423 
424 int AbstractRunner::minLetterCount() const
425 {
426  return d->minLetterCount;
427 }
428 
430 {
431  d->minLetterCount = count;
432 }
433 
434 QRegularExpression AbstractRunner::matchRegex() const
435 {
436  return d->matchRegex;
437 }
438 
440 {
441  d->matchRegex = regex;
442  d->hasMatchRegex = regex.isValid() && !regex.pattern().isEmpty();
443 }
444 
446 {
447  int minTriggerWordLetters = 0;
448  QString constructedRegex = QStringLiteral("^");
449  for (const QString &triggerWord : triggerWords) {
450  // We want to link them with an or
451  if (constructedRegex.length() > 1) {
452  constructedRegex += QLatin1Char('|');
453  }
454  constructedRegex += QRegularExpression::escape(triggerWord);
455  if (minTriggerWordLetters == 0 || triggerWord.length() < minTriggerWordLetters) {
456  minTriggerWordLetters = triggerWord.length();
457  }
458  }
459  // If we can reject the query because of the length we don't need the regex
460  setMinLetterCount(minTriggerWordLetters);
461  QRegularExpression regex(constructedRegex);
462  setMatchRegex(regex);
463 }
464 
466 {
467  return d->hasMatchRegex;
468 }
469 
470 AbstractRunnerPrivate::AbstractRunnerPrivate(AbstractRunner *r)
471  : priority(AbstractRunner::NormalPriority)
472 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
473  , speed(AbstractRunner::NormalSpeed)
474 #endif
475  , blackListed(RunnerContext::None)
476  , runner(r)
477  , fastRuns(0)
478  , defaultSyntax(nullptr)
479  , hasRunOptions(false)
480  , suspendMatching(false)
481 {
482 }
483 
484 AbstractRunnerPrivate::~AbstractRunnerPrivate()
485 {
486 }
487 
488 void AbstractRunnerPrivate::init()
489 {
490  minLetterCount = runnerDescription.value(QStringLiteral("X-Plasma-Runner-Min-Letter-Count"), 0);
491  if (runnerDescription.isValid()) {
492  const auto rawData = runnerDescription.rawData();
493  if (rawData.contains(QStringLiteral("X-Plasma-Runner-Match-Regex"))) {
494  matchRegex = QRegularExpression(rawData.value(QStringLiteral("X-Plasma-Runner-Match-Regex")).toString());
495  hasMatchRegex = matchRegex.isValid() && !matchRegex.pattern().isEmpty();
496  }
497  hasUniqueResults = runnerDescription.value(QStringLiteral("X-Plasma-Runner-Unique-Results"), false);
498  hasWeakResults = runnerDescription.value(QStringLiteral("X-Plasma-Runner-Weak-Results"), false);
499  }
500 }
501 
502 void AbstractRunnerPrivate::init(const KPluginMetaData &pluginMetaData)
503 {
504  runnerDescription = pluginMetaData;
505  init();
506 }
507 
508 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
509 void AbstractRunnerPrivate::init(const KService::Ptr service)
510 {
511  QT_WARNING_PUSH
512  QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
513  QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
514  const KPluginInfo pluginInfo(service);
515  runnerDescription = pluginInfo.isValid() ? pluginInfo.toMetaData() : KPluginMetaData();
516  QT_WARNING_POP
517  init();
518 }
519 #endif
520 
521 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 77)
522 void AbstractRunnerPrivate::init(const QString &path)
523 {
524  runnerDescription = KPluginMetaData(path + QStringLiteral("/metadata.desktop"));
525  init();
526 }
527 #endif
528 
529 } // Plasma namespace
530 
531 #include "moc_abstractrunner.cpp"
QString pattern() const const
void setMinLetterCount(int count)
Set the minLetterCount property.
QHash< QString, QAction * > actions() const
Returns all registered actions.
int size() const const
Priority
Specifies a priority for the runner.
void setUrls(const QList< QUrl > &urls)
Q_EMITQ_EMIT
virtual void reloadConfiguration()
Signal runner to reload its configuration.
void setTriggerWords(const QStringList &triggerWords)
Constructs internally a regex which requires the query to start with the trigger words.
KConfigGroup config() const
Provides access to the runner's configuration object.
QAction * addAction(const QString &id, const QIcon &icon, const QString &text)
Creates and then adds an action to the action registry.
A match returned by an AbstractRunner in response to a given RunnerContext.
Definition: querymatch.h:34
QIcon fromTheme(const QString &name)
void setHasRunOptions(bool hasRunOptions)
Sets whether or not the runner has options for matches.
bool hasMatchRegex() const
If the runner has a valid regex and non empty regex.
void clearActions()
Clears the action registry.
An abstract base class for Plasma Runner plugins.
QString escape(const QString &str)
virtual QMimeData * mimeDataForMatch(const Plasma::QueryMatch &match)
Reimplement this slot if you want your runner to support serialization and drag and drop.
static KPluginInfo fromMetaData(const KPluginMetaData &meta)
Package package() const
Accessor for the associated Package object if any.
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
void addSyntax(const RunnerSyntax &syntax)
Adds a registered syntax that this runner understands.
Priority priority() const
The priority of the runner.
virtual QIcon categoryIcon(const QString &category) const
Returns the icon which accurately describes the category category.
virtual void createRunOptions(QWidget *widget)
If hasRunOptions() returns true, this method may be called to get a widget displaying the options the...
bool isEmpty() const const
int length() const const
virtual void match(Plasma::RunnerContext &context)
This is the main query method.
RunnerContext::Types ignoredTypes() const
Returns the OR'ed value of all the Information types (as defined in RunnerContext::Type) this runner ...
void suspend()
QAction * action(const QString &id) const
Returns the action associated with the id.
bool isMatchingSuspended() const
qint64 elapsed() const const
void suspendMatching(bool suspend)
Sets whether or not the runner is available for match requests.
void setMatchRegex(const QRegularExpression &regex)
Set the matchRegex property.
Speed
Specifies a nominal speed for the runner.
bool value(const QString &key, bool defaultValue) const
virtual QStringList categories() const
Return a list of categories that this runner provides.
Q_INVOKABLE DataEngine * dataEngine(const QString &name) const
Loads the given DataEngine.
RunnerSyntax * defaultSyntax() const
static Ptr serviceByStorageId(const QString &_storageId)
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
bool hasRunOptions()
If the runner has options that the user can interact with to modify what happens when run or one of t...
virtual void init()
Reimplement this slot to run any initialization routines on first load.
Speed speed() const
The nominal speed of the runner.
The RunnerContext class provides information related to a search, including the search term,...
Definition: runnercontext.h:31
void setSyntaxes(const QList< RunnerSyntax > &syns)
Sets the list of syntaxes; passing in an empty list effectively clears the syntaxes.
QString query() const
KPluginInfo metadata() const
void setDefaultSyntax(const RunnerSyntax &syntax)
Set syntax as the default syntax for the runner; the default syntax will be substituted to the empty ...
void removeAction(const QString &id)
Removes the action from the action registry.
void setPriority(Priority newPriority)
Sets the priority of the runner.
virtual void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
Called whenever an exact or possible match associated with this runner is triggered.
AbstractRunner(QObject *parent, const KPluginMetaData &pluginMetaData, const QVariantList &args)
Constructor for a KRunner plugin.
void setIgnoredTypes(RunnerContext::Types types)
Sets the types this runner will ignore.
QList< RunnerSyntax > syntaxes() const
void performMatch(Plasma::RunnerContext &context)
Triggers a call to match.
void setSpeed(Speed newSpeed)
Sets the nominal speed of the runner.
virtual QList< QAction * > actionsForMatch(const Plasma::QueryMatch &match)
A given match can have more than action that can be performed on it.
QObject * parent() const const
bool isValid() const const
char * toString(const EngineQuery &query)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Dec 2 2023 03:50:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.