kio Library: kdirlister.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kdirlister.cpp

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

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:23:56 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003