File indexing completed on 2024-04-28 07:45:38

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 #ifndef KRUNNER_ABSTRACTRUNNER_H
0009 #define KRUNNER_ABSTRACTRUNNER_H
0010 
0011 #include "krunner_export.h"
0012 
0013 #include <QObject>
0014 #include <QStringList>
0015 
0016 #include <KPluginFactory>
0017 #include <KPluginMetaData>
0018 
0019 #include <memory>
0020 
0021 #include "querymatch.h"
0022 #include "runnercontext.h"
0023 #include "runnersyntax.h"
0024 
0025 class KConfigGroup;
0026 class QMimeData;
0027 class QRegularExpression;
0028 class QIcon;
0029 
0030 namespace KRunner
0031 {
0032 class AbstractRunnerPrivate;
0033 
0034 /**
0035  * @class AbstractRunner abstractrunner.h <KRunner/AbstractRunner>
0036  *
0037  * @short An abstract base class for Plasma Runner plugins.
0038  *
0039  * Be aware that runners will be moved to their own thread after being instantiated.
0040  * This means that except for AbstractRunner::run and the constructor, all methods will be non-blocking
0041  * for the UI.
0042  * Consider doing heavy resource initialization in the init method instead of the constructor.
0043  */
0044 class KRUNNER_EXPORT AbstractRunner : public QObject
0045 {
0046     Q_OBJECT
0047 
0048 public:
0049     ~AbstractRunner() override;
0050 
0051     /**
0052      * This is the main query method. It should trigger creation of
0053      * QueryMatch instances through RunnerContext::addMatch and
0054      * RunnerContext::addMatches.
0055      *
0056      * If the runner can run precisely the requested term (RunnerContext::query()),
0057      * it should create an exact match by setting the type to RunnerContext::ExactMatch.
0058      * The first runner that creates a QueryMatch will be the
0059      * default runner. Other runner's matches will be suggested in the
0060      * interface. Non-exact matches should be offered via RunnerContext::PossibleMatch.
0061      *
0062      * The match will be activated via run() if the user selects it.
0063      *
0064      * All matches need to be reported once this method returns. Asynchronous runners therefore need
0065      * to make use of a local event loop to wait for all matches.
0066      *
0067      * It is recommended to use local status data in async runners. The simplest way is
0068      * to have a separate class doing all the work like so:
0069      *
0070      * \code
0071      * void MyFancyAsyncRunner::match(RunnerContext &context)
0072      * {
0073      *     QEventLoop loop;
0074      *     MyAsyncWorker worker(context);
0075      *     connect(&worker, &MyAsyncWorker::finished, &loop, &MyAsyncWorker::quit);
0076      *     worker.work();
0077      *     loop.exec();
0078      * }
0079      * \endcode
0080      *
0081      * Here MyAsyncWorker creates all the matches and calls RunnerContext::addMatch
0082      * in some internal slot. It emits the finished() signal once done which will
0083      * quit the loop and make the match() method return.
0084      *
0085      * Execution of the correct action should be handled in the run method.
0086      *
0087      * @warning Returning from this method means to end execution of the runner.
0088      *
0089      * @sa run(), RunnerContext::addMatch, RunnerContext::addMatches, QueryMatch
0090      */
0091     virtual void match(KRunner::RunnerContext &context) = 0;
0092 
0093     /**
0094      * Called whenever an exact or possible match associated with this
0095      * runner is triggered.
0096      *
0097      * @param context The context in which the match is triggered, i.e. for which
0098      *                the match was created.
0099      * @param match The actual match to run/execute.
0100      */
0101     virtual void run(const KRunner::RunnerContext &context, const KRunner::QueryMatch &match);
0102 
0103     /**
0104      * @return the plugin metadata for this runner that was passed in the constructor
0105      */
0106     KPluginMetaData metadata() const;
0107 
0108     /**
0109      * Returns the translated name from the runner's metadata
0110      */
0111     QString name() const;
0112 
0113     /**
0114      * @return an id from the runner's metadata'
0115      */
0116     QString id() const;
0117 
0118     /**
0119      * Reloads the runner's configuration. This is called when it's KCM in the PluginSelector is applied.
0120      * This function may be used to set for example using setMatchRegex, setMinLetterCount or setTriggerWords.
0121      * Also, syntaxes should be updated when this method is called.
0122      * While reloading the config, matching is suspended.
0123      */
0124     virtual void reloadConfiguration();
0125 
0126     /**
0127      * @return the syntaxes the runner has registered that it accepts and understands
0128      */
0129     QList<RunnerSyntax> syntaxes() const;
0130 
0131     /**
0132      * @return true if the runner is currently busy with non-interuptable work, signaling that
0133      * the RunnerManager may not query it or read it's config properties
0134      */
0135     bool isMatchingSuspended() const;
0136 
0137     /**
0138      * This is the minimum letter count for the query. If the query is shorter than this value
0139      * and KRunner is not in the singleRunnerMode, match method is not called.
0140      * This can be set using the X-Plasma-Runner-Min-Letter-Count property or the setMinLetterCount method.
0141      * The default value is 0.
0142      *
0143      * @see setMinLetterCount
0144      * @see match
0145      * @since 5.75
0146      */
0147     int minLetterCount() const;
0148 
0149     /**
0150      * Set the minLetterCount property
0151      * @param count
0152      * @since 5.75
0153      */
0154     void setMinLetterCount(int count);
0155 
0156     /**
0157      * If this regex is set with a non empty pattern it must match the query in order for match being called.
0158      * Just like the minLetterCount property this check is ignored when the runner is in the singleRunnerMode.
0159      * In case both the regex and the letter count is set the letter count is checked first.
0160      * @return matchRegex property
0161      * @see hasMatchRegex
0162      * @since 5.75
0163      */
0164     QRegularExpression matchRegex() const;
0165 
0166     /**
0167      * Set the matchRegex property
0168      * @param regex
0169      * @since 5.75
0170      */
0171     void setMatchRegex(const QRegularExpression &regex);
0172 
0173     /**
0174      * Constructs internally a regex which requires the query to start with the trigger words.
0175      * Multiple words are concatenated with or, for instance: "^word1|word2|word3".
0176      * The trigger words are internally escaped.
0177      * Also the minLetterCount is set to the shortest word in the list.
0178      * @since 5.75
0179      * @see matchRegex
0180      */
0181     void setTriggerWords(const QStringList &triggerWords);
0182 
0183     /**
0184      * If the runner has a valid regex and non empty regex
0185      * @internal
0186      * @since 5.75
0187      */
0188     bool hasMatchRegex() const;
0189 
0190 Q_SIGNALS:
0191     /**
0192      * This signal is emitted when matching is about to commence, giving runners
0193      * an opportunity to prepare themselves, e.g. loading data sets or preparing
0194      * IPC or network connections. Things that should be loaded once and remain
0195      * extant for the lifespan of the AbstractRunner should be done in init().
0196      * @see init()
0197      */
0198     void prepare();
0199 
0200     /**
0201      * This signal is emitted when a session of matches is complete, giving runners
0202      * the opportunity to tear down anything set up as a result of the prepare()
0203      * method.
0204      */
0205     void teardown();
0206 
0207 protected:
0208     friend class RunnerManager;
0209     friend class RunnerManagerPrivate;
0210 
0211     /**
0212      * Constructor for a KRunner plugin
0213      *
0214      * @note You should connect here to the prepare/teardown signals. However, avoid doing heavy initialization here
0215      * in favor of doing it in AbstractRunner::init
0216      *
0217      * @param parent parent object for this runner
0218      * @param pluginMetaData metadata that was embedded in the runner
0219      * @param args for compatibility with KPluginFactory, since 6.0 this can be omitted
0220      * @since 5.72
0221      */
0222     explicit AbstractRunner(QObject *parent, const KPluginMetaData &pluginMetaData);
0223 
0224     /**
0225      * Sets whether or not the runner is available for match requests. Useful to
0226      * prevent queries when the runner is in a busy state.
0227      * @note Do not permanently suspend the runner. This is only intended as a temporary measure to
0228      * avoid useless queries being launched or async fetching of config/data being interfered with.
0229      */
0230     void suspendMatching(bool suspend);
0231 
0232     /**
0233      * Provides access to the runner's configuration object.
0234      */
0235     KConfigGroup config() const;
0236 
0237     /**
0238      * Adds a registered syntax that this runner understands. This is used to
0239      * display to the user what this runner can understand and how it can be
0240      * used.
0241      *
0242      * @param syntax the syntax to register
0243      */
0244     void addSyntax(const RunnerSyntax &syntax);
0245 
0246     /**
0247      * Utility overload for creating a syntax based on the given parameters
0248      * @see RunnerSyntax
0249      * @since 5.106
0250      */
0251     inline void addSyntax(const QString &exampleQuery, const QString &description)
0252     {
0253         addSyntax(QStringList(exampleQuery), description);
0254     }
0255 
0256     /// @copydoc addSyntax(const QString &exampleQuery, const QString &description)
0257     inline void addSyntax(const QStringList &exampleQueries, const QString &description)
0258     {
0259         addSyntax(KRunner::RunnerSyntax(exampleQueries, description));
0260     }
0261 
0262     /**
0263      * Sets the list of syntaxes; passing in an empty list effectively clears
0264      * the syntaxes.
0265      *
0266      * @param the syntaxes to register for this runner
0267      */
0268     void setSyntaxes(const QList<RunnerSyntax> &syns);
0269 
0270     /**
0271      * Reimplement this to run any initialization routines on first load.
0272      * Because it is executed in the runner's thread, it will not block the UI and is thus preferred.
0273      * By default, it calls reloadConfiguration();
0274      *
0275      * Until the runner is initialized, it will not be queried by the RunnerManager.
0276      */
0277     virtual void init();
0278 
0279     /**
0280      * Reimplement this if you want your runner to support serialization and drag and drop.
0281      * By default, this sets the QMimeData urls to the ones specified in @ref QueryMatch::urls
0282      */
0283     virtual QMimeData *mimeDataForMatch(const KRunner::QueryMatch &match);
0284 
0285 private:
0286     std::unique_ptr<AbstractRunnerPrivate> const d;
0287     KRUNNER_NO_EXPORT Q_INVOKABLE void matchInternal(KRunner::RunnerContext context);
0288     KRUNNER_NO_EXPORT Q_INVOKABLE void reloadConfigurationInternal();
0289     KRUNNER_NO_EXPORT Q_SIGNAL void matchInternalFinished(const QString &jobId);
0290     KRUNNER_NO_EXPORT Q_SIGNAL void matchingResumed();
0291     friend class RunnerManager;
0292     friend class RunnerContext;
0293     friend class RunnerContextPrivate;
0294     friend class QueryMatchPrivate;
0295     friend class DBusRunner; // Because it "overrides" matchInternal
0296 };
0297 
0298 } // KRunner namespace
0299 #endif