kio Library API Documentation

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