index.cpp
00001 /* This file is part of KMail 00002 * Copyright (C) 2005 Luís Pedro Coelho <luis@luispedro.org> 00003 * 00004 * KMail is free software; you can redistribute it and/or modify it 00005 * under the terms of the GNU General Public License, version 2, as 00006 * published by the Free Software Foundation. 00007 * 00008 * KMail is distributed in the hope that it will be useful, but 00009 * WITHOUT ANY WARRANTY; without even the implied warranty of 00010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 * General Public License for more details. 00012 * 00013 * You should have received a copy of the GNU General Public License 00014 * along with this program; if not, write to the Free Software 00015 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00016 * 00017 * In addition, as a special exception, the copyright holders give 00018 * permission to link the code of this program with any edition of 00019 * the TQt library by Trolltech AS, Norway (or with modified versions 00020 * of TQt that use the same license as TQt), and distribute linked 00021 * combinations including the two. You must obey the GNU General 00022 * Public License in all respects for all of the code used other than 00023 * TQt. If you modify this file, you may extend this exception to 00024 * your version of the file, but you are not obligated to do so. If 00025 * you do not wish to do so, delete this exception statement from 00026 * your version. 00027 */ 00028 00029 #include "index.h" 00030 00031 #include "kmkernel.h" 00032 #include "kmfoldermgr.h" 00033 #include "kmmsgdict.h" 00034 #include "kmfolder.h" 00035 #include "kmsearchpattern.h" 00036 #include "kmfoldersearch.h" 00037 00038 #include <kdebug.h> 00039 #include <kapplication.h> 00040 #include <tqfile.h> 00041 #include <tqtimer.h> 00042 #include <tqvaluestack.h> 00043 #include <tqptrlist.h> 00044 #include <tqfileinfo.h> 00045 #ifdef HAVE_INDEXLIB 00046 #include <indexlib/create.h> 00047 #endif 00048 00049 #include <sys/types.h> 00050 #include <sys/stat.h> 00051 00052 #include <iostream> 00053 #include <algorithm> 00054 #include <cstdlib> 00055 00056 namespace { 00057 const unsigned int MaintenanceLimit = 1000; 00058 const char* const folderIndexDisabledKey = "fulltextIndexDisabled"; 00059 } 00060 00061 #ifdef HAVE_INDEXLIB 00062 static 00063 TQValueList<int> vectorToTQValueList( const std::vector<TQ_UINT32>& input ) { 00064 TQValueList<int> res; 00065 std::copy( input.begin(), input.end(), std::back_inserter( res ) ); 00066 return res; 00067 } 00068 00069 static 00070 std::vector<TQ_UINT32> TQValueListToVector( const TQValueList<int>& input ) { 00071 std::vector<TQ_UINT32> res; 00072 // res.assign( input.begin(), input.end() ) doesn't work for some reason 00073 for ( TQValueList<int>::const_iterator first = input.begin(), past = input.end(); first != past; ++first ) { 00074 res.push_back( *first ); 00075 } 00076 return res; 00077 } 00078 #endif 00079 00080 KMMsgIndex::KMMsgIndex( TQObject* parent ): 00081 TQObject( parent, "index" ), 00082 mState( s_idle ), 00083 #ifdef HAVE_INDEXLIB 00084 mLockFile( std::string( static_cast<const char*>( TQFile::encodeName( defaultPath() ) + "/lock" ) ) ), 00085 mIndex( 0 ), 00086 #endif 00087 mIndexPath( TQFile::encodeName( defaultPath() ) ), 00088 mTimer( new TQTimer( this, "mTimer" ) ), 00089 //mSyncState( ss_none ), 00090 //mSyncTimer( new TQTimer( this ) ), 00091 mSlowDown( false ) { 00092 kdDebug( 5006 ) << "KMMsgIndex::KMMsgIndex()" << endl; 00093 00094 connect( kmkernel->folderMgr(), TQT_SIGNAL( msgRemoved( KMFolder*, TQ_UINT32 ) ), TQT_SLOT( slotRemoveMessage( KMFolder*, TQ_UINT32 ) ) ); 00095 connect( kmkernel->folderMgr(), TQT_SIGNAL( msgAdded( KMFolder*, TQ_UINT32 ) ), TQT_SLOT( slotAddMessage( KMFolder*, TQ_UINT32 ) ) ); 00096 connect( kmkernel->dimapFolderMgr(), TQT_SIGNAL( msgRemoved( KMFolder*, TQ_UINT32 ) ), TQT_SLOT( slotRemoveMessage( KMFolder*, TQ_UINT32 ) ) ); 00097 connect( kmkernel->dimapFolderMgr(), TQT_SIGNAL( msgAdded( KMFolder*, TQ_UINT32 ) ), TQT_SLOT( slotAddMessage( KMFolder*, TQ_UINT32 ) ) ); 00098 00099 connect( mTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( act() ) ); 00100 //connect( mSyncTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( syncIndex() ) ); 00101 00102 #ifdef HAVE_INDEXLIB 00103 KConfigGroup cfg( KMKernel::config(), "text-index" ); 00104 if ( !cfg.readBoolEntry( "enabled", false ) ) { 00105 indexlib::remove( mIndexPath ); 00106 mLockFile.force_unlock(); 00107 mState = s_disabled; 00108 return; 00109 } 00110 if ( !mLockFile.trylock() ) { 00111 indexlib::remove( mIndexPath ); 00112 00113 mLockFile.force_unlock(); 00114 mLockFile.trylock(); 00115 } else { 00116 mIndex = indexlib::open( mIndexPath, indexlib::open_flags::fail_if_nonexistant ).release(); 00117 } 00118 if ( !mIndex ) { 00119 TQTimer::singleShot( 8000, this, TQT_SLOT( create() ) ); 00120 mState = s_willcreate; 00121 } else { 00122 if ( cfg.readBoolEntry( "creating" ) ) { 00123 TQTimer::singleShot( 8000, this, TQT_SLOT( continueCreation() ) ); 00124 mState = s_creating; 00125 } else { 00126 mPendingMsgs = TQValueListToVector( cfg.readIntListEntry( "pending" ) ); 00127 mRemovedMsgs = TQValueListToVector( cfg.readIntListEntry( "removed" ) ); 00128 } 00129 } 00130 mIndex = 0; 00131 #else 00132 mState = s_error; 00133 #endif 00134 //if ( mState == s_idle ) mSyncState = ss_synced; 00135 } 00136 00137 00138 KMMsgIndex::~KMMsgIndex() { 00139 kdDebug( 5006 ) << "KMMsgIndex::~KMMsgIndex()" << endl; 00140 #ifdef HAVE_INDEXLIB 00141 KConfigGroup cfg( KMKernel::config(), "text-index" ); 00142 cfg.writeEntry( "creating", mState == s_creating ); 00143 TQValueList<int> pendingMsg; 00144 if ( mState == s_processing ) { 00145 Q_ASSERT( mAddedMsgs.empty() ); 00146 pendingMsg = vectorToTQValueList( mPendingMsgs ); 00147 } 00148 cfg.writeEntry( "pending", pendingMsg ); 00149 cfg.writeEntry( "removed", vectorToTQValueList( mRemovedMsgs ) ); 00150 delete mIndex; 00151 #endif 00152 } 00153 00154 bool KMMsgIndex::isIndexable( KMFolder* folder ) const { 00155 if ( !folder || !folder->parent() ) return false; 00156 const KMFolderMgr* manager = folder->parent()->manager(); 00157 return manager == kmkernel->folderMgr() || manager == kmkernel->dimapFolderMgr(); 00158 } 00159 00160 bool KMMsgIndex::isIndexed( KMFolder* folder ) const { 00161 if ( !isIndexable( folder ) ) return false; 00162 KConfig* config = KMKernel::config(); 00163 KConfigGroupSaver saver( config, "Folder-" + folder->idString() ); 00164 return !config->readBoolEntry( folderIndexDisabledKey, false ); 00165 } 00166 00167 void KMMsgIndex::setEnabled( bool e ) { 00168 kdDebug( 5006 ) << "KMMsgIndex::setEnabled( " << e << " )" << endl; 00169 KConfig* config = KMKernel::config(); 00170 KConfigGroupSaver saver( config, "text-index" ); 00171 if ( config->readBoolEntry( "enabled", !e ) == e ) return; 00172 config->writeEntry( "enabled", e ); 00173 if ( e ) { 00174 switch ( mState ) { 00175 case s_idle: 00176 case s_willcreate: 00177 case s_creating: 00178 case s_processing: 00179 // nothing to do 00180 return; 00181 case s_error: 00182 // nothing can be done, probably 00183 return; 00184 case s_disabled: 00185 TQTimer::singleShot( 8000, this, TQT_SLOT( create() ) ); 00186 mState = s_willcreate; 00187 } 00188 } else { 00189 clear(); 00190 } 00191 } 00192 00193 void KMMsgIndex::setIndexingEnabled( KMFolder* folder, bool e ) { 00194 KConfig* config = KMKernel::config(); 00195 KConfigGroupSaver saver( config, "Folder-" + folder->idString() ); 00196 if ( config->readBoolEntry( folderIndexDisabledKey, e ) == e ) return; // nothing to do 00197 config->writeEntry( folderIndexDisabledKey, e ); 00198 00199 if ( e ) { 00200 switch ( mState ) { 00201 case s_idle: 00202 case s_creating: 00203 case s_processing: 00204 mPendingFolders.push_back( folder ); 00205 scheduleAction(); 00206 break; 00207 case s_willcreate: 00208 // do nothing, create() will handle this 00209 break; 00210 case s_error: 00211 case s_disabled: 00212 // nothing can be done 00213 break; 00214 } 00215 00216 } else { 00217 switch ( mState ) { 00218 case s_willcreate: 00219 // create() will notice that folder is disabled 00220 break; 00221 case s_creating: 00222 if ( std::find( mPendingFolders.begin(), mPendingFolders.end(), folder ) != mPendingFolders.end() ) { 00223 // easy: 00224 mPendingFolders.erase( std::find( mPendingFolders.begin(), mPendingFolders.end(), folder ) ); 00225 break; 00226 } 00227 //else fall-through 00228 case s_idle: 00229 case s_processing: 00230 00231 case s_error: 00232 case s_disabled: 00233 // nothing can be done 00234 break; 00235 } 00236 } 00237 } 00238 00239 void KMMsgIndex::clear() { 00240 kdDebug( 5006 ) << "KMMsgIndex::clear()" << endl; 00241 #ifdef HAVE_INDEXLIB 00242 delete mIndex; 00243 mLockFile.force_unlock(); 00244 mIndex = 0; 00245 indexlib::remove( mIndexPath ); 00246 mPendingMsgs.clear(); 00247 mPendingFolders.clear(); 00248 mMaintenanceCount = 0; 00249 mAddedMsgs.clear(); 00250 mRemovedMsgs.clear(); 00251 mExisting.clear(); 00252 mState = s_disabled; 00253 for ( std::set<KMFolder*>::const_iterator first = mOpenedFolders.begin(), past = mOpenedFolders.end(); first != past; ++first ) { 00254 ( *first )->close("msgindex"); 00255 } 00256 mOpenedFolders.clear(); 00257 for ( std::vector<Search*>::const_iterator first = mSearches.begin(), past = mSearches.end(); first != past; ++first ) { 00258 delete *first; 00259 } 00260 mSearches.clear(); 00261 mTimer->stop(); 00262 #endif 00263 } 00264 00265 void KMMsgIndex::maintenance() { 00266 #ifdef HAVE_INDEXLIB 00267 if ( mState != s_idle || kapp->hasPendingEvents() ) { 00268 TQTimer::singleShot( 8000, this, TQT_SLOT( maintenance() ) ); 00269 return; 00270 } 00271 mIndex->maintenance(); 00272 #endif 00273 } 00274 00275 int KMMsgIndex::addMessage( TQ_UINT32 serNum ) { 00276 kdDebug( 5006 ) << "KMMsgIndex::addMessage( " << serNum << " )" << endl; 00277 if ( mState == s_error ) return 0; 00278 #ifdef HAVE_INDEXLIB 00279 assert( mIndex ); 00280 if ( !mExisting.empty() && std::binary_search( mExisting.begin(), mExisting.end(), serNum ) ) return 0; 00281 00282 int idx = -1; 00283 KMFolder* folder = 0; 00284 KMMsgDict::instance()->getLocation( serNum, &folder, &idx ); 00285 if ( !folder || idx == -1 ) return -1; 00286 if ( !mOpenedFolders.count( folder ) ) { 00287 mOpenedFolders.insert( folder ); 00288 folder->open("msgindex"); 00289 } 00290 KMMessage* msg = folder->getMsg( idx ); 00291 /* I still don't know whether we should allow decryption or not. 00292 * Setting to false which makes more sense. 00293 * We keep signature to get the person's name 00294 */ 00295 TQString body = msg->asPlainText( false, false ); 00296 if ( !body.isEmpty() && static_cast<const char*>( body.latin1() ) ) { 00297 mIndex->add( body.latin1(), TQString::number( serNum ).latin1() ); 00298 } else { 00299 kdDebug( 5006 ) << "Funny, no body" << endl; 00300 } 00301 folder->unGetMsg( idx ); 00302 #endif 00303 return 0; 00304 } 00305 00306 void KMMsgIndex::act() { 00307 kdDebug( 5006 ) << "KMMsgIndex::act()" << endl; 00308 if ( kapp->hasPendingEvents() ) { 00309 //nah, some other time.. 00310 mTimer->start( 500 ); 00311 mSlowDown = true; 00312 return; 00313 } 00314 if ( mSlowDown ) { 00315 mSlowDown = false; 00316 mTimer->start( 0 ); 00317 } 00318 if ( !mPendingMsgs.empty() ) { 00319 addMessage( mPendingMsgs.back() ); 00320 mPendingMsgs.pop_back(); 00321 return; 00322 } 00323 if ( !mPendingFolders.empty() ) { 00324 KMFolder *f = mPendingFolders.back(); 00325 mPendingFolders.pop_back(); 00326 if ( !mOpenedFolders.count( f ) ) { 00327 mOpenedFolders.insert( f ); 00328 f->open("msgindex"); 00329 } 00330 const KMMsgDict* dict = KMMsgDict::instance(); 00331 KConfig* config = KMKernel::config(); 00332 KConfigGroupSaver saver( config, "Folder-" + f->idString() ); 00333 if ( config->readBoolEntry( folderIndexDisabledKey, true ) ) { 00334 for ( int i = 0; i < f->count(); ++i ) { 00335 mPendingMsgs.push_back( dict->getMsgSerNum( f, i ) ); 00336 } 00337 } 00338 return; 00339 } 00340 if ( !mAddedMsgs.empty() ) { 00341 std::swap( mAddedMsgs, mPendingMsgs ); 00342 mState = s_processing; 00343 return; 00344 } 00345 for ( std::set<KMFolder*>::const_iterator first = mOpenedFolders.begin(), past = mOpenedFolders.end(); 00346 first != past; 00347 ++first ) { 00348 ( *first )->close("msgindex"); 00349 } 00350 mOpenedFolders.clear(); 00351 mState = s_idle; 00352 mTimer->stop(); 00353 } 00354 00355 void KMMsgIndex::continueCreation() { 00356 kdDebug( 5006 ) << "KMMsgIndex::continueCreation()" << endl; 00357 #ifdef HAVE_INDEXLIB 00358 create(); 00359 unsigned count = mIndex->ndocs(); 00360 mExisting.clear(); 00361 mExisting.reserve( count ); 00362 for ( unsigned i = 0; i != count; ++i ) { 00363 mExisting.push_back( std::atoi( mIndex->lookup_docname( i ).c_str() ) ); 00364 } 00365 std::sort( mExisting.begin(), mExisting.end() ); 00366 #endif 00367 } 00368 00369 void KMMsgIndex::create() { 00370 kdDebug( 5006 ) << "KMMsgIndex::create()" << endl; 00371 00372 #ifdef HAVE_INDEXLIB 00373 if ( !TQFileInfo( mIndexPath ).exists() ) { 00374 ::mkdir( mIndexPath, S_IRWXU ); 00375 } 00376 mState = s_creating; 00377 if ( !mIndex ) mIndex = indexlib::create( mIndexPath ).release(); 00378 if ( !mIndex ) { 00379 kdDebug( 5006 ) << "Error creating index" << endl; 00380 mState = s_error; 00381 return; 00382 } 00383 TQValueStack<KMFolderDir*> folders; 00384 folders.push(&(kmkernel->folderMgr()->dir())); 00385 folders.push(&(kmkernel->dimapFolderMgr()->dir())); 00386 while ( !folders.empty() ) { 00387 KMFolderDir *dir = folders.pop(); 00388 for(KMFolderNode *child = dir->first(); child; child = dir->next()) { 00389 if ( child->isDir() ) 00390 folders.push((KMFolderDir*)child); 00391 else 00392 mPendingFolders.push_back( (KMFolder*)child ); 00393 } 00394 } 00395 mTimer->start( 4000 ); // wait a couple of seconds before starting up... 00396 mSlowDown = true; 00397 #endif 00398 } 00399 00400 bool KMMsgIndex::startQuery( KMSearch* s ) { 00401 kdDebug( 5006 ) << "KMMsgIndex::startQuery( . )" << endl; 00402 if ( mState != s_idle ) return false; 00403 if ( !isIndexed( s->root() ) || !canHandleQuery( s->searchPattern() ) ) return false; 00404 00405 kdDebug( 5006 ) << "KMMsgIndex::startQuery( . ) starting query" << endl; 00406 Search* search = new Search( s ); 00407 connect( search, TQT_SIGNAL( finished( bool ) ), s, TQT_SIGNAL( finished( bool ) ) ); 00408 connect( search, TQT_SIGNAL( finished( bool ) ), s, TQT_SLOT( indexFinished() ) ); 00409 connect( search, TQT_SIGNAL( destroyed( TQObject* ) ), TQT_SLOT( removeSearch( TQObject* ) ) ); 00410 connect( search, TQT_SIGNAL( found( TQ_UINT32 ) ), s, TQT_SIGNAL( found( TQ_UINT32 ) ) ); 00411 mSearches.push_back( search ); 00412 return true; 00413 } 00414 00415 00416 //void KMMsgIndex::startSync() { 00417 // switch ( mSyncState ) { 00418 // case ss_none: 00419 // mIndex->start_sync(); 00420 // mSyncState = ss_started; 00421 // mSyncTimer.start( 4000, true ); 00422 // break; 00423 // case ss_started: 00424 // mIndex->sync_now(); 00425 // mSyncState = ss_synced; 00426 // mLockFile.unlock(); 00427 // break; 00428 // } 00429 //} 00430 // 00431 //void KMMsgIndex::finishSync() { 00432 // 00433 //} 00434 00435 void KMMsgIndex::removeSearch( TQObject* destroyed ) { 00436 mSearches.erase( std::find( mSearches.begin(), mSearches.end(), destroyed ) ); 00437 } 00438 00439 00440 bool KMMsgIndex::stopQuery( KMSearch* s ) { 00441 kdDebug( 5006 ) << "KMMsgIndex::stopQuery( . )" << endl; 00442 for ( std::vector<Search*>::iterator iter = mSearches.begin(), past = mSearches.end(); iter != past; ++iter ) { 00443 if ( ( *iter )->search() == s ) { 00444 delete *iter; 00445 mSearches.erase( iter ); 00446 return true; 00447 } 00448 } 00449 return false; 00450 } 00451 00452 std::vector<TQ_UINT32> KMMsgIndex::simpleSearch( TQString s, bool* ok ) const { 00453 kdDebug( 5006 ) << "KMMsgIndex::simpleSearch( -" << s.latin1() << "- )" << endl; 00454 if ( mState == s_error || mState == s_disabled ) { 00455 if ( ok ) *ok = false; 00456 return std::vector<TQ_UINT32>(); 00457 } 00458 std::vector<TQ_UINT32> res; 00459 #ifdef HAVE_INDEXLIB 00460 assert( mIndex ); 00461 std::vector<unsigned> residx = mIndex->search( s.latin1() )->list(); 00462 res.reserve( residx.size() ); 00463 for ( std::vector<unsigned>::const_iterator first = residx.begin(), past = residx.end();first != past; ++first ) { 00464 res.push_back( std::atoi( mIndex->lookup_docname( *first ).c_str() ) ); 00465 } 00466 if ( ok ) *ok = true; 00467 #endif 00468 return res; 00469 } 00470 00471 bool KMMsgIndex::canHandleQuery( const KMSearchPattern* pat ) const { 00472 kdDebug( 5006 ) << "KMMsgIndex::canHandleQuery( . )" << endl; 00473 if ( !pat ) return false; 00474 TQPtrListIterator<KMSearchRule> it( *pat ); 00475 KMSearchRule* rule; 00476 while ( (rule = it.current()) != 0 ) { 00477 ++it; 00478 if ( !rule->field().isEmpty() && !rule->contents().isEmpty() && 00479 rule->function() == KMSearchRule::FuncContains && 00480 rule->field() == "<body>" ) return true; 00481 } 00482 return false; 00483 } 00484 00485 void KMMsgIndex::slotAddMessage( KMFolder*, TQ_UINT32 serNum ) { 00486 kdDebug( 5006 ) << "KMMsgIndex::slotAddMessage( . , " << serNum << " )" << endl; 00487 if ( mState == s_error || mState == s_disabled ) return; 00488 00489 if ( mState == s_creating ) mAddedMsgs.push_back( serNum ); 00490 else mPendingMsgs.push_back( serNum ); 00491 00492 if ( mState == s_idle ) mState = s_processing; 00493 scheduleAction(); 00494 } 00495 00496 void KMMsgIndex::slotRemoveMessage( KMFolder*, TQ_UINT32 serNum ) { 00497 kdDebug( 5006 ) << "KMMsgIndex::slotRemoveMessage( . , " << serNum << " )" << endl; 00498 if ( mState == s_error || mState == s_disabled ) return; 00499 00500 if ( mState == s_idle ) mState = s_processing; 00501 mRemovedMsgs.push_back( serNum ); 00502 scheduleAction(); 00503 } 00504 00505 void KMMsgIndex::scheduleAction() { 00506 #ifdef HAVE_INDEXLIB 00507 if ( mState == s_willcreate || !mIndex ) return; 00508 if ( !mSlowDown ) mTimer->start( 0 ); 00509 #endif 00510 } 00511 00512 void KMMsgIndex::removeMessage( TQ_UINT32 serNum ) { 00513 kdDebug( 5006 ) << "KMMsgIndex::removeMessage( " << serNum << " )" << endl; 00514 if ( mState == s_error || mState == s_disabled ) return; 00515 00516 #ifdef HAVE_INDEXLIB 00517 mIndex->remove_doc( TQString::number( serNum ).latin1() ); 00518 ++mMaintenanceCount; 00519 if ( mMaintenanceCount > MaintenanceLimit && mRemovedMsgs.empty() ) { 00520 TQTimer::singleShot( 100, this, TQT_SLOT( maintenance() ) ); 00521 } 00522 #endif 00523 } 00524 00525 TQString KMMsgIndex::defaultPath() { 00526 return KMKernel::localDataPath() + "text-index"; 00527 } 00528 00529 bool KMMsgIndex::creating() const { 00530 return !mPendingMsgs.empty() || !mPendingFolders.empty(); 00531 } 00532 00533 KMMsgIndex::Search::Search( KMSearch* s ): 00534 mSearch( s ), 00535 mTimer( new TQTimer( this, "mTimer" ) ), 00536 mResidual( new KMSearchPattern ), 00537 mState( s_starting ) { 00538 connect( mTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( act() ) ); 00539 mTimer->start( 0 ); 00540 } 00541 00542 KMMsgIndex::Search::~Search() { 00543 delete mTimer; 00544 } 00545 00546 void KMMsgIndex::Search::act() { 00547 switch ( mState ) { 00548 case s_starting: { 00549 KMSearchPattern* pat = mSearch->searchPattern(); 00550 TQString terms; 00551 for ( KMSearchRule* rule = pat->first(); rule; rule = pat->next() ) { 00552 Q_ASSERT( rule->function() == KMSearchRule::FuncContains ); 00553 terms += TQString::fromLatin1( " %1 " ).arg( rule->contents() ); 00554 } 00555 00556 mValues = kmkernel->msgIndex()->simpleSearch( terms, 0 ); 00557 break; 00558 } 00559 case s_emitstopped: 00560 mTimer->start( 0 ); 00561 mState = s_emitting; 00562 // fall throu 00563 case s_emitting: 00564 if ( kapp->hasPendingEvents() ) { 00565 //nah, some other time.. 00566 mTimer->start( 250 ); 00567 mState = s_emitstopped; 00568 return; 00569 } 00570 for ( int i = 0; i != 16 && !mValues.empty(); ++i ) { 00571 KMFolder* folder; 00572 int index; 00573 KMMsgDict::instance()->getLocation( mValues.back(), &folder, &index ); 00574 if ( folder && 00575 mSearch->inScope( folder ) && 00576 ( !mResidual || mResidual->matches( mValues.back() ) ) ) { 00577 00578 emit found( mValues.back() ); 00579 } 00580 mValues.pop_back(); 00581 } 00582 if ( mValues.empty() ) { 00583 emit finished( true ); 00584 mState = s_done; 00585 mTimer->stop(); 00586 delete this; 00587 } 00588 break; 00589 default: 00590 Q_ASSERT( 0 ); 00591 } 00592 } 00593 #include "index.moc" 00594