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