kde-playground/khelpcenter/searchengine.cpp

474 lines
12 KiB
C++
Raw Permalink Normal View History

#include "searchengine.h"
#include "stdlib.h"
#include <QTextDocument>
#include <KApplication>
#include <KConfig>
#include <KDebug>
#include <KStandardDirs>
#include <KProcess>
#include <KShell>
#include <KLocale>
#include <KMessageBox>
#include "docmetainfo.h"
#include "formatter.h"
#include "view.h"
#include "searchhandler.h"
#include "prefs.h"
namespace KHC
{
SearchTraverser::SearchTraverser( SearchEngine *engine, int level ) :
mMaxLevel( 999 ), mEngine( engine), mLevel( level )
{
#if 0
kDebug() << "SearchTraverser(): " << mLevel
<< " 0x" << QString::number( int( this ), 16 ) << endl;
#endif
}
SearchTraverser::~SearchTraverser()
{
#if 0
kDebug() << "~SearchTraverser(): " << mLevel
<< " 0x" << QString::number( int( this ), 16 ) << endl;
#endif
QString section;
if ( parentEntry() ) {
section = parentEntry()->name();
} else {
section = ("Unknown Section");
}
if ( !mResult.isEmpty() ) {
mEngine->view()->writeSearchResult(
mEngine->formatter()->sectionHeader( section ) );
mEngine->view()->writeSearchResult( mResult );
}
}
void SearchTraverser::process( DocEntry * )
{
kDebug() << "SearchTraverser::process()";
}
void SearchTraverser::startProcess( DocEntry *entry )
{
// kDebug() << "SearchTraverser::startProcess(): " << entry->name() << " "
// << "SEARCH: '" << entry->search() << "'" << endl;
if ( !mEngine->canSearch( entry ) || !entry->searchEnabled() ) {
mNotifyee->endProcess( entry, this );
return;
}
// kDebug() << "SearchTraverser::startProcess(): " << entry->identifier()
// << endl;
SearchHandler *handler = mEngine->handler( entry->documentType() );
if ( !handler ) {
QString txt;
if ( entry->documentType().isEmpty() ) {
txt = i18n("Error: No document type specified.");
} else {
txt = i18n("Error: No search handler for document type '%1'.",
entry->documentType() );
}
showSearchError( handler, entry, txt );
return;
}
connectHandler( handler );
handler->search( entry, mEngine->words(), mEngine->maxResults(),
mEngine->operation() );
// kDebug() << "SearchTraverser::startProcess() done: " << entry->name();
}
void SearchTraverser::connectHandler( SearchHandler *handler )
{
QMap<SearchHandler *,int>::Iterator it;
it = mConnectCount.find( handler );
int count = 0;
if ( it != mConnectCount.end() ) count = *it;
if ( count == 0 ) {
connect( handler, SIGNAL( searchError( SearchHandler *, DocEntry *, const QString & ) ),
SLOT( showSearchError( SearchHandler *, DocEntry *, const QString & ) ) );
connect( handler, SIGNAL( searchFinished( SearchHandler *, DocEntry *, const QString & ) ),
SLOT( showSearchResult( SearchHandler *, DocEntry *, const QString & ) ) );
}
mConnectCount[ handler ] = ++count;
}
void SearchTraverser::disconnectHandler( SearchHandler *handler )
{
QMap<SearchHandler *,int>::Iterator it;
it = mConnectCount.find( handler );
if ( it == mConnectCount.end() ) {
kError() << "SearchTraverser::disconnectHandler() handler not connected."
<< endl;
} else {
int count = *it;
--count;
if ( count == 0 ) {
disconnect( handler, SIGNAL( searchError( SearchHandler *, DocEntry *, const QString & ) ),
this, SLOT( showSearchError( SearchHandler *, DocEntry *, const QString & ) ) );
disconnect( handler, SIGNAL( searchFinished( SearchHandler *, DocEntry *, const QString & ) ),
this, SLOT( showSearchResult( SearchHandler *, DocEntry *, const QString & ) ) );
}
mConnectCount[ handler ] = count;
}
}
DocEntryTraverser *SearchTraverser::createChild( DocEntry *parentEntry )
{
// kDebug() << "SearchTraverser::createChild() level " << mLevel;
if ( mLevel >= mMaxLevel ) {
++mLevel;
return this;
} else {
DocEntryTraverser *t = new SearchTraverser( mEngine, mLevel + 1 );
t->setParentEntry( parentEntry );
return t;
}
}
DocEntryTraverser *SearchTraverser::parentTraverser()
{
// kDebug() << "SearchTraverser::parentTraverser(): level: " << mLevel;
if ( mLevel > mMaxLevel ) {
return this;
} else {
return mParent;
}
}
void SearchTraverser::deleteTraverser()
{
// kDebug() << "SearchTraverser::deleteTraverser()";
if ( mLevel > mMaxLevel ) {
--mLevel;
} else {
delete this;
}
}
void SearchTraverser::showSearchError( SearchHandler *handler, DocEntry *entry, const QString &error )
{
// kDebug() << "SearchTraverser::showSearchError(): " << entry->name()
// << endl;
mResult += mEngine->formatter()->docTitle( entry->name() );
mResult += mEngine->formatter()->paragraph( error );
mEngine->logError( entry, error );
disconnectHandler( handler );
mNotifyee->endProcess( entry, this );
}
void SearchTraverser::showSearchResult( SearchHandler *handler, DocEntry *entry, const QString &result )
{
// kDebug() << "SearchTraverser::showSearchResult(): " << entry->name()
// << endl;
mResult += mEngine->formatter()->docTitle( entry->name() );
mResult += mEngine->formatter()->processResult( result );
disconnectHandler( handler );
mNotifyee->endProcess( entry, this );
}
void SearchTraverser::finishTraversal()
{
// kDebug() << "SearchTraverser::finishTraversal()";
mEngine->view()->writeSearchResult( mEngine->formatter()->footer() );
mEngine->view()->endSearchResult();
mEngine->finishSearch();
}
SearchEngine::SearchEngine( View *destination )
: QObject(),
mProc( 0 ), mSearchRunning( false ), mView( destination ),
mRootTraverser( 0 )
{
mLang = KGlobal::locale()->language().left( 2 );
}
SearchEngine::~SearchEngine()
{
delete mRootTraverser;
}
bool SearchEngine::initSearchHandlers()
{
const QStringList resources = KGlobal::dirs()->findAllResources(
"appdata", "searchhandlers/*.desktop" );
QStringList::ConstIterator it;
for( it = resources.constBegin(); it != resources.constEnd(); ++it ) {
QString filename = *it;
kDebug() << "SearchEngine::initSearchHandlers(): " << filename;
SearchHandler *handler = SearchHandler::initFromFile( filename );
if ( !handler ) {
QString txt = i18n("Unable to initialize SearchHandler from file '%1'.",
filename );
kWarning() << txt ;
// KMessageBox::sorry( mView->widget(), txt );
} else {
QStringList documentTypes = handler->documentTypes();
QStringList::ConstIterator it;
for( it = documentTypes.constBegin(); it != documentTypes.constEnd(); ++it ) {
mHandlers.insert( *it, handler );
}
}
}
if ( mHandlers.isEmpty() ) {
QString txt = i18n("No valid search handler found.");
kWarning() << txt ;
// KMessageBox::sorry( mView->widget(), txt );
return false;
}
return true;
}
void SearchEngine::searchExited(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitCode);
Q_UNUSED(exitStatus);
kDebug() << "Search terminated";
mSearchRunning = false;
}
bool SearchEngine::search( const QString & words, const QString & method, int matches,
const QString & scope )
{
if ( mSearchRunning ) return false;
// These should be removed
mWords = words;
mMethod = method;
mMatches = matches;
mScope = scope;
// Saner variables to store search parameters:
mWordList = words.split(' ');
mMaxResults = matches;
if ( method == "or" ) mOperation = Or;
else mOperation = And;
KConfigGroup cfg(KGlobal::config(), "Search");
QString commonSearchProgram = cfg.readPathEntry( "CommonProgram", QString() );
bool useCommon = cfg.readEntry( "UseCommonProgram", false);
if ( commonSearchProgram.isEmpty() || !useCommon ) {
if ( !mView ) {
return false;
}
QString txt = i18n("Search Results for '%1':", Qt::escape(words) );
mStderr = "<b>" + txt + "</b>\n";
mView->beginSearchResult();
mView->writeSearchResult( formatter()->header( i18n("Search Results") ) );
mView->writeSearchResult( formatter()->title( txt ) );
if ( mRootTraverser ) {
kDebug() << "SearchEngine::search(): mRootTraverser not null.";
return false;
}
mRootTraverser = new SearchTraverser( this, 0 );
DocMetaInfo::self()->startTraverseEntries( mRootTraverser );
return true;
} else {
QString lang = KGlobal::locale()->language().left(2);
if ( lang.toLower() == "c" || lang.toLower() == "posix" )
lang = "en";
// if the string contains '&' replace with a '+' and set search method to and
if (mWords.indexOf("&") != -1) {
mWords.replace('&', ' ');
mMethod = "and";
}
// replace whitespace with a '+'
mWords = mWords.trimmed();
mWords = mWords.simplified();
mWords.replace(QRegExp("\\s"), "+");
commonSearchProgram = substituteSearchQuery( commonSearchProgram );
kDebug() << "Common Search: " << commonSearchProgram;
mProc = new KProcess();
*mProc << KShell::splitArgs(commonSearchProgram);
connect( mProc, SIGNAL( finished(int, QProcess::ExitStatus) ),
this, SLOT( searchExited(int, QProcess::ExitStatus) ) );
mSearchRunning = true;
mSearchResult = "";
mStderr = "<b>" + commonSearchProgram + "</b>\n\n";
mProc->start();
if (!mProc->waitForStarted()) {
kError() << "could not start search program '" << commonSearchProgram
<< "'" << endl;
delete mProc;
return false;
}
while (mSearchRunning && mProc->state() == QProcess::Running)
kapp->processEvents();
// no need to use signals/slots
mStderr += mProc->readAllStandardError();
mSearchResult += mProc->readAllStandardOutput();
if ( mProc->exitStatus() == KProcess::CrashExit || mProc->exitCode() != 0 ) {
kError() << "Unable to run search program '" << commonSearchProgram
<< "'" << endl;
delete mProc;
return false;
}
delete mProc;
// modify the search result
mSearchResult = mSearchResult.replace("http://localhost/", "file:/");
mSearchResult = mSearchResult.mid( mSearchResult.indexOf( '<' ) );
mView->beginSearchResult();
mView->writeSearchResult( mSearchResult );
mView->endSearchResult();
emit searchFinished();
}
return true;
}
QString SearchEngine::substituteSearchQuery( const QString &query )
{
QString result = query;
result.replace( QLatin1String("%k"), mWords );
result.replace( QLatin1String("%n"), QString::number( mMatches ) );
result.replace( QLatin1String("%m"), mMethod );
result.replace( QLatin1String("%l"), mLang );
result.replace( QLatin1String("%s"), mScope );
return result;
}
QString SearchEngine::substituteSearchQuery( const QString &query,
const QString &identifier, const QStringList &words, int maxResults,
Operation operation, const QString &lang, const QString& binary )
{
QString result = query;
result.replace( QLatin1String("%i"), identifier );
result.replace( QLatin1String("%w"), words.join( "+" ) );
result.replace( QLatin1String("%m"), QString::number( maxResults ) );
QString o = QLatin1String(operation == Or ? "or" : "and");
result.replace( QLatin1String("%o"), o );
result.replace( QLatin1String("%d"), Prefs::indexDirectory() );
result.replace( QLatin1String("%l"), lang );
result.replace( QLatin1String("%b"), binary );
return result;
}
Formatter *SearchEngine::formatter() const
{
return mView->formatter();
}
View *SearchEngine::view() const
{
return mView;
}
void SearchEngine::finishSearch()
{
delete mRootTraverser;
mRootTraverser = 0;
emit searchFinished();
}
QString SearchEngine::errorLog() const
{
return mStderr;
}
void SearchEngine::logError( DocEntry *entry, const QString &msg )
{
mStderr += entry->identifier() + QLatin1String(": ") + msg;
}
bool SearchEngine::isRunning() const
{
return mSearchRunning;
}
SearchHandler *SearchEngine::handler( const QString &documentType ) const
{
return mHandlers.value( documentType, 0 );
}
QStringList SearchEngine::words() const
{
return mWordList;
}
int SearchEngine::maxResults() const
{
return mMaxResults;
}
SearchEngine::Operation SearchEngine::operation() const
{
return mOperation;
}
bool SearchEngine::canSearch( DocEntry *entry )
{
return entry->docExists() && !entry->documentType().isEmpty() &&
handler( entry->documentType() );
}
bool SearchEngine::needsIndex( DocEntry *entry )
{
if ( !canSearch( entry ) ) return false;
SearchHandler *h = handler( entry->documentType() );
if ( !h || h->indexCommand( entry->identifier() ).isEmpty() ) return false;
return true;
}
}
#include "moc_searchengine.cpp"
// vim:ts=2:sw=2:et