#include "stdlib.h" #include <tqstylesheet.h> #include <kapplication.h> #include <kconfig.h> #include <kdebug.h> #include <kstandarddirs.h> #include <kprocess.h> #include <klocale.h> #include <kmessagebox.h> #include "docmetainfo.h" #include "formatter.h" #include "view.h" #include "searchhandler.h" #include "prefs.h" #include "searchengine.h" namespace KHC { SearchTraverser::SearchTraverser( SearchEngine *engine, int level ) : mMaxLevel( 999 ), mEngine( engine), mLevel( level ) { #if 0 kdDebug() << "SearchTraverser(): " << mLevel << " 0x" << TQString::number( int( this ), 16 ) << endl; #endif } SearchTraverser::~SearchTraverser() { #if 0 kdDebug() << "~SearchTraverser(): " << mLevel << " 0x" << TQString::number( int( this ), 16 ) << endl; #endif TQString 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 * ) { kdDebug() << "SearchTraverser::process()" << endl; } void SearchTraverser::startProcess( DocEntry *entry ) { // kdDebug() << "SearchTraverser::startProcess(): " << entry->name() << " " // << "SEARCH: '" << entry->search() << "'" << endl; if ( !mEngine->canSearch( entry ) || !entry->searchEnabled() ) { mNotifyee->endProcess( entry, this ); return; } // kdDebug() << "SearchTraverser::startProcess(): " << entry->identifier() // << endl; SearchHandler *handler = mEngine->handler( entry->documentType() ); if ( !handler ) { TQString txt; if ( entry->documentType().isEmpty() ) { txt = i18n("Error: No document type specified."); } else { txt = i18n("Error: No search handler for document type '%1'.") .arg( entry->documentType() ); } showSearchError( handler, entry, txt ); return; } connectHandler( handler ); handler->search( entry, mEngine->words(), mEngine->maxResults(), mEngine->operation() ); // kdDebug() << "SearchTraverser::startProcess() done: " << entry->name() << endl; } void SearchTraverser::connectHandler( SearchHandler *handler ) { TQMap<SearchHandler *,int>::Iterator it; it = mConnectCount.tqfind( handler ); int count = 0; if ( it != mConnectCount.end() ) count = *it; if ( count == 0 ) { connect( handler, TQT_SIGNAL( searchError( SearchHandler *, DocEntry *, const TQString & ) ), TQT_SLOT( showSearchError( SearchHandler *, DocEntry *, const TQString & ) ) ); connect( handler, TQT_SIGNAL( searchFinished( SearchHandler *, DocEntry *, const TQString & ) ), TQT_SLOT( showSearchResult( SearchHandler *, DocEntry *, const TQString & ) ) ); } mConnectCount[ handler ] = ++count; } void SearchTraverser::disconnectHandler( SearchHandler *handler ) { TQMap<SearchHandler *,int>::Iterator it; it = mConnectCount.tqfind( handler ); if ( it == mConnectCount.end() ) { kdError() << "SearchTraverser::disconnectHandler() handler not connected." << endl; } else { int count = *it; --count; if ( count == 0 ) { disconnect( handler, TQT_SIGNAL( searchError( SearchHandler *, DocEntry *, const TQString & ) ), this, TQT_SLOT( showSearchError( SearchHandler *, DocEntry *, const TQString & ) ) ); disconnect( handler, TQT_SIGNAL( searchFinished( SearchHandler *, DocEntry *, const TQString & ) ), this, TQT_SLOT( showSearchResult( SearchHandler *, DocEntry *, const TQString & ) ) ); } mConnectCount[ handler ] = count; } } DocEntryTraverser *SearchTraverser::createChild( DocEntry *parentEntry ) { // kdDebug() << "SearchTraverser::createChild() level " << mLevel << endl; if ( mLevel >= mMaxLevel ) { ++mLevel; return this; } else { DocEntryTraverser *t = new SearchTraverser( mEngine, mLevel + 1 ); t->setParentEntry( parentEntry ); return t; } } DocEntryTraverser *SearchTraverser::parentTraverser() { // kdDebug() << "SearchTraverser::parentTraverser(): level: " << mLevel << endl; if ( mLevel > mMaxLevel ) { return this; } else { return mParent; } } void SearchTraverser::deleteTraverser() { // kdDebug() << "SearchTraverser::deleteTraverser()" << endl; if ( mLevel > mMaxLevel ) { --mLevel; } else { delete this; } } void SearchTraverser::showSearchError( SearchHandler *handler, DocEntry *entry, const TQString &error ) { // kdDebug() << "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 TQString &result ) { // kdDebug() << "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() { // kdDebug() << "SearchTraverser::finishTraversal()" << endl; mEngine->view()->writeSearchResult( mEngine->formatter()->footer() ); mEngine->view()->endSearchResult(); mEngine->finishSearch(); } SearchEngine::SearchEngine( View *destination ) : TQObject(), mProc( 0 ), mSearchRunning( false ), mView( destination ), mRootTraverser( 0 ) { mLang = KGlobal::locale()->language().left( 2 ); } SearchEngine::~SearchEngine() { delete mRootTraverser; } bool SearchEngine::initSearchHandlers() { TQStringList resources = KGlobal::dirs()->findAllResources( "appdata", "searchhandlers/*.desktop" ); TQStringList::ConstIterator it; for( it = resources.begin(); it != resources.end(); ++it ) { TQString filename = *it; kdDebug() << "SearchEngine::initSearchHandlers(): " << filename << endl; SearchHandler *handler = SearchHandler::initFromFile( filename ); if ( !handler || !handler->checkPaths() ) { TQString txt = i18n("Unable to initialize SearchHandler from file '%1'.") .arg( filename ); kdWarning() << txt << endl; // KMessageBox::sorry( mView->widget(), txt ); } else { TQStringList documentTypes = handler->documentTypes(); TQStringList::ConstIterator it; for( it = documentTypes.begin(); it != documentTypes.end(); ++it ) { mHandlers.insert( *it, handler ); } } } if ( mHandlers.isEmpty() ) { TQString txt = i18n("No valid search handler found."); kdWarning() << txt << endl; // KMessageBox::sorry( mView->widget(), txt ); return false; } return true; } void SearchEngine::searchStdout(KProcess *, char *buffer, int len) { if ( !buffer || len == 0 ) return; TQString bufferStr; char *p; p = (char*) malloc( sizeof(char) * (len+1) ); p = strncpy( p, buffer, len ); p[len] = '\0'; mSearchResult += bufferStr.fromUtf8(p); free(p); } void SearchEngine::searchStderr(KProcess *, char *buffer, int len) { if ( !buffer || len == 0 ) return; mStderr.append( TQString::fromUtf8( buffer, len ) ); } void SearchEngine::searchExited(KProcess *) { kdDebug() << "Search terminated" << endl; mSearchRunning = false; } bool SearchEngine::search( TQString words, TQString method, int matches, TQString scope ) { if ( mSearchRunning ) return false; // These should be removed mWords = words; mMethod = method; mMatches = matches; mScope = scope; // Saner variables to store search parameters: mWordList = TQStringList::split( " ", words ); mMaxResults = matches; if ( method == "or" ) mOperation = Or; else mOperation = And; KConfig *cfg = KGlobal::config(); cfg->setGroup( "Search" ); TQString commonSearchProgram = cfg->readPathEntry( "CommonProgram" ); bool useCommon = cfg->readBoolEntry( "UseCommonProgram", false ); if ( commonSearchProgram.isEmpty() || !useCommon ) { if ( !mView ) { return false; } TQString txt = i18n("Search Results for '%1':").arg( TQStyleSheet::escape(words) ); mStderr = "<b>" + txt + "</b>\n"; mView->beginSearchResult(); mView->writeSearchResult( formatter()->header( i18n("Search Results") ) ); mView->writeSearchResult( formatter()->title( txt ) ); if ( mRootTraverser ) { kdDebug() << "SearchEngine::search(): mRootTraverser not null." << endl; return false; } mRootTraverser = new SearchTraverser( this, 0 ); DocMetaInfo::self()->startTraverseEntries( mRootTraverser ); return true; } else { TQString lang = KGlobal::locale()->language().left(2); if ( lang.lower() == "c" || lang.lower() == "posix" ) lang = "en"; // if the string contains '&' replace with a '+' and set search method to and if (mWords.tqfind("&") != -1) { mWords.tqreplace("&", " "); method = "and"; } // replace whitespace with a '+' mWords = mWords.stripWhiteSpace(); mWords = mWords.simplifyWhiteSpace(); mWords.tqreplace(TQRegExp("\\s"), "+"); commonSearchProgram = substituteSearchQuery( commonSearchProgram ); kdDebug() << "Common Search: " << commonSearchProgram << endl; mProc = new KProcess(); TQStringList cmd = TQStringList::split( " ", commonSearchProgram ); TQStringList::ConstIterator it; for( it = cmd.begin(); it != cmd.end(); ++it ) { TQString arg = *it; if ( arg.left( 1 ) == "\"" && arg.right( 1 ) =="\"" ) { arg = arg.mid( 1, arg.length() - 2 ); } *mProc << arg.utf8(); } connect( mProc, TQT_SIGNAL( receivedStdout( KProcess *, char *, int ) ), TQT_SLOT( searchStdout( KProcess *, char *, int ) ) ); connect( mProc, TQT_SIGNAL( receivedStderr( KProcess *, char *, int ) ), TQT_SLOT( searchStderr( KProcess *, char *, int ) ) ); connect( mProc, TQT_SIGNAL( processExited( KProcess * ) ), TQT_SLOT( searchExited( KProcess * ) ) ); mSearchRunning = true; mSearchResult = ""; mStderr = "<b>" + commonSearchProgram + "</b>\n\n"; mProc->start(KProcess::NotifyOnExit, KProcess::All); while (mSearchRunning && mProc->isRunning()) kapp->processEvents(); if ( !mProc->normalExit() || mProc->exitStatus() != 0 ) { kdError() << "Unable to run search program '" << commonSearchProgram << "'" << endl; delete mProc; return false; } delete mProc; // modify the search result mSearchResult = mSearchResult.tqreplace("http://localhost/", "file:/"); mSearchResult = mSearchResult.mid( mSearchResult.tqfind( '<' ) ); mView->beginSearchResult(); mView->writeSearchResult( mSearchResult ); mView->endSearchResult(); emit searchFinished(); } return true; } TQString SearchEngine::substituteSearchQuery( const TQString &query ) { TQString result = query; result.tqreplace( "%k", mWords ); result.tqreplace( "%n", TQString::number( mMatches ) ); result.tqreplace( "%m", mMethod ); result.tqreplace( "%l", mLang ); result.tqreplace( "%s", mScope ); return result; } TQString SearchEngine::substituteSearchQuery( const TQString &query, const TQString &identifier, const TQStringList &words, int maxResults, Operation operation, const TQString &lang ) { TQString result = query; result.tqreplace( "%i", identifier ); result.tqreplace( "%w", words.join( "+" ) ); result.tqreplace( "%m", TQString::number( maxResults ) ); TQString o; if ( operation == Or ) o = "or"; else o = "and"; result.tqreplace( "%o", o ); result.tqreplace( "%d", Prefs::indexDirectory() ); result.tqreplace( "%l", lang ); return result; } Formatter *SearchEngine::formatter() const { return mView->formatter(); } View *SearchEngine::view() const { return mView; } void SearchEngine::finishSearch() { delete mRootTraverser; mRootTraverser = 0; emit searchFinished(); } TQString SearchEngine::errorLog() const { return mStderr; } void SearchEngine::logError( DocEntry *entry, const TQString &msg ) { mStderr += entry->identifier() + ": " + msg; } bool SearchEngine::isRunning() const { return mSearchRunning; } SearchHandler *SearchEngine::handler( const TQString &documentType ) const { TQMap<TQString,SearchHandler *>::ConstIterator it; it = mHandlers.tqfind( documentType ); if ( it == mHandlers.end() ) return 0; else return *it; } TQStringList 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->indexCommand( entry->identifier() ).isEmpty() ) return false; return true; } } #include "searchengine.moc" // vim:ts=2:sw=2:et