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