File indexing completed on 2024-04-21 03:56:47

0001 /*
0002     SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <aseigo@kde.org>
0003     SPDX-FileCopyrightText: 2020-2023 Alexander Lohnau <alexander.lohnau@gmx.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "abstractrunner.h"
0009 #include "abstractrunner_p.h"
0010 
0011 #include <QHash>
0012 #include <QIcon>
0013 #include <QMimeData>
0014 #include <QRegularExpression>
0015 #include <QTimer>
0016 
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 #include <KSharedConfig>
0020 
0021 namespace KRunner
0022 {
0023 AbstractRunner::AbstractRunner(QObject *parent, const KPluginMetaData &pluginMetaData)
0024     : QObject(nullptr)
0025     , d(new AbstractRunnerPrivate(this, pluginMetaData))
0026 {
0027     // By now, runners who do "qobject_cast<Krunner::RunnerManager*>(parent)" should have saved the value
0028     // By setting the parent to a nullptr, we are allowed to move the object to another thread
0029     Q_ASSERT(parent);
0030     setObjectName(pluginMetaData.pluginId()); // Only for debugging purposes
0031 
0032     // Suspend matching while we initialize the runner. Once it is ready, the last query will be run
0033     QTimer::singleShot(0, this, [this]() {
0034         init();
0035         // In case the runner didn't specify anything explicitly, we resume matching after the initialization
0036         bool doesNotHaveExplicitSuspend = true;
0037         {
0038             QReadLocker l(&d->lock);
0039             doesNotHaveExplicitSuspend = !d->suspendMatching.has_value();
0040         }
0041         if (doesNotHaveExplicitSuspend) {
0042             suspendMatching(false);
0043         }
0044     });
0045 }
0046 
0047 AbstractRunner::~AbstractRunner() = default;
0048 
0049 KConfigGroup AbstractRunner::config() const
0050 {
0051     KConfigGroup runners(KSharedConfig::openConfig(QStringLiteral("krunnerrc")), QStringLiteral("Runners"));
0052     return runners.group(id());
0053 }
0054 
0055 void AbstractRunner::reloadConfiguration()
0056 {
0057 }
0058 
0059 void AbstractRunner::addSyntax(const RunnerSyntax &syntax)
0060 {
0061     d->syntaxes.append(syntax);
0062 }
0063 
0064 void AbstractRunner::setSyntaxes(const QList<RunnerSyntax> &syntaxes)
0065 {
0066     d->syntaxes = syntaxes;
0067 }
0068 
0069 QList<RunnerSyntax> AbstractRunner::syntaxes() const
0070 {
0071     return d->syntaxes;
0072 }
0073 
0074 QMimeData *AbstractRunner::mimeDataForMatch(const QueryMatch &match)
0075 {
0076     if (match.urls().isEmpty()) {
0077         return nullptr;
0078     }
0079     QMimeData *result = new QMimeData();
0080     result->setUrls(match.urls());
0081     return result;
0082 }
0083 
0084 void AbstractRunner::run(const KRunner::RunnerContext & /*search*/, const KRunner::QueryMatch & /*action*/)
0085 {
0086 }
0087 
0088 QString AbstractRunner::name() const
0089 {
0090     return d->translatedName;
0091 }
0092 
0093 QString AbstractRunner::id() const
0094 {
0095     return d->runnerDescription.pluginId();
0096 }
0097 
0098 KPluginMetaData AbstractRunner::metadata() const
0099 {
0100     return d->runnerDescription;
0101 }
0102 
0103 void AbstractRunner::init()
0104 {
0105     reloadConfiguration();
0106 }
0107 
0108 bool AbstractRunner::isMatchingSuspended() const
0109 {
0110     QReadLocker lock(&d->lock);
0111     return d->suspendMatching.value_or(true);
0112 }
0113 
0114 void AbstractRunner::suspendMatching(bool suspend)
0115 {
0116     QWriteLocker lock(&d->lock);
0117     if (d->suspendMatching.has_value() && d->suspendMatching.value() == suspend) {
0118         return;
0119     }
0120 
0121     d->suspendMatching = suspend;
0122     if (!suspend) {
0123         Q_EMIT matchingResumed();
0124     }
0125 }
0126 
0127 int AbstractRunner::minLetterCount() const
0128 {
0129     return d->minLetterCount;
0130 }
0131 
0132 void AbstractRunner::setMinLetterCount(int count)
0133 {
0134     d->minLetterCount = count;
0135 }
0136 
0137 QRegularExpression AbstractRunner::matchRegex() const
0138 {
0139     return d->matchRegex;
0140 }
0141 
0142 void AbstractRunner::setMatchRegex(const QRegularExpression &regex)
0143 {
0144     d->matchRegex = regex;
0145     d->hasMatchRegex = regex.isValid() && !regex.pattern().isEmpty();
0146 }
0147 
0148 void AbstractRunner::setTriggerWords(const QStringList &triggerWords)
0149 {
0150     int minTriggerWordLetters = 0;
0151     QString constructedRegex = QStringLiteral("^");
0152     for (const QString &triggerWord : triggerWords) {
0153         // We want to link them with an or
0154         if (constructedRegex.length() > 1) {
0155             constructedRegex += QLatin1Char('|');
0156         }
0157         constructedRegex += QRegularExpression::escape(triggerWord);
0158         if (minTriggerWordLetters == 0 || triggerWord.length() < minTriggerWordLetters) {
0159             minTriggerWordLetters = triggerWord.length();
0160         }
0161     }
0162     // If we can reject the query because of the length we don't need the regex
0163     setMinLetterCount(minTriggerWordLetters);
0164     setMatchRegex(QRegularExpression(constructedRegex));
0165 }
0166 
0167 bool AbstractRunner::hasMatchRegex() const
0168 {
0169     return d->hasMatchRegex;
0170 }
0171 
0172 void AbstractRunner::matchInternal(KRunner::RunnerContext context)
0173 {
0174     if (context.isValid()) { // Otherwise, we would just waste resources
0175         match(context);
0176     }
0177     Q_EMIT matchInternalFinished(context.runnerJobId(this));
0178 }
0179 // Suspend the runner while reloading the config
0180 void AbstractRunner::reloadConfigurationInternal()
0181 {
0182     bool isSuspended = isMatchingSuspended();
0183     suspendMatching(true);
0184     reloadConfiguration();
0185     suspendMatching(isSuspended);
0186 }
0187 
0188 } // KRunner namespace
0189 
0190 #include "moc_abstractrunner.cpp"