24#include "config-plasma.h"
28#include <QCoreApplication>
31#include <kplugininfo.h>
32#include <kservicetypetrader.h>
33#include <kstandarddirs.h>
35#ifndef PLASMA_NO_SOLID
36#include <solid/device.h>
37#include <solid/deviceinterface.h>
40#include <Weaver/DebuggingAids.h>
41#include <Weaver/State.h>
42#include <Weaver/Thread.h>
43#include <Weaver/ThreadWeaver.h>
45#include "private/runnerjobs_p.h"
49using ThreadWeaver::Weaver;
50using ThreadWeaver::Job;
61class RunnerManagerPrivate
65 RunnerManagerPrivate(RunnerManager *parent)
68 currentSingleRunner(0),
70 allRunnersPrepped(false),
71 singleRunnerPrepped(false),
72 teardownRequested(false),
74 singleRunnerWasLoaded(false)
76 matchChangeTimer.setSingleShot(
true);
77 delayTimer.setSingleShot(
true);
79 QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged()));
80 QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged()));
81 QObject::connect(&delayTimer, SIGNAL(timeout()), q, SLOT(unblockJobs()));
84 ~RunnerManagerPrivate()
86 KConfigGroup config = configGroup();
90 void scheduleMatchesChanged()
92 matchChangeTimer.start(100);
97 emit q->matchesChanged(context.matches());
100 void loadConfiguration()
102 KConfigGroup config = configGroup();
105#ifndef PLASMA_NO_SOLID
107 qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
109 const int numProcs = 1;
112 const int maxThreads = config.readEntry(
"maxThreads", 16);
113 const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
115 if (numThreads > Weaver::instance()->maximumNumberOfThreads()) {
116 Weaver::instance()->setMaximumNumberOfThreads(numThreads);
120 const int cap = qMax(2, numThreads/2);
121 DefaultRunnerPolicy::instance().setCap(cap);
123 context.restore(config);
126 KConfigGroup configGroup()
128 return conf.isValid() ? conf : KConfigGroup(KGlobal::config(),
"PlasmaRunnerManager");
131 void clearSingleRunner()
133 if (singleRunnerWasLoaded) {
134 delete currentSingleRunner;
137 currentSingleRunner = 0;
140 void loadSingleRunner()
142 if (!singleMode || singleModeRunnerId.isEmpty()) {
147 if (currentSingleRunner) {
148 if (currentSingleRunner->id() == singleModeRunnerId) {
155 AbstractRunner *loadedRunner = q->runner(singleModeRunnerId);
157 singleRunnerWasLoaded =
false;
158 currentSingleRunner = loadedRunner;
162 KService::List offers = KServiceTypeTrader::self()->query(
"Plasma/Runner", QString(
"[X-KDE-PluginInfo-Name] == '%1'").arg(singleModeRunnerId));
163 if (!offers.isEmpty()) {
164 const KService::Ptr &service = offers[0];
165 currentSingleRunner = loadInstalledRunner(service);
167 if (currentSingleRunner) {
168 emit currentSingleRunner->prepare();
169 singleRunnerWasLoaded =
true;
176 KConfigGroup config = configGroup();
179 const bool loadAll = config.readEntry(
"loadAll",
false);
180 const QStringList whiteList = config.readEntry(
"pluginWhiteList", QStringList());
181 const bool noWhiteList = whiteList.isEmpty();
182 KConfigGroup pluginConf;
183 if (conf.isValid()) {
184 pluginConf = KConfigGroup(&conf,
"Plugins");
186 pluginConf = KConfigGroup(KGlobal::config(),
"Plugins");
189 advertiseSingleRunnerIds.clear();
191 QSet<AbstractRunner *> deadRunners;
192 QMutableListIterator<KPluginInfo> it(offers);
193 while (it.hasNext()) {
194 KPluginInfo &description = it.next();
196 QString tryExec = description.property(
"TryExec").toString();
198 if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
203 const QString runnerName = description.pluginName();
204 description.load(pluginConf);
206 const bool loaded = runners.contains(runnerName);
207 const bool selected = loadAll || (description.isPluginEnabled() && (noWhiteList || whiteList.contains(runnerName)));
209 const bool singleQueryModeEnabled = description.property(
"X-Plasma-AdvertiseSingleRunnerQueryMode").toBool();
211 if (singleQueryModeEnabled) {
212 advertiseSingleRunnerIds.insert(runnerName, description.name());
218 AbstractRunner *runner = loadInstalledRunner(description.service());
221 runners.insert(runnerName, runner);
226 deadRunners.insert(runners.take(runnerName));
227 kDebug() <<
"Removing runner: " << runnerName;
231 if (!deadRunners.isEmpty()) {
232 QSet<FindMatchesJob *> deadJobs;
233 foreach (FindMatchesJob *job, searchJobs) {
234 if (deadRunners.contains(job->runner())) {
235 QObject::disconnect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
236 searchJobs.remove(job);
237 deadJobs.insert(job);
241 foreach (FindMatchesJob *job, oldSearchJobs) {
242 if (deadRunners.contains(job->runner())) {
243 oldSearchJobs.remove(job);
244 deadJobs.insert(job);
248 if (deadJobs.isEmpty()) {
249 qDeleteAll(deadRunners);
251 new DelayedJobCleaner(deadJobs, deadRunners);
255 if (!singleRunnerWasLoaded) {
260 kDebug() <<
"All runners loaded, total:" << runners.count();
263 AbstractRunner *loadInstalledRunner(
const KService::Ptr service)
272 runner->setParent(q);
274 const QString api = service->property(
"X-Plasma-API").toString();
278 args << service->storageId();
281 runner = service->createInstance<AbstractRunner>(q, args, &error);
283 kDebug() <<
"Failed to load runner:" << service->
name() <<
". error reported:" << error;
288 runner =
new AbstractRunner(service, q);
293 kDebug() <<
"================= loading runner:" << service->name() <<
"=================";
294 QObject::connect(runner, SIGNAL(matchingSuspended(
bool)), q, SLOT(runnerMatchingSuspended(
bool)));
295 QMetaObject::invokeMethod(runner,
"init");
297 emit runner->prepare();
304 void jobDone(ThreadWeaver::Job *job)
306 FindMatchesJob *runJob =
dynamic_cast<FindMatchesJob *
>(job);
312 if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
314 QueryMatch tmpRun = deferredRun;
315 deferredRun = QueryMatch(0);
319 searchJobs.remove(runJob);
320 oldSearchJobs.remove(runJob);
321 runJob->deleteLater();
323 if (searchJobs.isEmpty() && context.matches().isEmpty()) {
327 emit q->matchesChanged(context.matches());
337 if (!prepped || !teardownRequested) {
341 if (Weaver::instance()->isIdle()) {
342 qDeleteAll(searchJobs);
344 qDeleteAll(oldSearchJobs);
345 oldSearchJobs.clear();
348 if (searchJobs.isEmpty() && oldSearchJobs.isEmpty()) {
349 if (allRunnersPrepped) {
350 foreach (AbstractRunner *runner, runners) {
351 emit runner->teardown();
354 allRunnersPrepped =
false;
357 if (singleRunnerPrepped) {
358 if (currentSingleRunner) {
359 emit currentSingleRunner->teardown();
362 singleRunnerPrepped =
false;
365 emit q->queryFinished();
368 teardownRequested =
false;
375 if (searchJobs.isEmpty() && Weaver::instance()->isIdle()) {
376 qDeleteAll(oldSearchJobs);
377 oldSearchJobs.clear();
382 DummyJob *dummy =
new DummyJob(q);
383 Weaver::instance()->enqueue(dummy);
384 QObject::connect(dummy, SIGNAL(done(ThreadWeaver::Job*)), dummy, SLOT(deleteLater()));
387 void runnerMatchingSuspended(
bool suspended)
389 if (suspended || !prepped || teardownRequested) {
393 AbstractRunner *runner = qobject_cast<AbstractRunner *>(q->sender());
400 void startJob(AbstractRunner *runner)
402 if ((runner->ignoredTypes() & context.type()) == 0) {
403 FindMatchesJob *job =
new FindMatchesJob(runner, &context, Weaver::instance());
404 QObject::connect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
406 job->setDelayTimer(&delayTimer);
408 Weaver::instance()->enqueue(job);
409 searchJobs.insert(job);
414 static const int slowRunDelay = 400;
417 QueryMatch deferredRun;
418 RunnerContext context;
419 QTimer matchChangeTimer;
421 QHash<QString, AbstractRunner*> runners;
422 QHash<QString, QString> advertiseSingleRunnerIds;
423 AbstractRunner* currentSingleRunner;
424 QSet<FindMatchesJob*> searchJobs;
425 QSet<FindMatchesJob*> oldSearchJobs;
427 QString singleModeRunnerId;
430 bool allRunnersPrepped : 1;
431 bool singleRunnerPrepped : 1;
432 bool teardownRequested : 1;
434 bool singleRunnerWasLoaded : 1;
443 d(new RunnerManagerPrivate(this))
445 d->loadConfiguration();
449RunnerManager::RunnerManager(KConfigGroup &c,
QObject *parent)
451 d(new RunnerManagerPrivate(this))
455 d->conf = KConfigGroup(&c,
"PlasmaRunnerManager");
456 d->loadConfiguration();
460RunnerManager::~RunnerManager()
462 if (!qApp->closingDown() && (!d->searchJobs.isEmpty() || !d->oldSearchJobs.isEmpty())) {
463 new DelayedJobCleaner(d->searchJobs + d->oldSearchJobs);
469void RunnerManager::reloadConfiguration()
471 d->loadConfiguration();
475void RunnerManager::setAllowedRunners(
const QStringList &runners)
477 KConfigGroup config = d->configGroup();
478 config.writeEntry(
"pluginWhiteList",
runners);
480 if (!d->runners.isEmpty()) {
486QStringList RunnerManager::allowedRunners()
const
488 KConfigGroup config = d->configGroup();
489 return config.readEntry(
"pluginWhiteList", QStringList());
492void RunnerManager::loadRunner(
const KService::Ptr service)
494 KPluginInfo description(service);
495 const QString
runnerName = description.pluginName();
504void RunnerManager::loadRunner(
const QString &path)
506 if (!d->runners.contains(path)) {
508 connect(
runner, SIGNAL(matchingSuspended(
bool)),
this, SLOT(runnerMatchingSuspended(
bool)));
509 d->runners.insert(path,
runner);
515 if (d->runners.isEmpty()) {
519 return d->runners.value(name, 0);
524 return d->currentSingleRunner;
527void RunnerManager::setSingleModeRunnerId(
const QString &
id)
529 d->singleModeRunnerId = id;
530 d->loadSingleRunner();
533QString RunnerManager::singleModeRunnerId()
const
535 return d->singleModeRunnerId;
538bool RunnerManager::singleMode()
const
540 return d->singleMode;
543void RunnerManager::setSingleMode(
bool singleMode)
552 d->loadSingleRunner();
553 d->singleMode = d->currentSingleRunner;
555 if (prevSingleRunner != d->currentSingleRunner) {
566QList<AbstractRunner *> RunnerManager::runners()
const
568 return d->runners.values();
571QStringList RunnerManager::singleModeAdvertisedRunnerIds()
const
573 return d->advertiseSingleRunnerIds.keys();
576QString RunnerManager::runnerName(
const QString &
id)
const
581 return d->advertiseSingleRunnerIds.value(
id, QString());
591QList<QueryMatch> RunnerManager::matches()
const
593 return d->context.matches();
596void RunnerManager::run(
const QString &
id)
598 run(d->context.match(
id));
610 foreach (FindMatchesJob *job, d->searchJobs) {
611 if (job->runner() ==
runner && !job->isFinished()) {
612 kDebug() <<
"deferred run";
613 d->deferredRun = match;
618 if (d->deferredRun.isValid()) {
622 d->context.run(match);
625QList<QAction*> RunnerManager::actionsForMatch(
const QueryMatch &match)
632 return QList<QAction*>();
635QMimeData * RunnerManager::mimeDataForMatch(
const QString &
id)
const
641QMimeData * RunnerManager::mimeDataForMatch(
const QueryMatch &match)
const
645 if (
runner && QMetaObject::invokeMethod(
647 "mimeDataForMatch", Qt::DirectConnection,
648 Q_RETURN_ARG(QMimeData*, mimeData),
657KPluginInfo::List RunnerManager::listRunnerInfo(
const QString &parentApp)
662void RunnerManager::setupMatchSession()
664 d->teardownRequested =
false;
672 if (d->currentSingleRunner) {
673 emit d->currentSingleRunner->prepare();
674 d->singleRunnerPrepped =
true;
678#ifdef MEASURE_PREPTIME
683#ifdef MEASURE_PREPTIME
688 d->allRunnersPrepped =
true;
692void RunnerManager::matchSessionComplete()
698 d->teardownRequested =
true;
702void RunnerManager::launchQuery(
const QString &term)
707void RunnerManager::launchQuery(
const QString &untrimmedTerm,
const QString &runnerName)
710 QString term = untrimmedTerm.trimmed();
715 if (term.isEmpty()) {
716 if (d->singleMode && d->currentSingleRunner && d->currentSingleRunner->defaultSyntax()) {
717 term = d->currentSingleRunner->defaultSyntax()->exampleQueries().first().remove(QRegExp(
":q:"));
724 if (d->context.query() == term) {
729 if (d->singleMode && !d->currentSingleRunner) {
734 if (d->runners.isEmpty()) {
740 d->context.setQuery(term);
742 QHash<QString, AbstractRunner*> runable;
745 if (d->singleMode && d->currentSingleRunner) {
746 runable.insert(QString(), d->currentSingleRunner);
747 d->context.setSingleRunnerQueryMode(
true);
749 runable = d->runners;
761 d->delayTimer.start(RunnerManagerPrivate::slowRunDelay);
764bool RunnerManager::execQuery(
const QString &term)
769bool RunnerManager::execQuery(
const QString &untrimmedTerm,
const QString &runnerName)
771 QString term = untrimmedTerm.trimmed();
773 if (term.isEmpty()) {
778 if (d->runners.isEmpty()) {
782 if (d->context.query() == term) {
790 d->context.setQuery(term);
809QString RunnerManager::query()
const
811 return d->context.query();
814void RunnerManager::reset()
817 if (Weaver::instance()->isIdle()) {
818 qDeleteAll(d->searchJobs);
819 qDeleteAll(d->oldSearchJobs);
820 d->oldSearchJobs.clear();
822 Q_FOREACH(FindMatchesJob *job, d->searchJobs) {
823 Weaver::instance()->dequeue(job);
825 d->oldSearchJobs += d->searchJobs;
828 d->searchJobs.clear();
830 if (d->deferredRun.isEnabled()) {
834 tmpRun.
run(d->context);
842#include "runnermanager.moc"
An abstract base class for Plasma Runner plugins.
RunnerContext::Types ignoredTypes() const
Returns the OR'ed value of all the Information types (as defined in RunnerContext::Type) this runner ...
void prepare()
This signal is emitted when matching is about to commence, giving runners an opportunity to prepare t...
virtual QList< QAction * > actionsForMatch(const Plasma::QueryMatch &match)
A given match can have more than action that can be performed on it.
void performMatch(Plasma::RunnerContext &context)
Triggers a call to match.
bool isMatchingSuspended() const
KPluginInfo::List listRunnerInfo(const QString &parentApp=QString())
Returns a list of all known Runner implementations.
static PluginLoader * pluginLoader()
Return the active plugin loader.
AbstractRunner * loadRunner(const QString &name)
Load a Runner plugin.
A match returned by an AbstractRunner in response to a given RunnerContext.
void run(const RunnerContext &context) const
Requests this match to activae using the given context.
AbstractRunner * runner() const
The RunnerContext class provides information related to a search, including the search term,...
void setSingleModeRunnerId(const QString &id)
Puts the manager into "single runner" mode using the given runner; if the runner does not exist or ca...
void matchesChanged(const QList< Plasma::QueryMatch > &matches)
Emitted each time a new match is added to the list.
void run(const QueryMatch &match)
Runs a given match.
QList< AbstractRunner * > runners() const
void matchSessionComplete()
Call this method when the query session is finished for the time being.
bool execQuery(const QString &term, const QString &runnerName)
Execute a query, this method will only return when the query is executed This means that the method m...
AbstractRunner * runner(const QString &name) const
Finds and returns a loaded runner or NULL.
QString runnerName(const QString &id) const
Returns the translated name of a runner.
void reset()
Reset the current data and stops the query.
static KPluginInfo::List listRunnerInfo(const QString &parentApp=QString())
Returns a list of all known Runner implementations.
QMimeData * mimeDataForMatch(const QueryMatch &match) const
void setupMatchSession()
Call this method when the runners should be prepared for a query session.
void launchQuery(const QString &term, const QString &runnerId)
Launch a query, this will create threads and return inmediately.
void setSingleMode(bool singleMode)
Sets whether or not the manager is in single mode.
Namespace for everything in libplasma.
bool isPluginVersionCompatible(unsigned int version)
Verifies that a plugin is compatible with plasma.