Plasma
runnercontext.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "runnercontext.h"
00021
00022 #include <QReadWriteLock>
00023
00024 #include <QDir>
00025 #include <QFile>
00026 #include <QFileInfo>
00027 #include <QSharedData>
00028
00029 #include <kcompletion.h>
00030 #include <kconfiggroup.h>
00031 #include <kdebug.h>
00032 #include <kmimetype.h>
00033 #include <kshell.h>
00034 #include <kstandarddirs.h>
00035 #include <kurl.h>
00036
00037 #include "abstractrunner.h"
00038 #include "querymatch.h"
00039
00040
00041
00042
00043
00044 #define LOCK_FOR_READ(context) context->d->lock.lockForRead();
00045 #define LOCK_FOR_WRITE(context) context->d->lock.lockForWrite();
00046 #define UNLOCK(context) context->d->lock.unlock();
00047
00048 namespace Plasma
00049 {
00050
00051
00052
00053
00054
00055
00056
00057
00058 bool correctLastComponentCase(const QString &path, QString &correctCasePath, const bool mustBeDir)
00059 {
00060
00061
00062
00063 if (QFile::exists(path)) {
00064 correctCasePath = path;
00065
00066 return true;
00067 }
00068
00069 const QFileInfo pathInfo(path);
00070
00071 const QDir fileDir = pathInfo.dir();
00072
00073
00074 const QString filename = pathInfo.fileName();
00075
00076
00077
00078
00079 const QStringList matchingFilenames = fileDir.entryList(QStringList(filename),
00080 mustBeDir ? QDir::Dirs : QDir::NoFilter);
00081
00082 if (matchingFilenames.empty()) {
00083
00084 return false;
00085 } else {
00086
00087
00088
00089
00090 if (fileDir.path().endsWith(QDir::separator())) {
00091 correctCasePath = fileDir.path() + matchingFilenames[0];
00092 } else {
00093 correctCasePath = fileDir.path() + QDir::separator() + matchingFilenames[0];
00094 }
00095
00096
00097 return true;
00098 }
00099 }
00100
00101
00102
00103
00104
00105
00106
00107 bool correctPathCase(const QString& path, QString &corrected)
00108 {
00109
00110 if (QFile::exists(path)) {
00111 corrected = path;
00112 return true;
00113 }
00114
00115
00116 QStringList components = QString(path).split(QDir::separator());
00117
00118 if (components.size() < 2) {
00119 return false;
00120 }
00121
00122 const bool mustBeDir = components.back().isEmpty();
00123
00124
00125
00126 QString correctPath;
00127
00128 if (components.back().isEmpty()) {
00129 components.pop_back();
00130 }
00131
00132 const unsigned initialComponents = components.size();
00133 for (unsigned i = 0; i < initialComponents - 1; i ++) {
00134 const QString tmp = components[0] + QDir::separator() + components[1];
00135
00136 if (!correctLastComponentCase(tmp, correctPath, components.size() > 2 || mustBeDir)) {
00137
00138 return false;
00139 }
00140
00141 components.removeFirst();
00142 components[0] = correctPath;
00143 }
00144
00145 corrected = correctPath;
00146 return true;
00147 }
00148
00149 class RunnerContextPrivate : public QSharedData
00150 {
00151 public:
00152 RunnerContextPrivate(RunnerContext *context)
00153 : QSharedData(),
00154 type(RunnerContext::UnknownType),
00155 q(context)
00156 {
00157 }
00158
00159 RunnerContextPrivate(const RunnerContextPrivate &p)
00160 : QSharedData(),
00161 launchCounts(p.launchCounts),
00162 type(RunnerContext::None),
00163 q(p.q)
00164 {
00165
00166 }
00167
00168 ~RunnerContextPrivate()
00169 {
00170 }
00171
00175 void determineType()
00176 {
00177
00178
00179
00180 type = RunnerContext::UnknownType;
00181 QString path = QDir::cleanPath(KShell::tildeExpand(term));
00182
00183 int space = path.indexOf(' ');
00184 if (!KStandardDirs::findExe(path.left(space)).isEmpty()) {
00185
00186
00187 type = (space > 0) ? RunnerContext::ShellCommand :
00188 RunnerContext::Executable;
00189 } else {
00190 KUrl url(term);
00191 QString correctCasePath;
00192 if (!url.protocol().isEmpty() && !url.isLocalFile()) {
00193 type = RunnerContext::NetworkLocation;
00194 } else if (correctPathCase(path, correctCasePath)) {
00195 path = correctCasePath;
00196 QFileInfo info(path);
00197 if (info.isSymLink()) {
00198 path = info.canonicalFilePath();
00199 info = QFileInfo(path);
00200 }
00201 if (info.isDir()) {
00202 type = RunnerContext::Directory;
00203 mimeType = "inode/folder";
00204 } else if (info.isFile()) {
00205 type = RunnerContext::File;
00206 KMimeType::Ptr mimeTypePtr = KMimeType::findByPath(path);
00207 if (mimeTypePtr) {
00208 mimeType = mimeTypePtr->name();
00209 }
00210 }
00211 }
00212 }
00213 }
00214
00215 void invalidate()
00216 {
00217 q = &s_dummyContext;
00218 }
00219
00220 QReadWriteLock lock;
00221 QList<QueryMatch> matches;
00222 QMap<QString, const QueryMatch*> matchesById;
00223 QHash<QString, int> launchCounts;
00224 QString term;
00225 QString mimeType;
00226 RunnerContext::Type type;
00227 RunnerContext * q;
00228 static RunnerContext s_dummyContext;
00229 };
00230
00231 RunnerContext RunnerContextPrivate::s_dummyContext;
00232
00233 RunnerContext::RunnerContext(QObject *parent)
00234 : QObject(parent),
00235 d(new RunnerContextPrivate(this))
00236 {
00237 }
00238
00239
00240 RunnerContext::RunnerContext(RunnerContext &other, QObject *parent)
00241 : QObject(parent)
00242 {
00243 LOCK_FOR_READ((&other))
00244 d = other.d;
00245 UNLOCK((&other))
00246 }
00247
00248 RunnerContext::~RunnerContext()
00249 {
00250 }
00251
00252 void RunnerContext::reset()
00253 {
00254
00255
00256
00257
00258
00259
00260 d->invalidate();
00261
00262 d.detach();
00263
00264
00265
00266 d->q = this;
00267
00268
00269
00270
00271 if (!d->matches.isEmpty()) {
00272 d->matchesById.clear();
00273 d->matches.clear();
00274 emit matchesChanged();
00275 }
00276
00277 d->term.clear();
00278 d->mimeType.clear();
00279 d->type = UnknownType;
00280
00281 }
00282
00283 void RunnerContext::setQuery(const QString &term)
00284 {
00285 reset();
00286
00287 if (term.isEmpty()) {
00288 return;
00289 }
00290
00291 d->term = term;
00292 d->determineType();
00293 }
00294
00295 QString RunnerContext::query() const
00296 {
00297
00298
00299
00300 return d->term;
00301 }
00302
00303 RunnerContext::Type RunnerContext::type() const
00304 {
00305 return d->type;
00306 }
00307
00308 QString RunnerContext::mimeType() const
00309 {
00310 return d->mimeType;
00311 }
00312
00313 bool RunnerContext::isValid() const
00314 {
00315
00316 return (d->q != &(d->s_dummyContext));
00317 }
00318
00319 bool RunnerContext::addMatches(const QString &term, const QList<QueryMatch> &matches)
00320 {
00321 Q_UNUSED(term)
00322
00323 if (matches.isEmpty() || !isValid()) {
00324
00325 return false;
00326 }
00327
00328 LOCK_FOR_WRITE(this)
00329 foreach (QueryMatch match, matches) {
00330
00331 if (int count = d->launchCounts.value(match.id())) {
00332 match.setRelevance(match.relevance() + 0.05 * count);
00333 }
00334
00335 d->matches.append(match);
00336 #ifndef NDEBUG
00337 if (d->matchesById.contains(match.id())) {
00338 kDebug() << "Duplicate match id " << match.id() << "from" << match.runner()->name();
00339 }
00340 #endif
00341 d->matchesById.insert(match.id(), &d->matches.at(d->matches.size() - 1));
00342 }
00343 UNLOCK(this);
00344
00345
00346
00347
00348 emit d->q->matchesChanged();
00349
00350 return true;
00351 }
00352
00353 bool RunnerContext::addMatch(const QString &term, const QueryMatch &match)
00354 {
00355 Q_UNUSED(term)
00356
00357 if (!isValid()) {
00358
00359 return false;
00360 }
00361
00362 QueryMatch m(match);
00363
00364 LOCK_FOR_WRITE(this)
00365
00366 if (int count = d->launchCounts.value(m.id())) {
00367 m.setRelevance(m.relevance() + 0.05 * count);
00368 }
00369
00370 d->matches.append(m);
00371 d->matchesById.insert(m.id(), &d->matches.at(d->matches.size() - 1));
00372 UNLOCK(this);
00373
00374 emit d->q->matchesChanged();
00375
00376 return true;
00377 }
00378
00379 QList<QueryMatch> RunnerContext::matches() const
00380 {
00381 LOCK_FOR_READ(this)
00382 QList<QueryMatch> matches = d->matches;
00383 UNLOCK(this);
00384 return matches;
00385 }
00386
00387 QueryMatch RunnerContext::match(const QString &id) const
00388 {
00389 LOCK_FOR_READ(this)
00390 const QueryMatch *match = d->matchesById.value(id, 0);
00391 UNLOCK(this)
00392
00393 if (match) {
00394 return *match;
00395 }
00396
00397 return QueryMatch(0);
00398 }
00399
00400 void RunnerContext::restore(const KConfigGroup &config)
00401 {
00402 QStringList cfgList = config.readEntry("LaunchCounts", QStringList());
00403
00404 QRegExp r("(\\d*) (.*)");
00405 foreach (QString entry, cfgList) {
00406 r.indexIn(entry);
00407 int count = r.cap(1).toInt();
00408 QString id = r.cap(2);
00409 d->launchCounts[id] = count;
00410 }
00411 }
00412
00413 void RunnerContext::save(KConfigGroup &config)
00414 {
00415 QStringList countList;
00416
00417 typedef QHash<QString, int>::const_iterator Iterator;
00418 Iterator end = d->launchCounts.constEnd();
00419 for (Iterator i = d->launchCounts.constBegin(); i != end; ++i) {
00420 countList << QString("%2 %1").arg(i.key()).arg(i.value());
00421 }
00422
00423 config.writeEntry("LaunchCounts", countList);
00424 config.sync();
00425 }
00426
00427 void RunnerContext::run(const QueryMatch &match)
00428 {
00429 ++d->launchCounts[match.id()];
00430 match.run(*this);
00431 }
00432
00433 }
00434
00435 #include "runnercontext.moc"