• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kdirlister.cpp

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

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal