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 
01211     // emit old items: listers, holders
01212     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01213     {
01214       if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
01215         kdl->d->rootFileItem = newDir->rootItem;
01216 
01217       kdl->addNewItems( *(newDir->lstItems) );
01218       kdl->emitItems();
01219     }
01220 
01221     if ( holders )
01222     {
01223       for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01224       {
01225         if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
01226           kdl->d->rootFileItem = newDir->rootItem;
01227 
01228         kdl->addNewItems( *(newDir->lstItems) );
01229         kdl->emitItems();
01230       }
01231     }
01232   }
01233   else
01234   {
01235     kdDebug(7004) << "slotRedirection: " << newUrl.url() << " has not been listed yet." << endl;
01236 
01237     delete dir->rootItem;
01238     dir->rootItem = 0;
01239     dir->lstItems->clear();
01240     dir->redirect( newUrl );
01241     itemsInUse.insert( newUrl.url(), dir );
01242     urlsCurrentlyListed.insert( newUrl.url(), listers );
01243 
01244     if ( holders )
01245       urlsCurrentlyHeld.insert( newUrl.url(), holders );
01246     else
01247     {
01248 #ifdef DEBUG_CACHE
01249       printDebug();
01250 #endif
01251       return; // only in this case the job doesn't need to be converted, 
01252     }
01253   }
01254 
01255   // make the job an update job
01256   job->disconnect( this );
01257     
01258   connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )),
01259            this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) );
01260   connect( job, SIGNAL(result( KIO::Job * )),
01261            this, SLOT(slotUpdateResult( KIO::Job * )) );
01262 
01263   // FIXME: autoUpdate-Counts!!
01264 
01265 #ifdef DEBUG_CACHE
01266   printDebug();
01267 #endif
01268 }
01269 
01270 void KDirListerCache::renameDir( const KURL &oldUrl, const KURL &newUrl )
01271 {
01272   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
01273   QString oldUrlStr = oldUrl.url(-1);
01274   QString newUrlStr = newUrl.url(-1);
01275 
01276   // Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
01277   //DirItem *dir = itemsInUse.take( oldUrlStr );
01278   //emitRedirections( oldUrl, url );
01279 
01280   // Look at all dirs being listed/shown
01281   QDictIterator<DirItem> itu( itemsInUse );
01282   bool goNext;
01283   while ( itu.current() )
01284   {
01285     goNext = true;
01286     DirItem *dir = itu.current();
01287     KURL oldDirUrl ( itu.currentKey() );
01288     //kdDebug(7004) << "itemInUse: " << oldDirUrl.prettyURL() << endl;
01289     // Check if this dir is oldUrl, or a subfolder of it
01290     if ( oldUrl.isParentOf( oldDirUrl ) )
01291     {
01292       // TODO should use KURL::cleanpath like isParentOf does
01293       QString relPath = oldDirUrl.path().mid( oldUrl.path().length() );
01294 
01295       KURL newDirUrl( newUrl ); // take new base
01296       if ( !relPath.isEmpty() )
01297         newDirUrl.addPath( relPath ); // add unchanged relative path
01298       //kdDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl.prettyURL() << endl;
01299 
01300       // Update URL in dir item and in itemsInUse
01301       dir->redirect( newDirUrl );
01302       itemsInUse.remove( itu.currentKey() ); // implies ++itu
01303       itemsInUse.insert( newDirUrl.url(-1), dir );
01304       goNext = false; // because of the implied ++itu above
01305       if ( dir->lstItems )
01306       {
01307         // Rename all items under that dir
01308         KFileItemListIterator kit( *dir->lstItems );
01309         for ( ; kit.current(); ++kit )
01310         {
01311           KURL oldItemUrl = (*kit)->url();
01312           QString oldItemUrlStr( oldItemUrl.url(-1) );
01313           KURL newItemUrl( oldItemUrl );
01314           newItemUrl.setPath( newDirUrl.path() );
01315           newItemUrl.addPath( oldItemUrl.fileName() );
01316           kdDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url() << endl;
01317           (*kit)->setURL( newItemUrl );
01318         }
01319       }
01320       emitRedirections( oldDirUrl, newDirUrl );
01321     }
01322     if ( goNext )
01323       ++itu;
01324   }
01325 
01326   // Is oldUrl a directory in the cache?
01327   // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
01328   removeDirFromCache( oldUrl );
01329   // TODO rename, instead.
01330 }
01331 
01332 void KDirListerCache::emitRedirections( const KURL &oldUrl, const KURL &url )
01333 {
01334   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << url.prettyURL() << endl;
01335   QString oldUrlStr = oldUrl.url(-1);
01336   QString urlStr = url.url(-1);
01337 
01338   KIO::ListJob *job = jobForUrl( oldUrlStr );
01339   if ( job )
01340     killJob( job );
01341 
01342   // Check if we were listing this dir. Need to abort and restart with new name in that case.
01343   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrlStr );
01344   if ( listers )
01345   {
01346     // Tell the world that the job listing the old url is dead.
01347     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01348     {
01349       if ( job )
01350         kdl->jobDone( job );
01351 
01352       emit kdl->canceled( oldUrl );
01353     }
01354 
01355     urlsCurrentlyListed.insert( urlStr, listers );
01356   }
01357 
01358   // Check if we are currently displaying this directory (odds opposite wrt above)
01359   // Update urlsCurrentlyHeld dict with new URL
01360   QPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrlStr );
01361   if ( holders )
01362   {
01363     if ( job )
01364       for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01365         kdl->jobDone( job );
01366 
01367     urlsCurrentlyHeld.insert( urlStr, holders );
01368   }
01369 
01370   if ( listers )
01371   {
01372     updateDirectory( url );
01373 
01374     // Tell the world about the new url
01375     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01376       emit kdl->started( url );
01377   }
01378 
01379   if ( holders )
01380   {
01381     // And notify the dirlisters of the redirection
01382     for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01383     {
01384       *kdl->d->lstDirs.find( oldUrl ) = url;
01385 
01386       if ( kdl->d->lstDirs.count() == 1 )
01387         emit kdl->redirection( url );
01388 
01389       emit kdl->redirection( oldUrl, url );
01390     }
01391   }
01392 }
01393 
01394 void KDirListerCache::removeDirFromCache( const KURL& dir )
01395 {
01396   kdDebug(7004) << "KDirListerCache::removeDirFromCache " << dir.prettyURL() << endl;
01397   QCacheIterator<DirItem> itc( itemsCached );
01398   while ( itc.current() )
01399   {
01400     if ( dir.isParentOf( KURL( itc.currentKey() ) ) )
01401       itemsCached.remove( itc.currentKey() );
01402     else
01403       ++itc;
01404   }
01405 }
01406 
01407 void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list )
01408 {
01409   jobs[static_cast<KIO::ListJob*>(job)] += list;
01410 }
01411 
01412 void KDirListerCache::slotUpdateResult( KIO::Job * j )
01413 {
01414   Q_ASSERT( j );
01415   KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
01416 
01417   KURL jobUrl = joburl( job );
01418   jobUrl.adjustPath(-1);  // need remove trailing slashes again, in case of redirections
01419   QString jobUrlStr = jobUrl.url();
01420 
01421   kdDebug(7004) << k_funcinfo << "finished update " << jobUrl << endl;
01422 
01423   KDirLister *kdl;
01424 
01425   QPtrList<KDirLister> *listers = urlsCurrentlyHeld[jobUrlStr];
01426   QPtrList<KDirLister> *tmpLst = urlsCurrentlyListed.take( jobUrlStr );
01427 
01428   if ( tmpLst )
01429   {
01430     if ( listers )
01431       for ( kdl = tmpLst->first(); kdl; kdl = tmpLst->next() )
01432       {
01433         Q_ASSERT( listers->containsRef( kdl ) == 0 );
01434         listers->append( kdl );
01435       }
01436     else
01437     {
01438       listers = tmpLst;
01439       urlsCurrentlyHeld.insert( jobUrlStr, listers );
01440     }
01441   }
01442 
01443   // once we are updating dirs that are only in the cache this will fail!
01444   Q_ASSERT( listers );
01445 
01446   if ( job->error() )
01447   {
01448     for ( kdl = listers->first(); kdl; kdl = listers->next() )
01449     {
01450       kdl->jobDone( job );
01451 
01452       //don't bother the user
01453       //kdl->handleError( job );
01454 
01455       emit kdl->canceled( jobUrl );
01456       if ( kdl->numJobs() == 0 )
01457       {
01458         kdl->d->complete = true;
01459         emit kdl->canceled();
01460       }
01461     }
01462 
01463     jobs.remove( job );
01464 
01465     // TODO: if job is a parent of one or more
01466     // of the pending urls we should cancel them
01467     processPendingUpdates();
01468     return;
01469   }
01470 
01471   DirItem *dir = itemsInUse[jobUrlStr];
01472   dir->complete = true;
01473 
01474 
01475   // check if anyone wants the mimetypes immediately
01476   bool delayedMimeTypes = true;
01477   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01478     delayedMimeTypes &= kdl->d->delayedMimeTypes;
01479 
01480   // should be enough to get reasonable speed in most cases
01481   QDict<KFileItem> fileItems( 9973 );
01482 
01483   KFileItemListIterator kit ( *(dir->lstItems) );
01484 
01485   // Unmark all items in url
01486   for ( ; kit.current(); ++kit )
01487   {
01488     (*kit)->unmark();
01489     fileItems.insert( (*kit)->url().url(), *kit );
01490   }
01491 
01492   static const QString& dot = KGlobal::staticQString(".");
01493   static const QString& dotdot = KGlobal::staticQString("..");
01494 
01495   KFileItem *item = 0, *tmp;
01496 
01497   QValueList<KIO::UDSEntry> buf = jobs[job];
01498   QValueListIterator<KIO::UDSEntry> it = buf.begin();
01499   for ( ; it != buf.end(); ++it )
01500   {
01501     // Form the complete url
01502     if ( !item )
01503       item = new KFileItem( *it, jobUrl, delayedMimeTypes, true );
01504     else
01505       item->setUDSEntry( *it, jobUrl, delayedMimeTypes, true );
01506 
01507     // Find out about the name
01508     QString name = item->name();
01509     Q_ASSERT( !name.isEmpty() );
01510 
01511     // we duplicate the check for dotdot here, to avoid iterating over
01512     // all items again and checking in matchesFilter() that way.
01513     if ( name.isEmpty() || name == dotdot )
01514       continue;
01515 
01516     if ( name == dot )
01517     {
01518       // if the update was started before finishing the original listing
01519       // there is no root item yet
01520       if ( !dir->rootItem )
01521       {
01522         dir->rootItem = item;
01523         item = 0;
01524 
01525         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01526           if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl )
01527             kdl->d->rootFileItem = dir->rootItem;
01528       }
01529 
01530       continue;
01531     }
01532 
01533     // Find this item
01534     if ( (tmp = fileItems[item->url().url()]) )
01535     {
01536       tmp->mark();
01537 
01538       // check if something changed for this file
01539       if ( !tmp->cmp( *item ) )
01540       {
01541         for ( kdl = listers->first(); kdl; kdl = listers->next() )
01542           kdl->aboutToRefreshItem( tmp );
01543 
01544         //kdDebug(7004) << "slotUpdateResult: file changed: " << tmp->name() << endl;
01545         tmp->assign( *item );
01546 
01547         for ( kdl = listers->first(); kdl; kdl = listers->next() )
01548           kdl->addRefreshItem( tmp );
01549       }
01550     }
01551     else // this is a new file
01552     {
01553       //kdDebug(7004) << "slotUpdateResult: new file: " << name << endl;
01554 
01555       item->mark();
01556       dir->lstItems->append( item );
01557 
01558       for ( kdl = listers->first(); kdl; kdl = listers->next() )
01559         kdl->addNewItem( item );
01560 
01561       // item used, we need a new one for the next iteration
01562       item = 0;
01563     }
01564   }
01565 
01566   if ( item )
01567     delete item;
01568 
01569   jobs.remove( job );
01570 
01571   deleteUnmarkedItems( listers, dir->lstItems );
01572 
01573   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01574   {
01575     kdl->emitItems();
01576 
01577     kdl->jobDone( job );
01578 
01579     emit kdl->completed( jobUrl );
01580     if ( kdl->numJobs() == 0 )
01581     {
01582       kdl->d->complete = true;
01583       emit kdl->completed();
01584     }
01585   }
01586 
01587   // TODO: hmm, if there was an error and job is a parent of one or more
01588   // of the pending urls we should cancel it/them as well
01589   processPendingUpdates();
01590 }
01591 
01592 // private
01593 
01594 KIO::ListJob *KDirListerCache::jobForUrl( const QString& url, KIO::ListJob *not_job )
01595 {
01596   KIO::ListJob *job;
01597   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator it = jobs.begin();
01598   while ( it != jobs.end() )
01599   {
01600     job = it.key();
01601     if ( joburl( job ).url(-1) == url && job != not_job )
01602        return job;
01603     ++it;
01604   }
01605   return 0;
01606 }
01607 
01608 const KURL& KDirListerCache::joburl( KIO::ListJob *job )
01609 {
01610   if ( job->redirectionURL().isValid() )
01611      return job->redirectionURL();
01612   else
01613      return job->url();
01614 }
01615 
01616 void KDirListerCache::killJob( KIO::ListJob *job )
01617 {
01618   jobs.remove( job );
01619   job->disconnect( this );
01620   job->kill();
01621 }
01622 
01623 void KDirListerCache::deleteUnmarkedItems( QPtrList<KDirLister> *listers, KFileItemList *lstItems )
01624 {
01625   // Find all unmarked items and delete them
01626   KFileItem* item;
01627   lstItems->first();
01628   while ( (item = lstItems->current()) )
01629     if ( !item->isMarked() )
01630     {
01631       //kdDebug() << k_funcinfo << item->name() << endl;
01632       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01633         kdl->emitDeleteItem( item );
01634 
01635       if ( item->isDir() )
01636         deleteDir( item->url() );
01637 
01638       // finally actually delete the item
01639       lstItems->take();
01640       delete item;
01641     }
01642     else
01643       lstItems->next();
01644 }
01645 
01646 void KDirListerCache::deleteDir( const KURL& dirUrl )
01647 {
01648   //kdDebug() << k_funcinfo << dirUrl.prettyURL() << endl;
01649   // unregister and remove the childs of the deleted item.
01650   // Idea: tell all the KDirListers that they should forget the dir
01651   //       and then remove it from the cache.
01652 
01653   QDictIterator<DirItem> itu( itemsInUse );
01654   while ( itu.current() )
01655   {
01656     KURL deletedUrl( itu.currentKey() );
01657     if ( dirUrl.isParentOf( deletedUrl ) )
01658     {
01659       // stop all jobs for deletedUrl
01660 
01661       QPtrList<KDirLister> *kdls = urlsCurrentlyListed[deletedUrl.url()];
01662       if ( kdls )  // yeah, I lack good names
01663       {
01664         // we need a copy because stop modifies the list
01665         kdls = new QPtrList<KDirLister>( *kdls );
01666         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01667           stop( kdl, deletedUrl );
01668 
01669         delete kdls;
01670       }
01671 
01672       // tell listers holding deletedUrl to forget about it
01673       // this will stop running updates for deletedUrl as well
01674 
01675       kdls = urlsCurrentlyHeld[deletedUrl.url()];
01676       if ( kdls )
01677       {
01678         // we need a copy because forgetDirs modifies the list
01679         kdls = new QPtrList<KDirLister>( *kdls );
01680 
01681         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01682         {
01683           // lister's root is the deleted item
01684           if ( kdl->d->url == deletedUrl )
01685           {
01686             // tell the view first. It might need the subdirs' items (which forgetDirs will delete)
01687             if ( kdl->d->rootFileItem )
01688               emit kdl->deleteItem( kdl->d->rootFileItem );
01689             forgetDirs( kdl );
01690             kdl->d->rootFileItem = 0;
01691           }
01692           else
01693           {
01694             bool treeview = kdl->d->lstDirs.count() > 1;
01695             if ( !treeview )
01696             {
01697               emit kdl->clear();
01698               kdl->d->lstDirs.clear();
01699             }
01700             else
01701               kdl->d->lstDirs.remove( kdl->d->lstDirs.find( deletedUrl ) );
01702 
01703             forgetDirs( kdl, deletedUrl, treeview );
01704           }
01705         }
01706 
01707         delete kdls;
01708       }
01709 
01710       // delete the entry for deletedUrl - should not be needed, it's in
01711       // items cached now
01712 
01713       DirItem *dir = itemsInUse.take( deletedUrl.url() );
01714       Q_ASSERT( !dir );
01715       if ( !dir ) // take didn't find it - move on
01716           ++itu;
01717     }
01718     else
01719       ++itu;
01720   }
01721 
01722   // remove the children from the cache
01723   removeDirFromCache( dirUrl );
01724 }
01725 
01726 void KDirListerCache::processPendingUpdates()
01727 {
01728   // TODO
01729 }
01730 
01731 #ifndef NDEBUG
01732 void KDirListerCache::printDebug()
01733 {
01734   kdDebug(7004) << "Items in use: " << endl;
01735   QDictIterator<DirItem> itu( itemsInUse );
01736   for ( ; itu.current() ; ++itu ) {
01737       kdDebug(7004) << "   " << itu.currentKey() << "  URL: " << itu.current()->url
01738                     << " rootItem: " << ( itu.current()->rootItem ? itu.current()->rootItem->url() : KURL() )
01739                     << " autoUpdates refcount: " << itu.current()->autoUpdates
01740                     << " complete: " << itu.current()->complete
01741                   << ( itu.current()->lstItems ? QString(" with %1 items.").arg(itu.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01742   }
01743 
01744   kdDebug(7004) << "urlsCurrentlyHeld: " << endl;
01745   QDictIterator< QPtrList<KDirLister> > it( urlsCurrentlyHeld );
01746   for ( ; it.current() ; ++it )
01747   {
01748     QString list;
01749     for ( QPtrListIterator<KDirLister> listit( *it.current() ); listit.current(); ++listit )
01750       list += " 0x" + QString::number( (long)listit.current(), 16 );
01751     kdDebug(7004) << "   " << it.currentKey() << "  " << it.current()->count() << " listers: " << list << endl;
01752   }
01753 
01754   kdDebug(7004) << "urlsCurrentlyListed: " << endl;
01755   QDictIterator< QPtrList<KDirLister> > it2( urlsCurrentlyListed );
01756   for ( ; it2.current() ; ++it2 )
01757   {
01758     QString list;
01759     for ( QPtrListIterator<KDirLister> listit( *it2.current() ); listit.current(); ++listit )
01760       list += " 0x" + QString::number( (long)listit.current(), 16 );
01761     kdDebug(7004) << "   " << it2.currentKey() << "  " << it2.current()->count() << " listers: " << list << endl;
01762   }
01763 
01764   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator jit = jobs.begin();
01765   kdDebug(7004) << "Jobs: " << endl;
01766   for ( ; jit != jobs.end() ; ++jit )
01767     kdDebug(7004) << "   " << jit.key() << " listing " << joburl( jit.key() ).prettyURL() << ": " << (*jit).count() << " entries." << endl;
01768 
01769   kdDebug(7004) << "Items in cache: " << endl;
01770   QCacheIterator<DirItem> itc( itemsCached );
01771   for ( ; itc.current() ; ++itc )
01772     kdDebug(7004) << "   " << itc.currentKey() << "  rootItem: "
01773                   << ( itc.current()->rootItem ? itc.current()->rootItem->url().prettyURL() : QString("NULL") )
01774                   << ( itc.current()->lstItems ? QString(" with %1 items.").arg(itc.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01775 }
01776 #endif
01777 
01778 /*********************** -- The new KDirLister -- ************************/
01779 
01780 
01781 KDirLister::KDirLister( bool _delayedMimeTypes )
01782 {
01783   kdDebug(7003) << "+KDirLister" << endl;
01784 
01785   d = new KDirListerPrivate;
01786 
01787   d->complete = true;
01788   d->delayedMimeTypes = _delayedMimeTypes;
01789 
01790   setAutoUpdate( true );
01791   setDirOnlyMode( false );
01792   setShowingDotFiles( false );
01793 
01794   setAutoErrorHandlingEnabled( true, 0 );
01795 }
01796 
01797 KDirLister::~KDirLister()
01798 {
01799   kdDebug(7003) << "-KDirLister" << endl;
01800 
01801   // Stop all running jobs
01802   stop();
01803   s_pCache->forgetDirs( this );
01804 
01805   delete d;
01806 }
01807 
01808 bool KDirLister::openURL( const KURL& _url, bool _keep, bool _reload )
01809 {
01810   if ( !validURL( _url ) )
01811     return false;
01812 
01813   kdDebug(7003) << k_funcinfo << _url.prettyURL()
01814                 << " keep=" << _keep << " reload=" << _reload << endl;
01815 
01816   // emit the current changes made to avoid an inconsistent treeview
01817   if ( d->changes != NONE && _keep )
01818     emitChanges();
01819 
01820   d->changes = NONE;
01821 
01822   s_pCache->listDir( this, _url, _keep, _reload );
01823 
01824   return true;
01825 }
01826 
01827 void KDirLister::stop()
01828 {
01829   kdDebug(7003) << k_funcinfo << endl;
01830   s_pCache->stop( this );
01831 }
01832 
01833 void KDirLister::stop( const KURL& _url )
01834 {
01835   kdDebug(7003) << k_funcinfo << _url.prettyURL() << endl;
01836   s_pCache->stop( this, _url );
01837 }
01838 
01839 bool KDirLister::autoUpdate() const
01840 {
01841   return d->autoUpdate;
01842 }
01843 
01844 void KDirLister::setAutoUpdate( bool _enable )
01845 {
01846   if ( d->autoUpdate == _enable )
01847     return;
01848 
01849   d->autoUpdate = _enable;
01850   s_pCache->setAutoUpdate( this, _enable );
01851 }
01852 
01853 bool KDirLister::showingDotFiles() const
01854 {
01855   return d->isShowingDotFiles;
01856 }
01857 
01858 void KDirLister::setShowingDotFiles( bool _showDotFiles )
01859 {
01860   if ( d->isShowingDotFiles == _showDotFiles )
01861     return;
01862 
01863   d->isShowingDotFiles = _showDotFiles;
01864   d->changes ^= DOT_FILES;
01865 }
01866 
01867 bool KDirLister::dirOnlyMode() const
01868 {
01869   return d->dirOnlyMode;
01870 }
01871 
01872 void KDirLister::setDirOnlyMode( bool _dirsOnly )
01873 {
01874   if ( d->dirOnlyMode == _dirsOnly )
01875     return;
01876 
01877   d->dirOnlyMode = _dirsOnly;
01878   d->changes ^= DIR_ONLY_MODE;
01879 }
01880 
01881 bool KDirLister::autoErrorHandlingEnabled() const
01882 {
01883   return d->autoErrorHandling;
01884 }
01885 
01886 void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent )
01887 {
01888   d->autoErrorHandling = enable;
01889   d->errorParent = parent;
01890 }
01891 
01892 const KURL& KDirLister::url() const
01893 {
01894   return d->url;
01895 }
01896 
01897 const KURL::List& KDirLister::directories() const
01898 {
01899   return d->lstDirs;
01900 }
01901 
01902 void KDirLister::emitChanges()
01903 {
01904   if ( d->changes == NONE )
01905     return;
01906 
01907   static const QString& dot = KGlobal::staticQString(".");
01908   static const QString& dotdot = KGlobal::staticQString("..");
01909 
01910   for ( KURL::List::Iterator it = d->lstDirs.begin();
01911         it != d->lstDirs.end(); ++it )
01912   {
01913     KFileItemListIterator kit( *s_pCache->itemsForDir( *it ) );
01914     for ( ; kit.current(); ++kit )
01915     {
01916       if ( (*kit)->text() == dot || (*kit)->text() == dotdot )
01917         continue;
01918 
01919       bool oldMime = true, newMime = true;
01920 
01921       if ( d->changes & MIME_FILTER )
01922       {
01923         oldMime = doMimeFilter( (*kit)->mimetype(), d->oldMimeFilter )
01924                 && doMimeExcludeFilter( (*kit)->mimetype(), d->oldMimeExcludeFilter );
01925         newMime = doMimeFilter( (*kit)->mimetype(), d->mimeFilter )
01926                 && doMimeExcludeFilter( (*kit)->mimetype(), d->mimeExcludeFilter );
01927 
01928         if ( oldMime && !newMime )
01929         {
01930           emit deleteItem( *kit );
01931           continue;
01932         }
01933       }
01934 
01935       if ( d->changes & DIR_ONLY_MODE )
01936       {
01937         // the lister switched to dirOnlyMode
01938         if ( d->dirOnlyMode )
01939         {
01940           if ( !(*kit)->isDir() )
01941             emit deleteItem( *kit );
01942         }
01943         else if ( !(*kit)->isDir() )
01944           addNewItem( *kit );
01945 
01946         continue;
01947       }
01948 
01949       if ( (*kit)->isHidden() )
01950       {
01951         if ( d->changes & DOT_FILES )
01952         {
01953           // the lister switched to dot files mode
01954           if ( d->isShowingDotFiles )
01955             addNewItem( *kit );
01956           else
01957             emit deleteItem( *kit );
01958 
01959           continue;
01960         }
01961       }
01962       else if ( d->changes & NAME_FILTER )
01963       {
01964         bool oldName = (*kit)->isDir() ||
01965                        d->oldFilters.isEmpty() ||
01966                        doNameFilter( (*kit)->text(), d->oldFilters );
01967 
01968         bool newName = (*kit)->isDir() ||
01969                        d->lstFilters.isEmpty() ||
01970                        doNameFilter( (*kit)->text(), d->lstFilters );
01971 
01972         if ( oldName && !newName )
01973         {
01974           emit deleteItem( *kit );
01975           continue;
01976         }
01977         else if ( !oldName && newName )
01978           addNewItem( *kit );
01979       }
01980 
01981       if ( (d->changes & MIME_FILTER) && !oldMime && newMime )
01982         addNewItem( *kit );
01983     }
01984 
01985     emitItems();
01986   }
01987 
01988   d->changes = NONE;
01989 }
01990 
01991 void KDirLister::updateDirectory( const KURL& _u )
01992 {
01993   s_pCache->updateDirectory( _u );
01994 }
01995 
01996 bool KDirLister::isFinished() const
01997 {
01998   return d->complete;
01999 }
02000 
02001 KFileItem *KDirLister::rootItem() const
02002 {
02003   return d->rootFileItem;
02004 }
02005 
02006 KFileItem *KDirLister::findByURL( const KURL& _url ) const
02007 {
02008   return s_pCache->findByURL( this, _url );
02009 }
02010 
02011 KFileItem *KDirLister::findByName( const QString& _name ) const
02012 {
02013   return s_pCache->findByName( this, _name );
02014 }
02015 
02016 #ifndef KDE_NO_COMPAT
02017 KFileItem *KDirLister::find( const KURL& _url ) const
02018 {
02019   return findByURL( _url );
02020 }
02021 #endif
02022 
02023 
02024 // ================ public filter methods ================ //
02025 
02026 void KDirLister::setNameFilter( const QString& nameFilter )
02027 {
02028   if ( !(d->changes & NAME_FILTER) )
02029   {
02030     d->oldFilters = d->lstFilters;
02031     d->lstFilters.setAutoDelete( false );
02032   }
02033 
02034   d->lstFilters.clear();
02035   d->lstFilters.setAutoDelete( true );
02036 
02037   d->nameFilter = nameFilter;
02038 
02039   // Split on white space
02040   QStringList list = QStringList::split( ' ', nameFilter );
02041   for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
02042     d->lstFilters.append( new QRegExp(*it, false, true ) );
02043 
02044   d->changes |= NAME_FILTER;
02045 }
02046 
02047 const QString& KDirLister::nameFilter() const
02048 {
02049   return d->nameFilter;
02050 }
02051 
02052 void KDirLister::setMimeFilter( const QStringList& mimeFilter )
02053 {
02054   if ( !(d->changes & MIME_FILTER) )
02055     d->oldMimeFilter = d->mimeFilter;
02056 
02057   if ( mimeFilter.find("all/allfiles") != mimeFilter.end() || 
02058        mimeFilter.find("all/all") != mimeFilter.end() )
02059     d->mimeFilter.clear();
02060   else
02061     d->mimeFilter = mimeFilter;
02062 
02063   d->changes |= MIME_FILTER;
02064 }
02065 
02066 void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter )
02067 {
02068   if ( !(d->changes & MIME_FILTER) )
02069     d->oldMimeExcludeFilter = d->mimeExcludeFilter;
02070 
02071   d->mimeExcludeFilter = mimeExcludeFilter;
02072   d->changes |= MIME_FILTER;
02073 }
02074 
02075 
02076 void KDirLister::clearMimeFilter()
02077 {
02078   if ( !(d->changes & MIME_FILTER) )
02079   {
02080     d->oldMimeFilter = d->mimeFilter;
02081     d->oldMimeExcludeFilter = d->mimeExcludeFilter;
02082   }
02083   d->mimeFilter.clear();
02084   d->mimeExcludeFilter.clear();
02085   d->changes |= MIME_FILTER;
02086 }
02087 
02088 const QStringList& KDirLister::mimeFilters() const
02089 {
02090   return d->mimeFilter;
02091 }
02092 
02093 bool KDirLister::matchesFilter( const QString& name ) const
02094 {
02095   return doNameFilter( name, d->lstFilters );
02096 }
02097 
02098 bool KDirLister::matchesMimeFilter( const QString& mime ) const
02099 {
02100   return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter);
02101 }
02102 
02103 // ================ protected methods ================ //
02104 
02105 bool KDirLister::matchesFilter( const KFileItem *item ) const
02106 {
02107   Q_ASSERT( item );
02108   static const QString& dotdot = KGlobal::staticQString("..");
02109 
02110   if ( item->text() == dotdot )
02111     return false;
02112 
02113   if ( !d->isShowingDotFiles && item->text()[0] == '.' )
02114     return false;
02115 
02116   if ( item->isDir() || d->lstFilters.isEmpty() )
02117     return true;
02118 
02119   return matchesFilter( item->text() );
02120 }
02121 
02122 bool KDirLister::matchesMimeFilter( const KFileItem *item ) const
02123 {
02124   Q_ASSERT( item );
02125   // Don't lose time determining the mimetype if there is no filter
02126   if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() )
02127       return true;
02128   return matchesMimeFilter( item->mimetype() );
02129 }
02130 
02131 bool KDirLister::doNameFilter( const QString& name, const QPtrList<QRegExp>& filters ) const
02132 {
02133   for ( QPtrListIterator<QRegExp> it( filters ); it.current(); ++it )
02134     if ( it.current()->exactMatch( name ) )
02135       return true;
02136 
02137   return false;
02138 }
02139 
02140 bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const
02141 {
02142   if ( filters.isEmpty() )
02143     return true;
02144 
02145   KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
02146   //kdDebug(7004) << "doMimeFilter: investigating: "<<mimeptr->name()<<endl;
02147   QStringList::ConstIterator it = filters.begin();
02148   for ( ; it != filters.end(); ++it )
02149     if ( mimeptr->is(*it) )
02150       return true;
02151     //else   kdDebug(7004) << "doMimeFilter: compared without result to  "<<*it<<endl;
02152 
02153 
02154   return false;
02155 }
02156 
02157 bool KDirLister::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const
02158 {
02159   if ( filters.isEmpty() )
02160     return true;
02161 
02162   QStringList::ConstIterator it = filters.begin();
02163   for ( ; it != filters.end(); ++it )
02164     if ( (*it) == mime )
02165       return false;
02166 
02167   return true;
02168 }
02169 
02170 
02171 bool KDirLister::validURL( const KURL& _url ) const
02172 {
02173   if ( !_url.isValid() )
02174   {
02175     if ( d->autoErrorHandling )
02176     {
02177       QString tmp = i18n("Malformed URL\n%1").arg( _url.prettyURL() );
02178       KMessageBox::error( d->errorParent, tmp );
02179     }
02180     return false;
02181   }
02182 
02183   // TODO: verify that this is really a directory?
02184 
02185   return true;
02186 }
02187 
02188 void KDirLister::handleError( KIO::Job *job )
02189 {
02190   if ( d->autoErrorHandling )
02191     job->showErrorDialog( d->errorParent );
02192 }
02193 
02194 
02195 // ================= private methods ================= //
02196 
02197 void KDirLister::addNewItem( const KFileItem *item )
02198 {
02199   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02200 
02201   if ( isNameFilterMatch )
02202     return; // No reason to continue... bailing out here prevents a mimetype scan.
02203 
02204   bool isMimeFilterMatch = !matchesMimeFilter( item );
02205 
02206   if ( !isNameFilterMatch && !isMimeFilterMatch )
02207   {
02208     if ( !d->lstNewItems )
02209       d->lstNewItems = new KFileItemList;
02210 
02211     d->lstNewItems->append( item );            // items not filtered
02212   }
02213   else
02214   {
02215     if ( !d->lstMimeFilteredItems )
02216       d->lstMimeFilteredItems = new KFileItemList;
02217 
02218     d->lstMimeFilteredItems->append( item );   // only filtered by mime
02219   }
02220 }
02221 
02222 void KDirLister::addNewItems( const KFileItemList& items )
02223 {
02224   // TODO: make this faster - test if we have a filter at all first
02225   for ( KFileItemListIterator kit( items ); kit.current(); ++kit )
02226     addNewItem( *kit );
02227 }
02228 
02229 void KDirLister::aboutToRefreshItem( const KFileItem *item )
02230 {
02231   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02232   bool isMimeFilterMatch = !matchesMimeFilter( item );
02233 
02234   if ( !isNameFilterMatch && !isMimeFilterMatch )
02235     d->refreshItemWasFiltered = false;
02236   else
02237     d->refreshItemWasFiltered = true;
02238 }
02239 
02240 void KDirLister::addRefreshItem( const KFileItem *item )
02241 {
02242   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02243   bool isMimeFilterMatch = !matchesMimeFilter( item );
02244 
02245   if ( !isNameFilterMatch && !isMimeFilterMatch )
02246   {
02247     if ( d->refreshItemWasFiltered )
02248     {
02249       if ( !d->lstNewItems )
02250         d->lstNewItems = new KFileItemList;
02251 
02252       d->lstNewItems->append( item );
02253     }
02254     else
02255     {
02256       if ( !d->lstRefreshItems )
02257         d->lstRefreshItems = new KFileItemList;
02258 
02259       d->lstRefreshItems->append( item );
02260     }
02261   }
02262   else if ( !d->refreshItemWasFiltered )
02263   {
02264     if ( !d->lstRemoveItems )
02265       d->lstRemoveItems = new KFileItemList;
02266 
02267     // notify the user that the mimetype of a file changed that doesn't match
02268     // a filter or does match an exclude filter
02269     d->lstRemoveItems->append( item );
02270   }
02271 }
02272 
02273 void KDirLister::emitItems()
02274 {
02275   KFileItemList *tmpNew = d->lstNewItems;
02276   d->lstNewItems = 0;
02277 
02278   KFileItemList *tmpMime = d->lstMimeFilteredItems;
02279   d->lstMimeFilteredItems = 0;
02280 
02281   KFileItemList *tmpRefresh = d->lstRefreshItems;
02282   d->lstRefreshItems = 0;
02283 
02284   KFileItemList *tmpRemove = d->lstRemoveItems;
02285   d->lstRemoveItems = 0;
02286 
02287   if ( tmpNew )
02288   {
02289     emit newItems( *tmpNew );
02290     delete tmpNew;
02291   }
02292 
02293   if ( tmpMime )
02294   {
02295     emit itemsFilteredByMime( *tmpMime );
02296     delete tmpMime;
02297   }
02298 
02299   if ( tmpRefresh )
02300   {
02301     emit refreshItems( *tmpRefresh );
02302     delete tmpRefresh;
02303   }
02304 
02305   if ( tmpRemove )
02306   {
02307     for ( KFileItem *tmp = tmpRemove->first(); tmp; tmp = tmpRemove->next() )
02308       emit deleteItem( tmp );
02309     delete tmpRemove;
02310   }
02311 }
02312 
02313 void KDirLister::emitDeleteItem( KFileItem *item )
02314 {
02315   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02316   bool isMimeFilterMatch = !matchesMimeFilter( item );
02317 
02318   if ( !isNameFilterMatch && !isMimeFilterMatch )
02319     emit deleteItem( item );
02320 }
02321 
02322 
02323 // ================ private slots ================ //
02324 
02325 void KDirLister::slotInfoMessage( KIO::Job *, const QString& message )
02326 {
02327   emit infoMessage( message );
02328 }
02329 
02330 void KDirLister::slotPercent( KIO::Job *job, unsigned long pcnt )
02331 {
02332   d->jobData[static_cast<KIO::ListJob *>(job)].percent = pcnt;
02333 
02334   int result = 0;
02335 
02336   KIO::filesize_t size = 0;
02337 
02338   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02339   while ( dataIt != d->jobData.end() )
02340   {
02341     result += (*dataIt).percent * (*dataIt).totalSize;
02342     size += (*dataIt).totalSize;
02343     ++dataIt;
02344   }
02345 
02346   if ( size != 0 )
02347     result /= size;
02348   else
02349     result = 100;
02350   emit percent( result );
02351 }
02352 
02353 void KDirLister::slotTotalSize( KIO::Job *job, KIO::filesize_t size )
02354 {
02355   d->jobData[static_cast<KIO::ListJob *>(job)].totalSize = size;
02356 
02357   KIO::filesize_t result = 0;
02358   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02359   while ( dataIt != d->jobData.end() )
02360   {
02361     result += (*dataIt).totalSize;
02362     ++dataIt;
02363   }
02364 
02365   emit totalSize( result );
02366 }
02367 
02368 void KDirLister::slotProcessedSize( KIO::Job *job, KIO::filesize_t size )
02369 {
02370   d->jobData[static_cast<KIO::ListJob *>(job)].processedSize = size;
02371 
02372   KIO::filesize_t result = 0;
02373   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02374   while ( dataIt != d->jobData.end() )
02375   {
02376     result += (*dataIt).processedSize;
02377     ++dataIt;
02378   }
02379 
02380   emit processedSize( result );
02381 }
02382 
02383 void KDirLister::slotSpeed( KIO::Job *job, unsigned long spd )
02384 {
02385   d->jobData[static_cast<KIO::ListJob *>(job)].speed = spd;
02386 
02387   int result = 0;
02388   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02389   while ( dataIt != d->jobData.end() )
02390   {
02391     result += (*dataIt).speed;
02392     ++dataIt;
02393   }
02394 
02395   emit speed( result );
02396 }
02397 
02398 uint KDirLister::numJobs()
02399 {
02400   return d->jobData.count();
02401 }
02402 
02403 void KDirLister::jobDone( KIO::ListJob *job )
02404 {
02405   d->jobData.remove( job );
02406 }
02407 
02408 void KDirLister::jobStarted( KIO::ListJob *job )
02409 {
02410   KDirListerPrivate::JobData jobData;
02411   jobData.speed = 0;
02412   jobData.percent = 0;
02413   jobData.processedSize = 0;
02414   jobData.totalSize = 0;
02415 
02416   d->jobData.insert( job, jobData );
02417   d->complete = false;
02418 }
02419 
02420 void KDirLister::connectJob( KIO::ListJob *job )
02421 {
02422   connect( job, SIGNAL(infoMessage( KIO::Job *, const QString& )),
02423            this, SLOT(slotInfoMessage( KIO::Job *, const QString& )) );
02424   connect( job, SIGNAL(percent( KIO::Job *, unsigned long )),
02425            this, SLOT(slotPercent( KIO::Job *, unsigned long )) );
02426   connect( job, SIGNAL(totalSize( KIO::Job *, KIO::filesize_t )),
02427            this, SLOT(slotTotalSize( KIO::Job *, KIO::filesize_t )) );
02428   connect( job, SIGNAL(processedSize( KIO::Job *, KIO::filesize_t )),
02429            this, SLOT(slotProcessedSize( KIO::Job *, KIO::filesize_t )) );
02430   connect( job, SIGNAL(speed( KIO::Job *, unsigned long )),
02431            this, SLOT(slotSpeed( KIO::Job *, unsigned long )) );
02432 }
02433 
02434 void KDirLister::setMainWindow( QWidget *window )
02435 {
02436   d->window = window;
02437 }
02438 
02439 QWidget *KDirLister::mainWindow()
02440 {
02441   return d->window;
02442 }
02443 
02444 KFileItemList KDirLister::items( WhichItems which ) const
02445 {
02446     return itemsForDir( url(), which );
02447 }
02448 
02449 KFileItemList KDirLister::itemsForDir( const KURL& dir, WhichItems which ) const
02450 {
02451     KFileItemList result;
02452     KFileItemList *allItems = s_pCache->itemsForDir( dir );
02453     if ( !allItems )
02454         return result;
02455 
02456     if ( which == AllItems )
02457         result = *allItems; // shallow copy
02458     else // only items passing the filters
02459     {
02460         for ( KFileItemListIterator kit( *allItems ); kit.current(); ++kit )
02461         {
02462             KFileItem *item = *kit;
02463             bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) ||
02464                                      !matchesFilter( item );
02465             bool isMimeFilterMatch = !matchesMimeFilter( item );
02466 
02467             if ( !isNameFilterMatch && !isMimeFilterMatch )
02468                 result.append( item );
02469         }
02470     }
02471 
02472     return result;
02473 }
02474 
02475 // to keep BC changes
02476 
02477 void KDirLister::virtual_hook( int, void * )
02478 { /*BASE::virtual_hook( id, data );*/ }
02479 
02480 #include "kdirlister.moc"
02481 #include "kdirlister_p.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed May 4 06:55:16 2005 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003