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-2004 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   dir->redirect( newUrl );
01040   itemsInUse.insert( newUrl.url(), dir );
01041   urlsCurrentlyListed.insert( newUrl.url(), listers );
01042 }
01043 
01044 void KDirListerCache::renameDir( const KURL &oldUrl, const KURL &newUrl )
01045 {
01046   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
01047   QString oldUrlStr = oldUrl.url(-1);
01048   QString newUrlStr = newUrl.url(-1);
01049 
01050   // Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
01051   //DirItem *dir = itemsInUse.take( oldUrlStr );
01052   //emitRedirections( oldUrl, url );
01053 
01054   // Look at all dirs being listed/shown
01055   QDictIterator<DirItem> itu( itemsInUse );
01056   bool goNext;
01057   while ( itu.current() )
01058   {
01059     goNext = true;
01060     DirItem* dir = itu.current();
01061     KURL oldDirUrl ( itu.currentKey() );
01062     //kdDebug(7004) << "itemInUse: " << oldDirUrl.prettyURL() << endl;
01063     // Check if this dir is oldUrl, or a subfolder of it
01064     if ( oldUrl.isParentOf( oldDirUrl ) )
01065     {
01066       QString relPath = oldDirUrl.path().mid( oldUrl.path().length() ); // ### should use KURL::cleanpath like isParentOf does
01067 
01068       KURL newDirUrl( newUrl ); // take new base
01069       if ( !relPath.isEmpty() )
01070         newDirUrl.addPath( relPath ); // add unchanged relative path
01071       //kdDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl.prettyURL() << endl;
01072 
01073       // Update URL in dir item and in itemsInUse
01074       dir->redirect( newDirUrl );
01075       itemsInUse.remove( itu.currentKey() ); // implies ++itu
01076       itemsInUse.insert( newDirUrl.url(-1), dir );
01077       goNext = false; // because of the implied ++itu above
01078       if ( dir->lstItems )
01079       {
01080         // Rename all items under that dir
01081         KFileItemListIterator kit( *dir->lstItems );
01082         for ( ; kit.current(); ++kit )
01083         {
01084           KURL oldItemUrl = (*kit)->url();
01085           QString oldItemUrlStr( oldItemUrl.url(-1) );
01086           KURL newItemUrl( oldItemUrl );
01087           newItemUrl.setPath( newDirUrl.path() );
01088           newItemUrl.addPath( oldItemUrl.fileName() );
01089           kdDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url() << endl;
01090           (*kit)->setURL( newItemUrl );
01091         }
01092       }
01093       emitRedirections( oldDirUrl, newDirUrl );
01094     }
01095     if (goNext)
01096       ++itu;
01097   }
01098 
01099   // Is oldUrl a directory in the cache?
01100   // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
01101   removeDirFromCache( oldUrl );
01102   // TODO rename, instead.
01103 }
01104 
01105 void KDirListerCache::emitRedirections( const KURL &oldUrl, const KURL &url )
01106 {
01107   kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << url.prettyURL() << endl;
01108   QString oldUrlStr = oldUrl.url(-1);
01109   QString urlStr = url.url(-1);
01110 
01111   KIO::ListJob *job = jobForUrl(oldUrlStr);
01112   if (job)
01113      killJob( job );
01114 
01115   // Check if we were listing this dir. Need to abort and restart with new name in that case.
01116   QPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrlStr );
01117   if ( listers )
01118   {
01119     // Tell the world that the job listing the old url is dead.
01120     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01121     {
01122        kdl->jobDone(job);
01123        emit kdl->canceled( oldUrl );
01124     }
01125 
01126     urlsCurrentlyListed.insert( urlStr, listers );
01127   }
01128 
01129   // Check if we are currently displaying this directory (odds opposite wrt above)
01130   // Update urlsCurrentlyHeld dict with new URL
01131   QPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrlStr );
01132   if ( holders )
01133   {
01134     for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01135     {
01136        kdl->jobDone(job);
01137     }
01138     urlsCurrentlyHeld.insert( urlStr, holders );
01139   }
01140 
01141   if (listers)
01142   {
01143     updateDirectory( url );
01144 
01145     // Tell the world about the new url
01146     for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01147     {
01148       emit kdl->started( url );
01149     }
01150   }
01151 
01152   if (holders)
01153   {
01154     // And notify the dirlisters of the redirection
01155     for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
01156     {
01157       *kdl->d->lstDirs.find( oldUrl ) = url;
01158       if ( kdl->d->lstDirs.count() == 1 )
01159       {
01160         emit kdl->redirection( url );
01161       }
01162       emit kdl->redirection( oldUrl, url );
01163     }
01164   }
01165 }
01166 
01167 void KDirListerCache::removeDirFromCache( const KURL& dir )
01168 {
01169   kdDebug(7004) << "KDirListerCache::removeDirFromCache " << dir.prettyURL() << endl;
01170   QCacheIterator<DirItem> itc( itemsCached );
01171   while ( itc.current() )
01172   {
01173     if ( dir.isParentOf( KURL( itc.currentKey() ) ) )
01174       itemsCached.remove( itc.currentKey() );
01175     else
01176       ++itc;
01177   }
01178 }
01179 
01180 void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list )
01181 {
01182   jobs[static_cast<KIO::ListJob*>(job)] += list;
01183 }
01184 
01185 void KDirListerCache::slotUpdateResult( KIO::Job * j )
01186 {
01187   Q_ASSERT( j );
01188   KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
01189 
01190   KURL jobUrl = job->url();
01191   jobUrl.adjustPath(-1);  // need remove trailing slashes again, in case of redirections
01192   QString jobUrlStr = jobUrl.url();
01193 
01194   kdDebug(7004) << k_funcinfo << "finished update " << jobUrl << endl;
01195 
01196   KDirLister *kdl;
01197 
01198   QPtrList<KDirLister> *listers = urlsCurrentlyHeld[jobUrlStr];
01199   QPtrList<KDirLister> *tmpLst = urlsCurrentlyListed.take( jobUrlStr );
01200 
01201   if ( tmpLst )
01202   {
01203     if ( listers )
01204       for ( kdl = tmpLst->first(); kdl; kdl = tmpLst->next() )
01205       {
01206         Q_ASSERT( listers->containsRef( kdl ) == 0 );
01207         listers->append( kdl );
01208       }
01209     else
01210     {
01211       listers = tmpLst;
01212       urlsCurrentlyHeld.insert( jobUrlStr, listers );
01213     }
01214   }
01215 
01216   // once we are updating dirs that are only in the cache this will fail!
01217   Q_ASSERT( listers );
01218 
01219   if ( job->error() )
01220   {
01221     for ( kdl = listers->first(); kdl; kdl = listers->next() )
01222     {
01223       kdl->jobDone(job);
01224       //don't bother the user
01225       //kdl->handleError( job );
01226 
01227       emit kdl->canceled( jobUrl );
01228       if ( kdl->numJobs() == 0 )
01229       {
01230         kdl->d->complete = true;
01231         emit kdl->canceled();
01232       }
01233     }
01234 
01235     jobs.remove( job );
01236 
01237     // TODO: if job is a parent of one or more
01238     // of the pending urls we should cancel them
01239     processPendingUpdates();
01240     return;
01241   }
01242 
01243   DirItem *dir = itemsInUse[jobUrlStr];
01244   dir->complete = true;
01245 
01246 
01247   // check if anyone wants the mimetypes immediately
01248   bool delayedMimeTypes = true;
01249   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01250     delayedMimeTypes &= kdl->d->delayedMimeTypes;
01251 
01252   // should be enough to get reasonable speed in most cases
01253   QDict<KFileItem> fileItems( 9973 );
01254 
01255   KFileItemListIterator kit ( *(dir->lstItems) );
01256 
01257   // Unmark all items in url
01258   for ( ; kit.current(); ++kit )
01259   {
01260     (*kit)->unmark();
01261     fileItems.insert( (*kit)->url().url(), *kit );
01262   }
01263 
01264   static const QString& dot = KGlobal::staticQString(".");
01265   static const QString& dotdot = KGlobal::staticQString("..");
01266 
01267   KFileItem *item, *tmp;
01268 
01269   QValueList<KIO::UDSEntry> buf = jobs[job];
01270   QValueListIterator<KIO::UDSEntry> it = buf.begin();
01271   for ( ; it != buf.end(); ++it )
01272   {
01273     QString name;
01274 
01275     // Find out about the name
01276     KIO::UDSEntry::Iterator it2 = (*it).begin();
01277     for ( ; it2 != (*it).end(); it2++ )
01278       if ( (*it2).m_uds == KIO::UDS_NAME )
01279       {
01280         name = (*it2).m_str;
01281         break;
01282       }
01283 
01284     Q_ASSERT( !name.isEmpty() );
01285 
01286     // we duplicate the check for dotdot here, to avoid iterating over
01287     // all items again and checking in matchesFilter() that way.
01288     if ( name.isEmpty() || name == dotdot )
01289       continue;
01290 
01291     if ( name == dot )
01292     {
01293       // if the update was started before finishing the original listing
01294       // there is no root item yet
01295       if ( !dir->rootItem )
01296       {
01297         dir->rootItem = new KFileItem( *it, jobUrl, delayedMimeTypes, true  );
01298 
01299         for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01300           if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl )
01301             kdl->d->rootFileItem = dir->rootItem;
01302       }
01303 
01304       continue;
01305     }
01306 
01307     // Form the complete url
01308     item = new KFileItem( *it, jobUrl, delayedMimeTypes, true  );
01309 
01310     QString url = item->url().url();
01311     //kdDebug(7004) << "slotUpdateResult : look for " << url << endl;
01312 
01313     // Find this item
01314     if ( (tmp = fileItems[url]) )
01315     {
01316       tmp->mark();
01317 
01318       // check if something changed for this file
01319       if ( !tmp->cmp( *item ) )
01320       {
01321         //kdDebug(7004) << "slotUpdateResult: file changed: " << tmp->name() << endl;
01322         tmp->assign( *item );
01323 
01324         for ( kdl = listers->first(); kdl; kdl = listers->next() )
01325           kdl->addRefreshItem( tmp );
01326       }
01327       delete item;  // gmbl, this is the most often case... IMPORTANT TODO: speed it up somehow!
01328     }
01329     else // this is a new file
01330     {
01331       //kdDebug(7004) << "slotUpdateResult: new file: " << name << endl;
01332 
01333       item->mark();
01334       dir->lstItems->append( item );
01335 
01336       for ( kdl = listers->first(); kdl; kdl = listers->next() )
01337         kdl->addNewItem( item );
01338     }
01339   }
01340 
01341   jobs.remove( job );
01342 
01343   deleteUnmarkedItems( listers, dir->lstItems );
01344 
01345   for ( kdl = listers->first(); kdl; kdl = listers->next() )
01346   {
01347     kdl->emitItems();
01348 
01349     kdl->jobDone(job);
01350 
01351     emit kdl->completed( jobUrl );
01352     if ( kdl->numJobs() == 0 )
01353     {
01354       kdl->d->complete = true;
01355       emit kdl->completed();
01356     }
01357   }
01358 
01359   // TODO: hmm, if there was an error and job is a parent of one or more
01360   // of the pending urls we should cancel it/them as well
01361   processPendingUpdates();
01362 }
01363 
01364 // private
01365 
01366 KIO::ListJob *KDirListerCache::jobForUrl(const QString& _url)
01367 {
01368   KIO::ListJob *job;
01369   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator it = jobs.begin();
01370   while ( it != jobs.end() )
01371   {
01372     job = it.key();
01373     if ( job->url().url(-1) == _url )
01374     {
01375        return job;
01376     }
01377     ++it;
01378   }
01379   return 0;
01380 }
01381 
01382 void KDirListerCache::killJob( KIO::ListJob *job )
01383 {
01384   jobs.remove( job );
01385   job->disconnect( this );
01386   job->kill();
01387 }
01388 
01389 void KDirListerCache::deleteUnmarkedItems( QPtrList<KDirLister> *listers, KFileItemList *lstItems )
01390 {
01391   // Find all unmarked items and delete them
01392   KFileItem* item;
01393   lstItems->first();
01394   while ( (item = lstItems->current()) )
01395     if ( !item->isMarked() )
01396     {
01397       //kdDebug() << k_funcinfo << item->name() << endl;
01398       for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
01399         kdl->emitDeleteItem( item );
01400 
01401       if ( item->isDir() )
01402         deleteDir( item->url() );
01403 
01404       // finally actually delete the item
01405       lstItems->take();
01406       delete item;
01407     }
01408     else
01409       lstItems->next();
01410 }
01411 
01412 void KDirListerCache::deleteDir( const KURL& dirUrl )
01413 {
01414   //kdDebug() << k_funcinfo << dirUrl.prettyURL() << endl;
01415   // unregister and remove the childs of the deleted item.
01416   // Idea: tell all the KDirListers that they should forget the dir
01417   //       and then remove it from the cache.
01418 
01419   QDictIterator<DirItem> itu( itemsInUse );
01420   while ( itu.current() )
01421   {
01422     KURL deletedUrl( itu.currentKey() );
01423     if ( dirUrl.isParentOf( deletedUrl ) )
01424     {
01425       // stop all jobs for deletedUrl
01426 
01427       QPtrList<KDirLister> *kdls = urlsCurrentlyListed[deletedUrl.url()];
01428       if ( kdls )  // yeah, I lack good names
01429       {
01430         // we need a copy because stop modifies the list
01431         kdls = new QPtrList<KDirLister>( *kdls );
01432         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01433           stop( kdl, deletedUrl );
01434 
01435         delete kdls;
01436       }
01437 
01438       // tell listers holding deletedUrl to forget about it
01439       // this will stop running updates for deletedUrl as well
01440 
01441       kdls = urlsCurrentlyHeld[deletedUrl.url()];
01442       if ( kdls )
01443       {
01444         // we need a copy because forgetDirs modifies the list
01445         kdls = new QPtrList<KDirLister>( *kdls );
01446 
01447         for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
01448         {
01449           // lister's root is the deleted item
01450           if ( kdl->d->url == deletedUrl )
01451           {
01452             // tell the view first. It might need the subdirs' items (which forgetDirs will delete)
01453             if ( kdl->d->rootFileItem )
01454               emit kdl->deleteItem( kdl->d->rootFileItem );
01455             forgetDirs( kdl );
01456             kdl->d->rootFileItem = 0;
01457           }
01458           else
01459           {
01460             bool treeview = kdl->d->lstDirs.count() > 1;
01461             if ( !treeview )
01462             {
01463               emit kdl->clear();
01464               kdl->d->lstDirs.clear();
01465             }
01466             else
01467               kdl->d->lstDirs.remove( kdl->d->lstDirs.find( deletedUrl ) );
01468 
01469             forgetDirs( kdl, deletedUrl, treeview );
01470           }
01471         }
01472 
01473         delete kdls;
01474       }
01475 
01476       // delete the entry for deletedUrl - should not be needed, it's in
01477       // items cached now
01478 
01479       DirItem *dir = itemsInUse.take( deletedUrl.url() );
01480       Q_ASSERT( !dir );
01481       if ( !dir ) // take didn't find it - move on
01482           ++itu;
01483     }
01484     else
01485       ++itu;
01486   }
01487 
01488   // remove the children from the cache
01489   removeDirFromCache( dirUrl );
01490 }
01491 
01492 void KDirListerCache::processPendingUpdates()
01493 {
01494   // TODO
01495 }
01496 
01497 #ifndef NDEBUG
01498 void KDirListerCache::printDebug()
01499 {
01500   kdDebug(7004) << "Items in use: " << endl;
01501   QDictIterator<DirItem> itu( itemsInUse );
01502   for ( ; itu.current() ; ++itu ) {
01503       kdDebug(7004) << "   " << itu.currentKey() << "  URL: " << itu.current()->url
01504                     << " rootItem: " << ( itu.current()->rootItem ? itu.current()->rootItem->url() : KURL() )
01505                     << " autoUpdates refcount: " << itu.current()->autoUpdates
01506                     << " complete: " << itu.current()->complete
01507                   << ( itu.current()->lstItems ? QString(" with %1 items.").arg(itu.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01508   }
01509 
01510   kdDebug(7004) << "urlsCurrentlyHeld: " << endl;
01511   QDictIterator< QPtrList<KDirLister> > it( urlsCurrentlyHeld );
01512   for ( ; it.current() ; ++it )
01513   {
01514     QString list;
01515     for ( QPtrListIterator<KDirLister> listit( *it.current() ); listit.current(); ++listit )
01516       list += " 0x" + QString::number( (long)listit.current(), 16 );
01517     kdDebug(7004) << "   " << it.currentKey() << "  " << it.current()->count() << " listers: " << list << endl;
01518   }
01519 
01520   kdDebug(7004) << "urlsCurrentlyListed: " << endl;
01521   QDictIterator< QPtrList<KDirLister> > it2( urlsCurrentlyListed );
01522   for ( ; it2.current() ; ++it2 )
01523   {
01524     QString list;
01525     for ( QPtrListIterator<KDirLister> listit( *it2.current() ); listit.current(); ++listit )
01526       list += " 0x" + QString::number( (long)listit.current(), 16 );
01527     kdDebug(7004) << "   " << it2.currentKey() << "  " << it2.current()->count() << " listers: " << list << endl;
01528   }
01529 
01530   QMap< KIO::ListJob *, QValueList<KIO::UDSEntry> >::Iterator jit = jobs.begin();
01531   kdDebug(7004) << "Jobs: " << endl;
01532   for ( ; jit != jobs.end() ; ++jit )
01533     kdDebug(7004) << "   " << jit.key() << " listing " << jit.key()->url().prettyURL() << ": " << (*jit).count() << " entries." << endl;
01534 
01535   kdDebug(7004) << "Items in cache: " << endl;
01536   QCacheIterator<DirItem> itc( itemsCached );
01537   for ( ; itc.current() ; ++itc )
01538     kdDebug(7004) << "   " << itc.currentKey() << "  rootItem: "
01539                   << ( itc.current()->rootItem ? itc.current()->rootItem->url().prettyURL() : QString("NULL") )
01540                   << ( itc.current()->lstItems ? QString(" with %1 items.").arg(itc.current()->lstItems->count()) : QString(" lstItems=NULL") ) << endl;
01541 }
01542 #endif
01543 
01544 /*********************** -- The new KDirLister -- ************************/
01545 
01546 
01547 KDirLister::KDirLister( bool _delayedMimeTypes )
01548 {
01549   kdDebug(7003) << "+KDirLister" << endl;
01550 
01551   d = new KDirListerPrivate;
01552 
01553   d->complete = true;
01554   d->delayedMimeTypes = _delayedMimeTypes;
01555 
01556   setAutoUpdate( true );
01557   setDirOnlyMode( false );
01558   setShowingDotFiles( false );
01559 
01560   setAutoErrorHandlingEnabled( true, 0 );
01561 }
01562 
01563 KDirLister::~KDirLister()
01564 {
01565   kdDebug(7003) << "-KDirLister" << endl;
01566 
01567   // Stop all running jobs
01568   stop();
01569   s_pCache->forgetDirs( this );
01570 
01571   delete d;
01572 }
01573 
01574 bool KDirLister::openURL( const KURL& _url, bool _keep, bool _reload )
01575 {
01576   if ( !validURL( _url ) )
01577     return false;
01578 
01579   kdDebug(7003) << k_funcinfo << _url.prettyURL()
01580                 << " keep=" << _keep << " reload=" << _reload << endl;
01581 
01582   // emit the current changes made to avoid an inconsistent treeview
01583   if ( d->changes != NONE && _keep )
01584     emitChanges();
01585 
01586   d->changes = NONE;
01587 
01588   s_pCache->listDir( this, _url, _keep, _reload );
01589 
01590   return true;
01591 }
01592 
01593 void KDirLister::stop()
01594 {
01595   kdDebug(7003) << k_funcinfo << endl;
01596   s_pCache->stop( this );
01597 }
01598 
01599 void KDirLister::stop( const KURL& _url )
01600 {
01601   kdDebug(7003) << k_funcinfo << _url.prettyURL() << endl;
01602   s_pCache->stop( this, _url );
01603 }
01604 
01605 bool KDirLister::autoUpdate() const
01606 {
01607   return d->autoUpdate;
01608 }
01609 
01610 void KDirLister::setAutoUpdate( bool _enable )
01611 {
01612   if ( d->autoUpdate == _enable )
01613     return;
01614 
01615   d->autoUpdate = _enable;
01616   s_pCache->setAutoUpdate( this, _enable );
01617 }
01618 
01619 bool KDirLister::showingDotFiles() const
01620 {
01621   return d->isShowingDotFiles;
01622 }
01623 
01624 void KDirLister::setShowingDotFiles( bool _showDotFiles )
01625 {
01626   if ( d->isShowingDotFiles == _showDotFiles )
01627     return;
01628 
01629   d->isShowingDotFiles = _showDotFiles;
01630   d->changes ^= DOT_FILES;
01631 }
01632 
01633 bool KDirLister::dirOnlyMode() const
01634 {
01635   return d->dirOnlyMode;
01636 }
01637 
01638 void KDirLister::setDirOnlyMode( bool _dirsOnly )
01639 {
01640   if ( d->dirOnlyMode == _dirsOnly )
01641     return;
01642 
01643   d->dirOnlyMode = _dirsOnly;
01644   d->changes ^= DIR_ONLY_MODE;
01645 }
01646 
01647 bool KDirLister::autoErrorHandlingEnabled() const
01648 {
01649   return d->autoErrorHandling;
01650 }
01651 
01652 void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent )
01653 {
01654   d->autoErrorHandling = enable;
01655   d->errorParent = parent;
01656 }
01657 
01658 const KURL& KDirLister::url() const
01659 {
01660   return d->url;
01661 }
01662 
01663 void KDirLister::emitChanges()
01664 {
01665   if ( d->changes == NONE )
01666     return;
01667 
01668   static const QString& dot = KGlobal::staticQString(".");
01669   static const QString& dotdot = KGlobal::staticQString("..");
01670 
01671   for ( KURL::List::Iterator it = d->lstDirs.begin();
01672         it != d->lstDirs.end(); ++it )
01673   {
01674     KFileItemListIterator kit( *s_pCache->itemsForDir( *it ) );
01675     for ( ; kit.current(); ++kit )
01676     {
01677       if ( (*kit)->text() == dot || (*kit)->text() == dotdot )
01678         continue;
01679 
01680       bool oldMime = true, newMime = true;
01681 
01682       if ( d->changes & MIME_FILTER )
01683       {
01684         oldMime = doMimeFilter( (*kit)->mimetype(), d->oldMimeFilter )
01685                 && doMimeExcludeFilter( (*kit)->mimetype(), d->oldMimeExcludeFilter );
01686         newMime = doMimeFilter( (*kit)->mimetype(), d->mimeFilter )
01687                 && doMimeExcludeFilter( (*kit)->mimetype(), d->mimeExcludeFilter );
01688 
01689         if ( oldMime && !newMime )
01690         {
01691           emit deleteItem( *kit );
01692           continue;
01693         }
01694       }
01695 
01696       if ( d->changes & DIR_ONLY_MODE )
01697       {
01698         // the lister switched to dirOnlyMode
01699         if ( d->dirOnlyMode )
01700         {
01701           if ( !(*kit)->isDir() )
01702             emit deleteItem( *kit );
01703         }
01704         else if ( !(*kit)->isDir() )
01705           addNewItem( *kit );
01706 
01707         continue;
01708       }
01709 
01710       if ( (*kit)->isHidden() )
01711       {
01712         if ( d->changes & DOT_FILES )
01713         {
01714           // the lister switched to dot files mode
01715           if ( d->isShowingDotFiles )
01716             addNewItem( *kit );
01717           else
01718             emit deleteItem( *kit );
01719 
01720           continue;
01721         }
01722       }
01723       else if ( d->changes & NAME_FILTER )
01724       {
01725         bool oldName = (*kit)->isDir() ||
01726                        d->oldFilters.isEmpty() ||
01727                        doNameFilter( (*kit)->text(), d->oldFilters );
01728 
01729         bool newName = (*kit)->isDir() ||
01730                        d->lstFilters.isEmpty() ||
01731                        doNameFilter( (*kit)->text(), d->lstFilters );
01732 
01733         if ( oldName && !newName )
01734         {
01735           emit deleteItem( *kit );
01736           continue;
01737         }
01738         else if ( !oldName && newName )
01739           addNewItem( *kit );
01740       }
01741 
01742       if ( (d->changes & MIME_FILTER) && !oldMime && newMime )
01743         addNewItem( *kit );
01744     }
01745 
01746     emitItems();
01747   }
01748 
01749   d->changes = NONE;
01750 }
01751 
01752 void KDirLister::updateDirectory( const KURL& _u )
01753 {
01754   s_pCache->updateDirectory( _u );
01755 }
01756 
01757 bool KDirLister::isFinished() const
01758 {
01759   return d->complete;
01760 }
01761 
01762 KFileItem* KDirLister::rootItem() const
01763 {
01764   return d->rootFileItem;
01765 }
01766 
01767 KFileItem* KDirLister::findByURL( const KURL& _url ) const
01768 {
01769   return s_pCache->findByURL( this, _url );
01770 }
01771 
01772 KFileItem* KDirLister::findByName( const QString& _name ) const
01773 {
01774   return s_pCache->findByName( this, _name );
01775 }
01776 
01777 #ifndef KDE_NO_COMPAT
01778 KFileItem* KDirLister::find( const KURL& _url ) const
01779 {
01780   return findByURL( _url );
01781 }
01782 #endif
01783 
01784 
01785 // ================ public filter methods ================ //
01786 
01787 void KDirLister::setNameFilter( const QString& nameFilter )
01788 {
01789   if ( !(d->changes & NAME_FILTER) )
01790   {
01791     d->oldFilters = d->lstFilters;
01792     d->lstFilters.setAutoDelete( false );
01793   }
01794 
01795   d->lstFilters.clear();
01796   d->lstFilters.setAutoDelete( true );
01797 
01798   d->nameFilter = nameFilter;
01799 
01800   // Split on white space
01801   QStringList list = QStringList::split( ' ', nameFilter );
01802   for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
01803     d->lstFilters.append( new QRegExp(*it, false, true ) );
01804 
01805   d->changes |= NAME_FILTER;
01806 }
01807 
01808 const QString& KDirLister::nameFilter() const
01809 {
01810   return d->nameFilter;
01811 }
01812 
01813 void KDirLister::setMimeFilter( const QStringList& mimeFilter )
01814 {
01815   if ( !(d->changes & MIME_FILTER) )
01816     d->oldMimeFilter = d->mimeFilter;
01817 
01818   if (mimeFilter.find ("all/allfiles") != mimeFilter.end () ||
01819       mimeFilter.find ("all/all") != mimeFilter.end ())
01820     d->mimeFilter.clear ();
01821   else
01822     d->mimeFilter = mimeFilter;
01823 
01824   d->changes |= MIME_FILTER;
01825 }
01826 
01827 void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter )
01828 {
01829   if ( !(d->changes & MIME_FILTER) )
01830     d->oldMimeExcludeFilter = d->mimeExcludeFilter;
01831 
01832   d->mimeExcludeFilter = mimeExcludeFilter;
01833   d->changes |= MIME_FILTER;
01834 }
01835 
01836 
01837 void KDirLister::clearMimeFilter()
01838 {
01839   if ( !(d->changes & MIME_FILTER) )
01840   {
01841        d->oldMimeFilter = d->mimeFilter;
01842        d->oldMimeExcludeFilter = d->mimeExcludeFilter;
01843   }
01844   d->mimeFilter.clear();
01845   d->mimeExcludeFilter.clear();
01846   d->changes |= MIME_FILTER;
01847 }
01848 
01849 const QStringList& KDirLister::mimeFilters() const
01850 {
01851   return d->mimeFilter;
01852 }
01853 
01854 bool KDirLister::matchesFilter( const QString& name ) const
01855 {
01856   return doNameFilter( name, d->lstFilters );
01857 }
01858 
01859 bool KDirLister::matchesMimeFilter( const QString& mime ) const
01860 {
01861   return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter);
01862 }
01863 
01864 // ================ protected methods ================ //
01865 
01866 bool KDirLister::matchesFilter( const KFileItem *item ) const
01867 {
01868   Q_ASSERT( item );
01869   static const QString& dotdot = KGlobal::staticQString("..");
01870 
01871   if ( item->text() == dotdot )
01872     return false;
01873 
01874   if ( !d->isShowingDotFiles && item->text()[0] == '.' )
01875     return false;
01876 
01877   if ( item->isDir() || d->lstFilters.isEmpty() )
01878     return true;
01879 
01880   return matchesFilter( item->text() );
01881 }
01882 
01883 bool KDirLister::matchesMimeFilter( const KFileItem *item ) const
01884 {
01885   Q_ASSERT( item );
01886   return matchesMimeFilter( item->mimetype() );
01887 }
01888 
01889 bool KDirLister::doNameFilter( const QString& name, const QPtrList<QRegExp>& filters ) const
01890 {
01891   for ( QPtrListIterator<QRegExp> it( filters ); it.current(); ++it )
01892     if ( it.current()->exactMatch( name ) )
01893       return true;
01894 
01895   return false;
01896 }
01897 
01898 bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const
01899 {
01900   if ( filters.isEmpty() )
01901     return true;
01902 
01903   KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
01904   QStringList::ConstIterator it = filters.begin();
01905   for ( ; it != filters.end(); ++it )
01906     if ( mimeptr->is(*it) )
01907       return true;
01908 
01909   return false;
01910 }
01911 
01912 bool KDirLister::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const
01913 {
01914   if ( filters.isEmpty() )
01915     return true;
01916 
01917   QStringList::ConstIterator it = filters.begin();
01918   for ( ; it != filters.end(); ++it )
01919     if ( (*it) == mime )
01920       return false;
01921 
01922   return true;
01923 }
01924 
01925 
01926 bool KDirLister::validURL( const KURL& _url ) const
01927 {
01928   if ( !_url.isValid() )
01929   {
01930     if ( d->autoErrorHandling )
01931     {
01932       QString tmp = i18n("Malformed URL\n%1").arg( _url.prettyURL() );
01933       KMessageBox::error( d->errorParent, tmp );
01934     }
01935     return false;
01936   }
01937 
01938   // TODO: verify that this is really a directory?
01939 
01940   return true;
01941 }
01942 
01943 void KDirLister::handleError( KIO::Job *job )
01944 {
01945   if ( d->autoErrorHandling )
01946     job->showErrorDialog( d->errorParent );
01947 }
01948 
01949 
01950 // ================= private methods ================= //
01951 
01952 void KDirLister::addNewItem( const KFileItem *item )
01953 {
01954   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
01955   if (isNameFilterMatch)
01956      return; // No reason to continue... bailing out here prevents a mimetype scan.
01957 
01958   bool isMimeFilterMatch = !matchesMimeFilter( item );
01959 
01960   if ( !isNameFilterMatch && !isMimeFilterMatch )
01961   {
01962     if ( !d->lstNewItems )
01963       d->lstNewItems = new KFileItemList;
01964 
01965     d->lstNewItems->append( item );            // items not filtered
01966   }
01967   else if ( !isNameFilterMatch )
01968   {
01969     if ( !d->lstMimeFilteredItems )
01970       d->lstMimeFilteredItems = new KFileItemList;
01971 
01972     d->lstMimeFilteredItems->append( item );   // only filtered by mime
01973   }
01974 }
01975 
01976 void KDirLister::addNewItems( const KFileItemList& items )
01977 {
01978   // TODO: make this faster - test if we have a filter at all first
01979   for ( KFileItemListIterator kit( items ); kit.current(); ++kit )
01980     addNewItem( *kit );
01981 }
01982 
01983 void KDirLister::addRefreshItem( const KFileItem *item )
01984 {
01985   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
01986   bool isMimeFilterMatch = !matchesMimeFilter( item );
01987 
01988   if ( !isNameFilterMatch && !isMimeFilterMatch )
01989   {
01990     if ( !d->lstRefreshItems )
01991       d->lstRefreshItems = new KFileItemList;
01992 
01993     d->lstRefreshItems->append( item );
01994   } else {
01995     if ( !d->lstRemoveItems )
01996       d->lstRemoveItems = new KFileItemList;
01997 
01998       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
01999   }
02000 }
02001 
02002 void KDirLister::emitItems()
02003 {
02004   KFileItemList *tmpNew = d->lstNewItems;
02005   d->lstNewItems = 0;
02006 
02007   KFileItemList *tmpMime = d->lstMimeFilteredItems;
02008   d->lstMimeFilteredItems = 0;
02009 
02010   KFileItemList *tmpRefresh = d->lstRefreshItems;
02011   d->lstRefreshItems = 0;
02012 
02013   KFileItemList *tmpRemove = d->lstRemoveItems;
02014   d->lstRemoveItems = 0;
02015 
02016   if ( tmpNew )
02017   {
02018     emit newItems( *tmpNew );
02019     delete tmpNew;
02020   }
02021 
02022   if ( tmpMime )
02023   {
02024     emit itemsFilteredByMime( *tmpMime );
02025     delete tmpMime;
02026   }
02027 
02028   if ( tmpRefresh )
02029   {
02030     emit refreshItems( *tmpRefresh );
02031     delete tmpRefresh;
02032   }
02033 
02034   if ( tmpRemove )
02035   {
02036     for ( KFileItem *tmp = tmpRemove->first(); tmp; tmp = tmpRemove->next() )
02037       emit deleteItem( tmp );
02038     delete tmpRemove;
02039   }
02040 }
02041 
02042 void KDirLister::emitDeleteItem( KFileItem *item )
02043 {
02044   bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
02045   bool isMimeFilterMatch = !matchesMimeFilter( item );
02046 
02047   if ( !isNameFilterMatch && !isMimeFilterMatch )
02048     emit deleteItem( item );
02049 }
02050 
02051 
02052 // ================ private slots ================ //
02053 
02054 void KDirLister::slotInfoMessage( KIO::Job *, const QString& message )
02055 {
02056   emit infoMessage( message );
02057 }
02058 
02059 void KDirLister::slotPercent( KIO::Job *job, unsigned long pcnt )
02060 {
02061   d->jobData[static_cast<KIO::ListJob*>(job)].percent = pcnt;
02062 
02063   int result = 0;
02064 
02065   KIO::filesize_t size = 0;
02066 
02067   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02068   while ( dataIt != d->jobData.end() )
02069   {
02070     result += (*dataIt).percent * (*dataIt).totalSize;
02071     size += (*dataIt).totalSize;
02072     ++dataIt;
02073   }
02074 
02075   if ( size != 0 )
02076     result /= size;
02077   else
02078     result = 100;
02079   emit percent( result );
02080 }
02081 
02082 void KDirLister::slotTotalSize( KIO::Job *job, KIO::filesize_t size )
02083 {
02084   d->jobData[static_cast<KIO::ListJob*>(job)].totalSize = size;
02085 
02086   KIO::filesize_t result = 0;
02087   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02088   while ( dataIt != d->jobData.end() )
02089   {
02090     result += (*dataIt).totalSize;
02091     ++dataIt;
02092   }
02093 
02094   emit totalSize( result );
02095 }
02096 
02097 void KDirLister::slotProcessedSize( KIO::Job *job, KIO::filesize_t size )
02098 {
02099   d->jobData[static_cast<KIO::ListJob*>(job)].processedSize = size;
02100 
02101   KIO::filesize_t result = 0;
02102   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02103   while ( dataIt != d->jobData.end() )
02104   {
02105     result += (*dataIt).processedSize;
02106     ++dataIt;
02107   }
02108 
02109   emit processedSize( result );
02110 }
02111 
02112 void KDirLister::slotSpeed( KIO::Job *job, unsigned long spd )
02113 {
02114   d->jobData[static_cast<KIO::ListJob*>(job)].speed = spd;
02115 
02116   int result = 0;
02117   QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
02118   while ( dataIt != d->jobData.end() )
02119   {
02120     result += (*dataIt).speed;
02121     ++dataIt;
02122   }
02123 
02124   emit speed( result );
02125 }
02126 
02127 uint KDirLister::numJobs()
02128 {
02129   return d->jobData.count();
02130 }
02131 
02132 void KDirLister::jobDone(KIO::ListJob *job)
02133 {
02134   if (job)
02135      d->jobData.remove(job);
02136 }
02137 
02138 void KDirLister::jobStarted(KIO::ListJob *job)
02139 {
02140   KDirListerPrivate::JobData jobData;
02141   jobData.speed = 0;
02142   jobData.percent = 0;
02143   jobData.processedSize = 0;
02144   jobData.totalSize = 0;
02145 
02146   d->jobData.insert(job, jobData);
02147 }
02148 
02149 void KDirLister::setMainWindow(QWidget *window)
02150 {
02151   d->window = window;
02152 }
02153 
02154 QWidget *KDirLister::mainWindow()
02155 {
02156   return d->window;
02157 }
02158 
02159 KFileItemList KDirLister::items( WhichItems which ) const
02160 {
02161     return itemsForDir( url(), which );
02162 }
02163 
02164 KFileItemList KDirLister::itemsForDir( const KURL &dir, WhichItems which) const
02165 {
02166     KFileItemList result;
02167     KFileItemList *allItems = s_pCache->itemsForDir( dir );
02168     if ( !allItems )
02169         return result;
02170 
02171     if ( which == AllItems )
02172         result = *allItems; // shallow copy
02173 
02174     else // only items passing the filters
02175     {
02176         for ( KFileItemListIterator kit( *allItems ); kit.current(); ++kit )
02177         {
02178             KFileItem *item = *kit;
02179             bool isNameFilterMatch = (d->dirOnlyMode && !item->isDir()) ||
02180                                      !matchesFilter( item );
02181             bool isMimeFilterMatch = !matchesMimeFilter( item );
02182 
02183             if ( !isNameFilterMatch && !isMimeFilterMatch )
02184                 result.append( item );
02185         }
02186     }
02187 
02188     return result;
02189 }
02190 
02191 // to keep BC changes
02192 
02193 void KDirLister::virtual_hook( int, void* )
02194 { /*BASE::virtual_hook( id, data );*/ }
02195 
02196 #include "kdirlister.moc"
02197 #include "kdirlister_p.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Feb 18 15:10:41 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003