File indexing completed on 2024-04-28 15:29:42

0001 /*
0002     SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <aseigo@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "abstractrunner.h"
0008 
0009 #ifndef KSERVICE_BUILD_DEPRECATED_SINCE
0010 #define KSERVICE_BUILD_DEPRECATED_SINCE(a, b) 0
0011 #endif
0012 
0013 #include "abstractrunner_p.h"
0014 
0015 #include <QAction>
0016 #include <QElapsedTimer>
0017 #include <QHash>
0018 #include <QMimeData>
0019 #include <QMutex>
0020 
0021 #include <KConfigGroup>
0022 #include <KLocalizedString>
0023 #include <KSharedConfig>
0024 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 65)
0025 #include <Plasma/Package>
0026 #endif
0027 
0028 #include "krunner_debug.h"
0029 
0030 namespace Plasma
0031 {
0032 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 77)
0033 AbstractRunner::AbstractRunner(QObject *parent, const QString &path)
0034     : QObject(parent)
0035     , d(new AbstractRunnerPrivate(this))
0036 {
0037     d->init(path);
0038 }
0039 #endif
0040 
0041 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 86)
0042 AbstractRunner::AbstractRunner(const KPluginMetaData &pluginMetaData, QObject *parent)
0043     : QObject(parent)
0044     , d(new AbstractRunnerPrivate(this))
0045 {
0046     d->init(pluginMetaData);
0047 }
0048 #endif
0049 
0050 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0051 AbstractRunner::AbstractRunner(const KService::Ptr service, QObject *parent)
0052     : QObject(parent)
0053     , d(new AbstractRunnerPrivate(this))
0054 {
0055     d->init(service);
0056 }
0057 #endif
0058 
0059 AbstractRunner::AbstractRunner(QObject *parent, const KPluginMetaData &pluginMetaData, const QVariantList &args)
0060     : QObject(parent)
0061     , d(new AbstractRunnerPrivate(this))
0062 {
0063     Q_UNUSED(args)
0064 
0065     d->init(pluginMetaData);
0066 }
0067 
0068 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 77)
0069 AbstractRunner::AbstractRunner(QObject *parent, const QVariantList &args)
0070     : QObject(parent)
0071     , d(new AbstractRunnerPrivate(this))
0072 {
0073     if (!args.isEmpty()) {
0074 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0075         // backward-compatible support has metadata only as second argument
0076         // which we prefer of course
0077         if (args.size() > 1) {
0078             const KPluginMetaData metaData = args[1].value<KPluginMetaData>();
0079 #else
0080         const KPluginMetaData metaData = args[0].value<KPluginMetaData>();
0081 #endif
0082             if (metaData.isValid()) {
0083                 d->init(metaData);
0084                 return;
0085             }
0086 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0087         }
0088 
0089         KService::Ptr service = KService::serviceByStorageId(args[0].toString());
0090         if (service) {
0091             d->init(service);
0092         }
0093 #endif
0094     }
0095 }
0096 #endif
0097 
0098 AbstractRunner::~AbstractRunner() = default;
0099 
0100 KConfigGroup AbstractRunner::config() const
0101 {
0102     QString group = id();
0103     if (group.isEmpty()) {
0104         group = QStringLiteral("UnnamedRunner");
0105     }
0106 
0107     KConfigGroup runners(KSharedConfig::openConfig(QStringLiteral("krunnerrc")), "Runners");
0108     return KConfigGroup(&runners, group);
0109 }
0110 
0111 void AbstractRunner::reloadConfiguration()
0112 {
0113 }
0114 
0115 void AbstractRunner::addSyntax(const RunnerSyntax &syntax)
0116 {
0117     d->syntaxes.append(syntax);
0118 }
0119 
0120 bool AbstractRunner::hasUniqueResults()
0121 {
0122     return d->hasUniqueResults;
0123 }
0124 
0125 bool AbstractRunner::hasWeakResults()
0126 {
0127     return d->hasWeakResults;
0128 }
0129 
0130 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
0131 void AbstractRunner::setDefaultSyntax(const RunnerSyntax &syntax)
0132 {
0133     d->syntaxes.append(syntax);
0134     d->defaultSyntax = &(d->syntaxes.last());
0135 }
0136 #endif
0137 
0138 void AbstractRunner::setSyntaxes(const QList<RunnerSyntax> &syntaxes)
0139 {
0140     d->syntaxes = syntaxes;
0141 }
0142 
0143 QList<RunnerSyntax> AbstractRunner::syntaxes() const
0144 {
0145     return d->syntaxes;
0146 }
0147 
0148 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
0149 RunnerSyntax *AbstractRunner::defaultSyntax() const
0150 {
0151     return d->defaultSyntax;
0152 }
0153 #endif
0154 
0155 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
0156 void AbstractRunner::performMatch(Plasma::RunnerContext &localContext)
0157 {
0158     static const int reasonableRunTime = 1500;
0159     static const int fastEnoughTime = 250;
0160 
0161     QElapsedTimer time;
0162     time.start();
0163 
0164     // The local copy is already obtained in the job
0165     match(localContext);
0166 
0167     // automatically rate limit runners that become slooow
0168     const int runtime = time.elapsed();
0169     bool slowed = speed() == SlowSpeed;
0170 
0171     if (!slowed && runtime > reasonableRunTime) {
0172         // we punish runners that return too slowly, even if they don't bring
0173         // back matches
0174         d->fastRuns = 0;
0175         setSpeed(SlowSpeed);
0176     }
0177 
0178     if (slowed && runtime < fastEnoughTime && localContext.query().size() > 2) {
0179         ++d->fastRuns;
0180 
0181         if (d->fastRuns > 2) {
0182             // we reward slowed runners who bring back matches fast enough
0183             // 3 times in a row
0184             setSpeed(NormalSpeed);
0185         }
0186     }
0187 }
0188 #endif
0189 
0190 QList<QAction *> AbstractRunner::actionsForMatch(const Plasma::QueryMatch &match)
0191 {
0192     return match.isValid() ? match.actions() : QList<QAction *>();
0193 }
0194 
0195 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 86)
0196 QAction *AbstractRunner::addAction(const QString &id, const QIcon &icon, const QString &text)
0197 {
0198     QAction *a = new QAction(icon, text, this);
0199     d->actions.insert(id, a);
0200     return a;
0201 }
0202 
0203 void AbstractRunner::addAction(const QString &id, QAction *action)
0204 {
0205     d->actions.insert(id, action);
0206 }
0207 
0208 void AbstractRunner::removeAction(const QString &id)
0209 {
0210     QAction *a = d->actions.take(id);
0211     delete a;
0212 }
0213 
0214 QAction *AbstractRunner::action(const QString &id) const
0215 {
0216     return d->actions.value(id);
0217 }
0218 
0219 QHash<QString, QAction *> AbstractRunner::actions() const
0220 {
0221     return d->actions;
0222 }
0223 
0224 void AbstractRunner::clearActions()
0225 {
0226     qDeleteAll(d->actions);
0227     d->actions.clear();
0228 }
0229 #endif
0230 
0231 QMimeData *AbstractRunner::mimeDataForMatch(const QueryMatch &match)
0232 {
0233     if (match.urls().isEmpty()) {
0234         return nullptr;
0235     }
0236     QMimeData *result = new QMimeData();
0237     result->setUrls(match.urls());
0238     return result;
0239 }
0240 
0241 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 71)
0242 bool AbstractRunner::hasRunOptions()
0243 {
0244     return d->hasRunOptions;
0245 }
0246 #endif
0247 
0248 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 71)
0249 void AbstractRunner::setHasRunOptions(bool hasRunOptions)
0250 {
0251     d->hasRunOptions = hasRunOptions;
0252 }
0253 #endif
0254 
0255 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 71)
0256 void AbstractRunner::createRunOptions(QWidget *parent)
0257 {
0258     Q_UNUSED(parent)
0259 }
0260 #endif
0261 
0262 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
0263 AbstractRunner::Speed AbstractRunner::speed() const
0264 {
0265     // the only time the read lock will fail is if we were slow are going to speed up
0266     // or if we were fast and are going to slow down; so don't wait in this case, just
0267     // say we're slow. we either will be soon or were just a moment ago and it doesn't
0268     // hurt to do one more run the slow way
0269     if (!d->speedLock.tryLockForRead()) {
0270         return SlowSpeed;
0271     }
0272     Speed s = d->speed;
0273     d->speedLock.unlock();
0274     return s;
0275 }
0276 #endif
0277 
0278 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
0279 void AbstractRunner::setSpeed(Speed speed)
0280 {
0281     d->speedLock.lockForWrite();
0282     d->speed = speed;
0283     d->speedLock.unlock();
0284 }
0285 #endif
0286 
0287 AbstractRunner::Priority AbstractRunner::priority() const
0288 {
0289     return d->priority;
0290 }
0291 
0292 void AbstractRunner::setPriority(Priority priority)
0293 {
0294     d->priority = priority;
0295 }
0296 
0297 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
0298 RunnerContext::Types AbstractRunner::ignoredTypes() const
0299 {
0300     return d->blackListed;
0301 }
0302 #endif
0303 
0304 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
0305 void AbstractRunner::setIgnoredTypes(RunnerContext::Types types)
0306 {
0307     d->blackListed = types;
0308 }
0309 #endif
0310 
0311 void AbstractRunner::run(const Plasma::RunnerContext &search, const Plasma::QueryMatch &action)
0312 {
0313     Q_UNUSED(search);
0314     Q_UNUSED(action);
0315 }
0316 
0317 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
0318 QStringList AbstractRunner::categories() const
0319 {
0320     return QStringList() << name();
0321 }
0322 #endif
0323 
0324 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 76)
0325 QIcon AbstractRunner::categoryIcon(const QString &) const
0326 {
0327     return icon();
0328 }
0329 #endif
0330 
0331 void AbstractRunner::match(Plasma::RunnerContext &)
0332 {
0333 }
0334 
0335 QString AbstractRunner::name() const
0336 {
0337     if (d->runnerDescription.isValid()) {
0338         return d->runnerDescription.name();
0339     }
0340 
0341     return objectName();
0342 }
0343 
0344 QIcon AbstractRunner::icon() const
0345 {
0346     if (d->runnerDescription.isValid()) {
0347         return QIcon::fromTheme(d->runnerDescription.iconName());
0348     }
0349 
0350     return QIcon();
0351 }
0352 
0353 QString AbstractRunner::id() const
0354 {
0355     if (d->runnerDescription.isValid()) {
0356         return d->runnerDescription.pluginId();
0357     }
0358 
0359     return objectName();
0360 }
0361 
0362 QString AbstractRunner::description() const
0363 {
0364     if (d->runnerDescription.isValid()) {
0365         return d->runnerDescription.description();
0366     }
0367 
0368     return objectName();
0369 }
0370 
0371 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72)
0372 KPluginInfo AbstractRunner::metadata() const
0373 {
0374     QT_WARNING_PUSH
0375     QT_WARNING_DISABLE_DEPRECATED
0376     return KPluginInfo::fromMetaData(d->runnerDescription);
0377     QT_WARNING_POP
0378 }
0379 #endif
0380 
0381 KPluginMetaData AbstractRunner::metadata(RunnerReturnPluginMetaDataConstant) const
0382 {
0383     return d->runnerDescription;
0384 }
0385 
0386 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 65)
0387 Package AbstractRunner::package() const
0388 {
0389     QT_WARNING_PUSH
0390     QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0391     QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0392     return Package();
0393     QT_WARNING_POP
0394 }
0395 #endif
0396 
0397 void AbstractRunner::init()
0398 {
0399     reloadConfiguration();
0400 }
0401 
0402 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 73)
0403 DataEngine *AbstractRunner::dataEngine(const QString &name) const
0404 {
0405     return d->dataEngine(name);
0406 }
0407 #endif
0408 
0409 bool AbstractRunner::isMatchingSuspended() const
0410 {
0411     return d->suspendMatching;
0412 }
0413 
0414 void AbstractRunner::suspendMatching(bool suspend)
0415 {
0416     if (d->suspendMatching == suspend) {
0417         return;
0418     }
0419 
0420     d->suspendMatching = suspend;
0421     Q_EMIT matchingSuspended(suspend);
0422 }
0423 
0424 int AbstractRunner::minLetterCount() const
0425 {
0426     return d->minLetterCount;
0427 }
0428 
0429 void AbstractRunner::setMinLetterCount(int count)
0430 {
0431     d->minLetterCount = count;
0432 }
0433 
0434 QRegularExpression AbstractRunner::matchRegex() const
0435 {
0436     return d->matchRegex;
0437 }
0438 
0439 void AbstractRunner::setMatchRegex(const QRegularExpression &regex)
0440 {
0441     d->matchRegex = regex;
0442     d->hasMatchRegex = regex.isValid() && !regex.pattern().isEmpty();
0443 }
0444 
0445 void AbstractRunner::setTriggerWords(const QStringList &triggerWords)
0446 {
0447     int minTriggerWordLetters = 0;
0448     QString constructedRegex = QStringLiteral("^");
0449     for (const QString &triggerWord : triggerWords) {
0450         // We want to link them with an or
0451         if (constructedRegex.length() > 1) {
0452             constructedRegex += QLatin1Char('|');
0453         }
0454         constructedRegex += QRegularExpression::escape(triggerWord);
0455         if (minTriggerWordLetters == 0 || triggerWord.length() < minTriggerWordLetters) {
0456             minTriggerWordLetters = triggerWord.length();
0457         }
0458     }
0459     // If we can reject the query because of the length we don't need the regex
0460     setMinLetterCount(minTriggerWordLetters);
0461     QRegularExpression regex(constructedRegex);
0462     setMatchRegex(regex);
0463 }
0464 
0465 bool AbstractRunner::hasMatchRegex() const
0466 {
0467     return d->hasMatchRegex;
0468 }
0469 
0470 AbstractRunnerPrivate::AbstractRunnerPrivate(AbstractRunner *r)
0471     : priority(AbstractRunner::NormalPriority)
0472 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 81)
0473     , speed(AbstractRunner::NormalSpeed)
0474 #endif
0475     , blackListed(RunnerContext::None)
0476     , runner(r)
0477     , fastRuns(0)
0478     , defaultSyntax(nullptr)
0479     , hasRunOptions(false)
0480     , suspendMatching(false)
0481 {
0482 }
0483 
0484 AbstractRunnerPrivate::~AbstractRunnerPrivate()
0485 {
0486 }
0487 
0488 void AbstractRunnerPrivate::init()
0489 {
0490     minLetterCount = runnerDescription.value(QStringLiteral("X-Plasma-Runner-Min-Letter-Count"), 0);
0491     if (runnerDescription.isValid()) {
0492         const auto rawData = runnerDescription.rawData();
0493         if (rawData.contains(QStringLiteral("X-Plasma-Runner-Match-Regex"))) {
0494             matchRegex = QRegularExpression(rawData.value(QStringLiteral("X-Plasma-Runner-Match-Regex")).toString());
0495             hasMatchRegex = matchRegex.isValid() && !matchRegex.pattern().isEmpty();
0496         }
0497         hasUniqueResults = runnerDescription.value(QStringLiteral("X-Plasma-Runner-Unique-Results"), false);
0498         hasWeakResults = runnerDescription.value(QStringLiteral("X-Plasma-Runner-Weak-Results"), false);
0499     }
0500 }
0501 
0502 void AbstractRunnerPrivate::init(const KPluginMetaData &pluginMetaData)
0503 {
0504     runnerDescription = pluginMetaData;
0505     init();
0506 }
0507 
0508 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0509 void AbstractRunnerPrivate::init(const KService::Ptr service)
0510 {
0511     QT_WARNING_PUSH
0512     QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0513     QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0514     const KPluginInfo pluginInfo(service);
0515     runnerDescription = pluginInfo.isValid() ? pluginInfo.toMetaData() : KPluginMetaData();
0516     QT_WARNING_POP
0517     init();
0518 }
0519 #endif
0520 
0521 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 77)
0522 void AbstractRunnerPrivate::init(const QString &path)
0523 {
0524     runnerDescription = KPluginMetaData(path + QStringLiteral("/metadata.desktop"));
0525     init();
0526 }
0527 #endif
0528 
0529 } // Plasma namespace
0530 
0531 #include "moc_abstractrunner.cpp"