kdirlister.cpp
00001 /* This file is part of the KDE project 00002 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> 00003 2000 Carsten Pfeiffer <pfeiffer@kde.org> 00004 2001-2005 Michael Brade <brade@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "kdirlister.h" 00023 00024 #include <qregexp.h> 00025 #include <qptrlist.h> 00026 #include <qtimer.h> 00027 00028 #include <kapplication.h> 00029 #include <kdebug.h> 00030 #include <klocale.h> 00031 #include <kio/job.h> 00032 #include <kmessagebox.h> 00033 #include <kglobal.h> 00034 #include <kglobalsettings.h> 00035 #include <kstaticdeleter.h> 00036 #include <kprotocolinfo.h> 00037 00038 #include "kdirlister_p.h" 00039 00040 #include <assert.h> 00041 00042 KDirListerCache* KDirListerCache::s_pSelf = 0; 00043 static KStaticDeleter<KDirListerCache> sd_KDirListerCache; 00044 00045 // Enable this to get printDebug() called often, to see the contents of the cache 00046 //#define DEBUG_CACHE 00047 00048 // Make really sure it doesn't get activated in the final build 00049 #ifdef NDEBUG 00050 #undef DEBUG_CACHE 00051 #endif 00052 00053 KDirListerCache::KDirListerCache( int maxCount ) 00054 : itemsCached( maxCount ) 00055 { 00056 kdDebug(7004) << "+KDirListerCache" << endl; 00057 00058 itemsInUse.setAutoDelete( false ); 00059 itemsCached.setAutoDelete( true ); 00060 urlsCurrentlyListed.setAutoDelete( true ); 00061 urlsCurrentlyHeld.setAutoDelete( true ); 00062 pendingUpdates.setAutoDelete( true ); 00063 00064 connect( kdirwatch, SIGNAL( dirty( const QString& ) ), 00065 this, SLOT( slotFileDirty( const QString& ) ) ); 00066 connect( kdirwatch, SIGNAL( created( const QString& ) ), 00067 this, SLOT( slotFileCreated( const QString& ) ) ); 00068 connect( kdirwatch, SIGNAL( deleted( const QString& ) ), 00069 this, SLOT( slotFileDeleted( const QString& ) ) ); 00070 } 00071 00072 KDirListerCache::~KDirListerCache() 00073 { 00074 kdDebug(7004) << "-KDirListerCache" << endl; 00075 00076 itemsInUse.setAutoDelete( true ); 00077 itemsInUse.clear(); 00078 itemsCached.clear(); 00079 urlsCurrentlyListed.clear(); 00080 urlsCurrentlyHeld.clear(); 00081 00082 if ( KDirWatch::exists() ) 00083 kdirwatch->disconnect( this ); 00084 } 00085 00086 // setting _reload to true will emit the old files and 00087 // call updateDirectory 00088 bool KDirListerCache::listDir( KDirLister *lister, const KURL& _u, 00089 bool _keep, bool _reload ) 00090 { 00091 // like this we don't have to worry about trailing slashes any further 00092 KURL _url = _u; 00093 _url.cleanPath(); // kill consecutive slashes 00094 _url.adjustPath(-1); 00095 QString urlStr = _url.url(); 00096 00097 if ( !lister->validURL( _url ) ) 00098 return false; 00099 00100 #ifdef DEBUG_CACHE 00101 printDebug(); 00102 #endif 00103 kdDebug(7004) << k_funcinfo << lister << " url=" << _url 00104 << " keep=" << _keep << " reload=" << _reload << endl; 00105 00106 if ( !_keep ) 00107 { 00108 // stop any running jobs for lister 00109 stop( lister ); 00110 00111 // clear our internal list for lister 00112 forgetDirs( lister ); 00113 00114 lister->d->rootFileItem = 0; 00115 } 00116 else if ( lister->d->lstDirs.find( _url ) != lister->d->lstDirs.end() ) 00117 { 00118 // stop the job listing _url for this lister 00119 stop( lister, _url ); 00120 00121 // clear _url for lister 00122 forgetDirs( lister, _url, true ); 00123 00124 if ( lister->d->url == _url ) 00125 lister->d->rootFileItem = 0; 00126 } 00127 00128 lister->d->lstDirs.append( _url ); 00129 00130 if ( lister->d->url.isEmpty() || !_keep ) // set toplevel URL only if not set yet 00131 lister->d->url = _url; 00132 00133 DirItem *itemU = itemsInUse[urlStr]; 00134 DirItem *itemC; 00135 00136 if ( !urlsCurrentlyListed[urlStr] ) 00137 { 00138 // if there is an update running for _url already we get into 00139 // the following case - it will just be restarted by updateDirectory(). 00140 00141 if ( itemU ) 00142 { 00143 kdDebug(7004) << "listDir: Entry already in use: " << _url << endl; 00144 00145 bool oldState = lister->d->complete; 00146 lister->d->complete = false; 00147 00148 emit lister->started( _url ); 00149 00150 if ( !lister->d->rootFileItem && lister->d->url == _url ) 00151 lister->d->rootFileItem = itemU->rootItem; 00152 00153 lister->addNewItems( *(itemU->lstItems) ); 00154 lister->emitItems(); 00155 00156 // _url is already in use, so there is already an entry in urlsCurrentlyHeld 00157 assert( urlsCurrentlyHeld[urlStr] ); 00158 urlsCurrentlyHeld[urlStr]->append( lister ); 00159 00160 lister->d->complete = oldState; 00161 00162 emit lister->completed( _url ); 00163 if ( lister->d->complete ) 00164 emit lister->completed(); 00165 00166 if ( _reload || !itemU->complete ) 00167 updateDirectory( _url ); 00168 } 00169 else if ( !_reload && (itemC = itemsCached.take( urlStr )) ) 00170 { 00171 kdDebug(7004) << "listDir: Entry in cache: " << _url << endl; 00172 00173 itemC->decAutoUpdate(); 00174 itemsInUse.insert( urlStr, itemC ); 00175 itemU = itemC; 00176 00177 bool oldState = lister->d->complete; 00178 lister->d->complete = false; 00179 00180 emit lister->started( _url ); 00181 00182 if ( !lister->d->rootFileItem && lister->d->url == _url ) 00183 lister->d->rootFileItem = itemC->rootItem; 00184 00185 lister->addNewItems( *(itemC->lstItems) ); 00186 lister->emitItems(); 00187 00188 Q_ASSERT( !urlsCurrentlyHeld[urlStr] ); 00189 QPtrList<KDirLister> *list = new QPtrList<KDirLister>; 00190 list->append( lister ); 00191 urlsCurrentlyHeld.insert( urlStr, list ); 00192 00193 lister->d->complete = oldState; 00194 00195 emit lister->completed( _url ); 00196 if ( lister->d->complete ) 00197 emit lister->completed(); 00198 00199 if ( !itemC->complete ) 00200 updateDirectory( _url ); 00201 } 00202 else // dir not in cache or _reload is true 00203 { 00204 kdDebug(7004) << "listDir: Entry not in cache or reloaded: " << _url << endl; 00205 00206 QPtrList<KDirLister> *list = new QPtrList<KDirLister>; 00207 list->append( lister ); 00208 urlsCurrentlyListed.insert( urlStr, list ); 00209 00210 itemsCached.remove( urlStr ); 00211 itemU = new DirItem( _url ); 00212 itemsInUse.insert( urlStr, itemU ); 00213 00214 // // we have a limit of MAX_JOBS_PER_LISTER concurrently running jobs 00215 // if ( lister->numJobs() >= MAX_JOBS_PER_LISTER ) 00216 // { 00217 // lstPendingUpdates.append( _url ); 00218 // } 00219 // else 00220 // { 00221 00222 if ( lister->d->url == _url ) 00223 lister->d->rootFileItem = 0; 00224 00225 KIO::ListJob* job = KIO::listDir( _url, false /* no default GUI */ ); 00226 jobs.insert( job, QValueList<KIO::UDSEntry>() ); 00227 00228 lister->jobStarted( job ); 00229 lister->connectJob( job ); 00230 00231 if ( lister->d->window ) 00232 job->setWindow( lister->d->window ); 00233 00234 connect( job, SIGNAL( entries( KIO::Job *, const KIO::UDSEntryList & ) ), 00235 this, SLOT( slotEntries( KIO::Job *, const KIO::UDSEntryList & ) ) ); 00236 connect( job, SIGNAL( result( KIO::Job * ) ), 00237 this, SLOT( slotResult( KIO::Job * ) ) ); 00238 connect( job, SIGNAL( redirection( KIO::Job *, const KURL & ) ), 00239 this, SLOT( slotRedirection( KIO::Job *, const KURL & ) ) ); 00240 00241 emit lister->started( _url ); 00242 00243 // } 00244 } 00245 } 00246 else 00247 { 00248 kdDebug(7004) << "listDir: Entry currently being listed: " << _url << endl; 00249 00250 emit lister->started( _url ); 00251 00252 urlsCurrentlyListed[urlStr]->append( lister ); 00253 00254 KIO::ListJob *job = jobForUrl( urlStr ); 00255 Q_ASSERT( job ); 00256 00257 lister->jobStarted( job ); 00258 lister->connectJob( job ); 00259 00260 Q_ASSERT( itemU ); 00261 00262 if ( !lister->d->rootFileItem && lister->d->url == _url ) 00263 lister->d->rootFileItem = itemU->rootItem; 00264 00265 lister->addNewItems( *(itemU->lstItems) ); 00266 lister->emitItems(); 00267 } 00268 00269 // automatic updating of directories 00270 if ( lister->d->autoUpdate ) 00271 itemU->incAutoUpdate(); 00272 00273 return true; 00274 } 00275 00276 bool KDirListerCache::validURL( const KDirLister *lister, const KURL& url ) const 00277 { 00278 if ( !url.isValid() ) 00279 { 00280 if ( lister->d->autoErrorHandling ) 00281 { 00282 QString tmp = i18n("Malformed URL\n%1").arg( url.prettyURL() ); 00283 KMessageBox::error( lister->d->errorParent, tmp ); 00284 } 00285 return false; 00286 } 00287 00288 if ( !KProtocolInfo::supportsListing( url ) ) 00289 { 00290 if ( lister->d->autoErrorHandling ) 00291 { 00292 // TODO: this message should be changed during next string unfreeze! 00293 QString tmp = i18n("Malformed URL\n%1").arg( url.prettyURL() ); 00294 KMessageBox::error( lister->d->errorParent, tmp ); 00295 } 00296 return false; 00297 } 00298 00299 return true; 00300 } 00301 00302 void KDirListerCache::stop( KDirLister *lister ) 00303 { 00304 #ifdef DEBUG_CACHE 00305 printDebug(); 00306 #endif 00307 kdDebug(7004) << k_funcinfo << "lister: " << lister << endl; 00308 bool stopped = false; 00309 00310 QDictIterator< QPtrList<KDirLister> > it( urlsCurrentlyListed ); 00311 QPtrList<KDirLister> *listers; 00312 while ( (listers = it.current()) ) 00313 { 00314 if ( listers->findRef( lister ) > -1 ) 00315 { 00316 // lister is listing url 00317 QString url = it.currentKey(); 00318 00319 //kdDebug(7004) << k_funcinfo << " found lister in list - for " << url << endl; 00320 bool ret = listers->removeRef( lister ); 00321 Q_ASSERT( ret ); 00322 00323 KIO::ListJob *job = jobForUrl( url ); 00324 if ( job ) 00325 lister->jobDone( job ); 00326 00327 // move lister to urlsCurrentlyHeld 00328 QPtrList<KDirLister> *holders = urlsCurrentlyHeld[url]; 00329 if ( !holders ) 00330 { 00331 holders = new QPtrList<KDirLister>; 00332 urlsCurrentlyHeld.insert( url, holders ); 00333 } 00334 00335 holders->append( lister ); 00336 00337 emit lister->canceled( KURL( url ) ); 00338 00339 //kdDebug(7004) << k_funcinfo << "remaining list: " << listers->count() << " listers" << endl; 00340 00341 if ( listers->isEmpty() ) 00342 { 00343 // kill the job since it isn't used any more 00344 if ( job ) 00345 killJob( job ); 00346 00347 urlsCurrentlyListed.remove( url ); 00348 } 00349 00350 stopped = true; 00351 } 00352 else 00353 ++it; 00354 } 00355 00356 if ( stopped ) 00357 { 00358 emit lister->canceled(); 00359 lister->d->complete = true; 00360 } 00361 00362 // this is wrong if there is still an update running! 00363 //Q_ASSERT( lister->d->complete ); 00364 } 00365 00366 void KDirListerCache::stop( KDirLister *lister, const KURL& _u ) 00367 { 00368 QString urlStr( _u.url(-1) ); 00369 KURL _url( urlStr ); 00370 00371 // TODO: consider to stop all the "child jobs" of _url as well 00372 kdDebug(7004) << k_funcinfo << lister << " url=" << _url << endl; 00373 00374 QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr]; 00375 if ( !listers || !listers->removeRef( lister ) ) 00376 return; 00377 00378 // move lister to urlsCurrentlyHeld 00379 QPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr]; 00380 if ( !holders ) 00381 { 00382 holders = new QPtrList<KDirLister>; 00383 urlsCurrentlyHeld.insert( urlStr, holders ); 00384 } 00385 00386 holders->append( lister ); 00387 00388 00389 KIO::ListJob *job = jobForUrl( urlStr ); 00390 if ( job ) 00391 lister->jobDone( job ); 00392 00393 emit lister->canceled( _url ); 00394 00395 if ( listers->isEmpty() ) 00396 { 00397 // kill the job since it isn't used any more 00398 if ( job ) 00399 killJob( job ); 00400 00401 urlsCurrentlyListed.remove( urlStr ); 00402 } 00403 00404 if ( lister->numJobs() == 0 ) 00405 { 00406 lister->d->complete = true; 00407 00408 // we killed the last job for lister 00409 emit lister->canceled(); 00410 } 00411 } 00412 00413 void KDirListerCache::setAutoUpdate( KDirLister *lister, bool enable ) 00414 { 00415 // IMPORTANT: this method does not check for the current autoUpdate state! 00416 00417 for ( KURL::List::Iterator it = lister->d->lstDirs.begin(); 00418 it != lister->d->lstDirs.end(); ++it ) 00419 { 00420 if ( enable ) 00421 itemsInUse[(*it).url()]->incAutoUpdate(); 00422 else 00423 itemsInUse[(*it).url()]->decAutoUpdate(); 00424 } 00425 } 00426 00427 void KDirListerCache::forgetDirs( KDirLister *lister ) 00428 { 00429 kdDebug(7004) << k_funcinfo << lister << endl; 00430 00431 emit lister->clear(); 00432 00433 // forgetDirs() will modify lstDirs, make a copy first 00434 KURL::List lstDirsCopy = lister->d->lstDirs; 00435 for ( KURL::List::Iterator it = lstDirsCopy.begin(); 00436 it != lstDirsCopy.end(); ++it ) 00437 { 00438 forgetDirs( lister, *it, false ); 00439 } 00440 } 00441 00442 void KDirListerCache::forgetDirs( KDirLister *lister, const KURL& _url, bool notify ) 00443 { 00444 kdDebug(7004) << k_funcinfo << lister << " _url: " << _url << endl; 00445 00446 KURL url( _url ); 00447 url.adjustPath( -1 ); 00448 QString urlStr = url.url(); 00449 QPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr]; 00450 Q_ASSERT( holders ); 00451 holders->removeRef( lister ); 00452 00453 // remove the dir from lister->d->lstDirs so that it doesn't contain things 00454 // that itemsInUse doesn't. When emitting the canceled signals lstDirs must 00455 // not contain anything that itemsInUse does not contain. (otherwise it 00456 // might crash in findByName()). 00457 lister->d->lstDirs.remove( lister->d->lstDirs.find( url ) ); 00458 00459 DirItem *item = itemsInUse[urlStr]; 00460 Q_ASSERT( item ); 00461 00462 if ( holders->isEmpty() ) 00463 { 00464 urlsCurrentlyHeld.remove( urlStr ); // this deletes the (empty) holders list 00465 if ( !urlsCurrentlyListed[urlStr] ) 00466 { 00467 // item not in use anymore -> move into cache if complete 00468 itemsInUse.remove( urlStr ); 00469 00470 // this job is a running update 00471 KIO::ListJob *job = jobForUrl( urlStr ); 00472 if ( job ) 00473 { 00474 lister->jobDone( job ); 00475 killJob( job ); 00476 kdDebug(7004) << k_funcinfo << "Killing update job for " << urlStr << endl; 00477 00478 emit lister->canceled( url ); 00479 if ( lister->numJobs() == 0 ) 00480 { 00481 lister->d->complete = true; 00482 emit lister->canceled(); 00483 } 00484 } 00485 00486 if ( notify ) 00487 emit lister->clear( url ); 00488 00489 if ( item->complete ) 00490 { 00491 kdDebug(7004) << k_funcinfo << lister << " item moved into cache: " << url << endl; 00492 itemsCached.insert( urlStr, item ); // TODO: may return false!! 00493 00494 // Should we forget the dir for good, or keep a watch on it? 00495 // Generally keep a watch, except when it would prevent 00496 // unmounting a removable device (#37780) 00497 const bool isLocal = item->url.isLocalFile(); 00498 const bool isManuallyMounted = isLocal && KIO::manually_mounted( item->url.path() ); 00499 bool containsManuallyMounted = false; 00500 if ( !isManuallyMounted && item->lstItems && isLocal ) 00501 { 00502 // Look for a manually-mounted directory inside 00503 // If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM 00504 // I hope this isn't too slow (manually_mounted caches the last device so most 00505 // of the time this is just a stat per subdir) 00506 KFileItemListIterator kit( *item->lstItems ); 00507 for ( ; kit.current() && !containsManuallyMounted; ++kit ) 00508 if ( (*kit)->isDir() && KIO::manually_mounted( (*kit)->url().path() ) ) 00509 containsManuallyMounted = true; 00510 } 00511 00512 if ( isManuallyMounted || containsManuallyMounted ) 00513 { 00514 kdDebug(7004) << "Not adding a watch on " << item->url << " because it " << 00515 ( isManuallyMounted ? "is manually mounted" : "contains a manually mounted subdir" ) << endl; 00516 item->complete = false; // set to "dirty" 00517 } 00518 else 00519 item->incAutoUpdate(); // keep watch 00520 } 00521 else 00522 { 00523 delete item; 00524 item = 0; 00525 } 00526 } 00527 } 00528 00529 if ( item && lister->d->autoUpdate ) 00530 item->decAutoUpdate(); 00531 } 00532 00533 void KDirListerCache::updateDirectory( const KURL& _dir ) 00534 { 00535 kdDebug(7004) << k_funcinfo << _dir << endl; 00536 00537 QString urlStr = _dir.url(-1); 00538 if ( !checkUpdate( urlStr ) ) 00539 return; 00540 00541 // A job can be running to 00542 // - only list a new directory: the listers are in urlsCurrentlyListed 00543 // - only update a directory: the listers are in urlsCurrentlyHeld 00544 // - update a currently running listing: the listers are in urlsCurrentlyListed 00545 // and urlsCurrentlyHeld 00546 00547 QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr]; 00548 QPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr]; 00549 00550 // restart the job for _dir if it is running already 00551 bool killed = false; 00552 QWidget *window = 0; 00553 KIO::ListJob *job = jobForUrl( urlStr ); 00554 if ( job ) 00555 { 00556 window = job->window(); 00557 00558 killJob( job ); 00559 killed = true; 00560 00561 if ( listers ) 00562 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00563 kdl->jobDone( job ); 00564 00565 if ( holders ) 00566 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 00567 kdl->jobDone( job ); 00568 } 00569 kdDebug(7004) << k_funcinfo << "Killed = " << killed << endl; 00570 00571 // we don't need to emit canceled signals since we only replaced the job, 00572 // the listing is continuing. 00573 00574 Q_ASSERT( !listers || (listers && killed) ); 00575 00576 job = KIO::listDir( _dir, false /* no default GUI */ ); 00577 jobs.insert( job, QValueList<KIO::UDSEntry>() ); 00578 00579 connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )), 00580 this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) ); 00581 connect( job, SIGNAL(result( KIO::Job * )), 00582 this, SLOT(slotUpdateResult( KIO::Job * )) ); 00583 00584 kdDebug(7004) << k_funcinfo << "update started in " << _dir << endl; 00585 00586 if ( listers ) 00587 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00588 kdl->jobStarted( job ); 00589 00590 if ( holders ) 00591 { 00592 if ( !killed ) 00593 { 00594 bool first = true; 00595 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 00596 { 00597 kdl->jobStarted( job ); 00598 if ( first && kdl->d->window ) 00599 { 00600 first = false; 00601 job->setWindow( kdl->d->window ); 00602 } 00603 emit kdl->started( _dir ); 00604 } 00605 } 00606 else 00607 { 00608 job->setWindow( window ); 00609 00610 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 00611 kdl->jobStarted( job ); 00612 } 00613 } 00614 } 00615 00616 bool KDirListerCache::checkUpdate( const QString& _dir ) 00617 { 00618 if ( !itemsInUse[_dir] ) 00619 { 00620 DirItem *item = itemsCached[_dir]; 00621 if ( item && item->complete ) 00622 { 00623 item->complete = false; 00624 item->decAutoUpdate(); 00625 // Hmm, this debug output might include login/password from the _dir URL. 00626 //kdDebug(7004) << k_funcinfo << "directory " << _dir << " not in use, marked dirty." << endl; 00627 } 00628 //else 00629 //kdDebug(7004) << k_funcinfo << "aborted, directory " << _dir << " not in cache." << endl; 00630 00631 return false; 00632 } 00633 else 00634 return true; 00635 } 00636 00637 KFileItemList *KDirListerCache::itemsForDir( const KURL &_dir ) const 00638 { 00639 QString urlStr = _dir.url(-1); 00640 DirItem *item = itemsInUse[ urlStr ]; 00641 if ( !item ) 00642 item = itemsCached[ urlStr ]; 00643 return item ? item->lstItems : 0; 00644 } 00645 00646 KFileItem *KDirListerCache::findByName( const KDirLister *lister, const QString& _name ) const 00647 { 00648 Q_ASSERT( lister ); 00649 00650 for ( KURL::List::Iterator it = lister->d->lstDirs.begin(); 00651 it != lister->d->lstDirs.end(); ++it ) 00652 { 00653 KFileItemListIterator kit( *itemsInUse[(*it).url()]->lstItems ); 00654 for ( ; kit.current(); ++kit ) 00655 if ( (*kit)->name() == _name ) 00656 return (*kit); 00657 } 00658 00659 return 0L; 00660 } 00661 00662 KFileItem *KDirListerCache::findByURL( const KDirLister *lister, const KURL& _u ) const 00663 { 00664 KURL _url = _u; 00665 _url.adjustPath(-1); 00666 00667 KURL parentDir( _url ); 00668 parentDir.setPath( parentDir.directory() ); 00669 00670 // If lister is set, check that it contains this dir 00671 if ( lister && !lister->d->lstDirs.contains( parentDir ) ) 00672 return 0L; 00673 00674 KFileItemList *itemList = itemsForDir( parentDir ); 00675 if ( itemList ) 00676 { 00677 KFileItemListIterator kit( *itemList ); 00678 for ( ; kit.current(); ++kit ) 00679 if ( (*kit)->url() == _url ) 00680 return (*kit); 00681 } 00682 return 0L; 00683 } 00684 00685 void KDirListerCache::FilesAdded( const KURL &dir ) 00686 { 00687 kdDebug(7004) << k_funcinfo << dir << endl; 00688 updateDirectory( dir ); 00689 } 00690 00691 void KDirListerCache::FilesRemoved( const KURL::List &fileList ) 00692 { 00693 kdDebug(7004) << k_funcinfo << endl; 00694 KURL::List::ConstIterator it = fileList.begin(); 00695 for ( ; it != fileList.end() ; ++it ) 00696 { 00697 // emit the deleteItem signal if this file was shown in any view 00698 KFileItem *fileitem = 0L; 00699 KURL parentDir( *it ); 00700 parentDir.setPath( parentDir.directory() ); 00701 KFileItemList *lstItems = itemsForDir( parentDir ); 00702 if ( lstItems ) 00703 { 00704 KFileItem *fit = lstItems->first(); 00705 for ( ; fit; fit = lstItems->next() ) 00706 if ( fit->url() == *it ) { 00707 fileitem = fit; 00708 lstItems->take(); // remove fileitem from list 00709 break; 00710 } 00711 } 00712 00713 // Tell the views about it before deleting the KFileItems. They might need the subdirs' 00714 // file items (see the dirtree). 00715 if ( fileitem ) 00716 { 00717 QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDir.url()]; 00718 if ( listers ) 00719 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00720 kdl->emitDeleteItem( fileitem ); 00721 } 00722 00723 // If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case. 00724 if ( !fileitem || fileitem->isDir() ) 00725 { 00726 // in case of a dir, check if we have any known children, there's much to do in that case 00727 // (stopping jobs, removing dirs from cache etc.) 00728 deleteDir( *it ); 00729 } 00730 00731 // now remove the item itself 00732 delete fileitem; 00733 } 00734 } 00735 00736 void KDirListerCache::FilesChanged( const KURL::List &fileList ) 00737 { 00738 KURL::List dirsToUpdate; 00739 kdDebug(7004) << k_funcinfo << "only half implemented" << endl; 00740 KURL::List::ConstIterator it = fileList.begin(); 00741 for ( ; it != fileList.end() ; ++it ) 00742 { 00743 if ( ( *it ).isLocalFile() ) 00744 { 00745 kdDebug(7004) << "KDirListerCache::FilesChanged " << *it << endl; 00746 KFileItem *fileitem = findByURL( 0, *it ); 00747 if ( fileitem ) 00748 { 00749 // we need to refresh the item, because e.g. the permissions can have changed. 00750 aboutToRefreshItem( fileitem ); 00751 fileitem->refresh(); 00752 emitRefreshItem( fileitem ); 00753 } 00754 else 00755 kdDebug(7004) << "item not found" << endl; 00756 } else { 00757 // For remote files, refresh() won't be able to figure out the new information. 00758 // Let's update the dir. 00759 KURL dir( *it ); 00760 dir.setPath( dir.directory( true ) ); 00761 if ( dirsToUpdate.find( dir ) == dirsToUpdate.end() ) 00762 dirsToUpdate.prepend( dir ); 00763 } 00764 } 00765 00766 KURL::List::ConstIterator itdir = dirsToUpdate.begin(); 00767 for ( ; itdir != dirsToUpdate.end() ; ++itdir ) 00768 updateDirectory( *itdir ); 00769 // ## TODO problems with current jobs listing/updating that dir 00770 // ( see kde-2.2.2's kdirlister ) 00771 } 00772 00773 void KDirListerCache::FileRenamed( const KURL &src, const KURL &dst ) 00774 { 00775 kdDebug(7004) << k_funcinfo << src.prettyURL() << " -> " << dst.prettyURL() << endl; 00776 #ifdef DEBUG_CACHE 00777 printDebug(); 00778 #endif 00779 00780 // Somehow this should only be called if src is a dir. But how could we know if it is? 00781 // (Note that looking into itemsInUse isn't good enough. One could rename a subdir in a view.) 00782 renameDir( src, dst ); 00783 00784 // Now update the KFileItem representing that file or dir (not exclusive with the above!) 00785 KURL oldurl( src ); 00786 oldurl.adjustPath( -1 ); 00787 KFileItem *fileitem = findByURL( 0, oldurl ); 00788 if ( fileitem ) 00789 { 00790 if ( !fileitem->isLocalFile() && !fileitem->localPath().isEmpty() ) // it uses UDS_LOCAL_PATH? ouch, needs an update then 00791 FilesChanged( src ); 00792 else 00793 { 00794 aboutToRefreshItem( fileitem ); 00795 fileitem->setURL( dst ); 00796 fileitem->refreshMimeType(); 00797 emitRefreshItem( fileitem ); 00798 } 00799 } 00800 #ifdef DEBUG_CACHE 00801 printDebug(); 00802 #endif 00803 } 00804 00805 void KDirListerCache::aboutToRefreshItem( KFileItem *fileitem ) 00806 { 00807 // Look whether this item was shown in any view, i.e. held by any dirlister 00808 KURL parentDir( fileitem->url() ); 00809 parentDir.setPath( parentDir.directory() ); 00810 QString parentDirURL = parentDir.url(); 00811 QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL]; 00812 if ( listers ) 00813 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00814 kdl->aboutToRefreshItem( fileitem ); 00815 00816 // Also look in urlsCurrentlyListed, in case the user manages to rename during a listing 00817 listers = urlsCurrentlyListed[parentDirURL]; 00818 if ( listers ) 00819 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00820 kdl->aboutToRefreshItem( fileitem ); 00821 } 00822 00823 void KDirListerCache::emitRefreshItem( KFileItem *fileitem ) 00824 { 00825 // Look whether this item was shown in any view, i.e. held by any dirlister 00826 KURL parentDir( fileitem->url() ); 00827 parentDir.setPath( parentDir.directory() ); 00828 QString parentDirURL = parentDir.url(); 00829 QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL]; 00830 if ( listers ) 00831 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00832 { 00833 kdl->addRefreshItem( fileitem ); 00834 kdl->emitItems(); 00835 } 00836 00837 // Also look in urlsCurrentlyListed, in case the user manages to rename during a listing 00838 listers = urlsCurrentlyListed[parentDirURL]; 00839 if ( listers ) 00840 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00841 { 00842 kdl->addRefreshItem( fileitem ); 00843 kdl->emitItems(); 00844 } 00845 } 00846 00847 KDirListerCache* KDirListerCache::self() 00848 { 00849 if ( !s_pSelf ) 00850 s_pSelf = sd_KDirListerCache.setObject( s_pSelf, new KDirListerCache ); 00851 00852 return s_pSelf; 00853 } 00854 00855 bool KDirListerCache::exists() 00856 { 00857 return s_pSelf != 0; 00858 } 00859 00860 00861 // private slots 00862 00863 // _file can also be a directory being currently held! 00864 void KDirListerCache::slotFileDirty( const QString& _file ) 00865 { 00866 kdDebug(7004) << k_funcinfo << _file << endl; 00867 00868 if ( !pendingUpdates[_file] ) 00869 { 00870 KURL dir; 00871 dir.setPath( _file ); 00872 if ( checkUpdate( dir.url(-1) ) ) 00873 updateDirectory( dir ); 00874 00875 // the parent directory of _file 00876 dir.setPath( dir.directory() ); 00877 if ( checkUpdate( dir.url() ) ) 00878 { 00879 // Nice hack to save memory: use the qt object name to store the filename 00880 QTimer *timer = new QTimer( this, _file.utf8() ); 00881 connect( timer, SIGNAL(timeout()), this, SLOT(slotFileDirtyDelayed()) ); 00882 pendingUpdates.insert( _file, timer ); 00883 timer->start( 500, true ); 00884 } 00885 } 00886 } 00887 00888 // delayed updating of files, FAM is flooding us with events 00889 void KDirListerCache::slotFileDirtyDelayed() 00890 { 00891 QString file = QString::fromUtf8( sender()->name() ); 00892 00893 kdDebug(7004) << k_funcinfo << file << endl; 00894 00895 // TODO: do it better: don't always create/delete the QTimer but reuse it. 00896 // Delete the timer after the parent directory is removed from the cache. 00897 pendingUpdates.remove( file ); 00898 00899 KURL u; 00900 u.setPath( file ); 00901 KFileItem *item = findByURL( 0, u ); // search all items 00902 if ( item ) 00903 { 00904 // we need to refresh the item, because e.g. the permissions can have changed. 00905 aboutToRefreshItem( item ); 00906 item->refresh(); 00907 emitRefreshItem( item ); 00908 } 00909 } 00910 00911 void KDirListerCache::slotFileCreated( const QString& _file ) 00912 { 00913 kdDebug(7004) << k_funcinfo << _file << endl; 00914 // XXX: how to avoid a complete rescan here? 00915 KURL u; 00916 u.setPath( _file ); 00917 u.setPath( u.directory() ); 00918 FilesAdded( u ); 00919 } 00920 00921 void KDirListerCache::slotFileDeleted( const QString& _file ) 00922 { 00923 kdDebug(7004) << k_funcinfo << _file << endl; 00924 KURL u; 00925 u.setPath( _file ); 00926 FilesRemoved( u ); 00927 } 00928 00929 void KDirListerCache::slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries ) 00930 { 00931 KURL url = joburl( static_cast<KIO::ListJob *>(job) ); 00932 url.adjustPath(-1); 00933 QString urlStr = url.url(); 00934 00935 kdDebug(7004) << k_funcinfo << "new entries for " << url << endl; 00936 00937 DirItem *dir = itemsInUse[urlStr]; 00938 Q_ASSERT( dir ); 00939 00940 QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr]; 00941 Q_ASSERT( listers ); 00942 Q_ASSERT( !listers->isEmpty() ); 00943 00944 // check if anyone wants the mimetypes immediately 00945 bool delayedMimeTypes = true; 00946 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00947 delayedMimeTypes = delayedMimeTypes && kdl->d->delayedMimeTypes; 00948 00949 // avoid creating these QStrings again and again 00950 static const QString& dot = KGlobal::staticQString("."); 00951 static const QString& dotdot = KGlobal::staticQString(".."); 00952 00953 KIO::UDSEntryListConstIterator it = entries.begin(); 00954 KIO::UDSEntryListConstIterator end = entries.end(); 00955 00956 for ( ; it != end; ++it ) 00957 { 00958 QString name; 00959 00960 // find out about the name 00961 KIO::UDSEntry::ConstIterator entit = (*it).begin(); 00962 for( ; entit != (*it).end(); ++entit ) 00963 if ( (*entit).m_uds == KIO::UDS_NAME ) 00964 { 00965 name = (*entit).m_str; 00966 break; 00967 } 00968 00969 Q_ASSERT( !name.isEmpty() ); 00970 if ( name.isEmpty() ) 00971 continue; 00972 00973 if ( name == dot ) 00974 { 00975 Q_ASSERT( !dir->rootItem ); 00976 dir->rootItem = new KFileItem( *it, url, delayedMimeTypes, true ); 00977 00978 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00979 if ( !kdl->d->rootFileItem && kdl->d->url == url ) 00980 kdl->d->rootFileItem = dir->rootItem; 00981 } 00982 else if ( name != dotdot ) 00983 { 00984 KFileItem* item = new KFileItem( *it, url, delayedMimeTypes, true ); 00985 Q_ASSERT( item ); 00986 00987 //kdDebug(7004)<< "Adding item: " << item->url() << endl; 00988 dir->lstItems->append( item ); 00989 00990 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00991 kdl->addNewItem( item ); 00992 } 00993 } 00994 00995 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 00996 kdl->emitItems(); 00997 } 00998 00999 void KDirListerCache::slotResult( KIO::Job *j ) 01000 { 01001 Q_ASSERT( j ); 01002 KIO::ListJob *job = static_cast<KIO::ListJob *>( j ); 01003 jobs.remove( job ); 01004 01005 KURL jobUrl = joburl( job ); 01006 jobUrl.adjustPath(-1); // need remove trailing slashes again, in case of redirections 01007 QString jobUrlStr = jobUrl.url(); 01008 01009 kdDebug(7004) << k_funcinfo << "finished listing " << jobUrl << endl; 01010 #ifdef DEBUG_CACHE 01011 printDebug(); 01012 #endif 01013 01014 QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( jobUrlStr ); 01015 Q_ASSERT( listers ); 01016 01017 // move the directory to the held directories, do it before emitting 01018 // the signals to make sure it exists in KDirListerCache in case someone 01019 // calls listDir during the signal emission 01020 Q_ASSERT( !urlsCurrentlyHeld[jobUrlStr] ); 01021 urlsCurrentlyHeld.insert( jobUrlStr, listers ); 01022 01023 KDirLister *kdl; 01024 01025 if ( job->error() ) 01026 { 01027 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01028 { 01029 kdl->jobDone( job ); 01030 kdl->handleError( job ); 01031 emit kdl->canceled( jobUrl ); 01032 if ( kdl->numJobs() == 0 ) 01033 { 01034 kdl->d->complete = true; 01035 emit kdl->canceled(); 01036 } 01037 } 01038 } 01039 else 01040 { 01041 DirItem *dir = itemsInUse[jobUrlStr]; 01042 Q_ASSERT( dir ); 01043 dir->complete = true; 01044 01045 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01046 { 01047 kdl->jobDone( job ); 01048 emit kdl->completed( jobUrl ); 01049 if ( kdl->numJobs() == 0 ) 01050 { 01051 kdl->d->complete = true; 01052 emit kdl->completed(); 01053 } 01054 } 01055 } 01056 01057 // TODO: hmm, if there was an error and job is a parent of one or more 01058 // of the pending urls we should cancel it/them as well 01059 processPendingUpdates(); 01060 01061 #ifdef DEBUG_CACHE 01062 printDebug(); 01063 #endif 01064 } 01065 01066 void KDirListerCache::slotRedirection( KIO::Job *j, const KURL& url ) 01067 { 01068 Q_ASSERT( j ); 01069 KIO::ListJob *job = static_cast<KIO::ListJob *>( j ); 01070 01071 KURL oldUrl = job->url(); // here we really need the old url! 01072 KURL newUrl = url; 01073 01074 // strip trailing slashes 01075 oldUrl.adjustPath(-1); 01076 newUrl.adjustPath(-1); 01077 01078 if ( oldUrl == newUrl ) 01079 { 01080 kdDebug(7004) << k_funcinfo << "New redirection url same as old, giving up." << endl; 01081 return; 01082 } 01083 01084 kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl; 01085 01086 #ifdef DEBUG_CACHE 01087 printDebug(); 01088 #endif 01089 01090 // I don't think there can be dirItems that are childs of oldUrl. 01091 // Am I wrong here? And even if so, we don't need to delete them, right? 01092 // DF: redirection happens before listDir emits any item. Makes little sense otherwise. 01093 01094 // oldUrl cannot be in itemsCached because only completed items are moved there 01095 DirItem *dir = itemsInUse.take( oldUrl.url() ); 01096 Q_ASSERT( dir ); 01097 01098 QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrl.url() ); 01099 Q_ASSERT( listers ); 01100 Q_ASSERT( !listers->isEmpty() ); 01101 01102 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01103 { 01104 // TODO: put in own method? 01105 if ( kdl->d->url.equals( oldUrl, true ) ) 01106 { 01107 kdl->d->rootFileItem = 0; 01108 kdl->d->url = newUrl; 01109 } 01110 01111 *kdl->d->lstDirs.find( oldUrl ) = newUrl; 01112 01113 if ( kdl->d->lstDirs.count() == 1 ) 01114 { 01115 emit kdl->clear(); 01116 emit kdl->redirection( newUrl ); 01117 emit kdl->redirection( oldUrl, newUrl ); 01118 } 01119 else 01120 { 01121 emit kdl->clear( oldUrl ); 01122 emit kdl->redirection( oldUrl, newUrl ); 01123 } 01124 } 01125 01126 // when a lister was stopped before the job emits the redirection signal, the old url will 01127 // also be in urlsCurrentlyHeld 01128 QPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrl.url() ); 01129 if ( holders ) 01130 { 01131 Q_ASSERT( !holders->isEmpty() ); 01132 01133 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 01134 { 01135 kdl->jobStarted( job ); 01136 01137 // do it like when starting a new list-job that will redirect later 01138 emit kdl->started( oldUrl ); 01139 01140 // TODO: maybe don't emit started if there's an update running for newUrl already? 01141 01142 if ( kdl->d->url.equals( oldUrl, true ) ) 01143 { 01144 kdl->d->rootFileItem = 0; 01145 kdl->d->url = newUrl; 01146 } 01147 01148 *kdl->d->lstDirs.find( oldUrl ) = newUrl; 01149 01150 if ( kdl->d->lstDirs.count() == 1 ) 01151 { 01152 emit kdl->clear(); 01153 emit kdl->redirection( newUrl ); 01154 emit kdl->redirection( oldUrl, newUrl ); 01155 } 01156 else 01157 { 01158 emit kdl->clear( oldUrl ); 01159 emit kdl->redirection( oldUrl, newUrl ); 01160 } 01161 } 01162 } 01163 01164 DirItem *newDir = itemsInUse[newUrl.url()]; 01165 if ( newDir ) 01166 { 01167 kdDebug(7004) << "slotRedirection: " << newUrl.url() << " already in use" << endl; 01168 01169 // only in this case there can newUrl already be in urlsCurrentlyListed or urlsCurrentlyHeld 01170 delete dir; 01171 01172 // get the job if one's running for newUrl already (can be a list-job or an update-job), but 01173 // do not return this 'job', which would happen because of the use of redirectionURL() 01174 KIO::ListJob *oldJob = jobForUrl( newUrl.url(), job ); 01175 01176 // listers of newUrl with oldJob: forget about the oldJob and use the already running one 01177 // which will be converted to an updateJob 01178 QPtrList<KDirLister> *curListers = urlsCurrentlyListed[newUrl.url()]; 01179 if ( curListers ) 01180 { 01181 kdDebug(7004) << "slotRedirection: and it is currently listed" << endl; 01182 01183 Q_ASSERT( oldJob ); // ?! 01184 01185 for ( KDirLister *kdl = curListers->first(); kdl; kdl = curListers->next() ) // listers of newUrl 01186 { 01187 kdl->jobDone( oldJob ); 01188 01189 kdl->jobStarted( job ); 01190 kdl->connectJob( job ); 01191 } 01192 01193 // append listers of oldUrl with newJob to listers of newUrl with oldJob 01194 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01195 curListers->append( kdl ); 01196 } 01197 else 01198 urlsCurrentlyListed.insert( newUrl.url(), listers ); 01199 01200 if ( oldJob ) // kill the old job, be it a list-job or an update-job 01201 killJob( oldJob ); 01202 01203 // holders of newUrl: use the already running job which will be converted to an updateJob 01204 QPtrList<KDirLister> *curHolders = urlsCurrentlyHeld[newUrl.url()]; 01205 if ( curHolders ) 01206 { 01207 kdDebug(7004) << "slotRedirection: and it is currently held." << endl; 01208 01209 for ( KDirLister *kdl = curHolders->first(); kdl; kdl = curHolders->next() ) // holders of newUrl 01210 { 01211 kdl->jobStarted( job ); 01212 emit kdl->started( newUrl ); 01213 } 01214 01215 // append holders of oldUrl to holders of newUrl 01216 if ( holders ) 01217 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 01218 curHolders->append( kdl ); 01219 } 01220 else if ( holders ) 01221 urlsCurrentlyHeld.insert( newUrl.url(), holders ); 01222 01223 01224 // emit old items: listers, holders. NOT: newUrlListers/newUrlHolders, they already have them listed 01225 // TODO: make this a separate method? 01226 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01227 { 01228 if ( !kdl->d->rootFileItem && kdl->d->url == newUrl ) 01229 kdl->d->rootFileItem = newDir->rootItem; 01230 01231 kdl->addNewItems( *(newDir->lstItems) ); 01232 kdl->emitItems(); 01233 } 01234 01235 if ( holders ) 01236 { 01237 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 01238 { 01239 if ( !kdl->d->rootFileItem && kdl->d->url == newUrl ) 01240 kdl->d->rootFileItem = newDir->rootItem; 01241 01242 kdl->addNewItems( *(newDir->lstItems) ); 01243 kdl->emitItems(); 01244 } 01245 } 01246 } 01247 else if ( (newDir = itemsCached.take( newUrl.url() )) ) 01248 { 01249 kdDebug(7004) << "slotRedirection: " << newUrl.url() << " is unused, but already in the cache." << endl; 01250 01251 delete dir; 01252 itemsInUse.insert( newUrl.url(), newDir ); 01253 urlsCurrentlyListed.insert( newUrl.url(), listers ); 01254 if ( holders ) 01255 urlsCurrentlyHeld.insert( newUrl.url(), holders ); 01256 01257 // emit old items: listers, holders 01258 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01259 { 01260 if ( !kdl->d->rootFileItem && kdl->d->url == newUrl ) 01261 kdl->d->rootFileItem = newDir->rootItem; 01262 01263 kdl->addNewItems( *(newDir->lstItems) ); 01264 kdl->emitItems(); 01265 } 01266 01267 if ( holders ) 01268 { 01269 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 01270 { 01271 if ( !kdl->d->rootFileItem && kdl->d->url == newUrl ) 01272 kdl->d->rootFileItem = newDir->rootItem; 01273 01274 kdl->addNewItems( *(newDir->lstItems) ); 01275 kdl->emitItems(); 01276 } 01277 } 01278 } 01279 else 01280 { 01281 kdDebug(7004) << "slotRedirection: " << newUrl.url() << " has not been listed yet." << endl; 01282 01283 delete dir->rootItem; 01284 dir->rootItem = 0; 01285 dir->lstItems->clear(); 01286 dir->redirect( newUrl ); 01287 itemsInUse.insert( newUrl.url(), dir ); 01288 urlsCurrentlyListed.insert( newUrl.url(), listers ); 01289 01290 if ( holders ) 01291 urlsCurrentlyHeld.insert( newUrl.url(), holders ); 01292 else 01293 { 01294 #ifdef DEBUG_CACHE 01295 printDebug(); 01296 #endif 01297 return; // only in this case the job doesn't need to be converted, 01298 } 01299 } 01300 01301 // make the job an update job 01302 job->disconnect( this ); 01303 01304 connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )), 01305 this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) ); 01306 connect( job, SIGNAL(result( KIO::Job * )), 01307 this, SLOT(slotUpdateResult( KIO::Job * )) ); 01308 01309 // FIXME: autoUpdate-Counts!! 01310 01311 #ifdef DEBUG_CACHE 01312 printDebug(); 01313 #endif 01314 } 01315 01316 void KDirListerCache::renameDir( const KURL &oldUrl, const KURL &newUrl ) 01317 { 01318 kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl; 01319 QString oldUrlStr = oldUrl.url(-1); 01320 QString newUrlStr = newUrl.url(-1); 01321 01322 // Not enough. Also need to look at any child dir, even sub-sub-sub-dir. 01323 //DirItem *dir = itemsInUse.take( oldUrlStr ); 01324 //emitRedirections( oldUrl, url ); 01325 01326 // Look at all dirs being listed/shown 01327 QDictIterator<DirItem> itu( itemsInUse ); 01328 bool goNext; 01329 while ( itu.current() ) 01330 { 01331 goNext = true; 01332 DirItem *dir = itu.current(); 01333 KURL oldDirUrl ( itu.currentKey() ); 01334 //kdDebug(7004) << "itemInUse: " << oldDirUrl.prettyURL() << endl; 01335 // Check if this dir is oldUrl, or a subfolder of it 01336 if ( oldUrl.isParentOf( oldDirUrl ) ) 01337 { 01338 // TODO should use KURL::cleanpath like isParentOf does 01339 QString relPath = oldDirUrl.path().mid( oldUrl.path().length() ); 01340 01341 KURL newDirUrl( newUrl ); // take new base 01342 if ( !relPath.isEmpty() ) 01343 newDirUrl.addPath( relPath ); // add unchanged relative path 01344 //kdDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl.prettyURL() << endl; 01345 01346 // Update URL in dir item and in itemsInUse 01347 dir->redirect( newDirUrl ); 01348 itemsInUse.remove( itu.currentKey() ); // implies ++itu 01349 itemsInUse.insert( newDirUrl.url(-1), dir ); 01350 goNext = false; // because of the implied ++itu above 01351 if ( dir->lstItems ) 01352 { 01353 // Rename all items under that dir 01354 KFileItemListIterator kit( *dir->lstItems ); 01355 for ( ; kit.current(); ++kit ) 01356 { 01357 KURL oldItemUrl = (*kit)->url(); 01358 QString oldItemUrlStr( oldItemUrl.url(-1) ); 01359 KURL newItemUrl( oldItemUrl ); 01360 newItemUrl.setPath( newDirUrl.path() ); 01361 newItemUrl.addPath( oldItemUrl.fileName() ); 01362 kdDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url() << endl; 01363 (*kit)->setURL( newItemUrl ); 01364 } 01365 } 01366 emitRedirections( oldDirUrl, newDirUrl ); 01367 } 01368 if ( goNext ) 01369 ++itu; 01370 } 01371 01372 // Is oldUrl a directory in the cache? 01373 // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it! 01374 removeDirFromCache( oldUrl ); 01375 // TODO rename, instead. 01376 } 01377 01378 void KDirListerCache::emitRedirections( const KURL &oldUrl, const KURL &url ) 01379 { 01380 kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << url.prettyURL() << endl; 01381 QString oldUrlStr = oldUrl.url(-1); 01382 QString urlStr = url.url(-1); 01383 01384 KIO::ListJob *job = jobForUrl( oldUrlStr ); 01385 if ( job ) 01386 killJob( job ); 01387 01388 // Check if we were listing this dir. Need to abort and restart with new name in that case. 01389 QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrlStr ); 01390 if ( listers ) 01391 { 01392 // Tell the world that the job listing the old url is dead. 01393 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01394 { 01395 if ( job ) 01396 kdl->jobDone( job ); 01397 01398 emit kdl->canceled( oldUrl ); 01399 } 01400 01401 urlsCurrentlyListed.insert( urlStr, listers ); 01402 } 01403 01404 // Check if we are currently displaying this directory (odds opposite wrt above) 01405 // Update urlsCurrentlyHeld dict with new URL 01406 QPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrlStr ); 01407 if ( holders ) 01408 { 01409 if ( job ) 01410 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 01411 kdl->jobDone( job ); 01412 01413 urlsCurrentlyHeld.insert( urlStr, holders ); 01414 } 01415 01416 if ( listers ) 01417 { 01418 updateDirectory( url ); 01419 01420 // Tell the world about the new url 01421 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01422 emit kdl->started( url ); 01423 } 01424 01425 if ( holders ) 01426 { 01427 // And notify the dirlisters of the redirection 01428 for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) 01429 { 01430 *kdl->d->lstDirs.find( oldUrl ) = url; 01431 01432 if ( kdl->d->lstDirs.count() == 1 ) 01433 emit kdl->redirection( url ); 01434 01435 emit kdl->redirection( oldUrl, url ); 01436 } 01437 } 01438 } 01439 01440 void KDirListerCache::removeDirFromCache( const KURL& dir ) 01441 { 01442 kdDebug(7004) << "KDirListerCache::removeDirFromCache " << dir.prettyURL() << endl; 01443 QCacheIterator<DirItem> itc( itemsCached ); 01444 while ( itc.current() ) 01445 { 01446 if ( dir.isParentOf( KURL( itc.currentKey() ) ) ) 01447 itemsCached.remove( itc.currentKey() ); 01448 else 01449 ++itc; 01450 } 01451 } 01452 01453 void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list ) 01454 { 01455 jobs[static_cast<KIO::ListJob*>(job)] += list; 01456 } 01457 01458 void KDirListerCache::slotUpdateResult( KIO::Job * j ) 01459 { 01460 Q_ASSERT( j ); 01461 KIO::ListJob *job = static_cast<KIO::ListJob *>( j ); 01462 01463 KURL jobUrl = joburl( job ); 01464 jobUrl.adjustPath(-1); // need remove trailing slashes again, in case of redirections 01465 QString jobUrlStr = jobUrl.url(); 01466 01467 kdDebug(7004) << k_funcinfo << "finished update " << jobUrl << endl; 01468 01469 KDirLister *kdl; 01470 01471 QPtrList<KDirLister> *listers = urlsCurrentlyHeld[jobUrlStr]; 01472 QPtrList<KDirLister> *tmpLst = urlsCurrentlyListed.take( jobUrlStr ); 01473 01474 if ( tmpLst ) 01475 { 01476 if ( listers ) 01477 for ( kdl = tmpLst->first(); kdl; kdl = tmpLst->next() ) 01478 { 01479 Q_ASSERT( listers->containsRef( kdl ) == 0 ); 01480 listers->append( kdl ); 01481 } 01482 else 01483 { 01484 listers = tmpLst; 01485 urlsCurrentlyHeld.insert( jobUrlStr, listers ); 01486 } 01487 } 01488 01489 // once we are updating dirs that are only in the cache this will fail! 01490 Q_ASSERT( listers ); 01491 01492 if ( job->error() ) 01493 { 01494 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01495 { 01496 kdl->jobDone( job ); 01497 01498 //don't bother the user 01499 //kdl->handleError( job ); 01500 01501 emit kdl->canceled( jobUrl ); 01502 if ( kdl->numJobs() == 0 ) 01503 { 01504 kdl->d->complete = true; 01505 emit kdl->canceled(); 01506 } 01507 } 01508 01509 jobs.remove( job ); 01510 01511 // TODO: if job is a parent of one or more 01512 // of the pending urls we should cancel them 01513 processPendingUpdates(); 01514 return; 01515 } 01516 01517 DirItem *dir = itemsInUse[jobUrlStr]; 01518 dir->complete = true; 01519 01520 01521 // check if anyone wants the mimetypes immediately 01522 bool delayedMimeTypes = true; 01523 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01524 delayedMimeTypes = delayedMimeTypes && kdl->d->delayedMimeTypes; 01525 01526 // should be enough to get reasonable speed in most cases 01527 QDict<KFileItem> fileItems( 9973 ); 01528 01529 KFileItemListIterator kit ( *(dir->lstItems) ); 01530 01531 // Unmark all items in url 01532 for ( ; kit.current(); ++kit ) 01533 { 01534 (*kit)->unmark(); 01535 fileItems.insert( (*kit)->url().url(), *kit ); 01536 } 01537 01538 static const QString& dot = KGlobal::staticQString("."); 01539 static const QString& dotdot = KGlobal::staticQString(".."); 01540 01541 KFileItem *item = 0, *tmp; 01542 01543 QValueList<KIO::UDSEntry> buf = jobs[job]; 01544 QValueListIterator<KIO::UDSEntry> it = buf.begin(); 01545 for ( ; it != buf.end(); ++it ) 01546 { 01547 // Form the complete url 01548 if ( !item ) 01549 item = new KFileItem( *it, jobUrl, delayedMimeTypes, true ); 01550 else 01551 item->setUDSEntry( *it, jobUrl, delayedMimeTypes, true ); 01552 01553 // Find out about the name 01554 QString name = item->name(); 01555 Q_ASSERT( !name.isEmpty() ); 01556 01557 // we duplicate the check for dotdot here, to avoid iterating over 01558 // all items again and checking in matchesFilter() that way. 01559 if ( name.isEmpty() || name == dotdot ) 01560 continue; 01561 01562 if ( name == dot ) 01563 { 01564 // if the update was started before finishing the original listing 01565 // there is no root item yet 01566 if ( !dir->rootItem ) 01567 { 01568 dir->rootItem = item; 01569 item = 0; 01570 01571 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01572 if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl ) 01573 kdl->d->rootFileItem = dir->rootItem; 01574 } 01575 01576 continue; 01577 } 01578 01579 // Find this item 01580 if ( (tmp = fileItems[item->url().url()]) ) 01581 { 01582 tmp->mark(); 01583 01584 // check if something changed for this file 01585 if ( !tmp->cmp( *item ) ) 01586 { 01587 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01588 kdl->aboutToRefreshItem( tmp ); 01589 01590 //kdDebug(7004) << "slotUpdateResult: file changed: " << tmp->name() << endl; 01591 tmp->assign( *item ); 01592 01593 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01594 kdl->addRefreshItem( tmp ); 01595 } 01596 } 01597 else // this is a new file 01598 { 01599 //kdDebug(7004) << "slotUpdateResult: new file: " << name << endl; 01600 01601 item->mark(); 01602 dir->lstItems->append( item ); 01603 01604 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01605 kdl->addNewItem( item ); 01606 01607 // item used, we need a new one for the next iteration 01608 item = 0; 01609 } 01610 } 01611 01612 if ( item ) 01613 delete item; 01614 01615 jobs.remove( job ); 01616 01617 deleteUnmarkedItems( listers, dir->lstItems ); 01618 01619 for ( kdl = listers->first(); kdl; kdl = listers->next() ) 01620 { 01621 kdl->emitItems(); 01622 01623 kdl->jobDone( job ); 01624 01625 emit kdl->completed( jobUrl ); 01626 if ( kdl->numJobs() == 0 ) 01627 { 01628 kdl->d->complete = true; 01629 emit kdl->completed(); 01630 } 01631 } 01632 01633 // TODO: hmm, if there was an error and job is a parent of one or more 01634 // of the pending urls we should cancel it/them as well 01635 processPendingUpdates(); 01636 } 01637 01638 // private 01639 01640 KIO::ListJob *KDirListerCache::jobForUrl( const QString& url, KIO::ListJob *not_job ) 01641 { 01642 KIO::ListJob *job; 01643 QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator it = jobs.begin(); 01644 while ( it != jobs.end() ) 01645 { 01646 job = it.key(); 01647 if ( joburl( job ).url(-1) == url && job != not_job ) 01648 return job; 01649 ++it; 01650 } 01651 return 0; 01652 } 01653 01654 const KURL& KDirListerCache::joburl( KIO::ListJob *job ) 01655 { 01656 if ( job->redirectionURL().isValid() ) 01657 return job->redirectionURL(); 01658 else 01659 return job->url(); 01660 } 01661 01662 void KDirListerCache::killJob( KIO::ListJob *job ) 01663 { 01664 jobs.remove( job ); 01665 job->disconnect( this ); 01666 job->kill(); 01667 } 01668 01669 void KDirListerCache::deleteUnmarkedItems( QPtrList<KDirLister> *listers, KFileItemList *lstItems ) 01670 { 01671 // Find all unmarked items and delete them 01672 KFileItem* item; 01673 lstItems->first(); 01674 while ( (item = lstItems->current()) ) 01675 if ( !item->isMarked() ) 01676 { 01677 //kdDebug() << k_funcinfo << item->name() << endl; 01678 for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) 01679 kdl->emitDeleteItem( item ); 01680 01681 if ( item->isDir() ) 01682 deleteDir( item->url() ); 01683 01684 // finally actually delete the item 01685 lstItems->take(); 01686 delete item; 01687 } 01688 else 01689 lstItems->next(); 01690 } 01691 01692 void KDirListerCache::deleteDir( const KURL& dirUrl ) 01693 { 01694 //kdDebug() << k_funcinfo << dirUrl.prettyURL() << endl; 01695 // unregister and remove the childs of the deleted item. 01696 // Idea: tell all the KDirListers that they should forget the dir 01697 // and then remove it from the cache. 01698 01699 QDictIterator<DirItem> itu( itemsInUse ); 01700 while ( itu.current() ) 01701 { 01702 KURL deletedUrl( itu.currentKey() ); 01703 if ( dirUrl.isParentOf( deletedUrl ) ) 01704 { 01705 // stop all jobs for deletedUrl 01706 01707 QPtrList<KDirLister> *kdls = urlsCurrentlyListed[deletedUrl.url()]; 01708 if ( kdls ) // yeah, I lack good names 01709 { 01710 // we need a copy because stop modifies the list 01711 kdls = new QPtrList<KDirLister>( *kdls ); 01712 for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() ) 01713 stop( kdl, deletedUrl ); 01714 01715 delete kdls; 01716 } 01717 01718 // tell listers holding deletedUrl to forget about it 01719 // this will stop running updates for deletedUrl as well 01720 01721 kdls = urlsCurrentlyHeld[deletedUrl.url()]; 01722 if ( kdls ) 01723 { 01724 // we need a copy because forgetDirs modifies the list 01725 kdls = new QPtrList<KDirLister>( *kdls ); 01726 01727 for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() ) 01728 { 01729 // lister's root is the deleted item 01730 if ( kdl->d->url == deletedUrl ) 01731 { 01732 // tell the view first. It might need the subdirs' items (which forgetDirs will delete) 01733 if ( kdl->d->rootFileItem ) 01734 emit kdl->deleteItem( kdl->d->rootFileItem ); 01735 forgetDirs( kdl ); 01736 kdl->d->rootFileItem = 0; 01737 } 01738 else 01739 { 01740 bool treeview = kdl->d->lstDirs.count() > 1; 01741 if ( !treeview ) 01742 emit kdl->clear(); 01743 01744 forgetDirs( kdl, deletedUrl, treeview ); 01745 } 01746 } 01747 01748 delete kdls; 01749 } 01750 01751 // delete the entry for deletedUrl - should not be needed, it's in 01752 // items cached now 01753 01754 DirItem *dir = itemsInUse.take( deletedUrl.url() ); 01755 Q_ASSERT( !dir ); 01756 if ( !dir ) // take didn't find it - move on 01757 ++itu; 01758 } 01759 else 01760 ++itu; 01761 } 01762 01763 // remove the children from the cache 01764 removeDirFromCache( dirUrl ); 01765 } 01766 01767 void KDirListerCache::processPendingUpdates() 01768 { 01769 // TODO 01770 } 01771 01772 #ifndef NDEBUG 01773 void KDirListerCache::printDebug() 01774 { 01775 kdDebug(7004) << "Items in use: " << endl; 01776 QDictIterator<DirItem> itu( itemsInUse ); 01777 for ( ; itu.current() ; ++itu ) { 01778 kdDebug(7004) << " " << itu.currentKey() << " URL: " << itu.current()->url 01779 << " rootItem: " << ( itu.current()->rootItem ? itu.current()->rootItem->url() : KURL() ) 01780 << " autoUpdates refcount: " << itu.current()->autoUpdates 01781 << " complete: " << itu.current()->complete 01782 << ( itu.current()->lstItems ? QString(" with %1 items.").arg(itu.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl; 01783 } 01784 01785 kdDebug(7004) << "urlsCurrentlyHeld: " << endl; 01786 QDictIterator< QPtrList<KDirLister> > it( urlsCurrentlyHeld ); 01787 for ( ; it.current() ; ++it ) 01788 { 01789 QString list; 01790 for ( QPtrListIterator<KDirLister> listit( *it.current() ); listit.current(); ++listit ) 01791 list += " 0x" + QString::number( (long)listit.current(), 16 ); 01792 kdDebug(7004) << " " << it.currentKey() << " " << it.current()->count() << " listers: " << list << endl; 01793 } 01794 01795 kdDebug(7004) << "urlsCurrentlyListed: " << endl; 01796 QDictIterator< QPtrList<KDirLister> > it2( urlsCurrentlyListed ); 01797 for ( ; it2.current() ; ++it2 ) 01798 { 01799 QString list; 01800 for ( QPtrListIterator<KDirLister> listit( *it2.current() ); listit.current(); ++listit ) 01801 list += " 0x" + QString::number( (long)listit.current(), 16 ); 01802 kdDebug(7004) << " " << it2.currentKey() << " " << it2.current()->count() << " listers: " << list << endl; 01803 } 01804 01805 QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator jit = jobs.begin(); 01806 kdDebug(7004) << "Jobs: " << endl; 01807 for ( ; jit != jobs.end() ; ++jit ) 01808 kdDebug(7004) << " " << jit.key() << " listing " << joburl( jit.key() ).prettyURL() << ": " << (*jit).count() << " entries." << endl; 01809 01810 kdDebug(7004) << "Items in cache: " << endl; 01811 QCacheIterator<DirItem> itc( itemsCached ); 01812 for ( ; itc.current() ; ++itc ) 01813 kdDebug(7004) << " " << itc.currentKey() << " rootItem: " 01814 << ( itc.current()->rootItem ? itc.current()->rootItem->url().prettyURL() : QString("NULL") ) 01815 << ( itc.current()->lstItems ? QString(" with %1 items.").arg(itc.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl; 01816 } 01817 #endif 01818 01819 /*********************** -- The new KDirLister -- ************************/ 01820 01821 01822 KDirLister::KDirLister( bool _delayedMimeTypes ) 01823 { 01824 kdDebug(7003) << "+KDirLister" << endl; 01825 01826 d = new KDirListerPrivate; 01827 01828 d->complete = true; 01829 d->delayedMimeTypes = _delayedMimeTypes; 01830 01831 setAutoUpdate( true ); 01832 setDirOnlyMode( false ); 01833 setShowingDotFiles( false ); 01834 01835 setAutoErrorHandlingEnabled( true, 0 ); 01836 } 01837 01838 KDirLister::~KDirLister() 01839 { 01840 kdDebug(7003) << "-KDirLister" << endl; 01841 01842 if ( KDirListerCache::exists() ) 01843 { 01844 // Stop all running jobs 01845 stop(); 01846 s_pCache->forgetDirs( this ); 01847 } 01848 01849 delete d; 01850 } 01851 01852 bool KDirLister::openURL( const KURL& _url, bool _keep, bool _reload ) 01853 { 01854 kdDebug(7003) << k_funcinfo << _url.prettyURL() 01855 << " keep=" << _keep << " reload=" << _reload << endl; 01856 01857 // emit the current changes made to avoid an inconsistent treeview 01858 if ( d->changes != NONE && _keep ) 01859 emitChanges(); 01860 01861 d->changes = NONE; 01862 01863 return s_pCache->listDir( this, _url, _keep, _reload ); 01864 } 01865 01866 void KDirLister::stop() 01867 { 01868 kdDebug(7003) << k_funcinfo << endl; 01869 s_pCache->stop( this ); 01870 } 01871 01872 void KDirLister::stop( const KURL& _url ) 01873 { 01874 kdDebug(7003) << k_funcinfo << _url.prettyURL() << endl; 01875 s_pCache->stop( this, _url ); 01876 } 01877 01878 bool KDirLister::autoUpdate() const 01879 { 01880 return d->autoUpdate; 01881 } 01882 01883 void KDirLister::setAutoUpdate( bool _enable ) 01884 { 01885 if ( d->autoUpdate == _enable ) 01886 return; 01887 01888 d->autoUpdate = _enable; 01889 s_pCache->setAutoUpdate( this, _enable ); 01890 } 01891 01892 bool KDirLister::showingDotFiles() const 01893 { 01894 return d->isShowingDotFiles; 01895 } 01896 01897 void KDirLister::setShowingDotFiles( bool _showDotFiles ) 01898 { 01899 if ( d->isShowingDotFiles == _showDotFiles ) 01900 return; 01901 01902 d->isShowingDotFiles = _showDotFiles; 01903 d->changes ^= DOT_FILES; 01904 } 01905 01906 bool KDirLister::dirOnlyMode() const 01907 { 01908 return d->dirOnlyMode; 01909 } 01910 01911 void KDirLister::setDirOnlyMode( bool _dirsOnly ) 01912 { 01913 if ( d->dirOnlyMode == _dirsOnly ) 01914 return; 01915 01916 d->dirOnlyMode = _dirsOnly; 01917 d->changes ^= DIR_ONLY_MODE; 01918 } 01919 01920 bool KDirLister::autoErrorHandlingEnabled() const 01921 { 01922 return d->autoErrorHandling; 01923 } 01924 01925 void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent ) 01926 { 01927 d->autoErrorHandling = enable; 01928 d->errorParent = parent; 01929 } 01930 01931 const KURL& KDirLister::url() const 01932 { 01933 return d->url; 01934 } 01935 01936 const KURL::List& KDirLister::directories() const 01937 { 01938 return d->lstDirs; 01939 } 01940 01941 void KDirLister::emitChanges() 01942 { 01943 if ( d->changes == NONE ) 01944 return; 01945 01946 static const QString& dot = KGlobal::staticQString("."); 01947 static const QString& dotdot = KGlobal::staticQString(".."); 01948 01949 for ( KURL::List::Iterator it = d->lstDirs.begin(); 01950 it != d->lstDirs.end(); ++it ) 01951 { 01952 KFileItemListIterator kit( *s_pCache->itemsForDir( *it ) ); 01953 for ( ; kit.current(); ++kit ) 01954 { 01955 if ( (*kit)->text() == dot || (*kit)->text() == dotdot ) 01956 continue; 01957 01958 bool oldMime = true, newMime = true; 01959 01960 if ( d->changes & MIME_FILTER ) 01961 { 01962 oldMime = doMimeFilter( (*kit)->mimetype(), d->oldMimeFilter ) 01963 && doMimeExcludeFilter( (*kit)->mimetype(), d->oldMimeExcludeFilter ); 01964 newMime = doMimeFilter( (*kit)->mimetype(), d->mimeFilter ) 01965 && doMimeExcludeFilter( (*kit)->mimetype(), d->mimeExcludeFilter ); 01966 01967 if ( oldMime && !newMime ) 01968 { 01969 emit deleteItem( *kit ); 01970 continue; 01971 } 01972 } 01973 01974 if ( d->changes & DIR_ONLY_MODE ) 01975 { 01976 // the lister switched to dirOnlyMode 01977 if ( d->dirOnlyMode ) 01978 { 01979 if ( !(*kit)->isDir() ) 01980 emit deleteItem( *kit ); 01981 } 01982 else if ( !(*kit)->isDir() ) 01983 addNewItem( *kit ); 01984 01985 continue; 01986 } 01987 01988 if ( (*kit)->isHidden() ) 01989 { 01990 if ( d->changes & DOT_FILES ) 01991 { 01992 // the lister switched to dot files mode 01993 if ( d->isShowingDotFiles ) 01994 addNewItem( *kit ); 01995 else 01996 emit deleteItem( *kit ); 01997 01998 continue; 01999 } 02000 } 02001 else if ( d->changes & NAME_FILTER ) 02002 { 02003 bool oldName = (*kit)->isDir() || 02004 d->oldFilters.isEmpty() || 02005 doNameFilter( (*kit)->text(), d->oldFilters ); 02006 02007 bool newName = (*kit)->isDir() || 02008 d->lstFilters.isEmpty() || 02009 doNameFilter( (*kit)->text(), d->lstFilters ); 02010 02011 if ( oldName && !newName ) 02012 { 02013 emit deleteItem( *kit ); 02014 continue; 02015 } 02016 else if ( !oldName && newName ) 02017 addNewItem( *kit ); 02018 } 02019 02020 if ( (d->changes & MIME_FILTER) && !oldMime && newMime ) 02021 addNewItem( *kit ); 02022 } 02023 02024 emitItems(); 02025 } 02026 02027 d->changes = NONE; 02028 } 02029 02030 void KDirLister::updateDirectory( const KURL& _u ) 02031 { 02032 s_pCache->updateDirectory( _u ); 02033 } 02034 02035 bool KDirLister::isFinished() const 02036 { 02037 return d->complete; 02038 } 02039 02040 KFileItem *KDirLister::rootItem() const 02041 { 02042 return d->rootFileItem; 02043 } 02044 02045 KFileItem *KDirLister::findByURL( const KURL& _url ) const 02046 { 02047 return s_pCache->findByURL( this, _url ); 02048 } 02049 02050 KFileItem *KDirLister::findByName( const QString& _name ) const 02051 { 02052 return s_pCache->findByName( this, _name ); 02053 } 02054 02055 #ifndef KDE_NO_COMPAT 02056 KFileItem *KDirLister::find( const KURL& _url ) const 02057 { 02058 return findByURL( _url ); 02059 } 02060 #endif 02061 02062 02063 // ================ public filter methods ================ // 02064 02065 void KDirLister::setNameFilter( const QString& nameFilter ) 02066 { 02067 if ( !(d->changes & NAME_FILTER) ) 02068 { 02069 d->oldFilters = d->lstFilters; 02070 d->lstFilters.setAutoDelete( false ); 02071 } 02072 02073 d->lstFilters.clear(); 02074 d->lstFilters.setAutoDelete( true ); 02075 02076 d->nameFilter = nameFilter; 02077 02078 // Split on white space 02079 QStringList list = QStringList::split( ' ', nameFilter ); 02080 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) 02081 d->lstFilters.append( new QRegExp(*it, false, true ) ); 02082 02083 d->changes |= NAME_FILTER; 02084 } 02085 02086 const QString& KDirLister::nameFilter() const 02087 { 02088 return d->nameFilter; 02089 } 02090 02091 void KDirLister::setMimeFilter( const QStringList& mimeFilter ) 02092 { 02093 if ( !(d->changes & MIME_FILTER) ) 02094 d->oldMimeFilter = d->mimeFilter; 02095 02096 if ( mimeFilter.find("all/allfiles") != mimeFilter.end() || 02097 mimeFilter.find("all/all") != mimeFilter.end() ) 02098 d->mimeFilter.clear(); 02099 else 02100 d->mimeFilter = mimeFilter; 02101 02102 d->changes |= MIME_FILTER; 02103 } 02104 02105 void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter ) 02106 { 02107 if ( !(d->changes & MIME_FILTER) ) 02108 d->oldMimeExcludeFilter = d->mimeExcludeFilter; 02109 02110 d->mimeExcludeFilter = mimeExcludeFilter; 02111 d->changes |= MIME_FILTER; 02112 } 02113 02114 02115 void KDirLister::clearMimeFilter() 02116 { 02117 if ( !(d->changes & MIME_FILTER) ) 02118 { 02119 d->oldMimeFilter = d->mimeFilter; 02120 d->oldMimeExcludeFilter = d->mimeExcludeFilter; 02121 } 02122 d->mimeFilter.clear(); 02123 d->mimeExcludeFilter.clear(); 02124 d->changes |= MIME_FILTER; 02125 } 02126 02127 const QStringList& KDirLister::mimeFilters() const 02128 { 02129 return d->mimeFilter; 02130 } 02131 02132 bool KDirLister::matchesFilter( const QString& name ) const 02133 { 02134 return doNameFilter( name, d->lstFilters ); 02135 } 02136 02137 bool KDirLister::matchesMimeFilter( const QString& mime ) const 02138 { 02139 return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter); 02140 } 02141 02142 // ================ protected methods ================ // 02143 02144 bool KDirLister::matchesFilter( const KFileItem *item ) const 02145 { 02146 Q_ASSERT( item ); 02147 static const QString& dotdot = KGlobal::staticQString(".."); 02148 02149 if ( item->text() == dotdot ) 02150 return false; 02151 02152 if ( !d->isShowingDotFiles && item->isHidden() ) 02153 return false; 02154 02155 if ( item->isDir() || d->lstFilters.isEmpty() ) 02156 return true; 02157 02158 return matchesFilter( item->text() ); 02159 } 02160 02161 bool KDirLister::matchesMimeFilter( const KFileItem *item ) const 02162 { 02163 Q_ASSERT( item ); 02164 // Don't lose time determining the mimetype if there is no filter 02165 if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() ) 02166 return true; 02167 return matchesMimeFilter( item->mimetype() ); 02168 } 02169 02170 bool KDirLister::doNameFilter( const QString& name, const QPtrList<QRegExp>& filters ) const 02171 { 02172 for ( QPtrListIterator<QRegExp> it( filters ); it.current(); ++it ) 02173 if ( it.current()->exactMatch( name ) ) 02174 return true; 02175 02176 return false; 02177 } 02178 02179 bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const 02180 { 02181 if ( filters.isEmpty() ) 02182 return true; 02183 02184 KMimeType::Ptr mimeptr = KMimeType::mimeType(mime); 02185 //kdDebug(7004) << "doMimeFilter: investigating: "<<mimeptr->name()<<endl; 02186 QStringList::ConstIterator it = filters.begin(); 02187 for ( ; it != filters.end(); ++it ) 02188 if ( mimeptr->is(*it) ) 02189 return true; 02190 //else kdDebug(7004) << "doMimeFilter: compared without result to "<<*it<<endl; 02191 02192 02193 return false; 02194 } 02195 02196 bool KDirLister::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const 02197 { 02198 if ( filters.isEmpty() ) 02199 return true; 02200 02201 QStringList::ConstIterator it = filters.begin(); 02202 for ( ; it != filters.end(); ++it ) 02203 if ( (*it) == mime ) 02204 return false; 02205 02206 return true; 02207 } 02208 02209 02210 bool KDirLister::validURL( const KURL& _url ) const 02211 { 02212 return s_pCache->validURL( this, _url ); 02213 } 02214 02215 void KDirLister::handleError( KIO::Job *job ) 02216 { 02217 if ( d->autoErrorHandling ) 02218 job->showErrorDialog( d->errorParent ); 02219 } 02220 02221 02222 // ================= private methods ================= // 02223 02224 void KDirLister::addNewItem( const KFileItem *item ) 02225 { 02226 if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) 02227 return; // No reason to continue... bailing out here prevents a mimetype scan. 02228 02229 if ( matchesMimeFilter( item ) ) 02230 { 02231 if ( !d->lstNewItems ) 02232 d->lstNewItems = new KFileItemList; 02233 02234 d->lstNewItems->append( item ); // items not filtered 02235 } 02236 else 02237 { 02238 if ( !d->lstMimeFilteredItems ) 02239 d->lstMimeFilteredItems = new KFileItemList; 02240 02241 d->lstMimeFilteredItems->append( item ); // only filtered by mime 02242 } 02243 } 02244 02245 void KDirLister::addNewItems( const KFileItemList& items ) 02246 { 02247 // TODO: make this faster - test if we have a filter at all first 02248 // DF: was this profiled? The matchesFoo() functions should be fast, w/o filters... 02249 // Of course if there is no filter and we can do a range-insertion instead of a loop, that might be good. 02250 // But that's for Qt4, not possible with QPtrList. 02251 for ( KFileItemListIterator kit( items ); kit.current(); ++kit ) 02252 addNewItem( *kit ); 02253 } 02254 02255 void KDirLister::aboutToRefreshItem( const KFileItem *item ) 02256 { 02257 // The code here follows the logic in addNewItem 02258 if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) 02259 d->refreshItemWasFiltered = true; 02260 else if ( !matchesMimeFilter( item ) ) 02261 d->refreshItemWasFiltered = true; 02262 else 02263 d->refreshItemWasFiltered = false; 02264 } 02265 02266 void KDirLister::addRefreshItem( const KFileItem *item ) 02267 { 02268 bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item ); 02269 02270 if ( !isExcluded && matchesMimeFilter( item ) ) 02271 { 02272 if ( d->refreshItemWasFiltered ) 02273 { 02274 if ( !d->lstNewItems ) 02275 d->lstNewItems = new KFileItemList; 02276 02277 d->lstNewItems->append( item ); 02278 } 02279 else 02280 { 02281 if ( !d->lstRefreshItems ) 02282 d->lstRefreshItems = new KFileItemList; 02283 02284 d->lstRefreshItems->append( item ); 02285 } 02286 } 02287 else if ( !d->refreshItemWasFiltered ) 02288 { 02289 if ( !d->lstRemoveItems ) 02290 d->lstRemoveItems = new KFileItemList; 02291 02292 // notify the user that the mimetype of a file changed that doesn't match 02293 // a filter or does match an exclude filter 02294 d->lstRemoveItems->append( item ); 02295 } 02296 } 02297 02298 void KDirLister::emitItems() 02299 { 02300 KFileItemList *tmpNew = d->lstNewItems; 02301 d->lstNewItems = 0; 02302 02303 KFileItemList *tmpMime = d->lstMimeFilteredItems; 02304 d->lstMimeFilteredItems = 0; 02305 02306 KFileItemList *tmpRefresh = d->lstRefreshItems; 02307 d->lstRefreshItems = 0; 02308 02309 KFileItemList *tmpRemove = d->lstRemoveItems; 02310 d->lstRemoveItems = 0; 02311 02312 if ( tmpNew ) 02313 { 02314 emit newItems( *tmpNew ); 02315 delete tmpNew; 02316 } 02317 02318 if ( tmpMime ) 02319 { 02320 emit itemsFilteredByMime( *tmpMime ); 02321 delete tmpMime; 02322 } 02323 02324 if ( tmpRefresh ) 02325 { 02326 emit refreshItems( *tmpRefresh ); 02327 delete tmpRefresh; 02328 } 02329 02330 if ( tmpRemove ) 02331 { 02332 for ( KFileItem *tmp = tmpRemove->first(); tmp; tmp = tmpRemove->next() ) 02333 emit deleteItem( tmp ); 02334 delete tmpRemove; 02335 } 02336 } 02337 02338 void KDirLister::emitDeleteItem( KFileItem *item ) 02339 { 02340 if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) 02341 return; // No reason to continue... bailing out here prevents a mimetype scan. 02342 if ( matchesMimeFilter( item ) ) 02343 emit deleteItem( item ); 02344 } 02345 02346 02347 // ================ private slots ================ // 02348 02349 void KDirLister::slotInfoMessage( KIO::Job *, const QString& message ) 02350 { 02351 emit infoMessage( message ); 02352 } 02353 02354 void KDirLister::slotPercent( KIO::Job *job, unsigned long pcnt ) 02355 { 02356 d->jobData[static_cast<KIO::ListJob *>(job)].percent = pcnt; 02357 02358 int result = 0; 02359 02360 KIO::filesize_t size = 0; 02361 02362 QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); 02363 while ( dataIt != d->jobData.end() ) 02364 { 02365 result += (*dataIt).percent * (*dataIt).totalSize; 02366 size += (*dataIt).totalSize; 02367 ++dataIt; 02368 } 02369 02370 if ( size != 0 ) 02371 result /= size; 02372 else 02373 result = 100; 02374 emit percent( result ); 02375 } 02376 02377 void KDirLister::slotTotalSize( KIO::Job *job, KIO::filesize_t size ) 02378 { 02379 d->jobData[static_cast<KIO::ListJob *>(job)].totalSize = size; 02380 02381 KIO::filesize_t result = 0; 02382 QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); 02383 while ( dataIt != d->jobData.end() ) 02384 { 02385 result += (*dataIt).totalSize; 02386 ++dataIt; 02387 } 02388 02389 emit totalSize( result ); 02390 } 02391 02392 void KDirLister::slotProcessedSize( KIO::Job *job, KIO::filesize_t size ) 02393 { 02394 d->jobData[static_cast<KIO::ListJob *>(job)].processedSize = size; 02395 02396 KIO::filesize_t result = 0; 02397 QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); 02398 while ( dataIt != d->jobData.end() ) 02399 { 02400 result += (*dataIt).processedSize; 02401 ++dataIt; 02402 } 02403 02404 emit processedSize( result ); 02405 } 02406 02407 void KDirLister::slotSpeed( KIO::Job *job, unsigned long spd ) 02408 { 02409 d->jobData[static_cast<KIO::ListJob *>(job)].speed = spd; 02410 02411 int result = 0; 02412 QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); 02413 while ( dataIt != d->jobData.end() ) 02414 { 02415 result += (*dataIt).speed; 02416 ++dataIt; 02417 } 02418 02419 emit speed( result ); 02420 } 02421 02422 uint KDirLister::numJobs() 02423 { 02424 return d->jobData.count(); 02425 } 02426 02427 void KDirLister::jobDone( KIO::ListJob *job ) 02428 { 02429 d->jobData.remove( job ); 02430 } 02431 02432 void KDirLister::jobStarted( KIO::ListJob *job ) 02433 { 02434 KDirListerPrivate::JobData jobData; 02435 jobData.speed = 0; 02436 jobData.percent = 0; 02437 jobData.processedSize = 0; 02438 jobData.totalSize = 0; 02439 02440 d->jobData.insert( job, jobData ); 02441 d->complete = false; 02442 } 02443 02444 void KDirLister::connectJob( KIO::ListJob *job ) 02445 { 02446 connect( job, SIGNAL(infoMessage( KIO::Job *, const QString& )), 02447 this, SLOT(slotInfoMessage( KIO::Job *, const QString& )) ); 02448 connect( job, SIGNAL(percent( KIO::Job *, unsigned long )), 02449 this, SLOT(slotPercent( KIO::Job *, unsigned long )) ); 02450 connect( job, SIGNAL(totalSize( KIO::Job *, KIO::filesize_t )), 02451 this, SLOT(slotTotalSize( KIO::Job *, KIO::filesize_t )) ); 02452 connect( job, SIGNAL(processedSize( KIO::Job *, KIO::filesize_t )), 02453 this, SLOT(slotProcessedSize( KIO::Job *, KIO::filesize_t )) ); 02454 connect( job, SIGNAL(speed( KIO::Job *, unsigned long )), 02455 this, SLOT(slotSpeed( KIO::Job *, unsigned long )) ); 02456 } 02457 02458 void KDirLister::setMainWindow( QWidget *window ) 02459 { 02460 d->window = window; 02461 } 02462 02463 QWidget *KDirLister::mainWindow() 02464 { 02465 return d->window; 02466 } 02467 02468 KFileItemList KDirLister::items( WhichItems which ) const 02469 { 02470 return itemsForDir( url(), which ); 02471 } 02472 02473 KFileItemList KDirLister::itemsForDir( const KURL& dir, WhichItems which ) const 02474 { 02475 KFileItemList result; 02476 KFileItemList *allItems = s_pCache->itemsForDir( dir ); 02477 if ( !allItems ) 02478 return result; 02479 02480 if ( which == AllItems ) 02481 result = *allItems; // shallow copy 02482 else // only items passing the filters 02483 { 02484 for ( KFileItemListIterator kit( *allItems ); kit.current(); ++kit ) 02485 { 02486 KFileItem *item = *kit; 02487 bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item ); 02488 if ( !isExcluded && matchesMimeFilter( item ) ) 02489 result.append( item ); 02490 } 02491 } 02492 02493 return result; 02494 } 02495 02496 // to keep BC changes 02497 02498 void KDirLister::virtual_hook( int, void * ) 02499 { /*BASE::virtual_hook( id, data );*/ } 02500 02501 #include "kdirlister.moc" 02502 #include "kdirlister_p.moc"