• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kio

kdirlister.cpp

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

kio

Skip menu "kio"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal