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-2005 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       {
00507         delete item;
00508         item = 0;
00509       }
00510     }
00511   }
00512 
00513   if ( item && lister->d->autoUpdate )
00514     item->decAutoUpdate();
00515 }
00516 
00517 void KDirListerCache::updateDirectory( const KURL& _dir )
00518 {
00519   kdDebug(7004) << k_funcinfo << _dir << endl;
00520 
00521   QString urlStr = _dir.url(-1);
00522   if ( !checkUpdate( urlStr ) )
00523     return;
00524 
00525   // A job can be running to
00526   //   - only list a new directory: the listers are in urlsCurrentlyListed
00527   //   - only update a directory: the listers are in urlsCurrentlyHeld
00528   //   - update a currently running listing: the listers are in urlsCurrently
00529 
00530   QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
00531   QPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr];
00532 
00533   // restart the job for _dir if it is running already
00534   bool killed = false;
00535   KIO::ListJob *job = jobForUrl( urlStr );
00536   if ( job )
00537   {
00538      killed = true;
00539      killJob( job );
00540      if ( listers )
00541         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00542            kdl->jobDone(job);
00543 
00544      if ( holders )
00545         for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
00546            kdl->jobDone(job);
00547   }
00548   kdDebug(7004) << k_funcinfo << "Killed = " << killed << endl;
00549 
00550   // we don't need to emit canceled signals since we only replaced the job,
00551   // the listing is continuing.
00552 
00553   Q_ASSERT( !listers || (listers && killed) );
00554 
00555   job = KIO::listDir( _dir, false /* no default GUI */ );
00556   jobs.insert( job, QValueList<KIO::UDSEntry>() );
00557 
00558   connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )),
00559            this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) );
00560   connect( job, SIGNAL(result( KIO::Job * )),
00561            this, SLOT(slotUpdateResult( KIO::Job * )) );
00562 
00563   kdDebug(7004) << k_funcinfo << "update started in " << _dir << endl;
00564 
00565   if ( listers )
00566      for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00567         kdl->jobStarted( job );
00568 
00569   if ( holders )
00570   {
00571      if ( killed )
00572      {
00573         bool first = true;
00574         for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
00575         {
00576            kdl->jobStarted( job );
00577            kdl->d->complete = false;
00578            if ( first && kdl->d->window )
00579            {
00580               first = false;
00581               job->setWindow( kdl->d->window );
00582            }
00583            emit kdl->started( _dir );
00584         }
00585      }
00586      else
00587      {
00588         for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
00589            kdl->jobStarted( job );
00590      }
00591   }
00592 }
00593 
00594 bool KDirListerCache::checkUpdate( const QString& _dir )
00595 {
00596   if ( !itemsInUse[_dir] )
00597   {
00598     DirItem *item = itemsCached[_dir];
00599     if ( item && item->complete )
00600     {
00601       item->complete = false;
00602       item->decAutoUpdate();
00603       // Hmm, this debug output might include login/password from the _dir URL.
00604       //kdDebug(7004) << k_funcinfo << "directory " << _dir << " not in use, marked dirty." << endl;
00605     }
00606     //else
00607       //kdDebug(7004) << k_funcinfo << "aborted, directory " << _dir << " not in cache." << endl;
00608 
00609     return false;
00610   }
00611   else
00612     return true;
00613 }
00614 
00615 KFileItemList *KDirListerCache::itemsForDir( const KURL &_dir ) const
00616 {
00617   QString urlStr = _dir.url(-1);
00618   DirItem *item = itemsInUse[ urlStr ];
00619   if ( !item )
00620     item = itemsCached[ urlStr ];
00621   return item ? item->lstItems : 0;
00622 }
00623 
00624 KFileItem *KDirListerCache::findByName( const KDirLister *lister, const QString& _name ) const
00625 {
00626   Q_ASSERT( lister );
00627 
00628   for ( KURL::List::Iterator it = lister->d->lstDirs.begin();
00629         it != lister->d->lstDirs.end(); ++it )
00630   {
00631     KFileItemListIterator kit( *itemsInUse[(*it).url()]->lstItems );
00632     for ( ; kit.current(); ++kit )
00633       if ( (*kit)->name() == _name )
00634         return (*kit);
00635   }
00636 
00637   return 0L;
00638 }
00639 
00640 KFileItem *KDirListerCache::findByURL( const KDirLister *lister, const KURL& _u ) const
00641 {
00642   KURL _url = _u;
00643   _url.adjustPath(-1);
00644 
00645   KURL parentDir( _url );
00646   parentDir.setPath( parentDir.directory() );
00647 
00648   // If lister is set, check that it contains this dir
00649   if ( lister && !lister->d->lstDirs.contains( parentDir ) )
00650       return 0L;
00651 
00652   KFileItemList *itemList = itemsForDir( parentDir );
00653   if ( itemList )
00654   {
00655     KFileItemListIterator kit( *itemList );
00656     for ( ; kit.current(); ++kit )
00657       if ( (*kit)->url() == _url )
00658         return (*kit);
00659   }
00660   return 0L;
00661 }
00662 
00663 void KDirListerCache::FilesAdded( const KURL &dir )
00664 {
00665   kdDebug(7004) << k_funcinfo << dir << endl;
00666   updateDirectory( dir );
00667 }
00668 
00669 void KDirListerCache::FilesRemoved( const KURL::List &fileList )
00670 {
00671   kdDebug(7004) << k_funcinfo << endl;
00672   KURL::List::ConstIterator it = fileList.begin();
00673   for ( ; it != fileList.end() ; ++it )
00674   {
00675     // emit the deleteItem signal if this file was shown in any view
00676     KFileItem *fileitem = 0L;
00677     KURL parentDir( *it );
00678     parentDir.setPath( parentDir.directory() );
00679     KFileItemList *lstItems = itemsForDir( parentDir );
00680     if ( lstItems )
00681     {
00682       KFileItem *fit = lstItems->first();
00683       for ( ; fit; fit = lstItems->next() )
00684         if ( fit->url() == *it ) {
00685           fileitem = fit;
00686           lstItems->take(); // remove fileitem from list
00687           break;
00688         }
00689     }
00690 
00691     // Tell the views about it before deleting the KFileItems. They might need the subdirs'
00692     // file items (see the dirtree).
00693     if ( fileitem )
00694     {
00695       QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDir.url()];
00696       if ( listers )
00697         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00698           kdl->emitDeleteItem( fileitem );
00699     }
00700 
00701     // If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case.
00702     if ( !fileitem || fileitem->isDir() )
00703     {
00704       // in case of a dir, check if we have any known children, there's much to do in that case
00705       // (stopping jobs, removing dirs from cache etc.)
00706       deleteDir( *it );
00707     }
00708 
00709     // now remove the item itself
00710     delete fileitem;
00711   }
00712 }
00713 
00714 void KDirListerCache::FilesChanged( const KURL::List &fileList )
00715 {
00716   KURL::List dirsToUpdate;
00717   kdDebug(7004) << k_funcinfo << "only half implemented" << endl;
00718   KURL::List::ConstIterator it = fileList.begin();
00719   for ( ; it != fileList.end() ; ++it )
00720   {
00721     if ( ( *it ).isLocalFile() )
00722     {
00723       kdDebug(7004) << "KDirListerCache::FilesChanged " << *it << endl;
00724       KFileItem *fileitem = findByURL( 0, *it );
00725       if ( fileitem )
00726       {
00727           // we need to refresh the item, because e.g. the permissions can have changed.
00728           aboutToRefreshItem( fileitem );
00729           fileitem->refresh();
00730           emitRefreshItem( fileitem );
00731       }
00732       else
00733           kdDebug(7004) << "item not found" << endl;
00734     } else {
00735       // For remote files, refresh() won't be able to figure out the new information.
00736       // Let's update the dir.
00737       KURL dir( *it );
00738       dir.setPath( dir.directory( true ) );
00739       if ( dirsToUpdate.find( dir ) == dirsToUpdate.end() )
00740         dirsToUpdate.prepend( dir );
00741     }
00742   }
00743 
00744   KURL::List::ConstIterator itdir = dirsToUpdate.begin();
00745   for ( ; itdir != dirsToUpdate.end() ; ++itdir )
00746     updateDirectory( *itdir );
00747   // ## TODO problems with current jobs listing/updating that dir
00748   // ( see kde-2.2.2's kdirlister )
00749 }
00750 
00751 void KDirListerCache::FileRenamed( const KURL &src, const KURL &dst )
00752 {
00753   kdDebug(7004) << k_funcinfo << src.prettyURL() << " -> " << dst.prettyURL() << endl;
00754 #ifdef DEBUG_CACHE
00755   printDebug();
00756 #endif
00757 
00758   // Somehow this should only be called if src is a dir. But how could we know if it is?
00759   // (Note that looking into itemsInUse isn't good enough. One could rename a subdir in a view.)
00760   renameDir( src, dst );
00761 
00762   // Now update the KFileItem representing that file or dir (not exclusive with the above!)
00763   KURL oldurl( src );
00764   oldurl.adjustPath( -1 );
00765   KFileItem *fileitem = findByURL( 0, oldurl );
00766   if ( fileitem )
00767   {
00768     aboutToRefreshItem( fileitem );
00769     fileitem->setURL( dst );
00770     fileitem->refreshMimeType();
00771     emitRefreshItem( fileitem );
00772   }
00773 #ifdef DEBUG_CACHE
00774   printDebug();
00775 #endif
00776 }
00777 
00778 void KDirListerCache::aboutToRefreshItem( KFileItem *fileitem )
00779 {
00780   // Look whether this item was shown in any view, i.e. held by any dirlister
00781   KURL parentDir( fileitem->url() );
00782   parentDir.setPath( parentDir.directory() );
00783   QString parentDirURL = parentDir.url();
00784   QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL];
00785   if ( listers )
00786     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00787       kdl->aboutToRefreshItem( fileitem );
00788 
00789   // Also look in urlsCurrentlyListed, in case the user manages to rename during a listing
00790   listers = urlsCurrentlyListed[parentDirURL];
00791   if ( listers )
00792     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00793       kdl->aboutToRefreshItem( fileitem );
00794 }
00795 
00796 void KDirListerCache::emitRefreshItem( KFileItem *fileitem )
00797 {
00798   // Look whether this item was shown in any view, i.e. held by any dirlister
00799   KURL parentDir( fileitem->url() );
00800   parentDir.setPath( parentDir.directory() );
00801   QString parentDirURL = parentDir.url();
00802   QPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL];
00803   if ( listers )
00804     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00805     {
00806       kdl->addRefreshItem( fileitem );
00807       kdl->emitItems();
00808     }
00809 
00810   // Also look in urlsCurrentlyListed, in case the user manages to rename during a listing
00811   listers = urlsCurrentlyListed[parentDirURL];
00812   if ( listers )
00813     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00814     {
00815       kdl->addRefreshItem( fileitem );
00816       kdl->emitItems();
00817     }
00818 }
00819 
00820 KDirListerCache* KDirListerCache::self()
00821 {
00822   if ( !s_pSelf )
00823     s_pSelf = sd_KDirListerCache.setObject( s_pSelf, new KDirListerCache );
00824 
00825   return s_pSelf;
00826 }
00827 
00828 // private slots
00829 
00830 // _file can also be a directory being currently held!
00831 void KDirListerCache::slotFileDirty( const QString& _file )
00832 {
00833   kdDebug(7004) << k_funcinfo << _file << endl;
00834 
00835   if ( !pendingUpdates[_file] )
00836   {
00837     KURL dir = KURL( _file );
00838     if ( checkUpdate( dir.url(-1) ) )
00839       updateDirectory( dir );
00840 
00841     // the parent directory of _file
00842     dir.setPath( dir.directory() );
00843     if ( checkUpdate( dir.url() ) )
00844     {
00845       // Nice hack to save memory: use the qt object name to store the filename
00846       QTimer *timer = new QTimer( this, _file.utf8() );
00847       connect( timer, SIGNAL(timeout()), this, SLOT(slotFileDirtyDelayed()) );
00848       pendingUpdates.insert( _file, timer );
00849       timer->start( 500, true );
00850     }
00851   }
00852 }
00853 
00854 // delayed updating of files, FAM is flooding us with events
00855 void KDirListerCache::slotFileDirtyDelayed()
00856 {
00857   QString file = QString::fromUtf8( sender()->name() );
00858 
00859   kdDebug(7004) << k_funcinfo << file << endl;
00860 
00861   // TODO: do it better: don't always create/delete the QTimer but reuse it.
00862   // Delete the timer after the parent directory is removed from the cache.
00863   pendingUpdates.remove( file );
00864 
00865   KURL u;
00866   u.setPath( file );
00867   KFileItem *item = findByURL( 0, u ); // search all items
00868   if ( item )
00869   {
00870     // we need to refresh the item, because e.g. the permissions can have changed.
00871     aboutToRefreshItem( item );
00872     item->refresh();
00873     emitRefreshItem( item );
00874   }
00875 }
00876 
00877 void KDirListerCache::slotFileCreated( const QString& _file )
00878 {
00879   kdDebug(7004) << k_funcinfo << _file << endl;
00880   // XXX: how to avoid a complete rescan here?
00881   KURL u;
00882   u.setPath( _file );
00883   u.setPath( u.directory() );
00884   FilesAdded( u );
00885 }
00886 
00887 void KDirListerCache::slotFileDeleted( const QString& _file )
00888 {
00889   kdDebug(7004) << k_funcinfo << _file << endl;
00890   KURL u;
00891   u.setPath( _file );
00892   FilesRemoved( u );
00893 }
00894 
00895 void KDirListerCache::slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries )
00896 {
00897   KURL url = static_cast<KIO::ListJob *>(job)->url();
00898   url.adjustPath(-1);
00899   QString urlStr = url.url();
00900 
00901   kdDebug(7004) << k_funcinfo << "new entries for " << url << endl;
00902 
00903   DirItem *dir = itemsInUse[urlStr];
00904   Q_ASSERT( dir );
00905 
00906   QPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
00907   Q_ASSERT( listers );
00908   Q_ASSERT( !listers->isEmpty() );
00909 
00910   // check if anyone wants the mimetypes immediately
00911   bool delayedMimeTypes = true;
00912   for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00913     delayedMimeTypes &= kdl->d->delayedMimeTypes;
00914 
00915   // avoid creating these QStrings again and again
00916   static const QString& dot = KGlobal::staticQString(".");
00917   static const QString& dotdot = KGlobal::staticQString("..");
00918 
00919   KIO::UDSEntryListConstIterator it = entries.begin();
00920   KIO::UDSEntryListConstIterator end = entries.end();
00921 
00922   for ( ; it != end; ++it )
00923   {
00924     QString name;
00925 
00926     // find out about the name
00927     KIO::UDSEntry::ConstIterator entit = (*it).begin();
00928     for( ; entit != (*it).end(); ++entit )
00929       if ( (*entit).m_uds == KIO::UDS_NAME )
00930       {
00931         name = (*entit).m_str;
00932         break;
00933       }
00934 
00935     Q_ASSERT( !name.isEmpty() );
00936     if ( name.isEmpty() )
00937       continue;
00938 
00939     if ( name == dot )
00940     {
00941       Q_ASSERT( !dir->rootItem );
00942       dir->rootItem = new KFileItem( *it, url, delayedMimeTypes, true  );
00943 
00944       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00945         if ( !kdl->d->rootFileItem && kdl->d->url == url )
00946           kdl->d->rootFileItem = dir->rootItem;
00947     }
00948     else if ( name != dotdot )
00949     {
00950       KFileItem* item = new KFileItem( *it, url, delayedMimeTypes, true );
00951       Q_ASSERT( item );
00952 
00953       //kdDebug(7004)<< "Adding item: " << item->url() << endl;
00954       dir->lstItems->append( item );
00955 
00956       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00957         kdl->addNewItem( item );
00958     }
00959   }
00960 
00961   for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
00962     kdl->emitItems();
00963 }
00964 
00965 void KDirListerCache::slotResult( KIO::Job* j )
00966 {
00967   Q_ASSERT( j );
00968   KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
00969   jobs.remove( job );
00970 
00971   KURL jobUrl = job->url();
00972   jobUrl.adjustPath(-1);  // need remove trailing slashes again, in case of redirections
00973   QString jobUrlStr = jobUrl.url();
00974 
00975   kdDebug(7004) << k_funcinfo << "finished listing " << jobUrl << endl;
00976 #ifdef DEBUG_CACHE
00977   printDebug();
00978 #endif
00979 
00980   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( jobUrlStr );
00981   Q_ASSERT( listers );
00982 
00983   // move the directory to the held directories, do it before emitting
00984   // the signals to make sure it exists in KDirListerCache in case someone
00985   // calls listDir during the signal emission
00986   Q_ASSERT( !urlsCurrentlyHeld[jobUrlStr] );
00987   urlsCurrentlyHeld.insert( jobUrlStr, listers );
00988 
00989   KDirLister *kdl;
00990 
00991   if ( job->error() )
00992   {
00993     for ( kdl = listers->first(); kdl; kdl = listers->next() )
00994     {
00995       kdl->jobDone(job);
00996       kdl->handleError( job );
00997       emit kdl->canceled( jobUrl );
00998       if ( kdl->numJobs() == 0 )
00999       {
01000         kdl->d->complete = true;
01001         emit kdl->canceled();
01002       }
01003     }
01004   }
01005   else
01006   {
01007     DirItem *dir = itemsInUse[jobUrlStr];
01008     Q_ASSERT( dir );
01009     dir->complete = true;
01010 
01011     for ( kdl = listers->first(); kdl; kdl = listers->next() )
01012     {
01013       kdl->jobDone(job);
01014       emit kdl->completed( jobUrl );
01015       if ( kdl->numJobs() == 0 )
01016       {
01017         kdl->d->complete = true;
01018         emit kdl->completed();
01019       }
01020     }
01021   }
01022 
01023   // TODO: hmm, if there was an error and job is a parent of one or more
01024   // of the pending urls we should cancel it/them as well
01025   processPendingUpdates();
01026 
01027 #ifdef DEBUG_CACHE
01028   printDebug();
01029 #endif
01030 }
01031 
01032 void KDirListerCache::slotRedirection( KIO::Job *job, const KURL &url )
01033 {
01034   Q_ASSERT( job );
01035   KURL oldUrl = static_cast<KIO::ListJob *>( job )->url();
01036 
01037   // strip trailing slashes
01038   oldUrl.adjustPath(-1);
01039   KURL newUrl = url;
01040   newUrl.adjustPath(-1);
01041 
01042   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
01043 
01044   // I don't think there can be dirItems that are childs of oldUrl.
01045   // Am I wrong here? And even if so, we don't need to delete them, right?
01046   // DF: redirection happens before listDir emits any item. Makes little sense otherwise.
01047 
01048   DirItem *dir = itemsInUse.take( oldUrl.url() );
01049   Q_ASSERT( dir );
01050 
01051   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrl.url() );
01052   Q_ASSERT( listers );
01053   Q_ASSERT( !listers->isEmpty() );
01054 
01055   for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01056   {
01057     if ( kdl->d->url.equals( oldUrl, true ) )
01058     {
01059       kdl->d->rootFileItem = 0;
01060       kdl->d->url = newUrl;
01061     }
01062 
01063     *kdl->d->lstDirs.find( oldUrl ) = newUrl;
01064 
01065     if ( kdl->d->lstDirs.count() == 1 )
01066     {
01067       emit kdl->clear();
01068       emit kdl->redirection( newUrl );
01069       emit kdl->redirection( oldUrl, newUrl );
01070     }
01071     else
01072     {
01073       emit kdl->clear( oldUrl );
01074       emit kdl->redirection( oldUrl, newUrl );
01075     }
01076   }
01077 
01078   delete dir->rootItem;
01079   dir->rootItem = 0;
01080   dir->lstItems->clear();
01081   dir->redirect( newUrl );
01082   itemsInUse.insert( newUrl.url(), dir );
01083   urlsCurrentlyListed.insert( newUrl.url(), listers );
01084 }
01085 
01086 void KDirListerCache::renameDir( const KURL &oldUrl, const KURL &newUrl )
01087 {
01088   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
01089   QString oldUrlStr = oldUrl.url(-1);
01090   QString newUrlStr = newUrl.url(-1);
01091 
01092   // Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
01093   //DirItem *dir = itemsInUse.take( oldUrlStr );
01094   //emitRedirections( oldUrl, url );
01095 
01096   // Look at all dirs being listed/shown
01097   QDictIterator<DirItem> itu( itemsInUse );
01098   bool goNext;
01099   while ( itu.current() )
01100   {
01101     goNext = true;
01102     DirItem *dir = itu.current();
01103     KURL oldDirUrl ( itu.currentKey() );
01104     //kdDebug(7004) << "itemInUse: " << oldDirUrl.prettyURL() << endl;
01105     // Check if this dir is oldUrl, or a subfolder of it
01106     if ( oldUrl.isParentOf( oldDirUrl ) )
01107     {
01108       // TODO should use KURL::cleanpath like isParentOf does
01109       QString relPath = oldDirUrl.path().mid( oldUrl.path().length() );
01110 
01111       KURL newDirUrl( newUrl ); // take new base
01112       if ( !relPath.isEmpty() )
01113         newDirUrl.addPath( relPath ); // add unchanged relative path
01114       //kdDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl.prettyURL() << endl;
01115 
01116       // Update URL in dir item and in itemsInUse
01117       dir->redirect( newDirUrl );
01118       itemsInUse.remove( itu.currentKey() ); // implies ++itu
01119       itemsInUse.insert( newDirUrl.url(-1), dir );
01120       goNext = false; // because of the implied ++itu above
01121       if ( dir->lstItems )
01122       {
01123         // Rename all items under that dir
01124         KFileItemListIterator kit( *dir->lstItems );
01125         for ( ; kit.current(); ++kit )
01126         {
01127           KURL oldItemUrl = (*kit)->url();
01128           QString oldItemUrlStr( oldItemUrl.url(-1) );
01129           KURL newItemUrl( oldItemUrl );
01130           newItemUrl.setPath( newDirUrl.path() );
01131           newItemUrl.addPath( oldItemUrl.fileName() );
01132           kdDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url() << endl;
01133           (*kit)->setURL( newItemUrl );
01134         }
01135       }
01136       emitRedirections( oldDirUrl, newDirUrl );
01137     }
01138     if (goNext)
01139       ++itu;
01140   }
01141 
01142   // Is oldUrl a directory in the cache?
01143   // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
01144   removeDirFromCache( oldUrl );
01145   // TODO rename, instead.
01146 }
01147 
01148 void KDirListerCache::emitRedirections( const KURL &oldUrl, const KURL &url )
01149 {
01150   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << url.prettyURL() << endl;
01151   QString oldUrlStr = oldUrl.url(-1);
01152   QString urlStr = url.url(-1);
01153 
01154   KIO::ListJob *job = jobForUrl(oldUrlStr);
01155   if (job)
01156      killJob( job );
01157 
01158   // Check if we were listing this dir. Need to abort and restart with new name in that case.
01159   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrlStr );
01160   if ( listers )
01161   {
01162     // Tell the world that the job listing the old url is dead.
01163     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01164     {
01165        kdl->jobDone(job);
01166        emit kdl->canceled( oldUrl );
01167     }
01168 
01169     urlsCurrentlyListed.insert( urlStr, listers );
01170   }
01171 
01172   // Check if we are currently displaying this directory (odds opposite wrt above)
01173   // Update urlsCurrentlyHeld dict with new URL
01174   QPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrlStr );
01175   if ( holders )
01176   {
01177     for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01178        kdl->jobDone(job);
01179 
01180     urlsCurrentlyHeld.insert( urlStr, holders );
01181   }
01182 
01183   if (listers)
01184   {
01185     updateDirectory( url );
01186 
01187     // Tell the world about the new url
01188     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01189       emit kdl->started( url );
01190   }
01191 
01192   if (holders)
01193   {
01194     // And notify the dirlisters of the redirection
01195     for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01196     {
01197       *kdl->d->lstDirs.find( oldUrl ) = url;
01198 
01199       if ( kdl->d->lstDirs.count() == 1 )
01200         emit kdl->redirection( url );
01201 
01202       emit kdl->redirection( oldUrl, url );
01203     }
01204   }
01205 }
01206 
01207 void KDirListerCache::removeDirFromCache( const KURL& dir )
01208 {
01209   kdDebug(7004) << "KDirListerCache::removeDirFromCache " << dir.prettyURL() << endl;
01210   QCacheIterator<DirItem> itc( itemsCached );
01211   while ( itc.current() )
01212   {
01213     if ( dir.isParentOf( KURL( itc.currentKey() ) ) )
01214       itemsCached.remove( itc.currentKey() );
01215     else
01216       ++itc;
01217   }
01218 }
01219 
01220 void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list )
01221 {
01222   jobs[static_cast<KIO::ListJob*>(job)] += list;
01223 }
01224 
01225 void KDirListerCache::slotUpdateResult( KIO::Job * j )
01226 {
01227 
01228   Q_ASSERT( j );
01229   KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
01230 
01231   KURL jobUrl = job->url();
01232   jobUrl.adjustPath(-1);  // need remove trailing slashes again, in case of redirections
01233   QString jobUrlStr = jobUrl.url();
01234 
01235   kdDebug(7004) << k_funcinfo << "finished update " << jobUrl << endl;
01236 
01237   KDirLister *kdl;
01238 
01239   QPtrList<KDirLister> *listers = urlsCurrentlyHeld[jobUrlStr];
01240   QPtrList<KDirLister> *tmpLst = urlsCurrentlyListed.take( jobUrlStr );
01241 
01242   if ( tmpLst )
01243   {
01244     if ( listers )
01245       for ( kdl = tmpLst->first(); kdl; kdl = tmpLst->next() )
01246       {
01247         Q_ASSERT( listers->containsRef( kdl ) == 0 );
01248         listers->append( kdl );
01249       }
01250     else
01251     {
01252       listers = tmpLst;
01253       urlsCurrentlyHeld.insert( jobUrlStr, listers );
01254     }
01255   }
01256 
01257   // once we are updating dirs that are only in the cache this will fail!
01258   Q_ASSERT( listers );
01259 
01260   if ( job->error() )
01261   {
01262     for ( kdl = listers->first(); kdl; kdl = listers->next() )
01263     {
01264       kdl->jobDone(job);
01265 
01266       //don't bother the user
01267       //kdl->handleError( job );
01268 
01269       emit kdl->canceled( jobUrl );
01270       if ( kdl->numJobs() == 0 )
01271       {
01272         kdl->d->complete = true;
01273         emit kdl->canceled();
01274       }
01275     }
01276 
01277     jobs.remove( job );
01278 
01279     // TODO: if job is a parent of one or more
01280     // of the pending urls we should cancel them
01281     processPendingUpdates();
01282     return;
01283   }
01284 
01285   DirItem *dir = itemsInUse[jobUrlStr];
01286   dir->complete = true;
01287 
01288 
01289   // check if anyone wants the mimetypes immediately
01290   bool delayedMimeTypes = true;
01291   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01292     delayedMimeTypes &= kdl->d->delayedMimeTypes;
01293 
01294   // should be enough to get reasonable speed in most cases
01295   QDict<KFileItem> fileItems( 9973 );
01296 
01297   KFileItemListIterator kit ( *(dir->lstItems) );
01298 
01299   // Unmark all items in url
01300   for ( ; kit.current(); ++kit )
01301   {
01302     (*kit)->unmark();
01303     fileItems.insert( (*kit)->url().url(), *kit );
01304   }
01305 
01306   static const QString& dot = KGlobal::staticQString(".");
01307   static const QString& dotdot = KGlobal::staticQString("..");
01308 
01309   KFileItem *item, *tmp;
01310 
01311   QValueList<KIO::UDSEntry> buf = jobs[job];
01312   QValueListIterator<KIO::UDSEntry> it = buf.begin();
01313   for ( ; it != buf.end(); ++it )
01314   {
01315     QString name;
01316 
01317     // Find out about the name
01318     KIO::UDSEntry::Iterator it2 = (*it).begin();
01319     for ( ; it2 != (*it).end(); it2++ )
01320       if ( (*it2).m_uds == KIO::UDS_NAME )
01321       {
01322         name = (*it2).m_str;
01323         break;
01324       }
01325 
01326     Q_ASSERT( !name.isEmpty() );
01327 
01328     // we duplicate the check for dotdot here, to avoid iterating over
01329     // all items again and checking in matchesFilter() that way.
01330     if ( name.isEmpty() || name == dotdot )
01331       continue;
01332 
01333     if ( name == dot )
01334     {
01335       // if the update was started before finishing the original listing
01336       // there is no root item yet
01337       if ( !dir->rootItem )
01338       {
01339         dir->rootItem = new KFileItem( *it, jobUrl, delayedMimeTypes, true  );
01340 
01341         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01342           if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl )
01343             kdl->d->rootFileItem = dir->rootItem;
01344       }
01345 
01346       continue;
01347     }
01348 
01349     // Form the complete url
01350     item = new KFileItem( *it, jobUrl, delayedMimeTypes, true  );
01351 
01352     QString url = item->url().url();
01353     //kdDebug(7004) << "slotUpdateResult : look for " << url << endl;
01354 
01355     // Find this item
01356     if ( (tmp = fileItems[url]) )
01357     {
01358       tmp->mark();
01359 
01360       // check if something changed for this file
01361       if ( !tmp->cmp( *item ) )
01362       {
01363         for ( kdl = listers->first(); kdl; kdl = listers->next() )
01364           kdl->aboutToRefreshItem( tmp );
01365 
01366         //kdDebug(7004) << "slotUpdateResult: file changed: " << tmp->name() << endl;
01367         tmp->assign( *item );
01368 
01369         for ( kdl = listers->first(); kdl; kdl = listers->next() )
01370           kdl->addRefreshItem( tmp );
01371       }
01372       delete item;  // gmbl, this is the most often case... IMPORTANT TODO: speed it up somehow!
01373     }
01374     else // this is a new file
01375     {
01376       //kdDebug(7004) << "slotUpdateResult: new file: " << name << endl;
01377 
01378       item->mark();
01379       dir->lstItems->append( item );
01380 
01381       for ( kdl = listers->first(); kdl; kdl = listers->next() )
01382         kdl->addNewItem( item );
01383     }
01384   }
01385 
01386   jobs.remove( job );
01387 
01388   deleteUnmarkedItems( listers, dir->lstItems );
01389 
01390   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01391   {
01392     kdl->emitItems();
01393 
01394     kdl->jobDone(job);
01395 
01396     emit kdl->completed( jobUrl );
01397     if ( kdl->numJobs() == 0 )
01398     {
01399       kdl->d->complete = true;
01400       emit kdl->completed();
01401     }
01402   }
01403 
01404   // TODO: hmm, if there was an error and job is a parent of one or more
01405   // of the pending urls we should cancel it/them as well
01406   processPendingUpdates();
01407 }
01408 
01409 // private
01410 
01411 KIO::ListJob *KDirListerCache::jobForUrl(const QString& _url)
01412 {
01413   KIO::ListJob *job;
01414   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator it = jobs.begin();
01415   while ( it != jobs.end() )
01416   {
01417     job = it.key();
01418     if ( job->url().url(-1) == _url )
01419     {
01420        return job;
01421     }
01422     ++it;
01423   }
01424   return 0;
01425 }
01426 
01427 void KDirListerCache::killJob( KIO::ListJob *job )
01428 {
01429   jobs.remove( job );
01430   job->disconnect( this );
01431   job->kill();
01432 }
01433 
01434 void KDirListerCache::deleteUnmarkedItems( QPtrList<KDirLister> *listers, KFileItemList *lstItems )
01435 {
01436   // Find all unmarked items and delete them
01437   KFileItem* item;
01438   lstItems->first();
01439   while ( (item = lstItems->current()) )
01440     if ( !item->isMarked() )
01441     {
01442       //kdDebug() << k_funcinfo << item->name() << endl;
01443       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01444         kdl->emitDeleteItem( item );
01445 
01446       if ( item->isDir() )
01447         deleteDir( item->url() );
01448 
01449       // finally actually delete the item
01450       lstItems->take();
01451       delete item;
01452     }
01453     else
01454       lstItems->next();
01455 }
01456 
01457 void KDirListerCache::deleteDir( const KURL& dirUrl )
01458 {
01459   //kdDebug() << k_funcinfo << dirUrl.prettyURL() << endl;
01460   // unregister and remove the childs of the deleted item.
01461   // Idea: tell all the KDirListers that they should forget the dir
01462   //       and then remove it from the cache.
01463 
01464   QDictIterator<DirItem> itu( itemsInUse );
01465   while ( itu.current() )
01466   {
01467     KURL deletedUrl( itu.currentKey() );
01468     if ( dirUrl.isParentOf( deletedUrl ) )
01469     {
01470       // stop all jobs for deletedUrl
01471 
01472       QPtrList<KDirLister> *kdls = urlsCurrentlyListed[deletedUrl.url()];
01473       if ( kdls )  // yeah, I lack good names
01474       {
01475         // we need a copy because stop modifies the list
01476         kdls = new QPtrList<KDirLister>( *kdls );
01477         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01478           stop( kdl, deletedUrl );
01479 
01480         delete kdls;
01481       }
01482 
01483       // tell listers holding deletedUrl to forget about it
01484       // this will stop running updates for deletedUrl as well
01485 
01486       kdls = urlsCurrentlyHeld[deletedUrl.url()];
01487       if ( kdls )
01488       {
01489         // we need a copy because forgetDirs modifies the list
01490         kdls = new QPtrList<KDirLister>( *kdls );
01491 
01492         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01493         {
01494           // lister's root is the deleted item
01495           if ( kdl->d->url == deletedUrl )
01496           {
01497             // tell the view first. It might need the subdirs' items (which forgetDirs will delete)
01498             if ( kdl->d->rootFileItem )
01499               emit kdl->deleteItem( kdl->d->rootFileItem );
01500             forgetDirs( kdl );
01501             kdl->d->rootFileItem = 0;
01502           }
01503           else
01504           {
01505             bool treeview = kdl->d->lstDirs.count() > 1;
01506             if ( !treeview )
01507             {
01508               emit kdl->clear();
01509               kdl->d->lstDirs.clear();
01510             }
01511             else
01512               kdl->d->lstDirs.remove( kdl->d->lstDirs.find( deletedUrl ) );
01513 
01514             forgetDirs( kdl, deletedUrl, treeview );
01515           }
01516         }
01517 
01518         delete kdls;
01519       }
01520 
01521       // delete the entry for deletedUrl - should not be needed, it's in
01522       // items cached now
01523 
01524       DirItem *dir = itemsInUse.take( deletedUrl.url() );
01525       Q_ASSERT( !dir );
01526       if ( !dir ) // take didn't find it - move on
01527           ++itu;
01528     }
01529     else
01530       ++itu;
01531   }
01532 
01533   // remove the children from the cache
01534   removeDirFromCache( dirUrl );
01535 }
01536 
01537 void KDirListerCache::processPendingUpdates()
01538 {
01539   // TODO
01540 }
01541 
01542 #ifndef NDEBUG
01543 void KDirListerCache::printDebug()
01544 {
01545   kdDebug(7004) << "Items in use: " << endl;
01546   QDictIterator<DirItem> itu( itemsInUse );
01547   for ( ; itu.current() ; ++itu ) {
01548       kdDebug(7004) << "   " << itu.currentKey() << "  URL: " << itu.current()->url
01549                     << " rootItem: " << ( itu.current()->rootItem ? itu.current()->rootItem->url() : KURL() )
01550                     << " autoUpdates refcount: " << itu.current()->autoUpdates
01551                     << " complete: " << itu.current()->complete
01552                   << ( itu.current()->lstItems ? QString(" with %1 items.").arg(itu.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01553   }
01554 
01555   kdDebug(7004) << "urlsCurrentlyHeld: " << endl;
01556   QDictIterator< QPtrList<KDirLister> > it( urlsCurrentlyHeld );
01557   for ( ; it.current() ; ++it )
01558   {
01559     QString list;
01560     for ( QPtrListIterator<KDirLister> listit( *it.current() ); listit.current(); ++listit )
01561       list += " 0x" + QString::number( (long)listit.current(), 16 );
01562     kdDebug(7004) << "   " << it.currentKey() << "  " << it.current()->count() << " listers: " << list << endl;
01563   }
01564 
01565   kdDebug(7004) << "urlsCurrentlyListed: " << endl;
01566   QDictIterator< QPtrList<KDirLister> > it2( urlsCurrentlyListed );
01567   for ( ; it2.current() ; ++it2 )
01568   {
01569     QString list;
01570     for ( QPtrListIterator<KDirLister> listit( *it2.current() ); listit.current(); ++listit )
01571       list += " 0x" + QString::number( (long)listit.current(), 16 );
01572     kdDebug(7004) << "   " << it2.currentKey() << "  " << it2.current()->count() << " listers: " << list << endl;
01573   }
01574 
01575   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator jit = jobs.begin();
01576   kdDebug(7004) << "Jobs: " << endl;
01577   for ( ; jit != jobs.end() ; ++jit )
01578     kdDebug(7004) << "   " << jit.key() << " listing " << jit.key()->url().prettyURL() << ": " << (*jit).count() << " entries." << endl;
01579 
01580   kdDebug(7004) << "Items in cache: " << endl;
01581   QCacheIterator<DirItem> itc( itemsCached );
01582   for ( ; itc.current() ; ++itc )
01583     kdDebug(7004) << "   " << itc.currentKey() << "  rootItem: "
01584                   << ( itc.current()->rootItem ? itc.current()->rootItem->url().prettyURL() : QString("NULL") )
01585                   << ( itc.current()->lstItems ? QString(" with %1 items.").arg(itc.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01586 }
01587 #endif
01588 
01589 /*********************** -- The new KDirLister -- ************************/
01590 
01591 
01592 KDirLister::KDirLister( bool _delayedMimeTypes )
01593 {
01594   kdDebug(7003) << "+KDirLister" << endl;
01595 
01596   d = new KDirListerPrivate;
01597 
01598   d->complete = true;
01599   d->delayedMimeTypes = _delayedMimeTypes;
01600 
01601   setAutoUpdate( true );
01602   setDirOnlyMode( false );
01603   setShowingDotFiles( false );
01604 
01605   setAutoErrorHandlingEnabled( true, 0 );
01606 }
01607 
01608 KDirLister::~KDirLister()
01609 {
01610   kdDebug(7003) << "-KDirLister" << endl;
01611 
01612   // Stop all running jobs
01613   stop();
01614   s_pCache->forgetDirs( this );
01615 
01616   delete d;
01617 }
01618 
01619 bool KDirLister::openURL( const KURL& _url, bool _keep, bool _reload )
01620 {
01621   if ( !validURL( _url ) )
01622     return false;
01623 
01624   kdDebug(7003) << k_funcinfo << _url.prettyURL()
01625                 << " keep=" << _keep << " reload=" << _reload << endl;
01626 
01627   // emit the current changes made to avoid an inconsistent treeview
01628   if ( d->changes != NONE && _keep )
01629     emitChanges();
01630 
01631   d->changes = NONE;
01632 
01633   s_pCache->listDir( this, _url, _keep, _reload );
01634 
01635   return true;
01636 }
01637 
01638 void KDirLister::stop()
01639 {
01640   kdDebug(7003) << k_funcinfo << endl;
01641   s_pCache->stop( this );
01642 }
01643 
01644 void KDirLister::stop( const KURL& _url )
01645 {
01646   kdDebug(7003) << k_funcinfo << _url.prettyURL() << endl;
01647   s_pCache->stop( this, _url );
01648 }
01649 
01650 bool KDirLister::autoUpdate() const
01651 {
01652   return d->autoUpdate;
01653 }
01654 
01655 void KDirLister::setAutoUpdate( bool _enable )
01656 {
01657   if ( d->autoUpdate == _enable )
01658     return;
01659 
01660   d->autoUpdate = _enable;
01661   s_pCache->setAutoUpdate( this, _enable );
01662 }
01663 
01664 bool KDirLister::showingDotFiles() const
01665 {
01666   return d->isShowingDotFiles;
01667 }
01668 
01669 void KDirLister::setShowingDotFiles( bool _showDotFiles )
01670 {
01671   if ( d->isShowingDotFiles == _showDotFiles )
01672     return;
01673 
01674   d->isShowingDotFiles = _showDotFiles;
01675   d->changes ^= DOT_FILES;
01676 }
01677 
01678 bool KDirLister::dirOnlyMode() const
01679 {
01680   return d->dirOnlyMode;
01681 }
01682 
01683 void KDirLister::setDirOnlyMode( bool _dirsOnly )
01684 {
01685   if ( d->dirOnlyMode == _dirsOnly )
01686     return;
01687 
01688   d->dirOnlyMode = _dirsOnly;
01689   d->changes ^= DIR_ONLY_MODE;
01690 }
01691 
01692 bool KDirLister::autoErrorHandlingEnabled() const
01693 {
01694   return d->autoErrorHandling;
01695 }
01696 
01697 void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent )
01698 {
01699   d->autoErrorHandling = enable;
01700   d->errorParent = parent;
01701 }
01702 
01703 const KURL& KDirLister::url() const
01704 {
01705   return d->url;
01706 }
01707 
01708 const KURL::List& KDirLister::directories() const
01709 {
01710   return d->lstDirs;
01711 }
01712 
01713 void KDirLister::emitChanges()
01714 {
01715   if ( d->changes == NONE )
01716     return;
01717 
01718   static const QString& dot = KGlobal::staticQString(".");
01719   static const QString& dotdot = KGlobal::staticQString("..");
01720 
01721   for ( KURL::List::Iterator it = d->lstDirs.begin();
01722         it != d->lstDirs.end(); ++it )
01723   {
01724     KFileItemListIterator kit( *s_pCache->itemsForDir( *it ) );
01725     for ( ; kit.current(); ++kit )
01726     {
01727       if ( (*kit)->text() == dot || (*kit)->text() == dotdot )
01728         continue;
01729 
01730       bool oldMime = true, newMime = true;
01731 
01732       if ( d->changes & MIME_FILTER )
01733       {
01734         oldMime = doMimeFilter( (*kit)->mimetype(), d->oldMimeFilter )
01735                 && doMimeExcludeFilter( (*kit)->mimetype(), d->oldMimeExcludeFilter );
01736         newMime = doMimeFilter( (*kit)->mimetype(), d->mimeFilter )
01737                 && doMimeExcludeFilter( (*kit)->mimetype(), d->mimeExcludeFilter );
01738 
01739         if ( oldMime && !newMime )
01740         {
01741           emit deleteItem( *kit );
01742           continue;
01743         }
01744       }
01745 
01746       if ( d->changes & DIR_ONLY_MODE )
01747       {
01748         // the lister switched to dirOnlyMode
01749         if ( d->dirOnlyMode )
01750         {
01751           if ( !(*kit)->isDir() )
01752             emit deleteItem( *kit );
01753         }
01754         else if ( !(*kit)->isDir() )
01755           addNewItem( *kit );
01756 
01757         continue;
01758       }
01759 
01760       if ( (*kit)->isHidden() )
01761       {
01762         if ( d->changes & DOT_FILES )
01763         {
01764           // the lister switched to dot files mode
01765           if ( d->isShowingDotFiles )
01766             addNewItem( *kit );
01767           else
01768             emit deleteItem( *kit );
01769 
01770           continue;
01771         }
01772       }
01773       else if ( d->changes & NAME_FILTER )
01774       {
01775         bool oldName = (*kit)->isDir() ||
01776                        d->oldFilters.isEmpty() ||
01777                        doNameFilter( (*kit)->text(), d->oldFilters );
01778 
01779         bool newName = (*kit)->isDir() ||
01780                        d->lstFilters.isEmpty() ||
01781                        doNameFilter( (*kit)->text(), d->lstFilters );
01782 
01783         if ( oldName && !newName )
01784         {
01785           emit deleteItem( *kit );
01786           continue;
01787         }
01788         else if ( !oldName && newName )
01789           addNewItem( *kit );
01790       }
01791 
01792       if ( (d->changes & MIME_FILTER) && !oldMime && newMime )
01793         addNewItem( *kit );
01794     }
01795 
01796     emitItems();
01797   }
01798 
01799   d->changes = NONE;
01800 }
01801 
01802 void KDirLister::updateDirectory( const KURL& _u )
01803 {
01804   s_pCache->updateDirectory( _u );
01805 }
01806 
01807 bool KDirLister::isFinished() const
01808 {
01809   return d->complete;
01810 }
01811 
01812 KFileItem *KDirLister::rootItem() const
01813 {
01814   return d->rootFileItem;
01815 }
01816 
01817 KFileItem *KDirLister::findByURL( const KURL& _url ) const
01818 {
01819   return s_pCache->findByURL( this, _url );
01820 }
01821 
01822 KFileItem *KDirLister::findByName( const QString& _name ) const
01823 {
01824   return s_pCache->findByName( this, _name );
01825 }
01826 
01827 #ifndef KDE_NO_COMPAT
01828 KFileItem *KDirLister::find( const KURL& _url ) const
01829 {
01830   return findByURL( _url );
01831 }
01832 #endif
01833 
01834 
01835 // ================ public filter methods ================ //
01836 
01837 void KDirLister::setNameFilter( const QString& nameFilter )
01838 {
01839   if ( !(d->changes & NAME_FILTER) )
01840   {
01841     d->oldFilters = d->lstFilters;
01842     d->lstFilters.setAutoDelete( false );
01843   }
01844 
01845   d->lstFilters.clear();
01846   d->lstFilters.setAutoDelete( true );
01847 
01848   d->nameFilter = nameFilter;
01849 
01850   // Split on white space
01851   QStringList list = QStringList::split( ' ', nameFilter );
01852   for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
01853     d->lstFilters.append( new QRegExp(*it, false, true ) );
01854 
01855   d->changes |= NAME_FILTER;
01856 }
01857 
01858 const QString& KDirLister::nameFilter() const
01859 {
01860   return d->nameFilter;
01861 }
01862 
01863 void KDirLister::setMimeFilter( const QStringList& mimeFilter )
01864 {
01865   if ( !(d->changes & MIME_FILTER) )
01866     d->oldMimeFilter = d->mimeFilter;
01867 
01868   if ( mimeFilter.find("all/allfiles") != mimeFilter.end() || 
01869        mimeFilter.find("all/all") != mimeFilter.end() )
01870     d->mimeFilter.clear();
01871   else
01872     d->mimeFilter = mimeFilter;
01873 
01874   d->changes |= MIME_FILTER;
01875 }
01876 
01877 void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter )
01878 {
01879   if ( !(d->changes & MIME_FILTER) )
01880     d->oldMimeExcludeFilter = d->mimeExcludeFilter;
01881 
01882   d->mimeExcludeFilter = mimeExcludeFilter;
01883   d->changes |= MIME_FILTER;
01884 }
01885 
01886 
01887 void KDirLister::clearMimeFilter()
01888 {
01889   if ( !(d->changes & MIME_FILTER) )
01890   {
01891     d->oldMimeFilter = d->mimeFilter;
01892     d->oldMimeExcludeFilter = d->mimeExcludeFilter;
01893   }
01894   d->mimeFilter.clear();
01895   d->mimeExcludeFilter.clear();
01896   d->changes |= MIME_FILTER;
01897 }
01898 
01899 const QStringList& KDirLister::mimeFilters() const
01900 {
01901   return d->mimeFilter;
01902 }
01903 
01904 bool KDirLister::matchesFilter( const QString& name ) const
01905 {
01906   return doNameFilter( name, d->lstFilters );
01907 }
01908 
01909 bool KDirLister::matchesMimeFilter( const QString& mime ) const
01910 {
01911   return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter);
01912 }
01913 
01914 // ================ protected methods ================ //
01915 
01916 bool KDirLister::matchesFilter( const KFileItem *item ) const
01917 {
01918   Q_ASSERT( item );
01919   static const QString& dotdot = KGlobal::staticQString("..");
01920 
01921   if ( item->text() == dotdot )
01922     return false;
01923 
01924   if ( !d->isShowingDotFiles && item->text()[0] == '.' )
01925     return false;
01926 
01927   if ( item->isDir() || d->lstFilters.isEmpty() )
01928     return true;
01929 
01930   return matchesFilter( item->text() );
01931 }
01932 
01933 bool KDirLister::matchesMimeFilter( const KFileItem *item ) const
01934 {
01935   Q_ASSERT( item );
01936   // Don't lose time determining the mimetype if there is no filter
01937   if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() )
01938       return true;
01939   return matchesMimeFilter( item->mimetype() );
01940 }
01941 
01942 bool KDirLister::doNameFilter( const QString& name, const QPtrList<QRegExp>& filters ) const
01943 {
01944   for ( QPtrListIterator<QRegExp> it( filters ); it.current(); ++it )
01945     if ( it.current()->exactMatch( name ) )
01946       return true;
01947 
01948   return false;
01949 }
01950 
01951 bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const
01952 {
01953   if ( filters.isEmpty() )
01954     return true;
01955 
01956   KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
01957   //kdDebug(7004) << "doMimeFilter: investigating: "<<mimeptr->name()<<endl;
01958   QStringList::ConstIterator it = filters.begin();
01959   for ( ; it != filters.end(); ++it )
01960     if ( mimeptr->is(*it) )
01961       return true;
01962     //else   kdDebug(7004) << "doMimeFilter: compared without result to  "<<*it<<endl;
01963 
01964 
01965   return false;
01966 }
01967 
01968 bool KDirLister::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const
01969 {
01970   if ( filters.isEmpty() )
01971     return true;
01972 
01973   QStringList::ConstIterator it = filters.begin();
01974   for ( ; it != filters.end(); ++it )
01975     if ( (*it) == mime )
01976       return false;
01977 
01978   return true;
01979 }
01980 
01981 
01982 bool KDirLister::validURL( const KURL& _url ) const
01983 {
01984   if ( !_url.isValid() )
01985   {
01986     if ( d->autoErrorHandling )
01987     {
01988       QString tmp = i18n("Malformed URL\n%1").arg( _url.prettyURL() );
01989       KMessageBox::error( d->errorParent, tmp );
01990     }
01991     return false;
01992   }
01993 
01994   // TODO: verify that this is really a directory?
01995 
01996   return true;
01997 }
01998 
01999 void KDirLister::handleError( KIO::Job *job )
02000 {
02001   if ( d->autoErrorHandling )
02002     job->showErrorDialog( d->errorParent );
02003 }
02004 
02005 
02006 // ================= private methods ================= //
02007 
02008 void KDirLister::addNewItem( const KFileItem *item )
02009 {
02010   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02011 
02012   if ( isNameFilterMatch )
02013     return; // No reason to continue... bailing out here prevents a mimetype scan.
02014 
02015   bool isMimeFilterMatch = !matchesMimeFilter( item );
02016 
02017   if ( !isNameFilterMatch && !isMimeFilterMatch )
02018   {
02019     if ( !d->lstNewItems )
02020       d->lstNewItems = new KFileItemList;
02021 
02022     d->lstNewItems->append( item );            // items not filtered
02023   }
02024   else
02025   {
02026     if ( !d->lstMimeFilteredItems )
02027       d->lstMimeFilteredItems = new KFileItemList;
02028 
02029     d->lstMimeFilteredItems->append( item );   // only filtered by mime
02030   }
02031 }
02032 
02033 void KDirLister::addNewItems( const KFileItemList& items )
02034 {
02035   // TODO: make this faster - test if we have a filter at all first
02036   for ( KFileItemListIterator kit( items ); kit.current(); ++kit )
02037     addNewItem( *kit );
02038 }
02039 
02040 void KDirLister::aboutToRefreshItem( const KFileItem *item )
02041 {
02042   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02043   bool isMimeFilterMatch = !matchesMimeFilter( item );
02044 
02045   if ( !isNameFilterMatch && !isMimeFilterMatch )
02046     d->refreshItemWasFiltered = false;
02047   else
02048     d->refreshItemWasFiltered = true;
02049 }
02050 
02051 void KDirLister::addRefreshItem( const KFileItem *item )
02052 {
02053   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02054   bool isMimeFilterMatch = !matchesMimeFilter( item );
02055 
02056   if ( !isNameFilterMatch && !isMimeFilterMatch )
02057   {
02058     if ( d->refreshItemWasFiltered )
02059     {
02060       if ( !d->lstNewItems )
02061         d->lstNewItems = new KFileItemList;
02062 
02063       d->lstNewItems->append( item );
02064     }
02065     else
02066     {
02067       if ( !d->lstRefreshItems )
02068         d->lstRefreshItems = new KFileItemList;
02069 
02070       d->lstRefreshItems->append( item );
02071     }
02072   }
02073   else if ( !d->refreshItemWasFiltered )
02074   {
02075     if ( !d->lstRemoveItems )
02076       d->lstRemoveItems = new KFileItemList;
02077 
02078     // notify the user that the mimetype of a file changed that doesn't match
02079     // a filter or does match an exclude filter
02080     d->lstRemoveItems->append( item );
02081   }
02082 }
02083 
02084 void KDirLister::emitItems()
02085 {
02086   KFileItemList *tmpNew = d->lstNewItems;
02087   d->lstNewItems = 0;
02088 
02089   KFileItemList *tmpMime = d->lstMimeFilteredItems;
02090   d->lstMimeFilteredItems = 0;
02091 
02092   KFileItemList *tmpRefresh = d->lstRefreshItems;
02093   d->lstRefreshItems = 0;
02094 
02095   KFileItemList *tmpRemove = d->lstRemoveItems;
02096   d->lstRemoveItems = 0;
02097 
02098   if ( tmpNew )
02099   {
02100     emit newItems( *tmpNew );
02101     delete tmpNew;
02102   }
02103 
02104   if ( tmpMime )
02105   {
02106     emit itemsFilteredByMime( *tmpMime );
02107     delete tmpMime;
02108   }
02109 
02110   if ( tmpRefresh )
02111   {
02112     emit refreshItems( *tmpRefresh );
02113     delete tmpRefresh;
02114   }
02115 
02116   if ( tmpRemove )
02117   {
02118     for ( KFileItem *tmp = tmpRemove->first(); tmp; tmp = tmpRemove->next() )
02119       emit deleteItem( tmp );
02120     delete tmpRemove;
02121   }
02122 }
02123 
02124 void KDirLister::emitDeleteItem( KFileItem *item )
02125 {
02126   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02127   bool isMimeFilterMatch = !matchesMimeFilter( item );
02128 
02129   if ( !isNameFilterMatch && !isMimeFilterMatch )
02130     emit deleteItem( item );
02131 }
02132 
02133 
02134 // ================ private slots ================ //
02135 
02136 void KDirLister::slotInfoMessage( KIO::Job *, const QString& message )
02137 {
02138   emit infoMessage( message );
02139 }
02140 
02141 void KDirLister::slotPercent( KIO::Job *job, unsigned long pcnt )
02142 {
02143   d->jobData[static_cast<KIO::ListJob *>(job)].percent = pcnt;
02144 
02145   int result = 0;
02146 
02147   KIO::filesize_t size = 0;
02148 
02149   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02150   while ( dataIt != d->jobData.end() )
02151   {
02152     result += (*dataIt).percent * (*dataIt).totalSize;
02153     size += (*dataIt).totalSize;
02154     ++dataIt;
02155   }
02156 
02157   if ( size != 0 )
02158     result /= size;
02159   else
02160     result = 100;
02161   emit percent( result );
02162 }
02163 
02164 void KDirLister::slotTotalSize( KIO::Job *job, KIO::filesize_t size )
02165 {
02166   d->jobData[static_cast<KIO::ListJob *>(job)].totalSize = size;
02167 
02168   KIO::filesize_t result = 0;
02169   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02170   while ( dataIt != d->jobData.end() )
02171   {
02172     result += (*dataIt).totalSize;
02173     ++dataIt;
02174   }
02175 
02176   emit totalSize( result );
02177 }
02178 
02179 void KDirLister::slotProcessedSize( KIO::Job *job, KIO::filesize_t size )
02180 {
02181   d->jobData[static_cast<KIO::ListJob *>(job)].processedSize = size;
02182 
02183   KIO::filesize_t result = 0;
02184   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02185   while ( dataIt != d->jobData.end() )
02186   {
02187     result += (*dataIt).processedSize;
02188     ++dataIt;
02189   }
02190 
02191   emit processedSize( result );
02192 }
02193 
02194 void KDirLister::slotSpeed( KIO::Job *job, unsigned long spd )
02195 {
02196   d->jobData[static_cast<KIO::ListJob *>(job)].speed = spd;
02197 
02198   int result = 0;
02199   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02200   while ( dataIt != d->jobData.end() )
02201   {
02202     result += (*dataIt).speed;
02203     ++dataIt;
02204   }
02205 
02206   emit speed( result );
02207 }
02208 
02209 uint KDirLister::numJobs()
02210 {
02211   return d->jobData.count();
02212 }
02213 
02214 void KDirLister::jobDone(KIO::ListJob *job)
02215 {
02216   if (job)
02217      d->jobData.remove(job);
02218 }
02219 
02220 void KDirLister::jobStarted(KIO::ListJob *job)
02221 {
02222   KDirListerPrivate::JobData jobData;
02223   jobData.speed = 0;
02224   jobData.percent = 0;
02225   jobData.processedSize = 0;
02226   jobData.totalSize = 0;
02227 
02228   d->jobData.insert(job, jobData);
02229 }
02230 
02231 void KDirLister::setMainWindow(QWidget *window)
02232 {
02233   d->window = window;
02234 }
02235 
02236 QWidget *KDirLister::mainWindow()
02237 {
02238   return d->window;
02239 }
02240 
02241 KFileItemList KDirLister::items( WhichItems which ) const
02242 {
02243     return itemsForDir( url(), which );
02244 }
02245 
02246 KFileItemList KDirLister::itemsForDir( const KURL &dir, WhichItems which) const
02247 {
02248     KFileItemList result;
02249     KFileItemList *allItems = s_pCache->itemsForDir( dir );
02250     if ( !allItems )
02251         return result;
02252 
02253     if ( which == AllItems )
02254         result = *allItems; // shallow copy
02255 
02256     else // only items passing the filters
02257     {
02258         for ( KFileItemListIterator kit( *allItems ); kit.current(); ++kit )
02259         {
02260             KFileItem *item = *kit;
02261             bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) ||
02262                                      !matchesFilter( item );
02263             bool isMimeFilterMatch = !matchesMimeFilter( item );
02264 
02265             if ( !isNameFilterMatch && !isMimeFilterMatch )
02266                 result.append( item );
02267         }
02268     }
02269 
02270     return result;
02271 }
02272 
02273 // to keep BC changes
02274 
02275 void KDirLister::virtual_hook( int, void * )
02276 { /*BASE::virtual_hook( id, data );*/ }
02277 
02278 #include "kdirlister.moc"
02279 #include "kdirlister_p.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 28 01:36:56 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003