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