Plasma
abstractrunner.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2006-2007 Aaron Seigo <aseigo@kde.org> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU Library General Public License as 00006 * published by the Free Software Foundation; either version 2, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details 00013 * 00014 * You should have received a copy of the GNU Library General Public 00015 * License along with this program; if not, write to the 00016 * Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "abstractrunner.h" 00021 00022 #include <QAction> 00023 #include <QHash> 00024 #include <QMenu> 00025 #include <QMimeData> 00026 #include <QMutex> 00027 #include <QMutexLocker> 00028 #include <QTimer> 00029 00030 #include <kdebug.h> 00031 #include <kicon.h> 00032 #include <kplugininfo.h> 00033 #include <kservicetypetrader.h> 00034 #include <kstandarddirs.h> 00035 00036 #include <plasma/package.h> 00037 #include <plasma/querymatch.h> 00038 00039 #include "private/abstractrunner_p.h" 00040 #include "runnercontext.h" 00041 #include "scripting/runnerscript.h" 00042 00043 namespace Plasma 00044 { 00045 00046 K_GLOBAL_STATIC(QMutex, s_bigLock) 00047 00048 AbstractRunner::AbstractRunner(QObject *parent, const QString &path) 00049 : QObject(parent), 00050 d(new AbstractRunnerPrivate(this)) 00051 { 00052 d->init(path); 00053 } 00054 00055 AbstractRunner::AbstractRunner(const KService::Ptr service, QObject *parent) 00056 : QObject(parent), 00057 d(new AbstractRunnerPrivate(this)) 00058 { 00059 d->init(service); 00060 } 00061 00062 AbstractRunner::AbstractRunner(QObject *parent, const QVariantList &args) 00063 : QObject(parent), 00064 d(new AbstractRunnerPrivate(this)) 00065 { 00066 if (args.count() > 0) { 00067 KService::Ptr service = KService::serviceByStorageId(args[0].toString()); 00068 if (service) { 00069 d->init(service); 00070 } 00071 } 00072 } 00073 00074 AbstractRunner::~AbstractRunner() 00075 { 00076 delete d; 00077 } 00078 00079 KConfigGroup AbstractRunner::config() const 00080 { 00081 QString group = objectName(); 00082 if (group.isEmpty()) { 00083 group = "UnnamedRunner"; 00084 } 00085 00086 KConfigGroup runners(KGlobal::config(), "Runners"); 00087 return KConfigGroup(&runners, group); 00088 } 00089 00090 void AbstractRunner::reloadConfiguration() 00091 { 00092 if (d->script) { 00093 emit d->script->reloadConfiguration(); 00094 } 00095 } 00096 00097 void AbstractRunner::addSyntax(const RunnerSyntax &syntax) 00098 { 00099 d->syntaxes.append(syntax); 00100 } 00101 00102 void AbstractRunner::setDefaultSyntax(const RunnerSyntax &syntax) 00103 { 00104 d->syntaxes.append(syntax); 00105 d->defaultSyntax = &(d->syntaxes.last()); 00106 } 00107 00108 void AbstractRunner::setSyntaxes(const QList<RunnerSyntax> &syntaxes) 00109 { 00110 d->syntaxes = syntaxes; 00111 } 00112 00113 QList<RunnerSyntax> AbstractRunner::syntaxes() const 00114 { 00115 return d->syntaxes; 00116 } 00117 00118 RunnerSyntax *AbstractRunner::defaultSyntax() const 00119 { 00120 return d->defaultSyntax; 00121 } 00122 00123 void AbstractRunner::performMatch(Plasma::RunnerContext &localContext) 00124 { 00125 static const int reasonableRunTime = 1500; 00126 static const int fastEnoughTime = 250; 00127 00128 if (d->suspendMatching) { 00129 return; 00130 } 00131 00132 QTime time; 00133 time.restart(); 00134 00135 //The local copy is already obtained in the job 00136 match(localContext); 00137 00138 // automatically rate limit runners that become slooow 00139 const int runtime = time.elapsed(); 00140 bool slowed = speed() == SlowSpeed; 00141 00142 if (!slowed && runtime > reasonableRunTime) { 00143 // we punish runners that return too slowly, even if they don't bring 00144 // back matches 00145 kDebug() << id() << "runner is too slow, putting it on the back burner."; 00146 d->fastRuns = 0; 00147 setSpeed(SlowSpeed); 00148 } 00149 00150 if (slowed && runtime < fastEnoughTime && localContext.query().size() > 2) { 00151 ++d->fastRuns; 00152 00153 if (d->fastRuns > 2) { 00154 // we reward slowed runners who bring back matches fast enough 00155 // 3 times in a row 00156 kDebug() << id() << "runner is faster than we thought, kicking it up a notch"; 00157 setSpeed(NormalSpeed); 00158 } 00159 } 00160 } 00161 00162 QList<QAction*> AbstractRunner::actionsForMatch(const Plasma::QueryMatch &match) 00163 { 00164 Q_UNUSED(match) 00165 QList<QAction*> ret; 00166 if (d->script) { 00167 emit d->script->actionsForMatch(match, &ret); 00168 } 00169 return ret; 00170 } 00171 00172 QAction* AbstractRunner::addAction(const QString &id, const QIcon &icon, const QString &text) 00173 { 00174 QAction *a = new QAction(icon, text, this); 00175 d->actions.insert(id, a); 00176 return a; 00177 } 00178 00179 void AbstractRunner::addAction(const QString &id, QAction *action) 00180 { 00181 d->actions.insert(id, action); 00182 } 00183 00184 void AbstractRunner::removeAction(const QString &id) 00185 { 00186 QAction *a = d->actions.take(id); 00187 delete a; 00188 } 00189 00190 QAction* AbstractRunner::action(const QString &id) const 00191 { 00192 return d->actions.value(id); 00193 } 00194 00195 QHash<QString, QAction*> AbstractRunner::actions() const 00196 { 00197 return d->actions; 00198 } 00199 00200 void AbstractRunner::clearActions() 00201 { 00202 qDeleteAll(d->actions); 00203 d->actions.clear(); 00204 } 00205 00206 QMimeData * AbstractRunner::mimeDataForMatch(const QueryMatch *match) 00207 { 00208 Q_UNUSED(match) 00209 return 0; 00210 } 00211 00212 bool AbstractRunner::hasRunOptions() 00213 { 00214 return d->hasRunOptions; 00215 } 00216 00217 void AbstractRunner::setHasRunOptions(bool hasRunOptions) 00218 { 00219 d->hasRunOptions = hasRunOptions; 00220 } 00221 00222 void AbstractRunner::createRunOptions(QWidget *parent) 00223 { 00224 if (d->script) { 00225 emit d->script->createRunOptions(parent); 00226 } 00227 } 00228 00229 AbstractRunner::Speed AbstractRunner::speed() const 00230 { 00231 // the only time the read lock will fail is if we were slow are going to speed up 00232 // or if we were fast and are going to slow down; so don't wait in this case, just 00233 // say we're slow. we either will be soon or were just a moment ago and it doesn't 00234 // hurt to do one more run the slow way 00235 if (!d->speedLock.tryLockForRead()) { 00236 return SlowSpeed; 00237 } 00238 Speed s = d->speed; 00239 d->speedLock.unlock(); 00240 return s; 00241 } 00242 00243 void AbstractRunner::setSpeed(Speed speed) 00244 { 00245 d->speedLock.lockForWrite(); 00246 d->speed = speed; 00247 d->speedLock.unlock(); 00248 } 00249 00250 AbstractRunner::Priority AbstractRunner::priority() const 00251 { 00252 return d->priority; 00253 } 00254 00255 void AbstractRunner::setPriority(Priority priority) 00256 { 00257 d->priority = priority; 00258 } 00259 00260 RunnerContext::Types AbstractRunner::ignoredTypes() const 00261 { 00262 return d->blackListed; 00263 } 00264 00265 void AbstractRunner::setIgnoredTypes(RunnerContext::Types types) 00266 { 00267 d->blackListed = types; 00268 } 00269 00270 KService::List AbstractRunner::serviceQuery(const QString &serviceType, const QString &constraint) const 00271 { 00272 return KServiceTypeTrader::self()->query(serviceType, constraint); 00273 } 00274 00275 QMutex* AbstractRunner::bigLock() 00276 { 00277 return s_bigLock; 00278 } 00279 00280 void AbstractRunner::run(const Plasma::RunnerContext &search, const Plasma::QueryMatch &action) 00281 { 00282 if (d->script) { 00283 return d->script->run(search, action); 00284 } 00285 } 00286 00287 void AbstractRunner::match(Plasma::RunnerContext &search) 00288 { 00289 if (d->script) { 00290 return d->script->match(search); 00291 } 00292 } 00293 00294 QString AbstractRunner::name() const 00295 { 00296 if (d->runnerDescription.isValid()) { 00297 return d->runnerDescription.name(); 00298 } 00299 00300 if (d->package) { 00301 return d->package->metadata().name(); 00302 } 00303 00304 return objectName(); 00305 } 00306 00307 QIcon AbstractRunner::icon() const 00308 { 00309 if (d->runnerDescription.isValid()) { 00310 return KIcon(d->runnerDescription.icon()); 00311 } 00312 00313 if (d->package) { 00314 return KIcon(d->package->metadata().icon()); 00315 } 00316 00317 return QIcon(); 00318 } 00319 00320 QString AbstractRunner::id() const 00321 { 00322 if (d->runnerDescription.isValid()) { 00323 return d->runnerDescription.pluginName(); 00324 } 00325 00326 if (d->package) { 00327 return d->package->metadata().pluginName(); 00328 } 00329 00330 return objectName(); 00331 } 00332 00333 QString AbstractRunner::description() const 00334 { 00335 if (d->runnerDescription.isValid()) { 00336 return d->runnerDescription.property("Comment").toString(); 00337 } 00338 00339 if (d->package) { 00340 return d->package->metadata().description(); 00341 } 00342 00343 return objectName(); 00344 } 00345 00346 const Package* AbstractRunner::package() const 00347 { 00348 return d->package; 00349 } 00350 00351 00352 void AbstractRunner::init() 00353 { 00354 if (d->script) { 00355 d->setupScriptSupport(); 00356 d->script->init(); 00357 } 00358 00359 reloadConfiguration(); 00360 } 00361 00362 DataEngine *AbstractRunner::dataEngine(const QString &name) const 00363 { 00364 return d->dataEngine(name); 00365 } 00366 00367 bool AbstractRunner::isMatchingSuspended() const 00368 { 00369 return d->suspendMatching; 00370 } 00371 00372 void AbstractRunner::suspendMatching(bool suspend) 00373 { 00374 if (d->suspendMatching == suspend) { 00375 return; 00376 } 00377 00378 d->suspendMatching = suspend; 00379 emit matchingSuspended(suspend); 00380 } 00381 00382 AbstractRunnerPrivate::AbstractRunnerPrivate(AbstractRunner *r) 00383 : priority(AbstractRunner::NormalPriority), 00384 speed(AbstractRunner::NormalSpeed), 00385 blackListed(0), 00386 script(0), 00387 runner(r), 00388 fastRuns(0), 00389 package(0), 00390 defaultSyntax(0), 00391 hasRunOptions(false), 00392 suspendMatching(false) 00393 { 00394 } 00395 00396 AbstractRunnerPrivate::~AbstractRunnerPrivate() 00397 { 00398 delete script; 00399 script = 0; 00400 delete package; 00401 package = 0; 00402 } 00403 00404 void AbstractRunnerPrivate::init(const KService::Ptr service) 00405 { 00406 runnerDescription = KPluginInfo(service); 00407 if (runnerDescription.isValid()) { 00408 const QString api = runnerDescription.property("X-Plasma-API").toString(); 00409 if (!api.isEmpty()) { 00410 const QString path = KStandardDirs::locate("data", "plasma/runners/" + runnerDescription.pluginName() + '/'); 00411 prepScripting(path, api); 00412 if (!script) { 00413 kDebug() << "Could not create a(n)" << api << "ScriptEngine for the" << runnerDescription.name() << "Runner."; 00414 } 00415 } 00416 } 00417 } 00418 00419 void AbstractRunnerPrivate::init(const QString &path) 00420 { 00421 prepScripting(path); 00422 } 00423 00424 void AbstractRunnerPrivate::prepScripting(const QString &path, QString api) 00425 { 00426 if (script) { 00427 return; 00428 } 00429 00430 delete package; 00431 00432 PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::RunnerComponent); 00433 structure->setPath(path); 00434 package = new Package(path, structure); 00435 00436 if (!package->isValid()) { 00437 kDebug() << "Invalid Runner package at" << path; 00438 delete package; 00439 package = 0; 00440 return; 00441 } 00442 00443 if (api.isEmpty()) { 00444 api = package->metadata().implementationApi(); 00445 } 00446 00447 script = Plasma::loadScriptEngine(api, runner); 00448 if (!script) { 00449 delete package; 00450 package = 0; 00451 } 00452 } 00453 00454 // put all setup routines for script here. at this point we can assume that 00455 // package exists and that we have a script engine 00456 void AbstractRunnerPrivate::setupScriptSupport() 00457 { 00458 if (!package) { 00459 return; 00460 } 00461 00462 kDebug() << "setting up script support, package is in" << package->path() 00463 << "which is a" << package->structure()->type() << "package" 00464 << ", main script is" << package->filePath("mainscript"); 00465 00466 QString translationsPath = package->filePath("translations"); 00467 if (!translationsPath.isEmpty()) { 00468 //FIXME: we should _probably_ use a KComponentData to segregate the applets 00469 // from each other; but I want to get the basics working first :) 00470 KGlobal::dirs()->addResourceDir("locale", translationsPath); 00471 KGlobal::locale()->insertCatalog(package->metadata().pluginName()); 00472 } 00473 } 00474 00475 } // Plasma namespace 00476 00477 #include "abstractrunner.moc"
KDE 4.6 API Reference