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