1 #include "searchengine.h"
5 #include <QTextDocument>
6 #include <KApplication>
9 #include <KStandardDirs>
13 #include <KMessageBox>
15 #include "docmetainfo.h"
16 #include "formatter.h"
18 #include "searchhandler.h"
24 SearchTraverser::SearchTraverser( SearchEngine
*engine
, int level
) :
25 mMaxLevel( 999 ), mEngine( engine
), mLevel( level
)
28 kDebug() << "SearchTraverser(): " << mLevel
29 << " 0x" << QString::number( int( this ), 16 ) << endl
;
33 SearchTraverser::~SearchTraverser()
36 kDebug() << "~SearchTraverser(): " << mLevel
37 << " 0x" << QString::number( int( this ), 16 ) << endl
;
41 if ( parentEntry() ) {
42 section
= parentEntry()->name();
44 section
= ("Unknown Section");
47 if ( !mResult
.isEmpty() ) {
48 mEngine
->view()->writeSearchResult(
49 mEngine
->formatter()->sectionHeader( section
) );
50 mEngine
->view()->writeSearchResult( mResult
);
54 void SearchTraverser::process( DocEntry
* )
56 kDebug() << "SearchTraverser::process()";
59 void SearchTraverser::startProcess( DocEntry
*entry
)
61 // kDebug() << "SearchTraverser::startProcess(): " << entry->name() << " "
62 // << "SEARCH: '" << entry->search() << "'" << endl;
64 if ( !mEngine
->canSearch( entry
) || !entry
->searchEnabled() ) {
65 mNotifyee
->endProcess( entry
, this );
69 // kDebug() << "SearchTraverser::startProcess(): " << entry->identifier()
72 SearchHandler
*handler
= mEngine
->handler( entry
->documentType() );
76 if ( entry
->documentType().isEmpty() ) {
77 txt
= i18n("Error: No document type specified.");
79 txt
= i18n("Error: No search handler for document type '%1'.",
80 entry
->documentType() );
82 showSearchError( handler
, entry
, txt
);
86 connectHandler( handler
);
88 handler
->search( entry
, mEngine
->words(), mEngine
->maxResults(),
89 mEngine
->operation() );
91 // kDebug() << "SearchTraverser::startProcess() done: " << entry->name();
94 void SearchTraverser::connectHandler( SearchHandler
*handler
)
96 QMap
<SearchHandler
*,int>::Iterator it
;
97 it
= mConnectCount
.find( handler
);
99 if ( it
!= mConnectCount
.end() ) count
= *it
;
101 connect( handler
, SIGNAL( searchError( SearchHandler
*, DocEntry
*, const QString
& ) ),
102 SLOT( showSearchError( SearchHandler
*, DocEntry
*, const QString
& ) ) );
103 connect( handler
, SIGNAL( searchFinished( SearchHandler
*, DocEntry
*, const QString
& ) ),
104 SLOT( showSearchResult( SearchHandler
*, DocEntry
*, const QString
& ) ) );
106 mConnectCount
[ handler
] = ++count
;
109 void SearchTraverser::disconnectHandler( SearchHandler
*handler
)
111 QMap
<SearchHandler
*,int>::Iterator it
;
112 it
= mConnectCount
.find( handler
);
113 if ( it
== mConnectCount
.end() ) {
114 kError() << "SearchTraverser::disconnectHandler() handler not connected."
120 disconnect( handler
, SIGNAL( searchError( SearchHandler
*, DocEntry
*, const QString
& ) ),
121 this, SLOT( showSearchError( SearchHandler
*, DocEntry
*, const QString
& ) ) );
122 disconnect( handler
, SIGNAL( searchFinished( SearchHandler
*, DocEntry
*, const QString
& ) ),
123 this, SLOT( showSearchResult( SearchHandler
*, DocEntry
*, const QString
& ) ) );
125 mConnectCount
[ handler
] = count
;
129 DocEntryTraverser
*SearchTraverser::createChild( DocEntry
*parentEntry
)
131 // kDebug() << "SearchTraverser::createChild() level " << mLevel;
133 if ( mLevel
>= mMaxLevel
) {
137 DocEntryTraverser
*t
= new SearchTraverser( mEngine
, mLevel
+ 1 );
138 t
->setParentEntry( parentEntry
);
143 DocEntryTraverser
*SearchTraverser::parentTraverser()
145 // kDebug() << "SearchTraverser::parentTraverser(): level: " << mLevel;
147 if ( mLevel
> mMaxLevel
) {
154 void SearchTraverser::deleteTraverser()
156 // kDebug() << "SearchTraverser::deleteTraverser()";
158 if ( mLevel
> mMaxLevel
) {
165 void SearchTraverser::showSearchError( SearchHandler
*handler
, DocEntry
*entry
, const QString
&error
)
167 // kDebug() << "SearchTraverser::showSearchError(): " << entry->name()
170 mResult
+= mEngine
->formatter()->docTitle( entry
->name() );
171 mResult
+= mEngine
->formatter()->paragraph( error
);
173 mEngine
->logError( entry
, error
);
175 disconnectHandler( handler
);
177 mNotifyee
->endProcess( entry
, this );
180 void SearchTraverser::showSearchResult( SearchHandler
*handler
, DocEntry
*entry
, const QString
&result
)
182 // kDebug() << "SearchTraverser::showSearchResult(): " << entry->name()
185 mResult
+= mEngine
->formatter()->docTitle( entry
->name() );
186 mResult
+= mEngine
->formatter()->processResult( result
);
188 disconnectHandler( handler
);
190 mNotifyee
->endProcess( entry
, this );
193 void SearchTraverser::finishTraversal()
195 // kDebug() << "SearchTraverser::finishTraversal()";
197 mEngine
->view()->writeSearchResult( mEngine
->formatter()->footer() );
198 mEngine
->view()->endSearchResult();
200 mEngine
->finishSearch();
204 SearchEngine::SearchEngine( View
*destination
)
206 mProc( 0 ), mSearchRunning( false ), mView( destination
),
209 mLang
= KGlobal::locale()->language().left( 2 );
212 SearchEngine::~SearchEngine()
214 delete mRootTraverser
;
217 bool SearchEngine::initSearchHandlers()
219 const QStringList resources
= KGlobal::dirs()->findAllResources(
220 "appdata", "searchhandlers/*.desktop" );
221 QStringList::ConstIterator it
;
222 for( it
= resources
.constBegin(); it
!= resources
.constEnd(); ++it
) {
223 QString filename
= *it
;
224 kDebug() << "SearchEngine::initSearchHandlers(): " << filename
;
225 SearchHandler
*handler
= SearchHandler::initFromFile( filename
);
226 if ( !handler
|| !handler
->checkPaths() ) {
227 QString txt
= i18n("Unable to initialize SearchHandler from file '%1'.",
230 // KMessageBox::sorry( mView->widget(), txt );
232 QStringList documentTypes
= handler
->documentTypes();
233 QStringList::ConstIterator it
;
234 for( it
= documentTypes
.constBegin(); it
!= documentTypes
.constEnd(); ++it
) {
235 mHandlers
.insert( *it
, handler
);
240 if ( mHandlers
.isEmpty() ) {
241 QString txt
= i18n("No valid search handler found.");
243 // KMessageBox::sorry( mView->widget(), txt );
250 void SearchEngine::searchExited(int exitCode
, QProcess::ExitStatus exitStatus
)
252 kDebug() << "Search terminated";
253 mSearchRunning
= false;
256 bool SearchEngine::search( const QString
& words
, const QString
& method
, int matches
,
257 const QString
& scope
)
259 if ( mSearchRunning
) return false;
261 // These should be removed
267 // Saner variables to store search parameters:
268 mWordList
= words
.split( " ");
269 mMaxResults
= matches
;
270 if ( method
== "or" ) mOperation
= Or
;
271 else mOperation
= And
;
273 KConfigGroup
cfg(KGlobal::config(), "Search");
274 QString commonSearchProgram
= cfg
.readPathEntry( "CommonProgram", QString() );
275 bool useCommon
= cfg
.readEntry( "UseCommonProgram", false);
277 if ( commonSearchProgram
.isEmpty() || !useCommon
) {
282 QString txt
= i18n("Search Results for '%1':", Qt::escape(words
) );
284 mStderr
= "<b>" + txt
+ "</b>\n";
286 mView
->beginSearchResult();
287 mView
->writeSearchResult( formatter()->header( i18n("Search Results") ) );
288 mView
->writeSearchResult( formatter()->title( txt
) );
290 if ( mRootTraverser
) {
291 kDebug() << "SearchEngine::search(): mRootTraverser not null.";
294 mRootTraverser
= new SearchTraverser( this, 0 );
295 DocMetaInfo::self()->startTraverseEntries( mRootTraverser
);
299 QString lang
= KGlobal::locale()->language().left(2);
301 if ( lang
.toLower() == "c" || lang
.toLower() == "posix" )
304 // if the string contains '&' replace with a '+' and set search method to and
305 if (mWords
.indexOf("&") != -1) {
306 mWords
.replace("&", " ");
310 // replace whitespace with a '+'
311 mWords
= mWords
.trimmed();
312 mWords
= mWords
.simplified();
313 mWords
.replace(QRegExp("\\s"), "+");
315 commonSearchProgram
= substituteSearchQuery( commonSearchProgram
);
317 kDebug() << "Common Search: " << commonSearchProgram
;
319 mProc
= new KProcess();
320 *mProc
<< KShell::splitArgs(commonSearchProgram
);
322 connect( mProc
, SIGNAL( finished(int, QProcess::ExitStatus
) ),
323 this, SLOT( searchExited(int, QProcess::ExitStatus
) ) );
325 mSearchRunning
= true;
327 mStderr
= "<b>" + commonSearchProgram
+ "</b>\n\n";
330 if (!mProc
->waitForStarted()) {
331 kError() << "could not start search program '" << commonSearchProgram
337 while (mSearchRunning
&& mProc
->state() == QProcess::Running
)
338 kapp
->processEvents();
340 // no need to use signals/slots
341 mStderr
+= mProc
->readAllStandardError();
342 mSearchResult
+= mProc
->readAllStandardOutput();
344 if ( mProc
->exitStatus() == KProcess::CrashExit
|| mProc
->exitCode() != 0 ) {
345 kError() << "Unable to run search program '" << commonSearchProgram
354 // modify the search result
355 mSearchResult
= mSearchResult
.replace("http://localhost/", "file:/");
356 mSearchResult
= mSearchResult
.mid( mSearchResult
.indexOf( '<' ) );
358 mView
->beginSearchResult();
359 mView
->writeSearchResult( mSearchResult
);
360 mView
->endSearchResult();
362 emit
searchFinished();
368 QString
SearchEngine::substituteSearchQuery( const QString
&query
)
370 QString result
= query
;
371 result
.replace( QLatin1String("%k"), mWords
);
372 result
.replace( QLatin1String("%n"), QString::number( mMatches
) );
373 result
.replace( QLatin1String("%m"), mMethod
);
374 result
.replace( QLatin1String("%l"), mLang
);
375 result
.replace( QLatin1String("%s"), mScope
);
380 QString
SearchEngine::substituteSearchQuery( const QString
&query
,
381 const QString
&identifier
, const QStringList
&words
, int maxResults
,
382 Operation operation
, const QString
&lang
)
384 QString result
= query
;
385 result
.replace( QLatin1String("%i"), identifier
);
386 result
.replace( QLatin1String("%w"), words
.join( "+" ) );
387 result
.replace( QLatin1String("%m"), QString::number( maxResults
) );
388 QString o
= QLatin1String(operation
== Or
? "or" : "and");
389 result
.replace( QLatin1String("%o"), o
);
390 result
.replace( QLatin1String("%d"), Prefs::indexDirectory() );
391 result
.replace( QLatin1String("%l"), lang
);
396 Formatter
*SearchEngine::formatter() const
398 return mView
->formatter();
401 View
*SearchEngine::view() const
406 void SearchEngine::finishSearch()
408 delete mRootTraverser
;
411 emit
searchFinished();
414 QString
SearchEngine::errorLog() const
419 void SearchEngine::logError( DocEntry
*entry
, const QString
&msg
)
421 mStderr
+= entry
->identifier() + QLatin1String(": ") + msg
;
424 bool SearchEngine::isRunning() const
426 return mSearchRunning
;
429 SearchHandler
*SearchEngine::handler( const QString
&documentType
) const
431 return mHandlers
.value( documentType
, 0 );
434 QStringList
SearchEngine::words() const
439 int SearchEngine::maxResults() const
444 SearchEngine::Operation
SearchEngine::operation() const
449 bool SearchEngine::canSearch( DocEntry
*entry
)
451 return entry
->docExists() && !entry
->documentType().isEmpty() &&
452 handler( entry
->documentType() );
455 bool SearchEngine::needsIndex( DocEntry
*entry
)
457 if ( !canSearch( entry
) ) return false;
459 SearchHandler
*h
= handler( entry
->documentType() );
460 if ( !h
|| h
->indexCommand( entry
->identifier() ).isEmpty() ) return false;
467 #include "searchengine.moc"