• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

runnercontext.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 "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 //#define LOCK_FOR_READ(context) if (context->d->policy == Shared) { context->d->lock.lockForRead(); }
00041 //#define LOCK_FOR_WRITE(context) if (context->d->policy == Shared) { context->d->lock.lockForWrite(); }
00042 //#define UNLOCK(context) if (context->d->policy == Shared) { context->d->lock.unlock(); }
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 Corrects the case of the last component in a path (e.g. /usr/liB -> /usr/lib)
00053 path: The path to be processed.
00054 correctCasePath: The corrected-case path
00055 mustBeDir: Tells whether the last component is a folder or doesn't matter
00056 Returns true on success and false on error, in case of error, correctCasePath is not modified
00057 */
00058 bool correctLastComponentCase(const QString &path, QString &correctCasePath, const bool mustBeDir)
00059 {
00060     //kDebug() << "Correcting " << path;
00061 
00062     // If the file already exists then no need to search for it.
00063     if (QFile::exists(path)) {
00064         correctCasePath = path;
00065         //kDebug() << "Correct path is" << correctCasePath;
00066         return true;
00067     }
00068 
00069     const QFileInfo pathInfo(path);
00070 
00071     const QDir fileDir = pathInfo.dir();
00072     //kDebug() << "Directory is" << fileDir;
00073 
00074     const QString filename = pathInfo.fileName();
00075     //kDebug() << "Filename is" << filename;
00076 
00077     //kDebug() << "searching for a" << (mustBeDir ? "directory" : "directory/file");
00078 
00079     const QStringList matchingFilenames = fileDir.entryList(QStringList(filename),
00080                                           mustBeDir ? QDir::Dirs : QDir::NoFilter);
00081 
00082     if (matchingFilenames.empty()) {
00083         //kDebug() << "No matches found!!\n";
00084         return false;
00085     } else {
00086         /*if (matchingFilenames.size() > 1) {
00087             kDebug() << "Found multiple matches!!\n";
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         //kDebug() << "Correct path is" << correctCasePath;
00097         return true;
00098     }
00099 }
00100 
00101 /*
00102 Corrects the case of a path (e.g. /uSr/loCAL/bIN -> /usr/local/bin)
00103 path: The path to be processed.
00104 corrected: The corrected-case path
00105 Returns true on success and false on error, in case of error, corrected is not modified
00106 */
00107 bool correctPathCase(const QString& path, QString &corrected)
00108 {
00109     // early exit check
00110     if (QFile::exists(path)) {
00111         corrected = path;
00112         return true;
00113     }
00114 
00115     // path components
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     //kDebug() << "Components are" << components;
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             //kDebug() << "search was not successfull";
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             //kDebug() << "¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿boo yeah" << type;
00166         }
00167 
00168         ~RunnerContextPrivate()
00169         {
00170         }
00171 
00175         void determineType()
00176         {
00177             // NOTE! this method must NEVER be called from
00178             // code that may be running in multiple threads
00179             // with the same data.
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                 // it's a shell command if there's a space because that implies
00186                 // that it has arguments!
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 //copy ctor
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     // We will detach if we are a copy of someone. But we will reset
00255     // if we are the 'main' context others copied from. Resetting
00256     // one RunnerContext makes all the copies obsolete.
00257 
00258     // We need to mark the q pointer of the detached RunnerContextPrivate
00259     // as dirty on detach to avoid receiving results for old queries
00260     d->invalidate();
00261 
00262     d.detach();
00263 
00264     // Now that we detached the d pointer we need to reset its q pointer
00265 
00266     d->q = this;
00267 
00268     // we still have to remove all the matches, since if the
00269     // ref count was 1 (e.g. only the RunnerContext is using
00270     // the dptr) then we won't get a copy made
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     //kDebug() << "match count" << d->matches.count();
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     // the query term should never be set after
00298     // a search starts. in fact, reset() ensures this
00299     // and setQuery(QString) calls reset()
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     // if our qptr is dirty, we aren't useful anymore
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        //Bail out if the query is empty or the qptr is dirty
00325         return false;
00326     }
00327 
00328     LOCK_FOR_WRITE(this)
00329     foreach (QueryMatch match, matches) {
00330         // Give previously launched matches a slight boost in relevance
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     //kDebug()<< "add matches";
00345     // A copied searchContext may share the d pointer,
00346     // we always want to sent the signal of the object that created
00347     // the d pointer
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         // Bail out if the qptr is dirty
00359         return false;
00360     }
00361 
00362     QueryMatch m(match); // match must be non-const to modify relevance
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     //kDebug()<< "added match" << match->text();
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 } // Plasma namespace
00434 
00435 #include "runnercontext.moc"

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal