kio Library API Documentation

kdirlister.cpp

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                  2001-2004 Michael Brade <brade@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00019    Boston, MA 02111-1307, USA.
00020 */
00021 
00022 #include "kdirlister.h"
00023 
00024 #include <qregexp.h>
00025 #include <qptrlist.h>
00026 #include <qtimer.h>
00027 
00028 #include <kapplication.h>
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031 #include <kio/job.h>
00032 #include <kmessagebox.h>
00033 #include <kglobal.h>
00034 #include <kglobalsettings.h>
00035 #include <kstaticdeleter.h>
00036 
00037 #include "kdirlister_p.h"
00038 
00039 #include <assert.h>
00040 
00041 KDirListerCache* KDirListerCache::s_pSelf = 0;
00042 static KStaticDeleter<KDirListerCache> sd_KDirListerCache;
00043 
00044 // Enable this to get printDebug() called often, to see the contents of the cache
00045 //#define DEBUG_CACHE
00046 
00047 // Make really sure it doesn't get activated in the final build
00048 #ifdef NDEBUG
00049 #undef DEBUG_CACHE
00050 #endif
00051 
00052 KDirListerCache::KDirListerCache( int maxCount )
00053   : itemsCached( maxCount )
00054 {
00055   kdDebug(7004) << "+KDirListerCache" << endl;
00056 
00057   itemsInUse.setAutoDelete( false );
00058   itemsCached.setAutoDelete( true );
00059   urlsCurrentlyListed.setAutoDelete( true );
00060   urlsCurrentlyHeld.setAutoDelete( true );
00061   pendingUpdates.setAutoDelete( true );
00062 
00063   connect( kdirwatch, SIGNAL( dirty( const QString& ) ),
00064            this, SLOT( slotFileDirty( const QString& ) ) );
00065   connect( kdirwatch, SIGNAL( created( const QString& ) ),
00066            this, SLOT( slotFileCreated( const QString& ) ) );
00067   connect( kdirwatch, SIGNAL( deleted( const QString& ) ),
00068            this, SLOT( slotFileDeleted( const QString& ) ) );
00069 }
00070 
00071 KDirListerCache::~KDirListerCache()
00072 {
00073   kdDebug(7004) << "-KDirListerCache" << endl;
00074 
00075   itemsInUse.setAutoDelete( true );
00076   itemsInUse.clear();
00077   itemsCached.clear();
00078   urlsCurrentlyListed.clear();
00079   urlsCurrentlyHeld.clear();
00080 
00081   if ( KDirWatch::exists() )
00082     kdirwatch->disconnect( this );
00083 }
00084 
00085 // setting _reload to true will emit the old files and
00086 // call updateDirectory
00087 void KDirListerCache::listDir( KDirLister* lister, const KURL& _u,
00088                                bool _keep, bool _reload )
00089 {
00090   // like this we don't have to worry about trailing slashes any further
00091   KURL _url = _u;
00092   _url.cleanPath(); // kill consecutive slashes
00093   _url.adjustPath(-1);
00094   QString urlStr = _url.url();
00095 
00096 #ifdef DEBUG_CACHE
00097   printDebug();
00098 #endif
00099   kdDebug(7004) << k_funcinfo << lister << " url=" << _url
00100                 << " keep=" << _keep << " reload=" << _reload << endl;
00101 
00102   if ( !_keep )
00103   {
00104     // stop any running jobs for lister
00105     stop( lister );
00106 
00107     // clear our internal list for lister
00108     forgetDirs( lister );
00109 
00110     lister->d->rootFileItem = 0;
00111   }
00112   else if ( lister->d->lstDirs.find( _url ) != lister->d->lstDirs.end() )
00113   {
00114     // stop the job listing _url for this lister
00115     stop( lister, _url );
00116 
00117     // remove the _url as well, it will be added in a couple of lines again!
00118     // forgetDirs with three args does not do this
00119     // TODO: think about moving this into forgetDirs
00120     lister->d->lstDirs.remove( lister->d->lstDirs.find( _url ) );
00121 
00122     // clear _url for lister
00123     forgetDirs( lister, _url, true );
00124 
00125     if ( lister->d->url == _url )
00126       lister->d->rootFileItem = 0;
00127   }
00128 
00129   lister->d->lstDirs.append( _url );
00130 
00131   if ( lister->d->url.isEmpty() || !_keep ) // set toplevel URL only if not set yet
00132     lister->d->url = _url;
00133 
00134   DirItem *itemU = itemsInUse[urlStr];
00135   DirItem *itemC;
00136 
00137   if ( !urlsCurrentlyListed[urlStr] )
00138   {
00139     // if there is an update running for _url already we get into
00140     // the following case - it will just be restarted by updateDirectory().
00141 
00142     if ( itemU )
00143     {
00144       kdDebug(7004) << "listDir: Entry already in use: " << _url << endl;
00145 
00146       bool oldState = lister->d->complete;
00147       lister->d->complete = false;
00148 
00149       emit lister->started( _url );
00150 
00151       if ( !lister->d->rootFileItem && lister->d->url == _url )
00152         lister->d->rootFileItem = itemU->rootItem;
00153 
00154       lister->addNewItems( *(itemU->lstItems) );
00155       lister->emitItems();
00156 
00157       lister->d->complete = oldState;
00158 
00159       emit lister->completed( _url );
00160       if ( lister->d->complete )
00161         emit lister->completed();
00162 
00163       // _url is already in use, so there is already an entry in urlsCurrentlyHeld
00164       assert( urlsCurrentlyHeld[urlStr] );
00165       urlsCurrentlyHeld[urlStr]->append( lister );
00166 
00167       if ( _reload || !itemU->complete )
00168         updateDirectory( _url );
00169     }
00170     else if ( !_reload && (itemC = itemsCached.take( urlStr )) )
00171     {
00172       kdDebug(7004) << "listDir: Entry in cache: " << _url << endl;
00173 
00174       itemC->decAutoUpdate();
00175       itemsInUse.insert( urlStr, itemC );
00176       itemU = itemC;
00177 
00178       bool oldState = lister->d->complete;
00179       lister->d->complete = false;
00180 
00181       emit lister->started( _url );
00182 
00183       if ( !lister->d->rootFileItem && lister->d->url == _url )
00184         lister->d->rootFileItem = itemC->rootItem;
00185 
00186       lister->addNewItems( *(itemC->lstItems) );
00187       lister->emitItems();
00188 
00189       lister->d->complete = oldState;
00190 
00191       emit lister->completed( _url );
00192       if ( lister->d->complete )
00193         emit lister->completed();
00194 
00195       Q_ASSERT( !urlsCurrentlyHeld[urlStr] );
00196       QPtrList<KDirLister> *list = new QPtrList<KDirLister>;
00197       list->append( lister );
00198       urlsCurrentlyHeld.insert( urlStr, list );
00199 
00200       if ( !itemC->complete )
00201         updateDirectory( _url );
00202     }
00203     else  // dir not in cache or _reload is true
00204     {
00205       kdDebug(7004) << "listDir: Entry not in cache or reloaded: " << _url << endl;
00206 
00207       QPtrList<KDirLister> *list = new QPtrList<KDirLister>;
00208       list->append( lister );
00209       urlsCurrentlyListed.insert( urlStr, list );
00210 
00211       itemsCached.remove( urlStr );
00212       itemU = new DirItem( _url );
00213       itemsInUse.insert( urlStr, itemU );
00214 
00215 //        // we have a limit of MAX_JOBS_PER_LISTER concurrently running jobs
00216 //        if ( lister->numJobs() >= MAX_JOBS_PER_LISTER )
00217 //        {
00218 //          lstPendingUpdates.append( _url );
00219 //        }
00220 //        else
00221 //        {
00222 
00223       if ( lister->d->url == _url )
00224         lister->d->rootFileItem = 0;
00225 
00226       lister->d->complete = false;
00227 
00228       KIO::ListJob* job = KIO::listDir( _url, false /* no default GUI */ );
00229       lister->jobStarted(job);
00230       jobs.insert( job, QValueList<KIO::UDSEntry>() );
00231 
00232       if (lister->d->window)
00233         job->setWindow(lister->d->window);
00234 
00235       connect( job, SIGNAL( entries( KIO::Job *, const KIO::UDSEntryList & ) ),
00236                this, SLOT( slotEntries( KIO::Job *, const KIO::UDSEntryList & ) ) );
00237       connect( job, SIGNAL( result( KIO::Job * ) ),
00238                this, SLOT( slotResult( KIO::Job * ) ) );
00239       connect( job, SIGNAL( redirection( KIO::Job *, const KURL & ) ),
00240                this, SLOT( slotRedirection( KIO::Job *, const KURL & ) ) );
00241 
00242       connect( job, SIGNAL( infoMessage( KIO::Job *, const QString& ) ),
00243                lister, SLOT( slotInfoMessage( KIO::Job *, const QString& ) ) );
00244       connect( job, SIGNAL( percent( KIO::Job *, unsigned long ) ),
00245                lister, SLOT( slotPercent( KIO::Job *, unsigned long ) ) );
00246       connect( job, SIGNAL( totalSize( KIO::Job *, KIO::filesize_t ) ),
00247                lister, SLOT( slotTotalSize( KIO::Job *, KIO::filesize_t ) ) );
00248       connect( job, SIGNAL( processedSize( KIO::Job *, KIO::filesize_t ) ),
00249                lister, SLOT( slotProcessedSize( KIO::Job *, KIO::filesize_t ) ) );
00250       connect( job, SIGNAL( speed( KIO::Job *, unsigned long ) ),
00251                lister, SLOT( slotSpeed( KIO::Job *, unsigned long ) ) );
00252 
00253       emit lister->started( _url );
00254 
00255 //        }
00256     }
00257   }
00258   else
00259   {
00260     kdDebug(7004) << k_funcinfo << "Entry currently being listed: " << _url << endl;
00261 
00262     emit lister->started( _url );
00263 
00264     lister->d->complete = false;
00265     urlsCurrentlyListed[urlStr]->append( lister );
00266 
00267     KIO::ListJob *job = jobForUrl(urlStr);
00268     Q_ASSERT(job);
00269 
00270     lister->jobStarted(job);
00271     connect( job, SIGNAL( infoMessage( KIO::Job *, const QString& ) ),
00272              lister, SLOT( slotInfoMessage( KIO::Job *, const QString& ) ) );
00273     connect( job, SIGNAL( percent( KIO::Job *, unsigned long ) ),
00274              lister, SLOT( slotPercent( KIO::Job *, unsigned long ) ) );
00275     connect( job, SIGNAL( totalSize( KIO::Job *, KIO::filesize_t ) ),
00276              lister, SLOT( slotTotalSize( KIO::Job *, KIO::filesize_t ) ) );
00277     connect( job, SIGNAL( processedSize( KIO::Job *, KIO::filesize_t ) ),
00278              lister, SLOT( slotProcessedSize( KIO::Job *, KIO::filesize_t ) ) );
00279     connect( job, SIGNAL( speed( KIO::Job *, unsigned long ) ),
00280              lister, SLOT( slotSpeed( KIO::Job *, unsigned long ) ) );
00281 
00282     Q_ASSERT( itemU );
00283 
00284     if ( !lister->d->rootFileItem && lister->d->url == _url )
00285       lister->d->rootFileItem = itemU->rootItem;
00286 
00287     lister->addNewItems( *(itemU->lstItems) );
00288     lister->emitItems();
00289   }
00290 
00291   // automatic updating of directories
00292   if ( lister->d->autoUpdate )
00293     itemU->incAutoUpdate();
00294 }
00295 
00296 void KDirListerCache::stop( KDirLister *lister )
00297 {
00298 #ifdef DEBUG_CACHE
00299   printDebug();
00300 #endif
00301   kdDebug(7004) << k_funcinfo << "lister: " << lister << endl;
00302   bool stopped = false;
00303 
00304   QDictIterator< QPtrList<KDirLister> > it( urlsCurrentlyListed );
00305   QPtrList<KDirLister> *listers;
00306   while ( (listers = it.current()) )
00307   {
00308     if ( listers->findRef( lister ) > -1 )
00309     {
00310       // lister is listing url
00311       QString url = it.currentKey();
00312 
00313       //kdDebug(7004) << k_funcinfo << " found lister in list - for " << url << endl;
00314       bool ret = listers->removeRef( lister );
00315       Q_ASSERT(ret);
00316       KIO::ListJob *job = jobForUrl(url);
00317       lister->jobDone(job);
00318 
00319       // move lister to urlsCurrentlyHeld
00320       QPtrList<KDirLister> *holders = urlsCurrentlyHeld[url];
00321       if ( !holders )
00322       {
00323         holders = new QPtrList<KDirLister>;
00324         holders->append( lister );
00325         urlsCurrentlyHeld.insert( url, holders );
00326       }
00327       else
00328         holders->append( lister );
00329 
00330       emit lister->canceled( KURL( url ) );
00331 
00332       //kdDebug(7004) << "KDirListerCache::stop(lister) remaining list: " << listers->count() << " listers" << endl;
00333       //kill the job if it isn't used any more
00334       if ( listers->isEmpty() )
00335       {
00336         killJob( job );
00337         urlsCurrentlyListed.remove( url );
00338       }
00339 
00340       stopped = true;
00341     }
00342     else
00343       ++it;
00344   }
00345 
00346   if ( stopped )
00347   {
00348     emit lister->canceled();
00349     lister->d->complete = true;
00350   }
00351 
00352   // this is wrong if there is still an update running!
00353   //Q_ASSERT( lister->d->complete );
00354 }
00355 
00356 void KDirListerCache::stop( KDirLister *lister, const KURL& _u )
00357 {
00358   QString urlStr( _u.url(-1) );
00359   KURL _url( urlStr );
00360 
00361   // TODO: consider to stop all the "child jobs" of _url as well
00362   kdDebug(7004) << k_funcinfo << lister << " url=" << _url << endl;
00363 
00364   QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
00365   if ( !listers || !listers->removeRef( lister ) )
00366     return;
00367 
00368   // move lister to urlsCurrentlyHeld
00369   QPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr];
00370   if ( !holders )
00371   {
00372     holders = new QPtrList<KDirLister>;
00373     holders->append( lister );
00374     urlsCurrentlyHeld.insert( urlStr, holders );
00375   }
00376   else
00377     holders->append( lister );
00378 
00379   KIO::ListJob *job = jobForUrl(urlStr);
00380   lister->jobDone(job);
00381   emit lister->canceled( _url );
00382 
00383   if ( listers->isEmpty() )   // kill the job
00384   {
00385     killJob( job );
00386     urlsCurrentlyListed.remove( urlStr );
00387   }
00388 
00389   if ( lister->numJobs() == 0 )
00390   {
00391     lister->d->complete = true;
00392 
00393     // we killed the last job for lister
00394     emit lister->canceled();
00395   }
00396 }
00397 
00398 void KDirListerCache::setAutoUpdate( KDirLister *lister, bool enable )
00399 {
00400   // IMPORTANT: this method does not check for the current autoUpdate state!
00401 
00402   for ( KURL::List::Iterator it = lister->d->lstDirs.begin();
00403         it != lister->d->lstDirs.end(); ++it )
00404   {
00405     if ( enable )
00406       itemsInUse[(*it).url()]->incAutoUpdate();
00407     else
00408       itemsInUse[(*it).url()]->decAutoUpdate();
00409   }
00410 }
00411 
00412 void KDirListerCache::forgetDirs( KDirLister *lister )
00413 {
00414   kdDebug(7004) << k_funcinfo << lister << endl;
00415 
00416   emit lister->clear();
00417   // clear lister->d->lstDirs before calling forgetDirs(), so that
00418   // it doesn't contain things that itemsInUse doesn't. When emitting
00419   // the canceled signals, lstDirs must not contain anything that
00420   // itemsInUse does not contain. (otherwise it might crash in findByName()).
00421   KURL::List lstDirsCopy = lister->d->lstDirs;
00422   lister->d->lstDirs.clear();
00423 
00424   for ( KURL::List::Iterator it = lstDirsCopy.begin();
00425         it != lstDirsCopy.end(); ++it )
00426   {
00427     forgetDirs( lister, *it, false );
00428   }
00429 }
00430 
00431 void KDirListerCache::forgetDirs( KDirLister *lister, const KURL& _url, bool notify )
00432 {
00433   kdDebug(7004) << k_funcinfo << lister << " _url: " << _url << endl;
00434 
00435   KURL url( _url );
00436   url.adjustPath( -1 );
00437   QString urlStr = url.url();
00438   QPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr];
00439   Q_ASSERT( holders );
00440   holders->removeRef( lister );
00441 
00442   DirItem *item = itemsInUse[urlStr];
00443   Q_ASSERT( item );
00444 
00445   if ( holders->isEmpty() )
00446   {
00447     urlsCurrentlyHeld.remove( urlStr ); // this deletes the (empty) holders list
00448     if ( !urlsCurrentlyListed[urlStr] )
00449     {
00450       // item not in use anymore -> move into cache if complete
00451       itemsInUse.remove( urlStr );
00452 
00453       // this job is a running update
00454       KIO::ListJob *job = jobForUrl(urlStr);
00455       if (job)
00456       {
00457         lister->jobDone(job);
00458         killJob( job );
00459         kdDebug(7004) << k_funcinfo << "Killing update job for " << urlStr << endl;
00460 
00461         emit lister->canceled( url );
00462         if ( lister->numJobs() == 0 )
00463         {
00464           lister->d->complete = true;
00465           emit lister->canceled();
00466         }
00467       }
00468 
00469       if ( notify )
00470       {
00471         lister->d->lstDirs.remove( url );
00472         emit lister->clear( url );
00473       }
00474 
00475       if ( item->complete )
00476       {
00477         kdDebug(7004) << k_funcinfo << lister << " item moved into cache: " << url << endl;
00478         itemsCached.insert( urlStr, item ); // TODO: may return false!!
00479 
00480        // Should we forget the dir for good, or keep a watch on it?
00481         // Generally keep a watch, except when it would prevent
00482         // unmounting a removable device (#37780)
00483         const bool isLocal = item->url.isLocalFile();
00484         const bool isManuallyMounted = isLocal && KIO::manually_mounted( item->url.path() );
00485         bool containsManuallyMounted = false;
00486         if ( !isManuallyMounted && item->lstItems && isLocal ) {
00487           // Look for a manually-mounted directory inside
00488           // If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM
00489           // I hope this isn't too slow (manually_mounted caches the last device so most
00490           // of the time this is just a stat per subdir)
00491           KFileItemListIterator kit( *item->lstItems );
00492           for ( ; kit.current() && !containsManuallyMounted; ++kit )
00493             if ( (*kit)->isDir() && KIO::manually_mounted( (*kit)->url().path() ) )
00494               containsManuallyMounted = true;
00495         }
00496         if ( isManuallyMounted || containsManuallyMounted ) {
00497           kdDebug(7004) << "Not adding a watch on " << item->url << " because it " <<
00498             ( isManuallyMounted ? "is manually mounted" : "contains a manually mounted subdir" )
00499                         << endl;
00500           item->complete = false; // set to "dirty"
00501         }
00502         else
00503             item->incAutoUpdate(); // keep watch
00504       }
00505       else {
00506         delete item;
00507         item = 0;
00508       }
00509     }
00510   }
00511 
00512   if ( item && lister->d->autoUpdate )
00513     item->decAutoUpdate();
00514 }
00515 
00516 void KDirListerCache::updateDirectory( const KURL& _dir )
00517 {
00518   kdDebug(7004) << k_funcinfo << _dir << endl;
00519 
00520   QString urlStr = _dir.url(-1);
00521   if ( !checkUpdate( urlStr ) )
00522     return;
00523 
00524   // A job can be running to
00525   //   - only list a new directory: the listers are in urlsCurrentlyListed
00526   //   - only update a directory: the listers are in urlsCurrentlyHeld
00527   //   - update a currently running listing: the listers are in urlsCurrently
00528 
00529   QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
00530   QPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr];
00531   // restart the job for _dir if it is running already
00532   bool killed = false;
00533   KIO::ListJob *job = jobForUrl(urlStr);
00534   if (job)
00535   {
00536      killed = true;
00537      killJob( job );
00538      if (listers)
00539         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00540            kdl->jobDone(job);
00541      if (holders)
00542         for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
00543            kdl->jobDone(job);
00544   }
00545   kdDebug(7004) << k_funcinfo << "Killed = " << killed << endl;
00546 
00547   // we don't need to emit canceled signals since we only replaced the job,
00548   // the listing is continuing.
00549 
00550   Q_ASSERT( !listers || ( listers && killed ) );
00551 
00552   job = KIO::listDir( _dir, false /* no default GUI */ );
00553   jobs.insert( job, QValueList<KIO::UDSEntry>() );
00554 
00555   connect( job, SIGNAL( entries( KIO::Job *, const KIO::UDSEntryList & ) ),
00556            this, SLOT( slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & ) ) );
00557   connect( job, SIGNAL( result( KIO::Job * ) ),
00558            this, SLOT( slotUpdateResult( KIO::Job * ) ) );
00559 
00560   kdDebug(7004) << k_funcinfo << "update started in " << _dir << endl;
00561 
00562   if (listers)
00563      for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00564         kdl->jobStarted(job);
00565 
00566   if (holders)
00567   {
00568      if ( killed )
00569      {
00570         bool first = true;
00571         for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
00572         {
00573            kdl->jobStarted(job);
00574            kdl->d->complete = false;
00575            if (first && kdl->d->window)
00576            {
00577               first = false;
00578               job->setWindow(kdl->d->window);
00579            }
00580            emit kdl->started( _dir );
00581         }
00582      }
00583      else
00584      {
00585         for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
00586            kdl->jobStarted(job);
00587      }
00588   }
00589 }
00590 
00591 bool KDirListerCache::checkUpdate( const QString& _dir )
00592 {
00593   if ( !itemsInUse[_dir] )
00594   {
00595     DirItem *item = itemsCached[_dir];
00596     if ( item && item->complete )
00597     {
00598       item->complete = false;
00599       item->decAutoUpdate();
00600       // Hmm, this debug output might include login/password from the _dir URL.
00601       //kdDebug(7004) << k_funcinfo << "directory " << _dir << " not in use, marked dirty." << endl;
00602     }
00603     //else
00604       //kdDebug(7004) << k_funcinfo << "aborted, directory " << _dir << " not in cache." << endl;
00605 
00606     return false;
00607   }
00608   else
00609     return true;
00610 }
00611 
00612 KFileItemList* KDirListerCache::itemsForDir( const KURL &_dir ) const
00613 {
00614   QString urlStr = _dir.url(-1);
00615   DirItem *item = itemsInUse[ urlStr ];
00616   if ( !item )
00617     item = itemsCached[ urlStr ];
00618   return item ? item->lstItems : 0;
00619 }
00620 
00621 KFileItem* KDirListerCache::findByName( const KDirLister *lister, const QString& _name ) const
00622 {
00623   Q_ASSERT( lister );
00624 
00625   for ( KURL::List::Iterator it = lister->d->lstDirs.begin();
00626         it != lister->d->lstDirs.end(); ++it )
00627   {
00628     KFileItemListIterator kit( *itemsInUse[(*it).url()]->lstItems );
00629     for ( ; kit.current(); ++kit )
00630       if ( (*kit)->name() == _name )
00631         return (*kit);
00632   }
00633 
00634   return 0L;
00635 }
00636 
00637 KFileItem* KDirListerCache::findByURL( const KDirLister *lister, const KURL& _u ) const
00638 {
00639   KURL _url = _u;
00640   _url.adjustPath(-1);
00641 
00642   KURL parentDir( _url );
00643   parentDir.setPath( parentDir.directory() );
00644 
00645   // If lister is set, check that it contains this dir
00646   if ( lister && !lister->d->lstDirs.contains( parentDir ) )
00647       return 0L;
00648 
00649   KFileItemList* itemList = itemsForDir( parentDir );
00650   if ( itemList )
00651   {
00652     KFileItemListIterator kit( *itemList );
00653     for ( ; kit.current(); ++kit )
00654       if ( (*kit)->url() == _url )
00655         return (*kit);
00656   }
00657   return 0L;
00658 }
00659 
00660 void KDirListerCache::FilesAdded( const KURL &dir )
00661 {
00662   kdDebug(7004) << k_funcinfo << dir << endl;
00663   updateDirectory( dir );
00664 }
00665 
00666 void KDirListerCache::FilesRemoved( const KURL::List &fileList )
00667 {
00668   kdDebug(7004) << k_funcinfo << endl;
00669   KURL::List::ConstIterator it = fileList.begin();
00670   for ( ; it != fileList.end() ; ++it )
00671   {
00672     // emit the deleteItem signal if this file was shown in any view
00673     KFileItem* fileitem = 0L;
00674     KURL parentDir( *it );
00675     parentDir.setPath( parentDir.directory() );
00676     KFileItemList* lstItems = itemsForDir( parentDir );
00677     if ( lstItems )
00678     {
00679       KFileItem* fit = lstItems->first();
00680       for ( ; fit; fit = lstItems->next() )
00681         if ( fit->url() == *it ) {
00682           fileitem = fit;
00683           lstItems->take(); // remove fileitem from list
00684           break;
00685         }
00686     }
00687 
00688     // Tell the views about it before deleting the KFileItems. They might need the subdirs'
00689     // file items (see the dirtree).
00690     if ( fileitem )
00691     {
00692       QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDir.url()];
00693       if ( listers )
00694         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00695           kdl->emitDeleteItem( fileitem );
00696     }
00697 
00698     // If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case.
00699     if ( !fileitem || fileitem->isDir() )
00700     {
00701       // in case of a dir, check if we have any known children, there's much to do in that case
00702       // (stopping jobs, removing dirs from cache etc.)
00703       deleteDir( *it );
00704     }
00705 
00706     // now remove the item itself
00707     delete fileitem;
00708   }
00709 }
00710 
00711 void KDirListerCache::FilesChanged( const KURL::List &fileList )
00712 {
00713   KURL::List dirsToUpdate;
00714   kdDebug(7004) << k_funcinfo << "only half implemented" << endl;
00715   KURL::List::ConstIterator it = fileList.begin();
00716   for ( ; it != fileList.end() ; ++it )
00717   {
00718     if ( ( *it ).isLocalFile() )
00719     {
00720       kdDebug(7004) << "KDirListerCache::FilesChanged " << *it << endl;
00721       KFileItem* fileitem = findByURL( 0, *it );
00722       if ( fileitem )
00723       {
00724           // we need to refresh the item, because e.g. the permissions can have changed.
00725           fileitem->refresh();
00726           emitRefreshItem( fileitem );
00727       }
00728       else
00729           kdDebug(7004) << "item not found" << endl;
00730     } else {
00731       // For remote files, refresh() won't be able to figure out the new information.
00732       // Let's update the dir.
00733       KURL dir( *it );
00734       dir.setPath( dir.directory(-1) );
00735       if ( dirsToUpdate.find( dir ) == dirsToUpdate.end() )
00736         dirsToUpdate.prepend( dir );
00737     }
00738   }
00739 
00740   KURL::List::ConstIterator itdir = dirsToUpdate.begin();
00741   for ( ; itdir != dirsToUpdate.end() ; ++itdir )
00742     updateDirectory( *itdir );
00743   // ## TODO problems with current jobs listing/updating that dir
00744   // ( see kde-2.2.2's kdirlister )
00745 }
00746 
00747 void KDirListerCache::FileRenamed( const KURL &src, const KURL &dst )
00748 {
00749   kdDebug(7004) << k_funcinfo << src.prettyURL() << " -> " << dst.prettyURL() << endl;
00750 #ifdef DEBUG_CACHE
00751   printDebug();
00752 #endif
00753 
00754   // Somehow this should only be called if src is a dir. But how could we know if it is?
00755   // (Note that looking into itemsInUse isn't good enough. One could rename a subdir in a view.)
00756   renameDir( src, dst );
00757 
00758   // Now update the KFileItem representing that file or dir (not exclusive with the above!)
00759   KURL oldurl( src );
00760   oldurl.adjustPath( -1 );
00761   KFileItem* fileitem = findByURL( 0, oldurl );
00762   if ( fileitem )
00763   {
00764     fileitem->setURL( dst );
00765     fileitem->refreshMimeType();
00766 
00767     emitRefreshItem( fileitem );
00768   }
00769 #ifdef DEBUG_CACHE
00770   printDebug();
00771 #endif
00772 }
00773 
00774 void KDirListerCache::emitRefreshItem( KFileItem* fileitem )
00775 {
00776   // Look whether this item was shown in any view, i.e. held by any dirlister
00777   KURL parentDir( fileitem->url() );
00778   parentDir.setPath( parentDir.directory() );
00779   QString parentDirURL = parentDir.url();
00780   QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL];
00781   if ( listers )
00782     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00783     {
00784       kdl->addRefreshItem( fileitem );
00785       kdl->emitItems();
00786     }
00787 
00788   // Also look in urlsCurrentlyListed, in case the user manages to rename during a listing
00789   listers = urlsCurrentlyListed[parentDirURL];
00790   if ( listers )
00791     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00792     {
00793       kdl->addRefreshItem( fileitem );
00794       kdl->emitItems();
00795     }
00796 }
00797 
00798 KDirListerCache* KDirListerCache::self()
00799 {
00800   if ( !s_pSelf )
00801     s_pSelf = sd_KDirListerCache.setObject( s_pSelf, new KDirListerCache );
00802 
00803   return s_pSelf;
00804 }
00805 
00806 // private slots
00807 
00808 // _file can also be a directory being currently held!
00809 void KDirListerCache::slotFileDirty( const QString& _file )
00810 {
00811   kdDebug(7004) << k_funcinfo << _file << endl;
00812 
00813   if ( !pendingUpdates[_file] )
00814   {
00815     KURL dir = KURL( _file );
00816     if ( checkUpdate( dir.url(-1) ) )
00817       updateDirectory( dir );
00818 
00819     // the parent directory of _file
00820     dir.setPath( dir.directory() );
00821     if ( checkUpdate( dir.url() ) )
00822     {
00823       // Nice hack to save memory: use the qt object name to store the filename
00824       QTimer *timer = new QTimer( this, _file.utf8() );
00825       connect( timer, SIGNAL(timeout()), this, SLOT(slotFileDirtyDelayed()) );
00826       pendingUpdates.insert( _file, timer );
00827       timer->start( 500, true );
00828     }
00829   }
00830 }
00831 
00832 // delayed updating of files, FAM is flooding us with events
00833 void KDirListerCache::slotFileDirtyDelayed()
00834 {
00835   QString file = QString::fromUtf8( sender()->name() );
00836 
00837   kdDebug(7004) << k_funcinfo << file << endl;
00838 
00839   // TODO: do it better: don't always create/delete the QTimer but reuse it.
00840   // Delete the timer after the parent directory is removed from the cache.
00841   pendingUpdates.remove( file );
00842 
00843   KURL u;
00844   u.setPath( file );
00845   KFileItem *item = findByURL( 0, u ); // search all items
00846   if ( item )
00847   {
00848     // we need to refresh the item, because e.g. the permissions can have changed.
00849     item->refresh();
00850     emitRefreshItem( item );
00851   }
00852 }
00853 
00854 void KDirListerCache::slotFileCreated( const QString& _file )
00855 {
00856   kdDebug(7004) << k_funcinfo << _file << endl;
00857   // XXX: how to avoid a complete rescan here?
00858   KURL u;
00859   u.setPath( _file );
00860   u.setPath( u.directory() );
00861   FilesAdded( u );
00862 }
00863 
00864 void KDirListerCache::slotFileDeleted( const QString& _file )
00865 {
00866   kdDebug(7004) << k_funcinfo << _file << endl;
00867   KURL u;
00868   u.setPath( _file );
00869   FilesRemoved( u );
00870 }
00871 
00872 void KDirListerCache::slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries )
00873 {
00874   KURL url = static_cast<KIO::ListJob *>(job)->url();
00875   url.adjustPath(-1);
00876   QString urlStr = url.url();
00877 
00878   kdDebug(7004) << k_funcinfo << "new entries for " << url << endl;
00879 
00880   DirItem *dir = itemsInUse[urlStr];
00881   Q_ASSERT( dir );
00882 
00883   QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
00884   Q_ASSERT( listers );
00885   Q_ASSERT( !listers->isEmpty() );
00886 
00887   // check if anyone wants the mimetypes immediately
00888   bool delayedMimeTypes = true;
00889   for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00890     delayedMimeTypes &= kdl->d->delayedMimeTypes;
00891 
00892   // avoid creating these QStrings again and again
00893   static const QString& dot = KGlobal::staticQString(".");
00894   static const QString& dotdot = KGlobal::staticQString("..");
00895 
00896   KIO::UDSEntryListConstIterator it = entries.begin();
00897   KIO::UDSEntryListConstIterator end = entries.end();
00898 
00899   for ( ; it != end; ++it )
00900   {
00901     QString name;
00902 
00903     // find out about the name
00904     KIO::UDSEntry::ConstIterator entit = (*it).begin();
00905     for( ; entit != (*it).end(); ++entit )
00906       if ( (*entit).m_uds == KIO::UDS_NAME )
00907       {
00908         name = (*entit).m_str;
00909         break;
00910       }
00911 
00912     Q_ASSERT( !name.isEmpty() );
00913     if ( name.isEmpty() )
00914       continue;
00915 
00916     if ( name == dot )
00917     {
00918       Q_ASSERT( !dir->rootItem );
00919       dir->rootItem = new KFileItem( *it, url, delayedMimeTypes, true  );
00920 
00921       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00922         if ( !kdl->d->rootFileItem && kdl->d->url == url )
00923           kdl->d->rootFileItem = dir->rootItem;
00924     }
00925     else if ( name != dotdot )
00926     {
00927       KFileItem* item = new KFileItem( *it, url, delayedMimeTypes, true );
00928       Q_ASSERT( item );
00929 
00930       //kdDebug(7004)<< "Adding item: " << item->url() << endl;
00931       dir->lstItems->append( item );
00932 
00933       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00934         kdl->addNewItem( item );
00935     }
00936   }
00937 
00938   for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00939     kdl->emitItems();
00940 }
00941 
00942 void KDirListerCache::slotResult( KIO::Job* j )
00943 {
00944   Q_ASSERT( j );
00945   KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
00946   jobs.remove( job );
00947 
00948   KURL jobUrl = job->url();
00949   jobUrl.adjustPath(-1);  // need remove trailing slashes again, in case of redirections
00950   QString jobUrlStr = jobUrl.url();
00951 
00952   kdDebug(7004) << k_funcinfo << "finished listing " << jobUrl << endl;
00953 #ifdef DEBUG_CACHE
00954   printDebug();
00955 #endif
00956 
00957   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( jobUrlStr );
00958   Q_ASSERT( listers );
00959 
00960   // move the directory to the held directories, do it before emitting
00961   // the signals to make sure it exists in KDirListerCache in case someone
00962   // calls listDir during the signal emission
00963   Q_ASSERT( !urlsCurrentlyHeld[jobUrlStr] );
00964   urlsCurrentlyHeld.insert( jobUrlStr, listers );
00965 
00966   KDirLister *kdl;
00967 
00968   if ( job->error() )
00969   {
00970     for ( kdl = listers->first(); kdl; kdl = listers->next() )
00971     {
00972       kdl->jobDone(job);
00973       kdl->handleError( job );
00974       emit kdl->canceled( jobUrl );
00975       if ( kdl->numJobs() == 0 )
00976       {
00977         kdl->d->complete = true;
00978         emit kdl->canceled();
00979       }
00980     }
00981   }
00982   else
00983   {
00984     DirItem *dir = itemsInUse[jobUrlStr];
00985     Q_ASSERT( dir );
00986     dir->complete = true;
00987 
00988     for ( kdl = listers->first(); kdl; kdl = listers->next() )
00989     {
00990       kdl->jobDone(job);
00991       emit kdl->completed( jobUrl );
00992       if ( kdl->numJobs() == 0 )
00993       {
00994         kdl->d->complete = true;
00995         emit kdl->completed();
00996       }
00997     }
00998   }
00999 
01000   // TODO: hmm, if there was an error and job is a parent of one or more
01001   // of the pending urls we should cancel it/them as well
01002   processPendingUpdates();
01003 
01004 #ifdef DEBUG_CACHE
01005   printDebug();
01006 #endif
01007 }
01008 
01009 void KDirListerCache::slotRedirection( KIO::Job *job, const KURL &url )
01010 {
01011   Q_ASSERT( job );
01012   KURL oldUrl = static_cast<KIO::ListJob *>( job )->url();
01013 
01014   // strip trailing slashes
01015   oldUrl.adjustPath(-1);
01016   KURL newUrl = url;
01017   newUrl.adjustPath(-1);
01018 
01019   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
01020 
01021   // I don't think there can be dirItems that are childs of oldUrl.
01022   // Am I wrong here? And even if so, we don't need to delete them, right?
01023   // DF: redirection happens before listDir emits any item. Makes little sense otherwise.
01024 
01025   DirItem *dir = itemsInUse.take( oldUrl.url() );
01026   Q_ASSERT( dir );
01027 
01028   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrl.url() );
01029   Q_ASSERT( listers );
01030   Q_ASSERT( !listers->isEmpty() );
01031 
01032   for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01033   {
01034     if ( kdl->d->url.equals( oldUrl, true ) )
01035     {
01036       kdl->d->rootFileItem = 0;
01037       kdl->d->url = newUrl;
01038     }
01039 
01040     *kdl->d->lstDirs.find( oldUrl ) = newUrl;
01041 
01042     if ( kdl->d->lstDirs.count() == 1 )
01043     {
01044       emit kdl->clear();
01045       emit kdl->redirection( newUrl );
01046       emit kdl->redirection( oldUrl, newUrl );
01047     }
01048     else
01049     {
01050       emit kdl->clear( oldUrl );
01051       emit kdl->redirection( oldUrl, newUrl );
01052     }
01053   }
01054 
01055   delete dir->rootItem;
01056   dir->rootItem = 0;
01057   dir->lstItems->clear();
01058   dir->redirect( newUrl );
01059   itemsInUse.insert( newUrl.url(), dir );
01060   urlsCurrentlyListed.insert( newUrl.url(), listers );
01061 }
01062 
01063 void KDirListerCache::renameDir( const KURL &oldUrl, const KURL &newUrl )
01064 {
01065   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
01066   QString oldUrlStr = oldUrl.url(-1);
01067   QString newUrlStr = newUrl.url(-1);
01068 
01069   // Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
01070   //DirItem *dir = itemsInUse.take( oldUrlStr );
01071   //emitRedirections( oldUrl, url );
01072 
01073   // Look at all dirs being listed/shown
01074   QDictIterator<DirItem> itu( itemsInUse );
01075   bool goNext;
01076   while ( itu.current() )
01077   {
01078     goNext = true;
01079     DirItem* dir = itu.current();
01080     KURL oldDirUrl ( itu.currentKey() );
01081     //kdDebug(7004) << "itemInUse: " << oldDirUrl.prettyURL() << endl;
01082     // Check if this dir is oldUrl, or a subfolder of it
01083     if ( oldUrl.isParentOf( oldDirUrl ) )
01084     {
01085       QString relPath = oldDirUrl.path().mid( oldUrl.path().length() ); // ### should use KURL::cleanpath like isParentOf does
01086 
01087       KURL newDirUrl( newUrl ); // take new base
01088       if ( !relPath.isEmpty() )
01089         newDirUrl.addPath( relPath ); // add unchanged relative path
01090       //kdDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl.prettyURL() << endl;
01091 
01092       // Update URL in dir item and in itemsInUse
01093       dir->redirect( newDirUrl );
01094       itemsInUse.remove( itu.currentKey() ); // implies ++itu
01095       itemsInUse.insert( newDirUrl.url(-1), dir );
01096       goNext = false; // because of the implied ++itu above
01097       if ( dir->lstItems )
01098       {
01099         // Rename all items under that dir
01100         KFileItemListIterator kit( *dir->lstItems );
01101         for ( ; kit.current(); ++kit )
01102         {
01103           KURL oldItemUrl = (*kit)->url();
01104           QString oldItemUrlStr( oldItemUrl.url(-1) );
01105           KURL newItemUrl( oldItemUrl );
01106           newItemUrl.setPath( newDirUrl.path() );
01107           newItemUrl.addPath( oldItemUrl.fileName() );
01108           kdDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url() << endl;
01109           (*kit)->setURL( newItemUrl );
01110         }
01111       }
01112       emitRedirections( oldDirUrl, newDirUrl );
01113     }
01114     if (goNext)
01115       ++itu;
01116   }
01117 
01118   // Is oldUrl a directory in the cache?
01119   // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
01120   removeDirFromCache( oldUrl );
01121   // TODO rename, instead.
01122 }
01123 
01124 void KDirListerCache::emitRedirections( const KURL &oldUrl, const KURL &url )
01125 {
01126   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << url.prettyURL() << endl;
01127   QString oldUrlStr = oldUrl.url(-1);
01128   QString urlStr = url.url(-1);
01129 
01130   KIO::ListJob *job = jobForUrl(oldUrlStr);
01131   if (job)
01132      killJob( job );
01133 
01134   // Check if we were listing this dir. Need to abort and restart with new name in that case.
01135   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrlStr );
01136   if ( listers )
01137   {
01138     // Tell the world that the job listing the old url is dead.
01139     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01140     {
01141        kdl->jobDone(job);
01142        emit kdl->canceled( oldUrl );
01143     }
01144 
01145     urlsCurrentlyListed.insert( urlStr, listers );
01146   }
01147 
01148   // Check if we are currently displaying this directory (odds opposite wrt above)
01149   // Update urlsCurrentlyHeld dict with new URL
01150   QPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrlStr );
01151   if ( holders )
01152   {
01153     for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01154     {
01155        kdl->jobDone(job);
01156     }
01157     urlsCurrentlyHeld.insert( urlStr, holders );
01158   }
01159 
01160   if (listers)
01161   {
01162     updateDirectory( url );
01163 
01164     // Tell the world about the new url
01165     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01166     {
01167       emit kdl->started( url );
01168     }
01169   }
01170 
01171   if (holders)
01172   {
01173     // And notify the dirlisters of the redirection
01174     for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01175     {
01176       *kdl->d->lstDirs.find( oldUrl ) = url;
01177       if ( kdl->d->lstDirs.count() == 1 )
01178       {
01179         emit kdl->redirection( url );
01180       }
01181       emit kdl->redirection( oldUrl, url );
01182     }
01183   }
01184 }
01185 
01186 void KDirListerCache::removeDirFromCache( const KURL& dir )
01187 {
01188   kdDebug(7004) << "KDirListerCache::removeDirFromCache " << dir.prettyURL() << endl;
01189   QCacheIterator<DirItem> itc( itemsCached );
01190   while ( itc.current() )
01191   {
01192     if ( dir.isParentOf( KURL( itc.currentKey() ) ) )
01193       itemsCached.remove( itc.currentKey() );
01194     else
01195       ++itc;
01196   }
01197 }
01198 
01199 void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list )
01200 {
01201   jobs[static_cast<KIO::ListJob*>(job)] += list;
01202 }
01203 
01204 void KDirListerCache::slotUpdateResult( KIO::Job * j )
01205 {
01206   Q_ASSERT( j );
01207   KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
01208 
01209   KURL jobUrl = job->url();
01210   jobUrl.adjustPath(-1);  // need remove trailing slashes again, in case of redirections
01211   QString jobUrlStr = jobUrl.url();
01212 
01213   kdDebug(7004) << k_funcinfo << "finished update " << jobUrl << endl;
01214 
01215   KDirLister *kdl;
01216 
01217   QPtrList<KDirLister> *listers = urlsCurrentlyHeld[jobUrlStr];
01218   QPtrList<KDirLister> *tmpLst = urlsCurrentlyListed.take( jobUrlStr );
01219 
01220   if ( tmpLst )
01221   {
01222     if ( listers )
01223       for ( kdl = tmpLst->first(); kdl; kdl = tmpLst->next() )
01224       {
01225         Q_ASSERT( listers->containsRef( kdl ) == 0 );
01226         listers->append( kdl );
01227       }
01228     else
01229     {
01230       listers = tmpLst;
01231       urlsCurrentlyHeld.insert( jobUrlStr, listers );
01232     }
01233   }
01234 
01235   // once we are updating dirs that are only in the cache this will fail!
01236   Q_ASSERT( listers );
01237 
01238   if ( job->error() )
01239   {
01240     for ( kdl = listers->first(); kdl; kdl = listers->next() )
01241     {
01242       kdl->jobDone(job);
01243       //don't bother the user
01244       //kdl->handleError( job );
01245 
01246       emit kdl->canceled( jobUrl );
01247       if ( kdl->numJobs() == 0 )
01248       {
01249         kdl->d->complete = true;
01250         emit kdl->canceled();
01251       }
01252     }
01253 
01254     jobs.remove( job );
01255 
01256     // TODO: if job is a parent of one or more
01257     // of the pending urls we should cancel them
01258     processPendingUpdates();
01259     return;
01260   }
01261 
01262   DirItem *dir = itemsInUse[jobUrlStr];
01263   dir->complete = true;
01264 
01265 
01266   // check if anyone wants the mimetypes immediately
01267   bool delayedMimeTypes = true;
01268   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01269     delayedMimeTypes &= kdl->d->delayedMimeTypes;
01270 
01271   // should be enough to get reasonable speed in most cases
01272   QDict<KFileItem> fileItems( 9973 );
01273 
01274   KFileItemListIterator kit ( *(dir->lstItems) );
01275 
01276   // Unmark all items in url
01277   for ( ; kit.current(); ++kit )
01278   {
01279     (*kit)->unmark();
01280     fileItems.insert( (*kit)->url().url(), *kit );
01281   }
01282 
01283   static const QString& dot = KGlobal::staticQString(".");
01284   static const QString& dotdot = KGlobal::staticQString("..");
01285 
01286   KFileItem *item, *tmp;
01287 
01288   QValueList<KIO::UDSEntry> buf = jobs[job];
01289   QValueListIterator<KIO::UDSEntry> it = buf.begin();
01290   for ( ; it != buf.end(); ++it )
01291   {
01292     QString name;
01293 
01294     // Find out about the name
01295     KIO::UDSEntry::Iterator it2 = (*it).begin();
01296     for ( ; it2 != (*it).end(); it2++ )
01297       if ( (*it2).m_uds == KIO::UDS_NAME )
01298       {
01299         name = (*it2).m_str;
01300         break;
01301       }
01302 
01303     Q_ASSERT( !name.isEmpty() );
01304 
01305     // we duplicate the check for dotdot here, to avoid iterating over
01306     // all items again and checking in matchesFilter() that way.
01307     if ( name.isEmpty() || name == dotdot )
01308       continue;
01309 
01310     if ( name == dot )
01311     {
01312       // if the update was started before finishing the original listing
01313       // there is no root item yet
01314       if ( !dir->rootItem )
01315       {
01316         dir->rootItem = new KFileItem( *it, jobUrl, delayedMimeTypes, true  );
01317 
01318         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01319           if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl )
01320             kdl->d->rootFileItem = dir->rootItem;
01321       }
01322 
01323       continue;
01324     }
01325 
01326     // Form the complete url
01327     item = new KFileItem( *it, jobUrl, delayedMimeTypes, true  );
01328 
01329     QString url = item->url().url();
01330     //kdDebug(7004) << "slotUpdateResult : look for " << url << endl;
01331 
01332     // Find this item
01333     if ( (tmp = fileItems[url]) )
01334     {
01335       tmp->mark();
01336 
01337       // check if something changed for this file
01338       if ( !tmp->cmp( *item ) )
01339       {
01340         //kdDebug(7004) << "slotUpdateResult: file changed: " << tmp->name() << endl;
01341         tmp->assign( *item );
01342 
01343         for ( kdl = listers->first(); kdl; kdl = listers->next() )
01344           kdl->addRefreshItem( tmp );
01345       }
01346       delete item;  // gmbl, this is the most often case... IMPORTANT TODO: speed it up somehow!
01347     }
01348     else // this is a new file
01349     {
01350       //kdDebug(7004) << "slotUpdateResult: new file: " << name << endl;
01351 
01352       item->mark();
01353       dir->lstItems->append( item );
01354 
01355       for ( kdl = listers->first(); kdl; kdl = listers->next() )
01356         kdl->addNewItem( item );
01357     }
01358   }
01359 
01360   jobs.remove( job );
01361 
01362   deleteUnmarkedItems( listers, dir->lstItems );
01363 
01364   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01365   {
01366     kdl->emitItems();
01367 
01368     kdl->jobDone(job);
01369 
01370     emit kdl->completed( jobUrl );
01371     if ( kdl->numJobs() == 0 )
01372     {
01373       kdl->d->complete = true;
01374       emit kdl->completed();
01375     }
01376   }
01377 
01378   // TODO: hmm, if there was an error and job is a parent of one or more
01379   // of the pending urls we should cancel it/them as well
01380   processPendingUpdates();
01381 }
01382 
01383 // private
01384 
01385 KIO::ListJob *KDirListerCache::jobForUrl(const QString& _url)
01386 {
01387   KIO::ListJob *job;
01388   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator it = jobs.begin();
01389   while ( it != jobs.end() )
01390   {
01391     job = it.key();
01392     if ( job->url().url(-1) == _url )
01393     {
01394        return job;
01395     }
01396     ++it;
01397   }
01398   return 0;
01399 }
01400 
01401 void KDirListerCache::killJob( KIO::ListJob *job )
01402 {
01403   jobs.remove( job );
01404   job->disconnect( this );
01405   job->kill();
01406 }
01407 
01408 void KDirListerCache::deleteUnmarkedItems( QPtrList<KDirLister> *listers, KFileItemList *lstItems )
01409 {
01410   // Find all unmarked items and delete them
01411   KFileItem* item;
01412   lstItems->first();
01413   while ( (item = lstItems->current()) )
01414     if ( !item->isMarked() )
01415     {
01416       //kdDebug() << k_funcinfo << item->name() << endl;
01417       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01418         kdl->emitDeleteItem( item );
01419 
01420       if ( item->isDir() )
01421         deleteDir( item->url() );
01422 
01423       // finally actually delete the item
01424       lstItems->take();
01425       delete item;
01426     }
01427     else
01428       lstItems->next();
01429 }
01430 
01431 void KDirListerCache::deleteDir( const KURL& dirUrl )
01432 {
01433   //kdDebug() << k_funcinfo << dirUrl.prettyURL() << endl;
01434   // unregister and remove the childs of the deleted item.
01435   // Idea: tell all the KDirListers that they should forget the dir
01436   //       and then remove it from the cache.
01437 
01438   QDictIterator<DirItem> itu( itemsInUse );
01439   while ( itu.current() )
01440   {
01441     KURL deletedUrl( itu.currentKey() );
01442     if ( dirUrl.isParentOf( deletedUrl ) )
01443     {
01444       // stop all jobs for deletedUrl
01445 
01446       QPtrList<KDirLister> *kdls = urlsCurrentlyListed[deletedUrl.url()];
01447       if ( kdls )  // yeah, I lack good names
01448       {
01449         // we need a copy because stop modifies the list
01450         kdls = new QPtrList<KDirLister>( *kdls );
01451         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01452           stop( kdl, deletedUrl );
01453 
01454         delete kdls;
01455       }
01456 
01457       // tell listers holding deletedUrl to forget about it
01458       // this will stop running updates for deletedUrl as well
01459 
01460       kdls = urlsCurrentlyHeld[deletedUrl.url()];
01461       if ( kdls )
01462       {
01463         // we need a copy because forgetDirs modifies the list
01464         kdls = new QPtrList<KDirLister>( *kdls );
01465 
01466         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01467         {
01468           // lister's root is the deleted item
01469           if ( kdl->d->url == deletedUrl )
01470           {
01471             // tell the view first. It might need the subdirs' items (which forgetDirs will delete)
01472             if ( kdl->d->rootFileItem )
01473               emit kdl->deleteItem( kdl->d->rootFileItem );
01474             forgetDirs( kdl );
01475             kdl->d->rootFileItem = 0;
01476           }
01477           else
01478           {
01479             bool treeview = kdl->d->lstDirs.count() > 1;
01480             if ( !treeview )
01481             {
01482               emit kdl->clear();
01483               kdl->d->lstDirs.clear();
01484             }
01485             else
01486               kdl->d->lstDirs.remove( kdl->d->lstDirs.find( deletedUrl ) );
01487 
01488             forgetDirs( kdl, deletedUrl, treeview );
01489           }
01490         }
01491 
01492         delete kdls;
01493       }
01494 
01495       // delete the entry for deletedUrl - should not be needed, it's in
01496       // items cached now
01497 
01498       DirItem *dir = itemsInUse.take( deletedUrl.url() );
01499       Q_ASSERT( !dir );
01500       if ( !dir ) // take didn't find it - move on
01501           ++itu;
01502     }
01503     else
01504       ++itu;
01505   }
01506 
01507   // remove the children from the cache
01508   removeDirFromCache( dirUrl );
01509 }
01510 
01511 void KDirListerCache::processPendingUpdates()
01512 {
01513   // TODO
01514 }
01515 
01516 #ifndef NDEBUG
01517 void KDirListerCache::printDebug()
01518 {
01519   kdDebug(7004) << "Items in use: " << endl;
01520   QDictIterator<DirItem> itu( itemsInUse );
01521   for ( ; itu.current() ; ++itu ) {
01522       kdDebug(7004) << "   " << itu.currentKey() << "  URL: " << itu.current()->url
01523                     << " rootItem: " << ( itu.current()->rootItem ? itu.current()->rootItem->url() : KURL() )
01524                     << " autoUpdates refcount: " << itu.current()->autoUpdates
01525                     << " complete: " << itu.current()->complete
01526                   << ( itu.current()->lstItems ? QString(" with %1 items.").arg(itu.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01527   }
01528 
01529   kdDebug(7004) << "urlsCurrentlyHeld: " << endl;
01530   QDictIterator< QPtrList<KDirLister> > it( urlsCurrentlyHeld );
01531   for ( ; it.current() ; ++it )
01532   {
01533     QString list;
01534     for ( QPtrListIterator<KDirLister> listit( *it.current() ); listit.current(); ++listit )
01535       list += " 0x" + QString::number( (long)listit.current(), 16 );
01536     kdDebug(7004) << "   " << it.currentKey() << "  " << it.current()->count() << " listers: " << list << endl;
01537   }
01538 
01539   kdDebug(7004) << "urlsCurrentlyListed: " << endl;
01540   QDictIterator< QPtrList<KDirLister> > it2( urlsCurrentlyListed );
01541   for ( ; it2.current() ; ++it2 )
01542   {
01543     QString list;
01544     for ( QPtrListIterator<KDirLister> listit( *it2.current() ); listit.current(); ++listit )
01545       list += " 0x" + QString::number( (long)listit.current(), 16 );
01546     kdDebug(7004) << "   " << it2.currentKey() << "  " << it2.current()->count() << " listers: " << list << endl;
01547   }
01548 
01549   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator jit = jobs.begin();
01550   kdDebug(7004) << "Jobs: " << endl;
01551   for ( ; jit != jobs.end() ; ++jit )
01552     kdDebug(7004) << "   " << jit.key() << " listing " << jit.key()->url().prettyURL() << ": " << (*jit).count() << " entries." << endl;
01553 
01554   kdDebug(7004) << "Items in cache: " << endl;
01555   QCacheIterator<DirItem> itc( itemsCached );
01556   for ( ; itc.current() ; ++itc )
01557     kdDebug(7004) << "   " << itc.currentKey() << "  rootItem: "
01558                   << ( itc.current()->rootItem ? itc.current()->rootItem->url().prettyURL() : QString("NULL") )
01559                   << ( itc.current()->lstItems ? QString(" with %1 items.").arg(itc.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01560 }
01561 #endif
01562 
01563 /*********************** -- The new KDirLister -- ************************/
01564 
01565 
01566 KDirLister::KDirLister( bool _delayedMimeTypes )
01567 {
01568   kdDebug(7003) << "+KDirLister" << endl;
01569 
01570   d = new KDirListerPrivate;
01571 
01572   d->complete = true;
01573   d->delayedMimeTypes = _delayedMimeTypes;
01574 
01575   setAutoUpdate( true );
01576   setDirOnlyMode( false );
01577   setShowingDotFiles( false );
01578 
01579   setAutoErrorHandlingEnabled( true, 0 );
01580 }
01581 
01582 KDirLister::~KDirLister()
01583 {
01584   kdDebug(7003) << "-KDirLister" << endl;
01585 
01586   // Stop all running jobs
01587   stop();
01588   s_pCache->forgetDirs( this );
01589 
01590   delete d;
01591 }
01592 
01593 bool KDirLister::openURL( const KURL& _url, bool _keep, bool _reload )
01594 {
01595   if ( !validURL( _url ) )
01596     return false;
01597 
01598   kdDebug(7003) << k_funcinfo << _url.prettyURL()
01599                 << " keep=" << _keep << " reload=" << _reload << endl;
01600 
01601   // emit the current changes made to avoid an inconsistent treeview
01602   if ( d->changes != NONE && _keep )
01603     emitChanges();
01604 
01605   d->changes = NONE;
01606 
01607   s_pCache->listDir( this, _url, _keep, _reload );
01608 
01609   return true;
01610 }
01611 
01612 void KDirLister::stop()
01613 {
01614   kdDebug(7003) << k_funcinfo << endl;
01615   s_pCache->stop( this );
01616 }
01617 
01618 void KDirLister::stop( const KURL& _url )
01619 {
01620   kdDebug(7003) << k_funcinfo << _url.prettyURL() << endl;
01621   s_pCache->stop( this, _url );
01622 }
01623 
01624 bool KDirLister::autoUpdate() const
01625 {
01626   return d->autoUpdate;
01627 }
01628 
01629 void KDirLister::setAutoUpdate( bool _enable )
01630 {
01631   if ( d->autoUpdate == _enable )
01632     return;
01633 
01634   d->autoUpdate = _enable;
01635   s_pCache->setAutoUpdate( this, _enable );
01636 }
01637 
01638 bool KDirLister::showingDotFiles() const
01639 {
01640   return d->isShowingDotFiles;
01641 }
01642 
01643 void KDirLister::setShowingDotFiles( bool _showDotFiles )
01644 {
01645   if ( d->isShowingDotFiles == _showDotFiles )
01646     return;
01647 
01648   d->isShowingDotFiles = _showDotFiles;
01649   d->changes ^= DOT_FILES;
01650 }
01651 
01652 bool KDirLister::dirOnlyMode() const
01653 {
01654   return d->dirOnlyMode;
01655 }
01656 
01657 void KDirLister::setDirOnlyMode( bool _dirsOnly )
01658 {
01659   if ( d->dirOnlyMode == _dirsOnly )
01660     return;
01661 
01662   d->dirOnlyMode = _dirsOnly;
01663   d->changes ^= DIR_ONLY_MODE;
01664 }
01665 
01666 bool KDirLister::autoErrorHandlingEnabled() const
01667 {
01668   return d->autoErrorHandling;
01669 }
01670 
01671 void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent )
01672 {
01673   d->autoErrorHandling = enable;
01674   d->errorParent = parent;
01675 }
01676 
01677 const KURL& KDirLister::url() const
01678 {
01679   return d->url;
01680 }
01681 
01682 void KDirLister::emitChanges()
01683 {
01684   if ( d->changes == NONE )
01685     return;
01686 
01687   static const QString& dot = KGlobal::staticQString(".");
01688   static const QString& dotdot = KGlobal::staticQString("..");
01689 
01690   for ( KURL::List::Iterator it = d->lstDirs.begin();
01691         it != d->lstDirs.end(); ++it )
01692   {
01693     KFileItemListIterator kit( *s_pCache->itemsForDir( *it ) );
01694     for ( ; kit.current(); ++kit )
01695     {
01696       if ( (*kit)->text() == dot || (*kit)->text() == dotdot )
01697         continue;
01698 
01699       bool oldMime = true, newMime = true;
01700 
01701       if ( d->changes & MIME_FILTER )
01702       {
01703         oldMime = doMimeFilter( (*kit)->mimetype(), d->oldMimeFilter )
01704                 && doMimeExcludeFilter( (*kit)->mimetype(), d->oldMimeExcludeFilter );
01705         newMime = doMimeFilter( (*kit)->mimetype(), d->mimeFilter )
01706                 && doMimeExcludeFilter( (*kit)->mimetype(), d->mimeExcludeFilter );
01707 
01708         if ( oldMime && !newMime )
01709         {
01710           emit deleteItem( *kit );
01711           continue;
01712         }
01713       }
01714 
01715       if ( d->changes & DIR_ONLY_MODE )
01716       {
01717         // the lister switched to dirOnlyMode
01718         if ( d->dirOnlyMode )
01719         {
01720           if ( !(*kit)->isDir() )
01721             emit deleteItem( *kit );
01722         }
01723         else if ( !(*kit)->isDir() )
01724           addNewItem( *kit );
01725 
01726         continue;
01727       }
01728 
01729       if ( (*kit)->isHidden() )
01730       {
01731         if ( d->changes & DOT_FILES )
01732         {
01733           // the lister switched to dot files mode
01734           if ( d->isShowingDotFiles )
01735             addNewItem( *kit );
01736           else
01737             emit deleteItem( *kit );
01738 
01739           continue;
01740         }
01741       }
01742       else if ( d->changes & NAME_FILTER )
01743       {
01744         bool oldName = (*kit)->isDir() ||
01745                        d->oldFilters.isEmpty() ||
01746                        doNameFilter( (*kit)->text(), d->oldFilters );
01747 
01748         bool newName = (*kit)->isDir() ||
01749                        d->lstFilters.isEmpty() ||
01750                        doNameFilter( (*kit)->text(), d->lstFilters );
01751 
01752         if ( oldName && !newName )
01753         {
01754           emit deleteItem( *kit );
01755           continue;
01756         }
01757         else if ( !oldName && newName )
01758           addNewItem( *kit );
01759       }
01760 
01761       if ( (d->changes & MIME_FILTER) && !oldMime && newMime )
01762         addNewItem( *kit );
01763     }
01764 
01765     emitItems();
01766   }
01767 
01768   d->changes = NONE;
01769 }
01770 
01771 void KDirLister::updateDirectory( const KURL& _u )
01772 {
01773   s_pCache->updateDirectory( _u );
01774 }
01775 
01776 bool KDirLister::isFinished() const
01777 {
01778   return d->complete;
01779 }
01780 
01781 KFileItem* KDirLister::rootItem() const
01782 {
01783   return d->rootFileItem;
01784 }
01785 
01786 KFileItem* KDirLister::findByURL( const KURL& _url ) const
01787 {
01788   return s_pCache->findByURL( this, _url );
01789 }
01790 
01791 KFileItem* KDirLister::findByName( const QString& _name ) const
01792 {
01793   return s_pCache->findByName( this, _name );
01794 }
01795 
01796 #ifndef KDE_NO_COMPAT
01797 KFileItem* KDirLister::find( const KURL& _url ) const
01798 {
01799   return findByURL( _url );
01800 }
01801 #endif
01802 
01803 
01804 // ================ public filter methods ================ //
01805 
01806 void KDirLister::setNameFilter( const QString& nameFilter )
01807 {
01808   if ( !(d->changes & NAME_FILTER) )
01809   {
01810     d->oldFilters = d->lstFilters;
01811     d->lstFilters.setAutoDelete( false );
01812   }
01813 
01814   d->lstFilters.clear();
01815   d->lstFilters.setAutoDelete( true );
01816 
01817   d->nameFilter = nameFilter;
01818 
01819   // Split on white space
01820   QStringList list = QStringList::split( ' ', nameFilter );
01821   for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
01822     d->lstFilters.append( new QRegExp(*it, false, true ) );
01823 
01824   d->changes |= NAME_FILTER;
01825 }
01826 
01827 const QString& KDirLister::nameFilter() const
01828 {
01829   return d->nameFilter;
01830 }
01831 
01832 void KDirLister::setMimeFilter( const QStringList& mimeFilter )
01833 {
01834   if ( !(d->changes & MIME_FILTER) )
01835     d->oldMimeFilter = d->mimeFilter;
01836 
01837   if (mimeFilter.find ("all/allfiles") != mimeFilter.end () ||
01838       mimeFilter.find ("all/all") != mimeFilter.end ())
01839     d->mimeFilter.clear ();
01840   else
01841     d->mimeFilter = mimeFilter;
01842 
01843   d->changes |= MIME_FILTER;
01844 }
01845 
01846 void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter )
01847 {
01848   if ( !(d->changes & MIME_FILTER) )
01849     d->oldMimeExcludeFilter = d->mimeExcludeFilter;
01850 
01851   d->mimeExcludeFilter = mimeExcludeFilter;
01852   d->changes |= MIME_FILTER;
01853 }
01854 
01855 
01856 void KDirLister::clearMimeFilter()
01857 {
01858   if ( !(d->changes & MIME_FILTER) )
01859   {
01860        d->oldMimeFilter = d->mimeFilter;
01861        d->oldMimeExcludeFilter = d->mimeExcludeFilter;
01862   }
01863   d->mimeFilter.clear();
01864   d->mimeExcludeFilter.clear();
01865   d->changes |= MIME_FILTER;
01866 }
01867 
01868 const QStringList& KDirLister::mimeFilters() const
01869 {
01870   return d->mimeFilter;
01871 }
01872 
01873 bool KDirLister::matchesFilter( const QString& name ) const
01874 {
01875   return doNameFilter( name, d->lstFilters );
01876 }
01877 
01878 bool KDirLister::matchesMimeFilter( const QString& mime ) const
01879 {
01880   return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter);
01881 }
01882 
01883 // ================ protected methods ================ //
01884 
01885 bool KDirLister::matchesFilter( const KFileItem *item ) const
01886 {
01887   Q_ASSERT( item );
01888   static const QString& dotdot = KGlobal::staticQString("..");
01889 
01890   if ( item->text() == dotdot )
01891     return false;
01892 
01893   if ( !d->isShowingDotFiles && item->text()[0] == '.' )
01894     return false;
01895 
01896   if ( item->isDir() || d->lstFilters.isEmpty() )
01897     return true;
01898 
01899   return matchesFilter( item->text() );
01900 }
01901 
01902 bool KDirLister::matchesMimeFilter( const KFileItem *item ) const
01903 {
01904   Q_ASSERT( item );
01905   // Don't lose time determining the mimetype if there is no filter
01906   if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() )
01907       return true;
01908   return matchesMimeFilter( item->mimetype() );
01909 }
01910 
01911 bool KDirLister::doNameFilter( const QString& name, const QPtrList<QRegExp>& filters ) const
01912 {
01913   for ( QPtrListIterator<QRegExp> it( filters ); it.current(); ++it )
01914     if ( it.current()->exactMatch( name ) )
01915       return true;
01916 
01917   return false;
01918 }
01919 
01920 bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const
01921 {
01922   if ( filters.isEmpty() )
01923     return true;
01924 
01925   KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
01926   QStringList::ConstIterator it = filters.begin();
01927   for ( ; it != filters.end(); ++it )
01928     if ( mimeptr->is(*it) )
01929       return true;
01930 
01931   return false;
01932 }
01933 
01934 bool KDirLister::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const
01935 {
01936   if ( filters.isEmpty() )
01937     return true;
01938 
01939   QStringList::ConstIterator it = filters.begin();
01940   for ( ; it != filters.end(); ++it )
01941     if ( (*it) == mime )
01942       return false;
01943 
01944   return true;
01945 }
01946 
01947 
01948 bool KDirLister::validURL( const KURL& _url ) const
01949 {
01950   if ( !_url.isValid() )
01951   {
01952     if ( d->autoErrorHandling )
01953     {
01954       QString tmp = i18n("Malformed URL\n%1").arg( _url.prettyURL() );
01955       KMessageBox::error( d->errorParent, tmp );
01956     }
01957     return false;
01958   }
01959 
01960   // TODO: verify that this is really a directory?
01961 
01962   return true;
01963 }
01964 
01965 void KDirLister::handleError( KIO::Job *job )
01966 {
01967   if ( d->autoErrorHandling )
01968     job->showErrorDialog( d->errorParent );
01969 }
01970 
01971 
01972 // ================= private methods ================= //
01973 
01974 void KDirLister::addNewItem( const KFileItem *item )
01975 {
01976   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
01977   if (isNameFilterMatch)
01978      return; // No reason to continue... bailing out here prevents a mimetype scan.
01979 
01980   bool isMimeFilterMatch = !matchesMimeFilter( item );
01981 
01982   if ( !isNameFilterMatch && !isMimeFilterMatch )
01983   {
01984     if ( !d->lstNewItems )
01985       d->lstNewItems = new KFileItemList;
01986 
01987     d->lstNewItems->append( item );            // items not filtered
01988   }
01989   else if ( !isNameFilterMatch )
01990   {
01991     if ( !d->lstMimeFilteredItems )
01992       d->lstMimeFilteredItems = new KFileItemList;
01993 
01994     d->lstMimeFilteredItems->append( item );   // only filtered by mime
01995   }
01996 }
01997 
01998 void KDirLister::addNewItems( const KFileItemList& items )
01999 {
02000   // TODO: make this faster - test if we have a filter at all first
02001   for ( KFileItemListIterator kit( items ); kit.current(); ++kit )
02002     addNewItem( *kit );
02003 }
02004 
02005 void KDirLister::addRefreshItem( const KFileItem *item )
02006 {
02007   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02008   bool isMimeFilterMatch = !matchesMimeFilter( item );
02009 
02010   if ( !isNameFilterMatch && !isMimeFilterMatch )
02011   {
02012     if ( !d->lstRefreshItems )
02013       d->lstRefreshItems = new KFileItemList;
02014 
02015     d->lstRefreshItems->append( item );
02016   } else {
02017     if ( !d->lstRemoveItems )
02018       d->lstRemoveItems = new KFileItemList;
02019 
02020       d->lstRemoveItems->append( item );//notify the user that the mimetype of a file changed, which doesn't match a filter or does match an exclude  filter
02021   }
02022 }
02023 
02024 void KDirLister::emitItems()
02025 {
02026   KFileItemList *tmpNew = d->lstNewItems;
02027   d->lstNewItems = 0;
02028 
02029   KFileItemList *tmpMime = d->lstMimeFilteredItems;
02030   d->lstMimeFilteredItems = 0;
02031 
02032   KFileItemList *tmpRefresh = d->lstRefreshItems;
02033   d->lstRefreshItems = 0;
02034 
02035   KFileItemList *tmpRemove = d->lstRemoveItems;
02036   d->lstRemoveItems = 0;
02037 
02038   if ( tmpNew )
02039   {
02040     emit newItems( *tmpNew );
02041     delete tmpNew;
02042   }
02043 
02044   if ( tmpMime )
02045   {
02046     emit itemsFilteredByMime( *tmpMime );
02047     delete tmpMime;
02048   }
02049 
02050   if ( tmpRefresh )
02051   {
02052     emit refreshItems( *tmpRefresh );
02053     delete tmpRefresh;
02054   }
02055 
02056   if ( tmpRemove )
02057   {
02058     for ( KFileItem *tmp = tmpRemove->first(); tmp; tmp = tmpRemove->next() )
02059       emit deleteItem( tmp );
02060     delete tmpRemove;
02061   }
02062 }
02063 
02064 void KDirLister::emitDeleteItem( KFileItem *item )
02065 {
02066   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02067   bool isMimeFilterMatch = !matchesMimeFilter( item );
02068 
02069   if ( !isNameFilterMatch && !isMimeFilterMatch )
02070     emit deleteItem( item );
02071 }
02072 
02073 
02074 // ================ private slots ================ //
02075 
02076 void KDirLister::slotInfoMessage( KIO::Job *, const QString& message )
02077 {
02078   emit infoMessage( message );
02079 }
02080 
02081 void KDirLister::slotPercent( KIO::Job *job, unsigned long pcnt )
02082 {
02083   d->jobData[static_cast<KIO::ListJob*>(job)].percent = pcnt;
02084 
02085   int result = 0;
02086 
02087   KIO::filesize_t size = 0;
02088 
02089   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02090   while ( dataIt != d->jobData.end() )
02091   {
02092     result += (*dataIt).percent * (*dataIt).totalSize;
02093     size += (*dataIt).totalSize;
02094     ++dataIt;
02095   }
02096 
02097   if ( size != 0 )
02098     result /= size;
02099   else
02100     result = 100;
02101   emit percent( result );
02102 }
02103 
02104 void KDirLister::slotTotalSize( KIO::Job *job, KIO::filesize_t size )
02105 {
02106   d->jobData[static_cast<KIO::ListJob*>(job)].totalSize = size;
02107 
02108   KIO::filesize_t result = 0;
02109   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02110   while ( dataIt != d->jobData.end() )
02111   {
02112     result += (*dataIt).totalSize;
02113     ++dataIt;
02114   }
02115 
02116   emit totalSize( result );
02117 }
02118 
02119 void KDirLister::slotProcessedSize( KIO::Job *job, KIO::filesize_t size )
02120 {
02121   d->jobData[static_cast<KIO::ListJob*>(job)].processedSize = size;
02122 
02123   KIO::filesize_t result = 0;
02124   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02125   while ( dataIt != d->jobData.end() )
02126   {
02127     result += (*dataIt).processedSize;
02128     ++dataIt;
02129   }
02130 
02131   emit processedSize( result );
02132 }
02133 
02134 void KDirLister::slotSpeed( KIO::Job *job, unsigned long spd )
02135 {
02136   d->jobData[static_cast<KIO::ListJob*>(job)].speed = spd;
02137 
02138   int result = 0;
02139   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02140   while ( dataIt != d->jobData.end() )
02141   {
02142     result += (*dataIt).speed;
02143     ++dataIt;
02144   }
02145 
02146   emit speed( result );
02147 }
02148 
02149 uint KDirLister::numJobs()
02150 {
02151   return d->jobData.count();
02152 }
02153 
02154 void KDirLister::jobDone(KIO::ListJob *job)
02155 {
02156   if (job)
02157      d->jobData.remove(job);
02158 }
02159 
02160 void KDirLister::jobStarted(KIO::ListJob *job)
02161 {
02162   KDirListerPrivate::JobData jobData;
02163   jobData.speed = 0;
02164   jobData.percent = 0;
02165   jobData.processedSize = 0;
02166   jobData.totalSize = 0;
02167 
02168   d->jobData.insert(job, jobData);
02169 }
02170 
02171 void KDirLister::setMainWindow(QWidget *window)
02172 {
02173   d->window = window;
02174 }
02175 
02176 QWidget *KDirLister::mainWindow()
02177 {
02178   return d->window;
02179 }
02180 
02181 KFileItemList KDirLister::items( WhichItems which ) const
02182 {
02183     return itemsForDir( url(), which );
02184 }
02185 
02186 KFileItemList KDirLister::itemsForDir( const KURL &dir, WhichItems which) const
02187 {
02188     KFileItemList result;
02189     KFileItemList *allItems = s_pCache->itemsForDir( dir );
02190     if ( !allItems )
02191         return result;
02192 
02193     if ( which == AllItems )
02194         result = *allItems; // shallow copy
02195 
02196     else // only items passing the filters
02197     {
02198         for ( KFileItemListIterator kit( *allItems ); kit.current(); ++kit )
02199         {
02200             KFileItem *item = *kit;
02201             bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) ||
02202                                      !matchesFilter( item );
02203             bool isMimeFilterMatch = !matchesMimeFilter( item );
02204 
02205             if ( !isNameFilterMatch && !isMimeFilterMatch )
02206                 result.append( item );
02207         }
02208     }
02209 
02210     return result;
02211 }
02212 
02213 // to keep BC changes
02214 
02215 void KDirLister::virtual_hook( int, void* )
02216 { /*BASE::virtual_hook( id, data );*/ }
02217 
02218 #include "kdirlister.moc"
02219 #include "kdirlister_p.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 30 10:15:28 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003