diff --git a/autotests/runnercontexttest.cpp b/autotests/runnercontexttest.cpp index bb38d3e..ba5f6a1 100644 --- a/autotests/runnercontexttest.cpp +++ b/autotests/runnercontexttest.cpp @@ -1,60 +1,60 @@ /****************************************************************************** * Copyright 2010 Aaron Seigo * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "runnercontexttest.h" #include -#include "plasma/runnercontext.h" +#include "runnercontext.h" Q_DECLARE_METATYPE(Plasma::RunnerContext::Type) void RunnerContextTest::typeDetection_data() { QTest::addColumn("url"); QTest::addColumn("type"); if (KProtocolInfo::isKnownProtocol("man")) { QTest::newRow("man page listing") << "man:/" << Plasma::RunnerContext::NetworkLocation; QTest::newRow("ls man page listing") << "man://ls" << Plasma::RunnerContext::NetworkLocation; } QTest::newRow("http without host") << "http://" << Plasma::RunnerContext::UnknownType; QTest::newRow("http with host") << "http://kde.org" << Plasma::RunnerContext::NetworkLocation; QTest::newRow("file double slash") << "file://home" << Plasma::RunnerContext::Directory; QTest::newRow("file triple slash") << "file:///home" << Plasma::RunnerContext::Directory; QTest::newRow("file single slash") << "file:/home" << Plasma::RunnerContext::Directory; QTest::newRow("file multiple path") << "file://usr/bin" << Plasma::RunnerContext::Directory; QTest::newRow("invalid file path") << "file://bad/path" << Plasma::RunnerContext::UnknownType; QTest::newRow("executable") << "ls" << Plasma::RunnerContext::Executable; QTest::newRow("executable with params") << "ls -R" << Plasma::RunnerContext::ShellCommand; QTest::newRow("full path executable") << "ls -R" << Plasma::RunnerContext::ShellCommand; QTest::newRow("full path executable with params") << "/bin/ls -R" << Plasma::RunnerContext::ShellCommand; QTest::newRow("protocol-less path") << "/home" << Plasma::RunnerContext::Directory; QTest::newRow("invalid protocol-less path") << "/bad/path" << Plasma::RunnerContext::UnknownType; } void RunnerContextTest::typeDetection() { QFETCH(QString, url); QFETCH(Plasma::RunnerContext::Type, type); m_context.setQuery(url); QCOMPARE(int(m_context.type()), int(type)); } QTEST_MAIN(RunnerContextTest) diff --git a/autotests/runnercontexttest.h b/autotests/runnercontexttest.h index 1c6feb7..26a2518 100644 --- a/autotests/runnercontexttest.h +++ b/autotests/runnercontexttest.h @@ -1,39 +1,39 @@ /****************************************************************************** * Copyright 2010 Aaron Seigo * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #ifndef PACKAGEMETADATATEST_H #include -#include "plasma/runnercontext.h" +#include "runnercontext.h" class RunnerContextTest : public QObject { Q_OBJECT private Q_SLOTS: void typeDetection_data(); void typeDetection(); private: Plasma::RunnerContext m_context; }; #endif diff --git a/src/abstractrunner.cpp b/src/abstractrunner.cpp index c01ac18..3adc086 100644 --- a/src/abstractrunner.cpp +++ b/src/abstractrunner.cpp @@ -1,458 +1,456 @@ /* * Copyright 2006-2007 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "abstractrunner.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include -#include "package.h" -#include "pluginloader.h" #include "private/abstractrunner_p.h" #include "querymatch.h" #include "runnercontext.h" #include "scripting/runnerscript.h" namespace Plasma { AbstractRunner::AbstractRunner(QObject *parent, const QString &path) : QObject(parent), d(new AbstractRunnerPrivate(this)) { d->init(path); } AbstractRunner::AbstractRunner(const KService::Ptr service, QObject *parent) : QObject(parent), d(new AbstractRunnerPrivate(this)) { d->init(service); } AbstractRunner::AbstractRunner(QObject *parent, const QVariantList &args) : QObject(parent), d(new AbstractRunnerPrivate(this)) { if (args.count() > 0) { KService::Ptr service = KService::serviceByStorageId(args[0].toString()); if (service) { d->init(service); } } } AbstractRunner::~AbstractRunner() { delete d; } KConfigGroup AbstractRunner::config() const { QString group = id(); if (group.isEmpty()) { group = "UnnamedRunner"; } KConfigGroup runners(KSharedConfig::openConfig(), "Runners"); return KConfigGroup(&runners, group); } void AbstractRunner::reloadConfiguration() { if (d->script) { emit d->script->reloadConfiguration(); } } void AbstractRunner::addSyntax(const RunnerSyntax &syntax) { d->syntaxes.append(syntax); } void AbstractRunner::setDefaultSyntax(const RunnerSyntax &syntax) { d->syntaxes.append(syntax); d->defaultSyntax = &(d->syntaxes.last()); } void AbstractRunner::setSyntaxes(const QList &syntaxes) { d->syntaxes = syntaxes; } QList AbstractRunner::syntaxes() const { return d->syntaxes; } RunnerSyntax *AbstractRunner::defaultSyntax() const { return d->defaultSyntax; } void AbstractRunner::performMatch(Plasma::RunnerContext &localContext) { static const int reasonableRunTime = 1500; static const int fastEnoughTime = 250; if (d->suspendMatching) { return; } QTime time; time.restart(); //The local copy is already obtained in the job match(localContext); // automatically rate limit runners that become slooow const int runtime = time.elapsed(); bool slowed = speed() == SlowSpeed; if (!slowed && runtime > reasonableRunTime) { // we punish runners that return too slowly, even if they don't bring // back matches #ifndef NDEBUG // qDebug() << id() << "runner is too slow, putting it on the back burner."; #endif d->fastRuns = 0; setSpeed(SlowSpeed); } if (slowed && runtime < fastEnoughTime && localContext.query().size() > 2) { ++d->fastRuns; if (d->fastRuns > 2) { // we reward slowed runners who bring back matches fast enough // 3 times in a row #ifndef NDEBUG // qDebug() << id() << "runner is faster than we thought, kicking it up a notch"; #endif setSpeed(NormalSpeed); } } } QList AbstractRunner::actionsForMatch(const Plasma::QueryMatch &match) { Q_UNUSED(match) QList ret; if (d->script) { emit d->script->actionsForMatch(match, &ret); } return ret; } QAction* AbstractRunner::addAction(const QString &id, const QIcon &icon, const QString &text) { QAction *a = new QAction(icon, text, this); d->actions.insert(id, a); return a; } void AbstractRunner::addAction(const QString &id, QAction *action) { d->actions.insert(id, action); } void AbstractRunner::removeAction(const QString &id) { QAction *a = d->actions.take(id); delete a; } QAction* AbstractRunner::action(const QString &id) const { return d->actions.value(id); } QHash AbstractRunner::actions() const { return d->actions; } void AbstractRunner::clearActions() { qDeleteAll(d->actions); d->actions.clear(); } QMimeData *AbstractRunner::mimeDataForMatch(const QueryMatch &match) { Q_UNUSED(match) return 0; } bool AbstractRunner::hasRunOptions() { return d->hasRunOptions; } void AbstractRunner::setHasRunOptions(bool hasRunOptions) { d->hasRunOptions = hasRunOptions; } void AbstractRunner::createRunOptions(QWidget *parent) { if (d->script) { emit d->script->createRunOptions(parent); } } AbstractRunner::Speed AbstractRunner::speed() const { // the only time the read lock will fail is if we were slow are going to speed up // or if we were fast and are going to slow down; so don't wait in this case, just // say we're slow. we either will be soon or were just a moment ago and it doesn't // hurt to do one more run the slow way if (!d->speedLock.tryLockForRead()) { return SlowSpeed; } Speed s = d->speed; d->speedLock.unlock(); return s; } void AbstractRunner::setSpeed(Speed speed) { d->speedLock.lockForWrite(); d->speed = speed; d->speedLock.unlock(); } AbstractRunner::Priority AbstractRunner::priority() const { return d->priority; } void AbstractRunner::setPriority(Priority priority) { d->priority = priority; } RunnerContext::Types AbstractRunner::ignoredTypes() const { return d->blackListed; } void AbstractRunner::setIgnoredTypes(RunnerContext::Types types) { d->blackListed = types; } void AbstractRunner::run(const Plasma::RunnerContext &search, const Plasma::QueryMatch &action) { if (d->script) { return d->script->run(search, action); } } void AbstractRunner::match(Plasma::RunnerContext &search) { if (d->script) { return d->script->match(search); } } QString AbstractRunner::name() const { if (d->runnerDescription.isValid()) { return d->runnerDescription.name(); } return objectName(); } QIcon AbstractRunner::icon() const { if (d->runnerDescription.isValid()) { return QIcon::fromTheme(d->runnerDescription.icon()); } return QIcon(); } QString AbstractRunner::id() const { if (d->runnerDescription.isValid()) { return d->runnerDescription.pluginName(); } return objectName(); } QString AbstractRunner::description() const { if (d->runnerDescription.isValid()) { return d->runnerDescription.property("Comment").toString(); } return objectName(); } Package AbstractRunner::package() const { return d->package ? *d->package : Package(); } void AbstractRunner::init() { if (d->script) { d->setupScriptSupport(); d->script->init(); } reloadConfiguration(); } DataEngine *AbstractRunner::dataEngine(const QString &name) const { return d->dataEngine(name); } bool AbstractRunner::isMatchingSuspended() const { return d->suspendMatching; } void AbstractRunner::suspendMatching(bool suspend) { if (d->suspendMatching == suspend) { return; } d->suspendMatching = suspend; emit matchingSuspended(suspend); } AbstractRunnerPrivate::AbstractRunnerPrivate(AbstractRunner *r) : priority(AbstractRunner::NormalPriority), speed(AbstractRunner::NormalSpeed), blackListed(0), script(0), runner(r), fastRuns(0), package(0), defaultSyntax(0), hasRunOptions(false), suspendMatching(false) { } AbstractRunnerPrivate::~AbstractRunnerPrivate() { delete script; script = 0; delete package; package = 0; } void AbstractRunnerPrivate::init(const KService::Ptr service) { runnerDescription = KPluginInfo(service); if (runnerDescription.isValid()) { const QString api = runnerDescription.property("X-Plasma-API").toString(); if (!api.isEmpty()) { const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "plasma/runners/" + runnerDescription.pluginName() + '/'); prepScripting(path, api); if (!script) { #ifndef NDEBUG // qDebug() << "Could not create a(n)" << api << "ScriptEngine for the" << runnerDescription.name() << "Runner."; #endif } } } } void AbstractRunnerPrivate::init(const QString &path) { runnerDescription = KPluginInfo(path + "/metadata.desktop"); const QString api = runnerDescription.property("X-Plasma-API").toString(); prepScripting(path, api); } void AbstractRunnerPrivate::prepScripting(const QString &path, const QString &api) { if (script) { return; } delete package; package = 0; if (api.isEmpty()) { return; } package = new Package(PluginLoader::self()->loadPackage("Plasma/Runner", api)); package->setPath(path); if (!package->isValid()) { #ifndef NDEBUG // qDebug() << "Invalid Runner package at" << path; #endif return; } script = Plasma::loadScriptEngine(api, runner); if (!script) { delete package; package = 0; } } // put all setup routines for script here. at this point we can assume that // package exists and that we have a script engine void AbstractRunnerPrivate::setupScriptSupport() { if (!package) { return; } #ifndef NDEBUG // qDebug() << "setting up script support, package is in" << package->path() // << ", main script is" << package->filePath("mainscript"); #endif // FIXME: Replace with ki18n functionality once semantics is clear. // const QString translationsPath = package->filePath("translations"); // if (!translationsPath.isEmpty()) { // KGlobal::dirs()->addResourceDir("locale", translationsPath); // } } } // Plasma namespace #include "moc_abstractrunner.cpp" diff --git a/src/abstractrunner.h b/src/abstractrunner.h index 219fa05..471a471 100644 --- a/src/abstractrunner.h +++ b/src/abstractrunner.h @@ -1,472 +1,472 @@ /* * Copyright 2006-2007 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PLASMA_ABSTRACTRUNNER_H #define PLASMA_ABSTRACTRUNNER_H #include #include #include #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include class QAction; class QMimeData; class KCompletion; namespace Plasma { class DataEngine; class Package; class RunnerScript; class QueryMatch; class AbstractRunnerPrivate; /** * @class AbstractRunner plasma/abstractrunner.h * * @short An abstract base class for Plasma Runner plugins. * * Be aware that runners have to be thread-safe. This is due to the fact that * each runner is executed in its own thread for each new term. Thus, a runner * may be executed more than once at the same time. See match() for details. * To let krunner expose a global shortcut for the single runner query mode, the runner * must set the "X-Plasma-AdvertiseSingleRunnerMode" key to true in the .desktop file * and set a default syntax. See setDefaultSyntax() for details. * */ class PLASMA_EXPORT AbstractRunner : public QObject { Q_OBJECT Q_PROPERTY(bool matchingSuspended READ isMatchingSuspended WRITE suspendMatching NOTIFY matchingSuspended) Q_PROPERTY(QString id READ id) Q_PROPERTY(QString description READ description) Q_PROPERTY(QString name READ name) Q_PROPERTY(QIcon icon READ icon) public: /** Specifies a nominal speed for the runner */ enum Speed { SlowSpeed, NormalSpeed }; /** Specifies a priority for the runner */ enum Priority { LowestPriority = 0, LowPriority, NormalPriority, HighPriority, HighestPriority }; /** An ordered list of runners */ typedef QList List; virtual ~AbstractRunner(); /** * This is the main query method. It should trigger creation of * QueryMatch instances through RunnerContext::addMatch and * RunnerContext::addMatches. It is called internally by performMatch(). * * If the runner can run precisely the requested term (RunnerContext::query()), * it should create an exact match by setting the type to RunnerContext::ExactMatch. * The first runner that creates a QueryMatch will be the * default runner. Other runner's matches will be suggested in the * interface. Non-exact matches should be offered via RunnerContext::PossibleMatch. * * The match will be activated via run() if the user selects it. * * Each runner is executed in its own thread. Whenever the user input changes this * method is called again. Thus, it needs to be thread-safe. Also, all matches need * to be reported once this method returns. Asynchronous runners therefore need * to make use of a local event loop to wait for all matches. * * It is recommended to use local status data in async runners. The simplest way is * to have a separate class doing all the work like so: * * \code * void MyFancyAsyncRunner::match( RunnerContext& context ) * { * QEventLoop loop; * MyAsyncWorker worker( context ); * connect( &worker, SIGNAL(finished()), * &loop, SLOT(quit()) ); * worker.work(); * loop.exec(); * } * \endcode * * Here MyAsyncWorker creates all the matches and calls RunnerContext::addMatch * in some internal slot. It emits the finished() signal once done which will * quit the loop and make the match() method return. The local status is kept * entirely in MyAsyncWorker which makes match() trivially thread-safe. * * If a particular match supports multiple actions, set up the corresponding * actions in the actionsForMatch method. Do not call any of the action methods * within this method! * * Execution of the correct action should be handled in the run method. * @caution This method needs to be thread-safe since KRunner will simply * start a new thread for each new term. * * @warning Returning from this method means to end execution of the runner. * * @sa run(), RunnerContext::addMatch, RunnerContext::addMatches, QueryMatch */ virtual void match(Plasma::RunnerContext &context); /** * Triggers a call to match. This will call match() internally. * * @param context the search context used in executing this match. */ void performMatch(Plasma::RunnerContext &context); /** * If the runner has options that the user can interact with to modify * what happens when run or one of the actions created in match * is called, the runner should return true */ bool hasRunOptions(); /** * If hasRunOptions() returns true, this method may be called to get * a widget displaying the options the user can interact with to modify * the behaviour of what happens when a given match is selected. * * @param widget the parent of the options widgets. */ virtual void createRunOptions(QWidget *widget); /** * Called whenever an exact or possible match associated with this * runner is triggered. * * @param context The context in which the match is triggered, i.e. for which * the match was created. * @param match The actual match to run/execute. */ virtual void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match); /** * The nominal speed of the runner. * @see setSpeed */ Speed speed() const; /** * The priority of the runner. * @see setPriority */ Priority priority() const; /** * Returns the OR'ed value of all the Information types (as defined in RunnerContext::Type) * this runner is not interested in. * @return OR'ed value of black listed types */ RunnerContext::Types ignoredTypes() const; /** * Sets the types this runner will ignore * @param types OR'ed listed of ignored types */ void setIgnoredTypes(RunnerContext::Types types); /** * @return the user visible engine name for the Runner */ QString name() const; /** * @return an id string for the Runner */ QString id() const; /** * @return the description of this Runner */ QString description() const; /** * @return the icon for this Runner */ QIcon icon() const; /** * Accessor for the associated Package object if any. * * Note that the returned pointer is only valid for the lifetime of * the runner. * * @return the Package object, which may be invalid **/ Package package() const; /** * Signal runner to reload its configuration. */ virtual void reloadConfiguration(); /** * @return the syntaxes the runner has registered that it accepts and understands * @since 4.3 */ QList syntaxes() const; /** * @return the default syntax for the runner or 0 if no default syntax has been defined * * @since 4.4 */ RunnerSyntax *defaultSyntax() const; /** * @return true if the runner is currently busy with non-interuptable work, signaling that * new threads should not be created for it at this time * @since 4.6 */ bool isMatchingSuspended() const; Q_SIGNALS: /** * This signal is emitted when matching is about to commence, giving runners * an opportunity to prepare themselves, e.g. loading data sets or preparing * IPC or network connections. This method should be fast so as not to cause * slow downs. Things that take longer or which should be loaded once and * remain extant for the lifespan of the AbstractRunner should be done in init(). * @see init() * @since 4.4 */ void prepare(); /** * This signal is emitted when a session of matches is complete, giving runners * the opportunity to tear down anything set up as a result of the prepare() * method. * @since 4.4 */ void teardown(); /** * Emitted when the runner enters or exits match suspension * @see matchingSuspended * @since 4.6 */ void matchingSuspended(bool suspended); protected: friend class RunnerManager; friend class RunnerManagerPrivate; explicit AbstractRunner(QObject *parent = 0, const QString &path = QString()); explicit AbstractRunner(const KService::Ptr service, QObject *parent = 0); AbstractRunner(QObject *parent, const QVariantList &args); /** * Sets whether or not the runner is available for match requests. Useful to * prevent more thread spawning when the thread is in a busy state. */ void suspendMatching(bool suspend); /** * Provides access to the runner's configuration object. */ KConfigGroup config() const; /** * Sets whether or not the runner has options for matches */ void setHasRunOptions(bool hasRunOptions); /** * Sets the nominal speed of the runner. Only slow runners need * to call this within their constructor because the default * speed is NormalSpeed. Runners that use DBUS should call * this within their constructors. */ void setSpeed(Speed newSpeed); /** * Sets the priority of the runner. Lower priority runners are executed * only after higher priority runners. */ void setPriority(Priority newPriority); /** * A given match can have more than action that can be performed on it. * For example, a song match returned by a music player runner can be queued, * added to the playlist, or played. * * Call this method to add actions that can be performed by the runner. * Actions must first be added to the runner's action registry. * Note: execution of correct action is left up to the runner. */ virtual QList actionsForMatch(const Plasma::QueryMatch &match); /** * Creates and then adds an action to the action registry. * AbstractRunner assumes ownership of the created action. * * @param id A unique identifier string * @param icon The icon to display * @param text The text to display * @return the created QAction */ QAction* addAction(const QString &id, const QIcon &icon, const QString &text); /** * Adds an action to the runner's action registry. * * The QAction must be created within the GUI thread; * do not create it within the match method of AbstractRunner. * * @param id A unique identifier string * @param action The QAction to be stored */ void addAction(const QString &id, QAction *action); /** * Removes the action from the action registry. * AbstractRunner deletes the action once removed. * * @param id The id of the action to be removed */ void removeAction(const QString &id); /** * Returns the action associated with the id */ QAction* action(const QString &id) const; /** * Returns all registered actions */ QHash actions() const; /** * Clears the action registry. * The action pool deletes the actions. */ void clearActions(); /** * Adds a registered syntax that this runner understands. This is used to * display to the user what this runner can understand and how it can be * used. * * @param syntax the syntax to register * @since 4.3 */ void addSyntax(const RunnerSyntax &syntax); /** * Set @p syntax as the default syntax for the runner; the default syntax will be * substituted to the empty query in single runner mode. This is also used to * display to the user what this runner can understand and how it can be * used. * The default syntax is automatically added to the list of registered syntaxes, there * is no need to add it using addSyntax. * Note that there can be only one default syntax; if called more than once, the last * call will determine the default syntax. * A default syntax (even trivial) is required to advertise single runner mode * * @param syntax the syntax to register and to set as default * @since 4.4 **/ void setDefaultSyntax(const RunnerSyntax &syntax); /** * Sets the list of syntaxes; passing in an empty list effectively clears * the syntaxes. * * @param the syntaxes to register for this runner * @since 4.3 */ void setSyntaxes(const QList &syns); /** * Loads the given DataEngine * * Tries to load the data engine given by @p name. Each engine is * only loaded once, and that instance is re-used on all subsequent * requests. * * If the data engine was not found, an invalid data engine is returned * (see DataEngine::isValid()). * * Note that you should not delete the returned engine. * * @param name Name of the data engine to load * @return pointer to the data engine if it was loaded, * or an invalid data engine if the requested engine * could not be loaded * * @since 4.4 */ Q_INVOKABLE DataEngine *dataEngine(const QString &name) const; /** * Reimplement this slot to run any initialization routines on first load. * By default, it calls reloadConfiguration(); for scripted Runners this * method also sets up the ScriptEngine. */ virtual void init(); /** * Reimplement this slot if you want your runner * to support serialization and drag and drop * @since 4.5 */ virtual QMimeData *mimeDataForMatch(const Plasma::QueryMatch &match); private: friend class RunnerScript; AbstractRunnerPrivate *const d; }; } // Plasma namespace #define K_EXPORT_PLASMA_RUNNER( libname, classname ) \ K_PLUGIN_FACTORY(factory, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) /** * These plugins are Used by the plugin selector dialog to show * configuration options specific to this runner. These options * must be runner global and not pertain to a specific match. */ #define K_EXPORT_RUNNER_CONFIG( name, classname ) \ K_PLUGIN_FACTORY(ConfigFactory, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) #endif diff --git a/src/declarative/runnermodel.cpp b/src/declarative/runnermodel.cpp index 170f511..83a0ec5 100644 --- a/src/declarative/runnermodel.cpp +++ b/src/declarative/runnermodel.cpp @@ -1,251 +1,251 @@ /* Copyright 2011 Aaron Seigo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "runnermodel.h" #include #include #include #include -#include +#include RunnerModel::RunnerModel(QObject *parent) : QAbstractListModel(parent), m_manager(0), m_startQueryTimer(new QTimer(this)), m_runningChangedTimeout(new QTimer(this)), m_running(false) { QHash roles; roles.insert(Qt::DisplayRole, "display"); roles.insert(Qt::DecorationRole, "decoration"); roles.insert(Label, "label"); roles.insert(Icon, "icon"); roles.insert(Type, "type"); roles.insert(Relevance, "relevance"); roles.insert(Data, "data"); roles.insert(Id, "id"); roles.insert(SubText, "description"); roles.insert(Enabled, "enabled"); roles.insert(RunnerId, "runnerid"); roles.insert(RunnerName, "runnerName"); roles.insert(Actions, "actions"); setRoleNames(roles); m_startQueryTimer->setSingleShot(true); m_startQueryTimer->setInterval(10); connect(m_startQueryTimer, SIGNAL(timeout()), this, SLOT(startQuery())); //FIXME: HACK: some runners stay in a running but finished state, not possible to say if it's actually over m_runningChangedTimeout->setSingleShot(true); connect(m_runningChangedTimeout, SIGNAL(timeout()), this, SLOT(queryHasFinished())); } int RunnerModel::rowCount(const QModelIndex& index) const { return index.isValid() ? 0 : m_matches.count(); } int RunnerModel::count() const { return m_matches.count(); } QStringList RunnerModel::runners() const { return m_manager ? m_manager->allowedRunners() : m_pendingRunnersList; } void RunnerModel::setRunners(const QStringList &allowedRunners) { //use sets to ensure comparison is order-independent if (allowedRunners.toSet() == runners().toSet()) { return; } if (m_manager) { m_manager->setAllowedRunners(allowedRunners); //automagically enter single runner mode if there's only 1 allowed runner m_manager->setSingleMode(allowedRunners.count() == 1); } else { m_pendingRunnersList = allowedRunners; // qDebug() << "runners set" << m_pendingRunnersList.count(); } // to trigger single runner fun! if (allowedRunners.count() == 1) { m_singleRunnerId = allowedRunners.first(); scheduleQuery(QString()); } else { m_singleRunnerId.clear(); } emit runnersChanged(); } void RunnerModel::run(int index) { if (index >= 0 && index < m_matches.count()) { m_manager->run(m_matches.at(index)); } } bool RunnerModel::isRunning() const { return m_running; } QVariant RunnerModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.parent().isValid() || index.column() > 0 || index.row() < 0 || index.row() >= m_matches.count()) { // index requested must be valid, but we have no child items! //qDebug() << "invalid index requested"; return QVariant(); } if (role == Qt::DisplayRole || role == Label) { return m_matches.at(index.row()).text(); } else if (role == Qt::DecorationRole || role == Icon) { return m_matches.at(index.row()).icon(); } else if (role == Type) { return m_matches.at(index.row()).type(); } else if (role == Relevance) { return m_matches.at(index.row()).relevance(); } else if (role == Data) { return m_matches.at(index.row()).data(); } else if (role == Id) { return m_matches.at(index.row()).id(); } else if (role == SubText) { return m_matches.at(index.row()).subtext(); } else if (role == Enabled) { return m_matches.at(index.row()).isEnabled(); } else if (role == RunnerId) { return m_matches.at(index.row()).runner()->id(); } else if (role == RunnerName) { return m_matches.at(index.row()).runner()->name(); } else if (role == Actions) { QVariantList actions; Plasma::QueryMatch amatch = m_matches.at(index.row()); QList theactions = m_manager->actionsForMatch(amatch); foreach(QAction* action, theactions) { actions += qVariantFromValue(action); } return actions; } return QVariant(); } QString RunnerModel::currentQuery() const { return m_manager ? m_manager->query() : QString(); } void RunnerModel::scheduleQuery(const QString &query) { m_pendingQuery = query; m_startQueryTimer->start(); } void RunnerModel::startQuery() { // avoid creating a manager just so we can run nothing // however, if we have one pending runner, then we'll be in single query mode // and a null query is a valid query if (!m_manager && m_pendingRunnersList.count() != 1 && m_pendingQuery.isEmpty()) { return; } //qDebug() << "!!!!!!!!!!!!!" << m_pendingQuery << m_manager; if (createManager() || m_pendingQuery != m_manager->query()) { //qDebug() << "running query" << m_pendingQuery << m_manager; m_manager->launchQuery(m_pendingQuery, m_singleRunnerId); emit queryChanged(); m_running = true; emit runningChanged(true); } } bool RunnerModel::createManager() { if (!m_manager) { m_manager = new Plasma::RunnerManager(this); connect(m_manager, SIGNAL(matchesChanged(QList)), this, SLOT(matchesChanged(QList))); connect(m_manager, SIGNAL(queryFinished()), this, SLOT(queryHasFinished())); if (!m_pendingRunnersList.isEmpty()) { setRunners(m_pendingRunnersList); m_pendingRunnersList.clear(); } //connect(m_manager, SIGNAL(queryFinished()), this, SLOT(queryFinished())); return true; } return false; } void RunnerModel::matchesChanged(const QList &matches) { //qDebug() << "got matches:" << matches.count(); bool fullReset = false; int oldCount = m_matches.count(); int newCount = matches.count(); if (newCount > oldCount) { // We received more matches than we had. If all common matches are the // same, we can just append new matches instead of resetting the whole // model for (int row = 0; row < oldCount; ++row) { if (!(m_matches.at(row) == matches.at(row))) { fullReset = true; break; } } if (!fullReset) { // Not a full reset, inserting rows beginInsertRows(QModelIndex(), oldCount, newCount-1); m_matches = matches; endInsertRows(); emit countChanged(); } } else { fullReset = true; } if (fullReset) { beginResetModel(); m_matches = matches; endResetModel(); emit countChanged(); } m_runningChangedTimeout->start(3000); } void RunnerModel::queryHasFinished() { m_running = false; emit runningChanged(false); } #include "runnermodel.moc" diff --git a/src/declarative/runnermodel.h b/src/declarative/runnermodel.h index bd21c21..8ea84dc 100644 --- a/src/declarative/runnermodel.h +++ b/src/declarative/runnermodel.h @@ -1,128 +1,128 @@ /* Copyright 2011 Aaron Seigo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef RUNNERMODEL_H #define RUNNERMODEL_H #include #include -#include +#include namespace Plasma { class RunnerManager; class QueryMatch; } // namespace Plasma class QTimer; /** * This model provides bindings to use KRunner from QML * * @author Aaron Seigo */ class RunnerModel : public QAbstractListModel { Q_OBJECT /** * @property string set the KRunner query */ Q_PROPERTY(QString query WRITE scheduleQuery READ currentQuery NOTIFY queryChanged) /** * @property Array The list of all allowed runner plugins that will be executed */ Q_PROPERTY(QStringList runners WRITE setRunners READ runners NOTIFY runnersChanged) /** * @property int The number of rows of the model */ Q_PROPERTY(int count READ count NOTIFY countChanged) /** * @property bool running: true when queries are in execution */ Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged) public: /** * @enum Roles of the model, they will be accessible from delegates */ enum Roles { Type = Qt::UserRole + 1, Label, Icon, Relevance, Data, Id, SubText, Enabled, RunnerId, RunnerName, Actions }; RunnerModel(QObject *parent = 0); QString currentQuery() const; QStringList runners() const; void setRunners(const QStringList &allowedRunners); Q_SCRIPTABLE void run(int row); bool isRunning() const; int rowCount(const QModelIndex&) const; int count() const; QVariant data(const QModelIndex&, int) const; public Q_SLOTS: void scheduleQuery(const QString &query); Q_SIGNALS: void queryChanged(); void countChanged(); void runnersChanged(); void runningChanged(bool running); private Q_SLOTS: void startQuery(); private: bool createManager(); private Q_SLOTS: void matchesChanged(const QList &matches); void queryHasFinished(); private: Plasma::RunnerManager *m_manager; QList m_matches; QStringList m_pendingRunnersList; QString m_singleRunnerId; QString m_pendingQuery; QTimer *m_startQueryTimer; QTimer *m_runningChangedTimeout; bool m_running; }; #endif diff --git a/src/declarative/runnermodelplugin.cpp b/src/declarative/runnermodelplugin.cpp index 3b466bf..33539a9 100644 --- a/src/declarative/runnermodelplugin.cpp +++ b/src/declarative/runnermodelplugin.cpp @@ -1,40 +1,40 @@ /* * Copyright 2011 by Marco Martin * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "runnermodelplugin.h" #include #include -#include +#include -#include "../core/runnermodel.h" +#include "runnermodel.h" void RunnerModelPlugin::registerTypes(const char *uri) { qWarning() << "Using deprecated import org.kde.runnermodel, please port to org.kde.plasma.core"; Q_ASSERT(uri == QLatin1String("org.kde.runnermodel")); qmlRegisterType(uri, 2, 0, "RunnerModel"); qmlRegisterInterface("QueryMatch"); qRegisterMetaType("QueryMatch"); } #include "runnermodelplugin.moc" diff --git a/src/private/abstractrunner_p.h b/src/private/abstractrunner_p.h index ada3222..5167bb4 100644 --- a/src/private/abstractrunner_p.h +++ b/src/private/abstractrunner_p.h @@ -1,61 +1,61 @@ /* * Copyright 2006-2009 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ABSTRACTRUNNER_P_H #define ABSTRACTRUNNER_P_H #include -#include "dataengineconsumer.h" +#include "plasma/dataengineconsumer.h" namespace Plasma { class AbstractRunner; class AbstractRunnerPrivate : public DataEngineConsumer { public: AbstractRunnerPrivate(AbstractRunner *r); ~AbstractRunnerPrivate(); void init(const KService::Ptr service); void init(const QString &path); void prepScripting(const QString &path, const QString &api); void setupScriptSupport(); AbstractRunner::Priority priority; AbstractRunner::Speed speed; RunnerContext::Types blackListed; RunnerScript *script; KPluginInfo runnerDescription; AbstractRunner *runner; int fastRuns; QReadWriteLock speedLock; Package *package; QHash actions; QList syntaxes; RunnerSyntax *defaultSyntax; bool hasRunOptions : 1; bool suspendMatching : 1; }; } // namespace Plasma #endif diff --git a/src/runnermanager.cpp b/src/runnermanager.cpp index 69f8ef4..af58849 100644 --- a/src/runnermanager.cpp +++ b/src/runnermanager.cpp @@ -1,801 +1,798 @@ /* * Copyright (C) 2006 Aaron Seigo * Copyright (C) 2007, 2009 Ryan P. Bitanga * Copyright (C) 2008 Jordi Polo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "runnermanager.h" -#include "config-plasma.h" - #include #include #include #include #include #include #include -#if !PLASMA_NO_SOLID #include #include -#endif #include #include #include #include "private/runnerjobs_p.h" -#include "pluginloader.h" +#include "plasma/pluginloader.h" +#include #include "querymatch.h" using ThreadWeaver::Queue; using ThreadWeaver::Job; //#define MEASURE_PREPTIME namespace Plasma { /***************************************************** * RunnerManager::Private class * *****************************************************/ class RunnerManagerPrivate { public: RunnerManagerPrivate(RunnerManager *parent) : q(parent), deferredRun(0), currentSingleRunner(0), prepped(false), allRunnersPrepped(false), singleRunnerPrepped(false), teardownRequested(false), singleMode(false), singleRunnerWasLoaded(false) { matchChangeTimer.setSingleShot(true); delayTimer.setSingleShot(true); QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged())); QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged())); QObject::connect(&delayTimer, SIGNAL(timeout()), q, SLOT(unblockJobs())); } ~RunnerManagerPrivate() { KConfigGroup config = configGroup(); context.save(config); } void scheduleMatchesChanged() { matchChangeTimer.start(100); } void matchesChanged() { emit q->matchesChanged(context.matches()); } void loadConfiguration() { KConfigGroup config = configGroup(); //The number of threads used scales with the number of processors. #if !PLASMA_NO_SOLID const int numProcs = qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1); #else const int numProcs = 1; #endif //This entry allows to define a hard upper limit independent of the number of processors. const int maxThreads = config.readEntry("maxThreads", 16); const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2)); //qDebug() << "setting up" << numThreads << "threads for" << numProcs << "processors"; if (numThreads > Queue::instance()->maximumNumberOfThreads()) { Queue::instance()->setMaximumNumberOfThreads(numThreads); } // Limit the number of instances of a single normal speed runner and all of the slow runners // to half the number of threads const int cap = qMax(2, numThreads/2); DefaultRunnerPolicy::instance().setCap(cap); context.restore(config); } KConfigGroup configGroup() { return conf.isValid() ? conf : KConfigGroup(KSharedConfig::openConfig(), "PlasmaRunnerManager"); } void clearSingleRunner() { if (singleRunnerWasLoaded) { delete currentSingleRunner; } currentSingleRunner = 0; } void loadSingleRunner() { if (!singleMode || singleModeRunnerId.isEmpty()) { clearSingleRunner(); return; } if (currentSingleRunner) { if (currentSingleRunner->id() == singleModeRunnerId) { return; } clearSingleRunner(); } AbstractRunner *loadedRunner = q->runner(singleModeRunnerId); if (loadedRunner) { singleRunnerWasLoaded = false; currentSingleRunner = loadedRunner; return; } KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner", QString("[X-KDE-PluginInfo-Name] == '%1'").arg(singleModeRunnerId)); if (!offers.isEmpty()) { const KService::Ptr &service = offers[0]; currentSingleRunner = loadInstalledRunner(service); if (currentSingleRunner) { emit currentSingleRunner->prepare(); singleRunnerWasLoaded = true; } } } void loadRunners() { KConfigGroup config = configGroup(); KPluginInfo::List offers = RunnerManager::listRunnerInfo(); const bool loadAll = config.readEntry("loadAll", false); const QStringList whiteList = config.readEntry("pluginWhiteList", QStringList()); const bool noWhiteList = whiteList.isEmpty(); KConfigGroup pluginConf; if (conf.isValid()) { pluginConf = KConfigGroup(&conf, "Plugins"); } else { pluginConf = KConfigGroup(KSharedConfig::openConfig(), "Plugins"); } advertiseSingleRunnerIds.clear(); QSet deadRunners; QMutableListIterator it(offers); while (it.hasNext()) { KPluginInfo &description = it.next(); //qDebug() << "Loading runner: " << service->name() << service->storageId(); QString tryExec = description.property("TryExec").toString(); //qDebug() << "TryExec is" << tryExec; if (!tryExec.isEmpty() && QStandardPaths::findExecutable(tryExec).isEmpty()) { // we don't actually have this application! continue; } const QString runnerName = description.pluginName(); description.load(pluginConf); const bool loaded = runners.contains(runnerName); const bool selected = loadAll || (description.isPluginEnabled() && (noWhiteList || whiteList.contains(runnerName))); const bool singleQueryModeEnabled = description.property("X-Plasma-AdvertiseSingleRunnerQueryMode").toBool(); if (singleQueryModeEnabled) { advertiseSingleRunnerIds.insert(runnerName, description.name()); } //qDebug() << loadAll << description.isPluginEnabled() << noWhiteList << whiteList.contains(runnerName); if (selected) { if (!loaded) { AbstractRunner *runner = loadInstalledRunner(description.service()); if (runner) { runners.insert(runnerName, runner); } } } else if (loaded) { //Remove runner deadRunners.insert(runners.take(runnerName)); #ifndef NDEBUG // qDebug() << "Removing runner: " << runnerName; #endif } } if (!deadRunners.isEmpty()) { QSet > deadJobs; auto it = searchJobs.begin(); while (it != searchJobs.end()) { auto &job = (*it); if (deadRunners.contains(job->runner())) { QObject::disconnect(job->decorator(), SIGNAL(done(ThreadWeaver::JobPointer)), q, SLOT(jobDone(ThreadWeaver::JobPointer))); it = searchJobs.erase(it); deadJobs.insert(job); } else { it++; } } it = oldSearchJobs.begin(); while (it != oldSearchJobs.end()) { auto &job = (*it); if (deadRunners.contains(job->runner())) { it = oldSearchJobs.erase(it); deadJobs.insert(job); } else { it++; } } if (deadJobs.isEmpty()) { qDeleteAll(deadRunners); } else { new DelayedJobCleaner(deadJobs, deadRunners); } } if (!singleRunnerWasLoaded) { // in case we deleted it up above clearSingleRunner(); } #ifndef NDEBUG // qDebug() << "All runners loaded, total:" << runners.count(); #endif } AbstractRunner *loadInstalledRunner(const KService::Ptr service) { if (!service) { return 0; } AbstractRunner *runner = PluginLoader::self()->loadRunner(service->property("X-KDE-PluginInfo-Name", QVariant::String).toString()); if (runner) { runner->setParent(q); } else { const QString api = service->property("X-Plasma-API").toString(); if (api.isEmpty()) { QVariantList args; args << service->storageId(); if (Plasma::isPluginVersionCompatible(KPluginLoader(*service).pluginVersion())) { QString error; runner = service->createInstance(q, args, &error); if (!runner) { #ifndef NDEBUG // qDebug() << "Failed to load runner:" << service->name() << ". error reported:" << error; #endif } } } else { //qDebug() << "got a script runner known as" << api; runner = new AbstractRunner(service, q); } } if (runner) { #ifndef NDEBUG // qDebug() << "================= loading runner:" << service->name() << "================="; #endif QObject::connect(runner, SIGNAL(matchingSuspended(bool)), q, SLOT(runnerMatchingSuspended(bool))); runner->init(); if (prepped) { emit runner->prepare(); } } return runner; } void jobDone(ThreadWeaver::JobPointer job) { auto runJob = job.dynamicCast(); if (!runJob) { return; } if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) { //qDebug() << "job actually done, running now **************"; QueryMatch tmpRun = deferredRun; deferredRun = QueryMatch(0); tmpRun.run(context); } searchJobs.remove(runJob); oldSearchJobs.remove(runJob); if (searchJobs.isEmpty() && context.matches().isEmpty()) { // we finished our run, and there are no valid matches, and so no // signal will have been sent out. so we need to emit the signal // ourselves here emit q->matchesChanged(context.matches()); } checkTearDown(); } void checkTearDown() { //qDebug() << prepped << teardownRequested << searchJobs.count() << oldSearchJobs.count(); if (!prepped || !teardownRequested) { return; } if (Queue::instance()->isIdle()) { searchJobs.clear(); oldSearchJobs.clear(); } if (searchJobs.isEmpty() && oldSearchJobs.isEmpty()) { if (allRunnersPrepped) { foreach (AbstractRunner *runner, runners) { emit runner->teardown(); } allRunnersPrepped = false; } if (singleRunnerPrepped) { if (currentSingleRunner) { emit currentSingleRunner->teardown(); } singleRunnerPrepped = false; } emit q->queryFinished(); prepped = false; teardownRequested = false; } } void unblockJobs() { if (searchJobs.isEmpty() && Queue::instance()->isIdle()) { oldSearchJobs.clear(); checkTearDown(); return; } Queue::instance()->reschedule(); } void runnerMatchingSuspended(bool suspended) { if (suspended || !prepped || teardownRequested) { return; } AbstractRunner *runner = qobject_cast(q->sender()); if (runner) { startJob(runner); } } void startJob(AbstractRunner *runner) { if ((runner->ignoredTypes() & context.type()) == 0) { QSharedPointer job(new FindMatchesJob(runner, &context, Queue::instance())); QObject::connect(job->decorator(), SIGNAL(done(ThreadWeaver::JobPointer)), q, SLOT(jobDone(ThreadWeaver::JobPointer))); if (runner->speed() == AbstractRunner::SlowSpeed) { job->setDelayTimer(&delayTimer); } Queue::instance()->enqueue(job); searchJobs.insert(job); } } // Delay in ms before slow runners are allowed to run static const int slowRunDelay = 400; RunnerManager *q; QueryMatch deferredRun; RunnerContext context; QTimer matchChangeTimer; QTimer delayTimer; // Timer to control when to run slow runners QHash runners; QHash advertiseSingleRunnerIds; AbstractRunner* currentSingleRunner; QSet > searchJobs; QSet > oldSearchJobs; KConfigGroup conf; QString singleModeRunnerId; bool loadAll : 1; bool prepped : 1; bool allRunnersPrepped : 1; bool singleRunnerPrepped : 1; bool teardownRequested : 1; bool singleMode : 1; bool singleRunnerWasLoaded : 1; }; /***************************************************** * RunnerManager::Public class * *****************************************************/ RunnerManager::RunnerManager(QObject *parent) : QObject(parent), d(new RunnerManagerPrivate(this)) { d->loadConfiguration(); //ThreadWeaver::setDebugLevel(true, 4); } RunnerManager::RunnerManager(KConfigGroup &c, QObject *parent) : QObject(parent), d(new RunnerManagerPrivate(this)) { // Should this be really needed? Maybe d->loadConfiguration(c) would make // more sense. d->conf = KConfigGroup(&c, "PlasmaRunnerManager"); d->loadConfiguration(); //ThreadWeaver::setDebugLevel(true, 4); } RunnerManager::~RunnerManager() { if (!qApp->closingDown() && (!d->searchJobs.isEmpty() || !d->oldSearchJobs.isEmpty())) { new DelayedJobCleaner(d->searchJobs + d->oldSearchJobs); } delete d; } void RunnerManager::reloadConfiguration() { d->loadConfiguration(); d->loadRunners(); } void RunnerManager::setAllowedRunners(const QStringList &runners) { KConfigGroup config = d->configGroup(); config.writeEntry("pluginWhiteList", runners); if (!d->runners.isEmpty()) { // this has been called with runners already created. so let's do an instant reload d->loadRunners(); } } QStringList RunnerManager::allowedRunners() const { KConfigGroup config = d->configGroup(); return config.readEntry("pluginWhiteList", QStringList()); } void RunnerManager::loadRunner(const KService::Ptr service) { KPluginInfo description(service); const QString runnerName = description.pluginName(); if (!runnerName.isEmpty() && !d->runners.contains(runnerName)) { AbstractRunner *runner = d->loadInstalledRunner(service); if (runner) { d->runners.insert(runnerName, runner); } } } void RunnerManager::loadRunner(const QString &path) { if (!d->runners.contains(path)) { AbstractRunner *runner = new AbstractRunner(this, path); connect(runner, SIGNAL(matchingSuspended(bool)), this, SLOT(runnerMatchingSuspended(bool))); d->runners.insert(path, runner); } } AbstractRunner* RunnerManager::runner(const QString &name) const { if (d->runners.isEmpty()) { d->loadRunners(); } return d->runners.value(name, 0); } AbstractRunner *RunnerManager::singleModeRunner() const { return d->currentSingleRunner; } void RunnerManager::setSingleModeRunnerId(const QString &id) { d->singleModeRunnerId = id; d->loadSingleRunner(); } QString RunnerManager::singleModeRunnerId() const { return d->singleModeRunnerId; } bool RunnerManager::singleMode() const { return d->singleMode; } void RunnerManager::setSingleMode(bool singleMode) { if (d->singleMode == singleMode) { return; } Plasma::AbstractRunner *prevSingleRunner = d->currentSingleRunner; d->singleMode = singleMode; d->loadSingleRunner(); d->singleMode = d->currentSingleRunner; if (prevSingleRunner != d->currentSingleRunner) { if (d->prepped) { matchSessionComplete(); if (d->singleMode) { setupMatchSession(); } } } } QList RunnerManager::runners() const { return d->runners.values(); } QStringList RunnerManager::singleModeAdvertisedRunnerIds() const { return d->advertiseSingleRunnerIds.keys(); } QString RunnerManager::runnerName(const QString &id) const { if (runner(id)) { return runner(id)->name(); } else { return d->advertiseSingleRunnerIds.value(id, QString()); } } RunnerContext* RunnerManager::searchContext() const { return &d->context; } //Reordering is here so data is not reordered till strictly needed QList RunnerManager::matches() const { return d->context.matches(); } void RunnerManager::run(const QString &matchId) { run(d->context.match(matchId)); } void RunnerManager::run(const QueryMatch &match) { if (!match.isEnabled()) { return; } //TODO: this function is not const as it may be used for learning AbstractRunner *runner = match.runner(); for (auto it = d->searchJobs.constBegin(); it != d->searchJobs.constEnd(); ++it) { if ((*it)->runner() == runner && !(*it)->isFinished()) { #ifndef NDEBUG // qDebug() << "deferred run"; #endif d->deferredRun = match; return; } } if (d->deferredRun.isValid()) { d->deferredRun = QueryMatch(0); } d->context.run(match); } QList RunnerManager::actionsForMatch(const QueryMatch &match) { AbstractRunner *runner = match.runner(); if (runner) { return runner->actionsForMatch(match); } return QList(); } QMimeData * RunnerManager::mimeDataForMatch(const QString &id) const { return mimeDataForMatch(d->context.match(id)); } QMimeData * RunnerManager::mimeDataForMatch(const QueryMatch &match) const { AbstractRunner *runner = match.runner(); if (runner) { return runner->mimeDataForMatch(match); } return 0; } KPluginInfo::List RunnerManager::listRunnerInfo(const QString &parentApp) { return PluginLoader::self()->listRunnerInfo(parentApp); } void RunnerManager::setupMatchSession() { d->teardownRequested = false; if (d->prepped) { return; } d->prepped = true; if (d->singleMode) { if (d->currentSingleRunner) { emit d->currentSingleRunner->prepare(); d->singleRunnerPrepped = true; } } else { foreach (AbstractRunner *runner, d->runners) { #ifdef MEASURE_PREPTIME QTime t; t.start(); #endif emit runner->prepare(); #ifdef MEASURE_PREPTIME #ifndef NDEBUG // qDebug() << t.elapsed() << runner->name(); #endif #endif } d->allRunnersPrepped = true; } } void RunnerManager::matchSessionComplete() { if (!d->prepped) { return; } d->teardownRequested = true; d->checkTearDown(); } void RunnerManager::launchQuery(const QString &term) { launchQuery(term, QString()); } void RunnerManager::launchQuery(const QString &untrimmedTerm, const QString &runnerName) { setupMatchSession(); QString term = untrimmedTerm.trimmed(); setSingleModeRunnerId(runnerName); setSingleMode(d->currentSingleRunner); if (!runnerName.isEmpty() && !d->currentSingleRunner) { reset(); return; } if (term.isEmpty()) { if (d->singleMode && d->currentSingleRunner->defaultSyntax()) { term = d->currentSingleRunner->defaultSyntax()->exampleQueries().first().remove(QRegExp(":q:")); } else { reset(); return; } } if (d->context.query() == term) { // we already are searching for this! return; } if (!d->singleMode && d->runners.isEmpty()) { d->loadRunners(); } reset(); // qDebug() << "runners searching for" << term << "on" << runnerName; d->context.setQuery(term); QHash runable; //if the name is not empty we will launch only the specified runner if (d->singleMode) { runable.insert(QString(), d->currentSingleRunner); d->context.setSingleRunnerQueryMode(true); } else { runable = d->runners; } foreach (Plasma::AbstractRunner *r, runable) { if (r->isMatchingSuspended()) { continue; } d->startJob(r); } // Start timer to unblock slow runners d->delayTimer.start(RunnerManagerPrivate::slowRunDelay); } QString RunnerManager::query() const { return d->context.query(); } void RunnerManager::reset() { // If ThreadWeaver is idle, it is safe to clear previous jobs if (Queue::instance()->isIdle()) { d->oldSearchJobs.clear(); } else { for (auto it = d->searchJobs.constBegin(); it != d->searchJobs.constEnd(); ++it) { Queue::instance()->dequeue((*it)); } d->oldSearchJobs += d->searchJobs; } d->searchJobs.clear(); if (d->deferredRun.isEnabled()) { //qDebug() << "job actually done, running now **************"; QueryMatch tmpRun = d->deferredRun; d->deferredRun = QueryMatch(0); tmpRun.run(d->context); } d->context.reset(); } } // Plasma namespace #include "moc_runnermanager.cpp"