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