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