Big patch with several internal changes to krunner:

-New RunnerManager class, it manages runners at high level 
and gives a simple API for interfaces to use. 
- AbstractRunners gains description, setIgnoredTypes, ignoredTypes methods. 
Now runners can specify what SearchContext::Types they are not going to process.
- SearchContext tries shared data between the original context and any local context.
It doesn't have its own completer no more.
- SearchMatch has a new type of match, CompleterMatch. Instead of using a hard coded 
completer in SearchContext
- Interface class on krunner has its own completer and other changes
done to accomodate the above changes in libplasma.
-A new configuration option blacklist for runnermanager and krunner 
instead of the whitelist of krunner. 


svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=802903
This commit is contained in:
Jordi Polo 2008-05-01 07:51:59 +00:00
parent 207968b217
commit 95157e20c4
9 changed files with 727 additions and 245 deletions

View File

@ -41,6 +41,7 @@ set(plasma_LIB_SRCS
packages.cpp
plasma.cpp
plasma_export.h
runnermanager.cpp
searchmatch.cpp
searchcontext.cpp
shadowitem.cpp
@ -77,7 +78,8 @@ endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
kde4_add_library(plasma SHARED ${plasma_LIB_SRCS})
target_link_libraries(plasma ${KDE4_KIO_LIBS} ${KDE4_KFILE_LIBS} ${QT_QTUITOOLS_LIBRARY} ${QT_QTWEBKIT_LIBRARY})
target_link_libraries(plasma ${KDE4_KIO_LIBS} ${KDE4_KFILE_LIBS}
${QT_QTUITOOLS_LIBRARY} ${QT_QTWEBKIT_LIBRARY} ${KDE4_THREADWEAVER_LIBRARIES})
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
target_link_libraries(plasma ${QT_QTOPENGL_LIBRARY} ${OPENGL_gl_LIBRARY})
@ -112,6 +114,7 @@ set(plasma_LIB_INCLUDES
dialog.h
plasma.h
plasma_export.h
runnermanager.h
searchmatch.h
searchcontext.h
shadowitem_p.h

View File

@ -28,8 +28,11 @@
#include <KStandardDirs>
#include <QTimer>
#include "package.h"
#include <plasma/searchmatch.h>
#include <plasma/package.h>
#include "scripting/runnerscript.h"
#include "searchcontext.h"
namespace Plasma
@ -41,6 +44,7 @@ public:
Private(AbstractRunner* r, KService::Ptr service)
: priority(NormalPriority),
speed(NormalSpeed),
blackListed(0),
script(0),
runnerDescription(service),
runner(r),
@ -73,6 +77,7 @@ public:
bool hasConfig;
Priority priority;
Speed speed;
SearchContext::Types blackListed;
RunnerScript* script;
KPluginInfo runnerDescription;
AbstractRunner* runner;
@ -119,10 +124,9 @@ void AbstractRunner::performMatch( Plasma::SearchContext &globalContext )
static const int fastEnoughTime = 250;
d->runtime.restart();
SearchContext localContext(0, globalContext);
//TODO :this is a copy ctor
SearchContext localContext(globalContext,0);
match(&localContext);
// automatically rate limit runners that become slooow
const int runtime = d->runtime.elapsed();
bool slowed = speed() == SlowSpeed;
@ -136,8 +140,7 @@ void AbstractRunner::performMatch( Plasma::SearchContext &globalContext )
}
//If matches were not added, delete items on the heap
if (localContext.moveMatchesTo(globalContext) &&
slowed && runtime < fastEnoughTime) {
if (slowed && runtime < fastEnoughTime) {
++d->fastRuns;
if (d->fastRuns > 2) {
@ -199,12 +202,24 @@ void AbstractRunner::setPriority(Priority priority)
d->priority = priority;
}
SearchContext::Types AbstractRunner::ignoredTypes() const
{
return d->blackListed;
}
void AbstractRunner::setIgnoredTypes(SearchContext::Types types)
{
d->blackListed = types;
}
KService::List AbstractRunner::serviceQuery(const QString &serviceType, const QString &constraint) const
{
QMutexLocker lock(&Private::bigLock);
return KServiceTypeTrader::self()->query(serviceType, constraint);
}
QMutex* AbstractRunner::bigLock() const
{
return &Private::bigLock;
@ -232,6 +247,15 @@ QString AbstractRunner::name() const
return d->runnerDescription.property("X-Plasma-RunnerName").toString();
}
QString AbstractRunner::description() const
{
if (!d->runnerDescription.isValid()) {
return objectName();
}
return d->runnerDescription.property("Comment").toString();
}
const Package* AbstractRunner::package() const
{
return d->package;
@ -244,47 +268,6 @@ void AbstractRunner::init()
}
}
AbstractRunner::List AbstractRunner::loadAll(QObject* parent, const QStringList& whitelist)
{
List firstRunners;
List runners;
List lastRunners;
KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner");
QString error;
foreach (const KService::Ptr &service, offers) {
if (whitelist.empty() || whitelist.contains(service->name())) {
QString api = service->property("X-Plasma-API").toString();
AbstractRunner* runner = 0;
if (api.isEmpty()) {
QVariantList args;
args << service->storageId();
runner = service->createInstance<AbstractRunner>(parent, args, &error);
} else {
runner = new AbstractRunner(parent, service->storageId());
}
if (runner) {
//kDebug() << "loaded runner : " << service->name();
QString phase = service->property("X-Plasma-RunnerPhase").toString();
if (phase == "last") {
lastRunners.append(runner);
} else if (phase == "first") {
firstRunners.append(runner);
} else {
runners.append(runner);
}
} else {
kDebug() << "failed to load runner : " << service->name() << ". error reported: " << error;
}
}
}
firstRunners << runners << lastRunners;
return firstRunners;
}
} // Plasma namespace
#include "abstractrunner.moc"

View File

@ -28,9 +28,8 @@
#include <KDE/KService>
#include <plasma/plasma_export.h>
#include <plasma/package.h>
#include <plasma/searchmatch.h>
#include <plasma/searchcontext.h>
#include <plasma/searchmatch.h>
class KCompletion;
@ -39,6 +38,7 @@ namespace Plasma
class Package;
class RunnerScript;
class SearchMatch;
/**
* An abstract base class for Plasma Runner plugins
@ -63,16 +63,9 @@ class PLASMA_EXPORT AbstractRunner : public QObject
HighPriority,
HighestPriority
};
typedef QList<AbstractRunner*> List;
/**
* Static method is called to load and get a list available of Runners.
*
* @param whitelist An optional whitelist of runners to load
*/
static List loadAll(QObject* parent, const QStringList& whitelist = QStringList());
virtual ~AbstractRunner();
/**
@ -153,11 +146,29 @@ class PLASMA_EXPORT AbstractRunner : public QObject
*/
Priority priority() const;
/**
* Returns the OR'ed value of all the Information types (as defined in SearchContext::Type)
* this runner is not interested in.
* @return OR'ed value of black listed types
*/
SearchContext::Types ignoredTypes() const;
/**
* Sets the types this runner will ignore
* @param types OR'ed listed of ignored types
*/
void setIgnoredTypes(SearchContext::Types types);
/**
* Returns the engine name for the Runner
*/
QString name() const;
/**
* Returns the description of this Runner
*/
QString description() const;
/**
* Accessor for the associated Package object if any.
*

431
runnermanager.cpp Normal file
View File

@ -0,0 +1,431 @@
/*
* Copyright (C) 2006-2007 Ryan P. Bitanga <ryan.bitanga@gmail.com>
* Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
* Copyright 2008 Jordi Polo <mumismo@gmail.com>
*
* 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 <KServiceTypeTrader>
#include <KPluginInfo>
#include <KDebug>
#include <Solid/Device>
#include <Solid/DeviceInterface>
#include <threadweaver/DebuggingAids.h>
#include <ThreadWeaver/Thread>
#include <ThreadWeaver/Job>
#include <ThreadWeaver/State>
#include <ThreadWeaver/QueuePolicy>
#include <ThreadWeaver/Weaver>
#include <ThreadWeaver/WeaverObserver>
#include <QMutex>
#include <QTimer>
#include <QCoreApplication>
#include <Plasma/SearchMatch>
#include "runnermanager.h"
using ThreadWeaver::Weaver;
using ThreadWeaver::Job;
namespace Plasma
{
/*****************************************************
* RunnerRestrictionPolicy class
* Restricts simultaneous jobs of the same type
* Similar to ResourceRestrictionPolicy but check the object type first
******************************************************/
class RunnerRestrictionPolicy : public ThreadWeaver::QueuePolicy
{
public:
~RunnerRestrictionPolicy();
static RunnerRestrictionPolicy& instance();
void setCap(int cap)
{
m_cap = cap;
}
int cap() const
{
return m_cap;
}
bool canRun(Job* job);
void free(Job* job);
void release(Job* job);
void destructed(Job* job);
private:
RunnerRestrictionPolicy();
// QHash<QString, int> m_runCounts;
int m_count;
int m_cap;
QMutex m_mutex;
};
RunnerRestrictionPolicy::RunnerRestrictionPolicy()
: QueuePolicy(),
m_cap(2)
{
}
RunnerRestrictionPolicy::~RunnerRestrictionPolicy()
{
}
RunnerRestrictionPolicy& RunnerRestrictionPolicy::instance()
{
static RunnerRestrictionPolicy policy;
return policy;
}
bool RunnerRestrictionPolicy::canRun(Job* job)
{
Q_UNUSED(job)
QMutexLocker l(&m_mutex);
// QString type = job->objectName();
if (m_count/*m_runCounts.value(type)*/ > m_cap) {
// kDebug() << "Denying job " << type << " because of " << m_count/*m_runCounts[type]*/ << " current jobs" << endl;
return false;
} else {
// m_runCounts[type]++;
++m_count;
return true;
}
}
void RunnerRestrictionPolicy::free(Job* job)
{
Q_UNUSED(job)
QMutexLocker l(&m_mutex);
// QString type = job->objectName();
--m_count;
// if (m_runCounts.value(type)) {
// m_runCounts[type]--;
// }
}
void RunnerRestrictionPolicy::release(Job* job)
{
free(job);
}
void RunnerRestrictionPolicy::destructed(Job* job)
{
Q_UNUSED(job)
}
/*****************************************************
* FindMatchesJob class
* Class to run queries in different threads
******************************************************/
class FindMatchesJob : public Job
{
public:
FindMatchesJob(const QString& term, Plasma::AbstractRunner* runner, Plasma::SearchContext* context, QObject* parent = 0);
int priority() const;
protected:
void run();
private:
QString m_term;
Plasma::SearchContext* m_context;
Plasma::AbstractRunner* m_runner;
};
FindMatchesJob::FindMatchesJob( const QString& term, Plasma::AbstractRunner* runner,
Plasma::SearchContext* context, QObject* parent )
: ThreadWeaver::Job(parent),
m_term(term),
m_context(context),
m_runner(runner)
{
// setObjectName( runner->objectName() );
if (runner->speed() == Plasma::AbstractRunner::SlowSpeed) {
assignQueuePolicy(&RunnerRestrictionPolicy::instance());
}
}
void FindMatchesJob::run()
{
// kDebug() << "Running match for " << m_runner->objectName() << " in Thread " << thread()->id() << endl;
m_runner->performMatch(*m_context);
}
int FindMatchesJob::priority() const
{
return m_runner->priority();
}
/*****************************************************
* RunnerManager::Private class
*
*****************************************************/
class RunnerManager::Private
{
public:
Private(RunnerManager *parent)
: q(parent)
{
connect(&context, SIGNAL(matchesChanged()), q, SIGNAL(matchesChanged()));
connect(Weaver::instance(), SIGNAL(finished()), q, SIGNAL(matchesCompleted()));
}
void configure(KConfigGroup& conf)
{
config=conf;
//The number of threads used scales with the number of processors.
const int numProcs = qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
//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));
// kDebug() << "setting up" << numThreads << "threads for" << numProcs << "processors";
Weaver::instance()->setMaximumNumberOfThreads(numThreads);
//Preferred order of execution of runners
//prioritylist = config.readEntry("priority", QStringList());
//If set, this list defines which runners won't be used at runtime
blacklist = config.readEntry("blacklist", QStringList());
}
void loadAll()
{
AbstractRunner::List firstRunners;
AbstractRunner::List normalRunners;
AbstractRunner::List lastRunners;
KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner");
QString error;
foreach (const KService::Ptr &service, offers) {
KPluginInfo description(service);
QString runnerName = description.property("X-Plasma-RunnerName").toString();
if (blacklist.contains(runnerName)) {
kDebug() << "The plugin" << service->name() << "was blackListed and will not load";
continue;
}
QString api = service->property("X-Plasma-API").toString();
AbstractRunner* runner = 0;
if (api.isEmpty()) {
QVariantList args;
args << service->storageId();
runner = service->createInstance<AbstractRunner>(q, args, &error);
} else {
//FIXME: this line should be uncommented, but the constructor is protected
//runner = new AbstractRunner(q, service->storageId());
}
if (runner) {
// kDebug() << "loading runner: " << service->name();
QString phase = service->property("X-Plasma-RunnerPhase").toString();
if (phase == "last") {
lastRunners.append(runner);
} else if (phase == "first") {
firstRunners.append(runner);
} else {
normalRunners.append(runner);
}
} else {
kDebug() << "failed to load runner : " << service->name() << ". error reported: " << error;
}
}
firstRunners << normalRunners << lastRunners;
runners.clear();
runners = firstRunners;
kDebug() << "All runners loaded, total:" << runners.count();
}
RunnerManager *q;
SearchContext context;
AbstractRunner::List runners;
QList<SearchMatch *> matches;
QList<ThreadWeaver::Job*> searchJobs;
QStringList prioritylist;
QStringList blacklist;
KConfigGroup config;
};
/*****************************************************
* RunnerManager::Public class
*
*****************************************************/
RunnerManager::RunnerManager(QObject *parent)
: QObject(parent),
d(new Private(this))
{
KConfigGroup config(KGlobal::config(), "PlasmaRunnerManager");
d->configure(config);
d->loadAll();
//ThreadWeaver::setDebugLevel(true, 4);
}
RunnerManager::RunnerManager(KConfigGroup& config, QObject *parent)
: QObject(parent),
d(new Private(this))
{
d->configure(config);
d->loadAll();
//ThreadWeaver::setDebugLevel(true, 4);
}
RunnerManager::~RunnerManager()
{
d->context.removeAllMatches();
}
AbstractRunner* RunnerManager::runner(const QString &name) const
{
//TODO: using a list of runners, if this gets slow, switch to hash
foreach (Plasma::AbstractRunner* runner, d->runners) {
if (name == runner->name()) {
return runner;
}
}
return 0;
}
SearchContext* RunnerManager::searchContext() const
{
return &d->context;
}
//Reordering is here so data is not reordered till strictly needed
QList<SearchMatch *> RunnerManager::matches() const
{
return d->context.matches();
}
void RunnerManager::run(const SearchMatch *match)
{
//TODO: this function is not const as it may be used for learning
match->run(&d->context);
}
void RunnerManager::launchQuery (const QString & term, const QString & runnerName)
{
if (term.isEmpty()) {
reset();
return;
}
if (d->context.searchTerm() == term) {
// we already are searching for this!
return;
}
reset();
// kDebug() << "runners searching for" << term << "on" << runnerName;
d->context.setSearchTerm(term);
AbstractRunner::List runable;
//if the name is not empty we will launch only the specified runner
if (!runnerName.isEmpty()) {
runable.append(runner(runnerName));
} else {
runable = d->runners;
}
bool jobsLaunched=false;
foreach (Plasma::AbstractRunner* r, runable) {
if ((r->ignoredTypes() & d->context.type()) == 0) {
// kDebug() << "launching" << r->name();
jobsLaunched=true;
Job *job = new FindMatchesJob(term, r, &d->context, this);
Weaver::instance()->enqueue( job );
d->searchJobs.append( job );
}
}
if (!jobsLaunched) {
emit matchesCompleted();
}
}
bool RunnerManager::execQuery (const QString & term, const QString & runnerName)
{
if (term.isEmpty()) {
reset();
return 0;
}
if (d->context.searchTerm() == term) {
// we already are searching for this!
return 0;
}
reset();
// kDebug() << "executing query about " << term << "on" << runnerName;
d->context.setSearchTerm(term);
AbstractRunner *r = runner(runnerName);
if (!r) {
return 0;
}
if ((r->ignoredTypes() & d->context.type()) != 0) {
return 0;
}
r->performMatch(d->context);
emit matchesCompleted();
return 1;
}
void RunnerManager::reset()
{
// If ThreadWeaver is idle, it is safe to clear previous jobs
if ( Weaver::instance()->isIdle() ) {
qDeleteAll( d->searchJobs );
d->searchJobs.clear();
}
else {
Weaver::instance()->dequeue();
}
d->context.reset();
//FIXME: if the weaver is not idle we have a number of jobs that
// will eventually return their matches
// we need a way to stop them (Weaver::requestAbort() Job::requestAbort )
// or discard results
}
}
#include "runnermanager.moc"

124
runnermanager.h Normal file
View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2006-2007 Ryan P. Bitanga <ryan.bitanga@gmail.com>
* Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
* Copyright 2008 Jordi Polo <mumismo@gmail.com>
*
* 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 RUNNERMANAGER_H
#define RUNNERMANAGER_H
#include <QtCore/QList>
#include <QtCore/QObject>
#include <plasma/plasma_export.h>
#include "abstractrunner.h"
namespace Plasma
{
class SearchMatch;
class AbstractRunner;
class SearchContext;
/**
* @short The RunnerManager class decides what installed runners are runnable,
* and their ratings. It is the main proxy to the runners.
*/
class PLASMA_EXPORT RunnerManager : public QObject
{
Q_OBJECT
public:
explicit RunnerManager(QObject *parent=0);
explicit RunnerManager(KConfigGroup& config, QObject *parent=0);
~RunnerManager();
/**
* Finds and returns a loaded runner or NULL
* @arg name the name of the runner
* @return Pointer to the runner
*/
AbstractRunner* runner(const QString &name) const;
/**
* Retrieves the current context
* @return pointer to the current context
*/
SearchContext* searchContext() const;
/**
* Retrieves all available matches found so far for the previously launched query
* @return List of matches
*/
QList<SearchMatch *> matches() const;
/**
* Runs a given match
* @arg pointer to the match to be executed
*/
void run(const SearchMatch *match);
public Q_SLOTS:
/**
* Launch a query, this will create threads and return inmediately.
* When the information will be available can be known using the
* matchesChanged, matchesUpdated, matchesCompleted signals
* The information can be retrieved calling matches
* @arg term the term we want to find matches for
* @arg runner optional, if only one specific runner is to be used
*/
void launchQuery(const QString &term, const QString & runnerName=QString());
/**
* Execute a query, this method will only return when the query is executed
* This means that the method may be dangerous as it wait a variable amount
* of time for the runner to finish.
* The runner parameter is mandatory, to avoid launching unwanted runners.
* @arg term the term we want to find matches for
* @arg runner the runner we will use, it is mandatory
* @return 0 if nothing was launched, 1 if launched.
*/
bool execQuery(const QString &term, const QString & runnerName);
/**
* Reset the current data and stops the query
*/
void reset();
Q_SIGNALS:
/**
* Emited each time a new match is added to the list
*/
void matchesChanged();
/**
* Emited when all the matches have been found
*/
void matchesCompleted();
private:
class Private;
Private * const d;
};
}
#endif

View File

@ -23,6 +23,7 @@
#include <QFile>
#include <QFileInfo>
#include <QSharedData>
#include <KCompletion>
#include <KDebug>
@ -36,34 +37,31 @@
namespace Plasma
{
class SearchContext::Private
class SearchContext::Private : public QSharedData
{
public:
Private(SearchContext::DataPolicy p)
Private(SearchContext *context, SearchContext::DataPolicy p)
: type(SearchContext::UnknownType),
completer(0),
q(context),
policy(p)
{
}
Private(const SearchContext::Private& p)
: term(p.term),
mimeType(p.mimeType),
type(p.type),
q(p.q),
policy(p.policy)
{
}
~Private()
{
lockForWrite();
qDeleteAll(matches);
matches.clear();
delete completer;
}
void resetState()
{
}
KCompletion* completionObject()
{
if (!completer) {
completer = new KCompletion;
}
return completer;
unlock();
}
void lockForRead()
@ -87,61 +85,90 @@ class SearchContext::Private
}
}
/**
* Determines type of query
*/
void determineType()
{
lockForWrite();
QString path = KShell::tildeExpand(term);
int space = term.indexOf(' ');
if (space > 0) {
if (!KStandardDirs::findExe(path.left(space)).isEmpty()) {
type = ShellCommand;
}
} else if (!KStandardDirs::findExe(path.left(space)).isEmpty()) {
type = Executable;
} else {
KUrl url(term);
if (!url.protocol().isEmpty() && !url.host().isEmpty()) {
type = NetworkLocation;
} else if (QFile::exists(path)) {
QFileInfo info(path);
if (info.isDir()) {
type = Directory;
mimeType = "inode/folder";
} else {
type = File;
KMimeType::Ptr mimeTypePtr = KMimeType::findByPath(path);
if (mimeTypePtr) {
mimeType = mimeTypePtr->name();
}
}
} else if (term.contains('.')) {
// default to a network location so we can can do things like www.kde.org
type = NetworkLocation;
}
}
unlock();
}
QReadWriteLock lock;
QList<SearchMatch *> matches;
QList<SearchMatch*> matches;
QString term;
QString mimeType;
SearchContext::Type type;
KCompletion *completer;
SearchContext * q;
const SearchContext::DataPolicy policy;
};
SearchContext::SearchContext(QObject *parent, DataPolicy policy)
: QObject(parent),
d(new Private(policy))
d(new Private(this, policy))
{
}
SearchContext::SearchContext(QObject *parent, const SearchContext &other)
: QObject(parent),
d(new Private(SingleConsumer))
//copy ctor
SearchContext::SearchContext(SearchContext &other, QObject *parent)
: QObject(parent)
{
other.d->lockForRead();
d->term = other.d->term;
d->mimeType = other.d->mimeType;
d->type = other.d->type;
d=other.d;
other.d->unlock();
}
SearchContext::~SearchContext()
{
delete d;
}
void SearchContext::reset()
{
d->lockForWrite();
QList<SearchMatch*> matches = d->matches;
// Locks are needed as other contexts can be copied of this one
d->matches.clear();
// We will detach if we are a copy of someone. But we will reset
// if we are the 'main' context others copied from. Resetting
// one SearchContext makes all the copies oneobsolete.
d.detach();
//kDebug() << "reset searchContext";
d->lockForWrite();
d->type = SearchContext::UnknownType;
d->term.clear();
d->mimeType.clear();
if (d->completer) {
d->completer->clear();
}
d->unlock();
emit matchesChanged();
// in case someone is still holding on to the Matches
// when we emit the matchesChanged() signal, we don't
// delete the matches until after the signal is handled.
// a bit safer.
qDeleteAll(matches);
removeAllMatches();
}
void SearchContext::setSearchTerm(const QString &term)
@ -153,46 +180,9 @@ void SearchContext::setSearchTerm(const QString &term)
d->lockForWrite();
d->term = term;
d->unlock();
determineType();
d->determineType();
}
void SearchContext::determineType()
{
d->lockForWrite();
QString term = d->term;
QString path = KShell::tildeExpand(term);
int space = term.indexOf(' ');
if (space > 0) {
if (!KStandardDirs::findExe(path.left(space)).isEmpty()) {
d->type = ShellCommand;
}
} else if (!KStandardDirs::findExe(path.left(space)).isEmpty()) {
d->type = Executable;
} else {
KUrl url(term);
if (!url.protocol().isEmpty() && !url.host().isEmpty()) {
d->type = NetworkLocation;
} else if (QFile::exists(path)) {
QFileInfo info(path);
if (info.isDir()) {
d->type = Directory;
d->mimeType = "inode/folder";
} else {
d->type = File;
KMimeType::Ptr mimeType = KMimeType::findByPath(path);
if (mimeType) {
d->mimeType = mimeType->name();
}
}
} else if (term.contains('.')) {
// default to a network location so we can can do things like www.kde.org
d->type = NetworkLocation;
}
}
d->unlock();
}
QString SearchContext::searchTerm() const
{
@ -212,40 +202,6 @@ QString SearchContext::mimeType() const
return d->mimeType;
}
KCompletion* SearchContext::completionObject() const
{
d->lockForRead();
KCompletion* comp = d->completionObject();
d->unlock();
return comp;
}
void SearchContext::addStringCompletion(const QString &completion)
{
d->lockForWrite();
if (!d->completer) {
d->unlock();
// if the completion object isn't actually used, don't bother
return;
}
d->completer->addItem(completion);
d->unlock();
}
void SearchContext::addStringCompletions(const QStringList &completion)
{
d->lockForWrite();
if (!d->completer) {
d->unlock();
// if the completion object isn't actually used, don't bother
return;
}
d->completer->insertItems(completion);
d->unlock();
}
bool SearchContext::addMatches(const QString& term, const QList<SearchMatch*> &matches)
{
if (searchTerm() != term || matches.isEmpty()) {
@ -255,12 +211,11 @@ bool SearchContext::addMatches(const QString& term, const QList<SearchMatch*> &m
d->lockForWrite();
d->matches << matches;
d->unlock();
// TODO: perhaps queue this signal so that it is only emitted after a small delay?
// currently we do this in krunner's Interface class, but it would probably
// be better to move that detail to SearchContext so that other apps that
// use SearchContext can also benefit from it
emit matchesChanged();
//kDebug()<< "add matches";
// A copied searchContext may share the d pointer,
// we always want to sent the signal of the object that created
// the d pointer
emit d->q->matchesChanged();
return true;
}
@ -273,30 +228,12 @@ bool SearchContext::addMatch(const QString &term, SearchMatch *match)
d->lockForWrite();
d->matches << match;
d->unlock();
emit matchesChanged();
//kDebug()<< "added match" << match->text();
emit d->q->matchesChanged();
return true;
}
bool SearchContext::moveMatchesTo(SearchContext &other)
{
//NOTE: we have moveMatchesTo instead of the more 'natural' addMatches
// because we can get away with one write lock on the local object
// this way, otherwise we'd need to lock once for searchTerm, once
// for matches() and again for removeAllMatches() (2 read, one write)
d->lockForWrite();
const bool success = other.addMatches(d->term, d->matches);
if (success) {
// the matches no longer belong to this SearchContext,
// so remove them from the data
d->matches.clear();
}
d->unlock();
return success;
}
QList<SearchMatch *> SearchContext::matches() const
{
@ -309,18 +246,19 @@ QList<SearchMatch *> SearchContext::matches() const
void SearchContext::removeAllMatches()
{
d->lockForWrite();
QList<SearchMatch*> matches = d->matches;
d->matches.clear();
bool emptyMatches = d->matches.isEmpty();
if (!emptyMatches) {
d->matches.clear();
d->unlock();
emit d->q->matchesChanged();
// in case someone is still holding on to the Matches
// when we emit the matchesChanged() signal, we don't
// delete the matches until after the signal is handled.
// a bit safer.
d->lockForWrite();
qDeleteAll(d->matches);
}
d->unlock();
emit matchesChanged();
// in case someone is still holding on to the Matches
// when we emit the matchesChanged() signal, we don't
// delete the matches until after the signal is handled.
// a bit safer.
qDeleteAll(matches);
}
}

View File

@ -22,6 +22,8 @@
#include <QtCore/QList>
#include <QtCore/QObject>
#include <QSharedDataPointer>
#include <QFlags>
#include <plasma/plasma_export.h>
@ -43,19 +45,22 @@ class PLASMA_EXPORT SearchContext : public QObject
Q_OBJECT
public:
enum Type { UnknownType = 0,
Directory,
File,
NetworkLocation,
Executable,
ShellCommand,
Help
enum Type { None = 0,
UnknownType = 1,
Directory = 2,
File = 4,
NetworkLocation = 8,
Executable = 16,
ShellCommand = 32,
Help = 64
};
Q_DECLARE_FLAGS(Types, Type)
enum DataPolicy { Shared = 0,
SingleConsumer
};
explicit SearchContext(QObject *parent = 0, DataPolicy policy = Shared);
/**
@ -64,7 +69,8 @@ class PLASMA_EXPORT SearchContext : public QObject
* matches) from the passed in SearchContext. Primarily useful for creating
* a thread-local copy of a Shared SearchContext.
*/
SearchContext(QObject *parent, const SearchContext& other);
explicit SearchContext(SearchContext& other, QObject *parent = 0);
~SearchContext();
@ -100,22 +106,7 @@ class PLASMA_EXPORT SearchContext : public QObject
*/
QString mimeType() const;
/**
* @return a completion object that can be used with UI elements
*/
KCompletion* completionObject() const;
/**
* Adds an item to the completion object.
*/
void addStringCompletion(const QString& completion);
/**
* Adds multiple items to the completion object.
*/
void addStringCompletions(const QStringList& completions);
/**
/**
* Appends lists of matches to the list of matches.
* The SearchContext takes over ownership of the matches on successful addition.
*
@ -154,11 +145,6 @@ class PLASMA_EXPORT SearchContext : public QObject
*/
QList<SearchMatch *> matches() const;
/**
* Determines type of query
*/
void determineType();
/**
* Removes all matches from this SearchContext.
*/
@ -169,9 +155,11 @@ class PLASMA_EXPORT SearchContext : public QObject
private:
class Private;
Private * const d;
QExplicitlySharedDataPointer<Private> d;
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS (Plasma::SearchContext::Types)
#endif

View File

@ -19,6 +19,7 @@
#include "searchmatch.h"
#include <QPointer>
#include <QVariant>
#include <QStringList>
#include <QIcon>
@ -41,7 +42,7 @@ class SearchMatch::Private
{
}
AbstractRunner *runner;
QPointer<AbstractRunner> runner;
SearchMatch::Type type;
QString text;
QString subtext;
@ -55,6 +56,7 @@ class SearchMatch::Private
SearchMatch::SearchMatch(AbstractRunner *runner)
: d(new Private(runner))
{
// kDebug() << "new match created";
}
SearchMatch::~SearchMatch()
@ -146,7 +148,7 @@ void SearchMatch::run(const SearchContext *context) const
{
Q_ASSERT(context);
//kDebug() << "we have" << context->searchTerm() << context->mimetype();
//kDebug() << "we run the term" << context->searchTerm() << "whose type is" << context->mimetype();
if (d->runner) {
//TODO: this could be dangerous if the runner is deleted behind our backs.
d->runner->run(context, this);

View File

@ -38,7 +38,8 @@ class PLASMA_EXPORT SearchMatch
/**
* The type of match. Value is important here as it is used for sorting
*/
enum Type { PossibleMatch = 0 /**< Something that may match the query */,
enum Type { CompletionMatch = 10 /**< Possible completion for the data of the query */,
PossibleMatch = 30 /**< Something that may match the query */,
InformationalMatch = 50 /**< A purely informational, non-actionable match,
such as the answer to a question or calculation*/,
HelperMatch = 70 /**< A match that represents an action not directly related
@ -51,6 +52,7 @@ class PLASMA_EXPORT SearchMatch
an action to be triggered. */,
ExactMatch = 100 /**< An exact match to the query */};
/**
* Constructs a PossibleMatch associated with a given SearchContext
* and runner.