00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
00044
00045
00046
00047 #ifdef NDEBUG
00048 #undef DEBUG_CACHE
00049 #endif
00050
00051 K_GLOBAL_STATIC(KDirListerCache, kDirListerCache)
00052
00053 KDirListerCache::KDirListerCache()
00054 : itemsCached( 10 )
00055 {
00056
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
00075
00076 qAddPostRoutine(kDirListerCache.destroy);
00077 }
00078
00079 KDirListerCache::~KDirListerCache()
00080 {
00081
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
00094
00095 bool KDirListerCache::listDir( KDirLister *lister, const KUrl& _u,
00096 bool _keep, bool _reload )
00097 {
00098 KUrl _url(_u);
00099 _url.cleanPath();
00100
00101 if (!_url.host().isEmpty() && KProtocolInfo::protocolClass(_url.protocol()) == ":local"
00102 && _url.protocol() != "file") {
00103
00104
00105 _url.setHost(QString());
00106 if (_keep == false)
00107 emit lister->redirection(_url);
00108 }
00109
00110
00111 _url.adjustPath(KUrl::RemoveTrailingSlash);
00112
00113 const QString urlStr = _url.url();
00114
00115 QString resolved;
00116 if (_url.isLocalFile()) {
00117
00118 const QString local = _url.toLocalFile();
00119 resolved = QFileInfo(local).canonicalFilePath();
00120 if (local != resolved)
00121 canonicalUrls[resolved].append(urlStr);
00122
00123
00124
00125
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
00137
00138 if (!_keep) {
00139
00140 stop(lister, true );
00141
00142
00143 forgetDirs(lister);
00144
00145 lister->d->rootFileItem = KFileItem();
00146 } else if (lister->d->lstDirs.contains(_url)) {
00147
00148 stop(lister, _url, true );
00149
00150
00151
00152
00153 lister->d->lstDirs.removeAll(_url);
00154
00155
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)
00167 lister->d->url = _url;
00168
00169 DirItem *itemU = itemsInUse.value(urlStr);
00170
00171 KDirListerCacheDirectoryData& dirData = directoryData[urlStr];
00172
00173 if (dirData.listersCurrentlyListing.isEmpty()) {
00174
00175
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
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
00194
00195 new KDirLister::Private::CachedItemsJob(lister, itemU->lstItems, itemU->rootItem, _url, _reload);
00196
00197 } else {
00198
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
00210
00211
00212
00213
00214
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
00245 Q_ASSERT(!dirData.listersCurrentlyListing.contains(lister));
00246 dirData.listersCurrentlyListing.append( lister );
00247
00248 KIO::ListJob *job = jobForUrl( urlStr );
00249
00250 if( job ) {
00251 lister->d->jobStarted( job );
00252 lister->d->connectJob( job );
00253 }
00254 Q_ASSERT( itemU );
00255
00256
00257
00258
00259 new KDirLister::Private::CachedItemsJob(lister, itemU->lstItems, itemU->rootItem, _url, _reload);
00260
00261 #ifdef DEBUG_CACHE
00262 printDebug();
00263 #endif
00264 }
00265
00266
00267 if (lister->d->autoUpdate)
00268 itemU->incAutoUpdate();
00269
00270 return true;
00271 }
00272
00273 void KDirLister::Private::CachedItemsJob::done()
00274 {
00275
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);
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
00297 kdl->addNewItems(_url, items);
00298 kdl->emitItems();
00299
00300 KDirListerCacheDirectoryData& dirData = directoryData[urlStr];
00301 Q_ASSERT(dirData.listersCurrentlyListing.contains(lister));
00302
00303
00304
00305
00306
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
00352 #endif
00353
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) ) {
00361
00362 const QString url = dirit.key();
00363
00364
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
00384
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
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) ) {
00406
00407 stopLister(lister, urlStr, dirData, silent);
00408
00409 if ( lister->d->numJobs() == 0 ) {
00410 lister->d->complete = true;
00411
00412 if (!silent) {
00413 emit lister->canceled();
00414 }
00415 }
00416 }
00417 }
00418
00419
00420 void KDirListerCache::stopLister(KDirLister* lister, const QString& url, KDirListerCacheDirectoryData& dirData, bool silent)
00421 {
00422
00423
00424
00425
00426
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
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
00451
00452 emit lister->clear();
00453
00454
00455
00456
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)
00470 return true;
00471 const bool supermount = mp->mountType() == "supermount";
00472 if (supermount) {
00473 return true;
00474 }
00475
00476 return mp->mountOptions().contains("noauto");
00477 }
00478
00479
00480 void KDirListerCache::forgetDirs( KDirLister *lister, const KUrl& _url, bool notify )
00481 {
00482
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
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
00504 directoryData.erase(dit);
00505 itemsInUse.remove( urlStr );
00506
00507
00508 if ( job ) {
00509 killJob( job );
00510 kDebug(7004) << "Killing update job for " << urlStr;
00511
00512
00513
00514
00515
00516 if ( lister->d->numJobs() == 0 ) {
00517 lister->d->complete = true;
00518
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
00534
00535
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
00543
00544
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;
00558 }
00559 else
00560 item->incAutoUpdate();
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
00582
00583
00584
00585
00586 KDirListerCacheDirectoryData& dirData = directoryData[urlStr];
00587 QList<KDirLister *> listers = dirData.listersCurrentlyListing;
00588 QList<KDirLister *> holders = dirData.listersCurrentlyHolding;
00589
00590
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
00607
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();
00613 delete job;
00614 killed = true;
00615 }
00616 }
00617 }
00618
00619
00620
00621
00622
00623
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
00683
00684 }
00685
00686
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
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
00758
00759
00760 dirItem = dirItemForUrl(url);
00761 if (dirItem && !dirItem->rootItem.isNull() && dirItem->rootItem.url() == url) {
00762
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 )
00772 {
00773 KUrl urlDir(dir);
00774 kDebug(7004) << urlDir;
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 )
00785 {
00786
00787
00788 slotFilesRemoved(KUrl::List(fileList));
00789 }
00790
00791 void KDirListerCache::slotFilesRemoved(const KUrl::List& fileList)
00792 {
00793
00794
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);
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
00818 if (fileitem.isNull() || fileitem.isDir()) {
00819 deletedSubdirs.append(url);
00820 }
00821 dirItem->lstItems.erase(fit);
00822 break;
00823 }
00824 }
00825 }
00826
00827 QMap<QString, KFileItemList>::const_iterator rit = removedItemsByDir.constBegin();
00828 for(; rit != removedItemsByDir.constEnd(); ++rit) {
00829
00830
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
00839
00840 deleteDir(url);
00841 }
00842 }
00843
00844 void KDirListerCache::slotFilesChanged( const QStringList &fileList )
00845 {
00846
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);
00858 } else {
00859 pendingRemoteUpdates.insert(fileitem);
00860
00861
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
00873
00874
00875 processPendingUpdates();
00876 }
00877
00878 void KDirListerCache::slotFileRenamed( const QString &_src, const QString &_dst )
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
00898
00899
00900
00901 KFileItem* existingDestItem = findByUrl(0, dst);
00902 if (existingDestItem) {
00903
00904 slotFilesRemoved(dst);
00905 }
00906
00907
00908
00909
00910
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
00918
00919 fileitem = findByUrl(0, oldurl);
00920 if (!fileitem)
00921 return;
00922 }
00923
00924
00925 if (!oldItem.isLocalFile() && !oldItem.localPath().isEmpty()) {
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
00948
00949
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
00956 if (dit != directoryData.end())
00957 listers += (*dit).listersCurrentlyHolding + (*dit).listersCurrentlyListing;
00958 if (oldItem.isDir()) {
00959
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
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();
00986
00987 if (dirs.count() > 1)
00988 kDebug() << dir << "known as" << dirs;
00989
00990 return dirs;
00991 }
00992
00993
00994
00995
00996
00997 void KDirListerCache::slotFileDirty( const QString& path )
00998 {
00999 kDebug(7004) << path;
01000
01001 KDE_struct_stat buff;
01002 if ( KDE::stat( path, &buff ) != 0 )
01003 return;
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
01021 void KDirListerCache::handleDirDirty(const KUrl& url)
01022 {
01023
01024
01025
01026 const QString dirPath = url.toLocalFile(KUrl::AddTrailingSlash);
01027 QMutableSetIterator<QString> pendingIt(pendingUpdates);
01028 while (pendingIt.hasNext()) {
01029 const QString updPath = pendingIt.next();
01030
01031 if (updPath.startsWith(dirPath) &&
01032 updPath.indexOf('/', dirPath.length()) == -1) {
01033 kDebug(7004) << "forgetting about individual update to" << updPath;
01034 pendingIt.remove();
01035 }
01036 }
01037
01038 updateDirectory(url);
01039 }
01040
01041
01042 void KDirListerCache::handleFileDirty(const KUrl& url)
01043 {
01044
01045 KFileItem* existingItem = findByUrl(0, url);
01046 if (!existingItem) {
01047
01048 KUrl dir(url);
01049 dir.setPath(url.directory());
01050 updateDirectory(dir);
01051 } else {
01052
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 )
01067 {
01068 kDebug(7004) << path;
01069
01070
01071 KUrl fileUrl(path);
01072 slotFilesAdded(fileUrl.directory());
01073 }
01074
01075 void KDirListerCache::slotFileDeleted( const QString& path )
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
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
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
01130
01131
01132
01133
01134
01135
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
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);
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
01183
01184
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
01221
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());
01235 KUrl newUrl(url);
01236
01237
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
01253
01254
01255 #endif
01256
01257
01258
01259
01260
01261
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 );
01275 }
01276
01277
01278
01279 const QList<KDirLister *> holders = oldDirData.listersCurrentlyHolding;
01280 foreach ( KDirLister *kdl, holders ) {
01281 kdl->d->jobStarted( job );
01282
01283
01284 emit kdl->started( oldUrl );
01285
01286 kdl->d->redirect(oldUrl, newUrl, false );
01287 }
01288
01289 DirItem *newDir = itemsInUse.value(newUrlStr);
01290 if ( newDir ) {
01291 kDebug(7004) << newUrl << "already in use";
01292
01293
01294 delete dir;
01295
01296
01297
01298 KIO::ListJob *oldJob = jobForUrl( newUrlStr, job );
01299
01300
01301
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 ) {
01311 kdl->d->jobDone( oldJob );
01312
01313 kdl->d->jobStarted( job );
01314 kdl->d->connectJob( job );
01315 }
01316
01317
01318 foreach ( KDirLister *kdl, listers )
01319 curListers.append( kdl );
01320 } else {
01321 curListers = listers;
01322 }
01323
01324 if ( oldJob )
01325 killJob( oldJob );
01326
01327
01328 QList<KDirLister *>& curHolders = newDirData.listersCurrentlyHolding;
01329 if ( !curHolders.isEmpty() ) {
01330 kDebug(7004) << "and it is currently held.";
01331
01332 foreach ( KDirLister *kdl, curHolders ) {
01333 kdl->d->jobStarted( job );
01334 emit kdl->started( newUrl );
01335 }
01336
01337
01338 foreach ( KDirLister *kdl, holders )
01339 curHolders.append( kdl );
01340 } else {
01341 curHolders = holders;
01342 }
01343
01344
01345
01346
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
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;
01387 }
01388 }
01389
01390
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
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
01421
01422
01423
01424 QLinkedList<ItemInUseChange> itemsToChange;
01425 QSet<KDirLister *> listers;
01426
01427
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
01434
01435 if ( oldUrl.isParentOf( oldDirUrl ) ) {
01436
01437 QString relPath = oldDirUrl.path().mid( oldUrl.path().length() );
01438
01439 KUrl newDirUrl( newUrl );
01440 if ( !relPath.isEmpty() )
01441 newDirUrl.addPath( relPath );
01442
01443
01444
01445 dir->redirect( newDirUrl );
01446
01447 itemsToChange.append(ItemInUseChange(oldDirUrl.url(KUrl::RemoveTrailingSlash),
01448 newDirUrl.url(KUrl::RemoveTrailingSlash),
01449 dir));
01450
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
01476
01477 foreach(const ItemInUseChange& i, itemsToChange) {
01478 itemsInUse.remove(i.oldUrl);
01479 itemsInUse.insert(i.newUrl, i.dirItem);
01480 }
01481
01482
01483
01484 removeDirFromCache( oldUrl );
01485
01486 }
01487
01488
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
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
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
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
01529 foreach ( KDirLister *kdl, listers )
01530 emit kdl->started( newUrl );
01531 }
01532
01533
01534 foreach ( KDirLister *kdl, holders ) {
01535 kdl->d->redirect(oldUrl, newUrl, true );
01536 }
01537 }
01538
01539 void KDirListerCache::removeDirFromCache( const KUrl& dir )
01540 {
01541 kDebug(7004) << dir;
01542 const QList<QString> cachedDirs = itemsCached.keys();
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);
01561 QString jobUrlStr (jobUrl.url());
01562
01563 kDebug(7004) << "finished update" << jobUrl;
01564
01565 KDirListerCacheDirectoryData& dirData = directoryData[jobUrlStr];
01566
01567
01568 dirData.moveListersWithoutCachedItemsJob();
01569 QList<KDirLister *> listers = dirData.listersCurrentlyHolding;
01570 listers += dirData.listersCurrentlyListing;
01571
01572
01573 Q_ASSERT( !listers.isEmpty() );
01574
01575 if ( job->error() ) {
01576 foreach ( KDirLister* kdl, listers ) {
01577 kdl->d->jobDone( job );
01578
01579
01580
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
01592
01593 processPendingUpdates();
01594 return;
01595 }
01596
01597 DirItem *dir = itemsInUse.value(jobUrlStr, 0);
01598 Q_ASSERT(dir);
01599 dir->complete = true;
01600
01601
01602
01603 bool delayedMimeTypes = true;
01604 foreach ( KDirLister *kdl, listers )
01605 delayedMimeTypes &= kdl->d->delayedMimeTypes;
01606
01607 QHash<QString, KFileItem*> fileItems;
01608
01609
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
01622 KFileItem item( *it, jobUrl, delayedMimeTypes, true );
01623
01624 const QString name = item.name();
01625 Q_ASSERT( !name.isEmpty() );
01626
01627
01628
01629 if ( name.isEmpty() || name == ".." )
01630 continue;
01631
01632 if ( name == "." )
01633 {
01634
01635
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
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
01654 if (!tmp->cmp( item ) || inPendingRemoteUpdates) {
01655
01656 if (inPendingRemoteUpdates) {
01657 pendingRemoteUpdates.erase(pru_it);
01658 }
01659
01660
01661
01662 const KFileItem oldItem = *tmp;
01663 *tmp = item;
01664 foreach ( KDirLister *kdl, listers )
01665 kdl->d->addRefreshItem(jobUrl, oldItem, *tmp);
01666 }
01667
01668 tmp->mark();
01669 }
01670 else
01671 {
01672
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
01701
01702 processPendingUpdates();
01703 }
01704
01705
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
01739 QMutableListIterator<KFileItem> kit(lstItems);
01740 while (kit.hasNext()) {
01741 const KFileItem& item = kit.next();
01742 if (!item.isMarked()) {
01743
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
01767
01768
01769
01770
01771
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
01786 DirectoryDataHash::iterator dit = directoryData.find(deletedUrlStr);
01787 if (dit != directoryData.end()) {
01788
01789 QList<KDirLister *> listers = (*dit).listersCurrentlyListing;
01790 foreach ( KDirLister *kdl, listers )
01791 stop( kdl, deletedUrl );
01792
01793
01794
01795
01796 QList<KDirLister *> holders = (*dit).listersCurrentlyHolding;
01797 foreach ( KDirLister *kdl, holders ) {
01798
01799 if ( kdl->d->url == deletedUrl )
01800 {
01801
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
01826
01827 int count = itemsInUse.remove( deletedUrlStr );
01828 Q_ASSERT( count == 0 );
01829 Q_UNUSED( count );
01830 }
01831
01832
01833 removeDirFromCache( dirUrl );
01834 }
01835
01836
01837 void KDirListerCache::processPendingUpdates()
01838 {
01839 QSet<KDirLister *> listers;
01840 foreach(const QString& file, pendingUpdates) {
01841 kDebug(7004) << file;
01842 KUrl u(file);
01843 KFileItem *item = findByUrl( 0, u );
01844 if ( item ) {
01845
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
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
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
01935
01936
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
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
02040
02041 hasPendingChanges = false;
02042
02043 const Private::FilterSettings newSettings = settings;
02044 settings = oldSettings;
02045
02046
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);
02075 else if (!nowVisible && item.isMarked())
02076 deletedItems.append(*kit);
02077 }
02078 if (!deletedItems.isEmpty()) {
02079 emit m_parent->itemsDeleted(deletedItems);
02080
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
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
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"))
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
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
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
02233 QStringList::const_iterator it = filters.begin();
02234 for ( ; it != filters.end(); ++it )
02235 if ( mimeptr->is(*it) )
02236 return true;
02237
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
02263
02264 void KDirLister::Private::addNewItem(const KUrl& directoryUrl, const KFileItem &item)
02265 {
02266 if (!isItemVisible(item))
02267 return;
02268
02269
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 );
02280 }
02281 else
02282 {
02283 if ( !lstMimeFilteredItems ) {
02284 lstMimeFilteredItems = new KFileItemList;
02285 }
02286
02287 Q_ASSERT( !item.isNull() );
02288 lstMimeFilteredItems->append( item );
02289 }
02290 }
02291
02292 void KDirLister::Private::addNewItems(const KUrl& directoryUrl, const KFileItemList& items)
02293 {
02294
02295
02296
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
02334
02335
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());
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
02387
02388
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
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
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
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
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
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
02612
02613
02614
02615
02616 QMutableListIterator<KDirLister *> lister_it(listersCurrentlyListing);
02617 while (lister_it.hasNext()) {
02618 KDirLister* kdl = lister_it.next();
02619 if (!kdl->d->m_cachedItemsJob) {
02620
02621
02622
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"