You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdeio/tdeio/kdirlister.cpp

2751 lines
78 KiB

/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
2000 Carsten Pfeiffer <pfeiffer@kde.org>
2001-2005 Michael Brade <brade@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kdirlister.h"
#include <tqregexp.h>
#include <tqptrlist.h>
#include <tqtimer.h>
#include <tqeventloop.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <tdeio/job.h>
#include <tdemessagebox.h>
#include <tdeglobal.h>
#include <tdeglobalsettings.h>
#include <kstaticdeleter.h>
#include <kprotocolinfo.h>
#include "kdirlister_p.h"
#include <assert.h>
#include <unistd.h>
KDirListerCache* KDirListerCache::s_pSelf = 0;
static KStaticDeleter<KDirListerCache> sd_KDirListerCache;
// Enable this to get printDebug() called often, to see the contents of the cache
//#define DEBUG_CACHE
// Make really sure it doesn't get activated in the final build
#ifdef NDEBUG
#undef DEBUG_CACHE
#endif
KDirListerCache::KDirListerCache( int maxCount )
: itemsCached( maxCount )
{
kdDebug(7004) << "+KDirListerCache" << endl;
itemsInUse.setAutoDelete( false );
itemsCached.setAutoDelete( true );
urlsCurrentlyListed.setAutoDelete( true );
urlsCurrentlyHeld.setAutoDelete( true );
pendingUpdates.setAutoDelete( true );
connect( kdirwatch, TQ_SIGNAL( dirty( const KURL& ) ),
this, TQ_SLOT( slotFileDirty( const KURL& ) ) );
connect( kdirwatch, TQ_SIGNAL( created( const TQString& ) ),
this, TQ_SLOT( slotFileCreated( const TQString& ) ) );
connect( kdirwatch, TQ_SIGNAL( deleted( const TQString& ) ),
this, TQ_SLOT( slotFileDeleted( const TQString& ) ) );
}
KDirListerCache::~KDirListerCache()
{
kdDebug(7004) << "-KDirListerCache" << endl;
itemsInUse.setAutoDelete( true );
itemsInUse.clear();
itemsCached.clear();
urlsCurrentlyListed.clear();
urlsCurrentlyHeld.clear();
if ( KDirWatch::exists() )
kdirwatch->disconnect( this );
}
// setting _reload to true will emit the old files and
// call updateDirectory
bool KDirListerCache::listDir( KDirLister *lister, const KURL& _u,
bool _keep, bool _reload )
{
// like this we don't have to worry about trailing slashes any further
KURL _url = _u;
_url.cleanPath(); // kill consecutive slashes
_url.adjustPath(-1);
TQString urlStr = _url.url();
TQString urlReferenceStr = _url.internalReferenceURL();
if ( !lister->validURL( _url ) ) {
return false;
}
#ifdef DEBUG_CACHE
printDebug();
#endif
kdDebug(7004) << k_funcinfo << lister << " url=" << _url
<< " keep=" << _keep << " reload=" << _reload << endl;
if ( !_keep )
{
// stop any running jobs for lister
stop( lister );
// clear our internal list for lister
forgetDirs( lister );
lister->d->rootFileItem = 0;
}
else if ( lister->d->lstDirs.find( _url ) != lister->d->lstDirs.end() )
{
// stop the job listing _url for this lister
stop( lister, _url );
// clear _url for lister
forgetDirs( lister, _url, true );
if ( lister->d->url == _url )
lister->d->rootFileItem = 0;
}
lister->d->lstDirs.append( _url );
if ( lister->d->url.isEmpty() || !_keep ) // set toplevel URL only if not set yet
lister->d->url = _url;
DirItem *itemU = itemsInUse[urlStr + ":" + urlReferenceStr];
DirItem *itemC;
if ( !urlsCurrentlyListed[urlStr + ":" + urlReferenceStr] )
{
// if there is an update running for _url already we get into
// the following case - it will just be restarted by updateDirectory().
if ( itemU )
{
kdDebug(7004) << "listDir: Entry already in use: " << _url << endl;
bool oldState = lister->d->complete;
lister->d->complete = false;
emit lister->started( _url );
if ( !lister->d->rootFileItem && lister->d->url == _url ) {
lister->d->rootFileItem = itemU->rootItem;
}
lister->addNewItems( *(itemU->lstItems) );
lister->emitItems();
// _url is already in use, so there is already an entry in urlsCurrentlyHeld
assert( urlsCurrentlyHeld[urlStr + ":" + urlReferenceStr] );
urlsCurrentlyHeld[urlStr + ":" + urlReferenceStr]->append( lister );
lister->d->complete = oldState;
lister->emitCompleted( _url );
if ( lister->d->complete ) {
emit lister->completed();
}
if ( _reload || !itemU->complete ) {
updateDirectory( _url );
}
}
else if ( !_reload && (itemC = itemsCached.take( urlStr )) )
{
kdDebug(7004) << "listDir: Entry in cache: " << _url << endl;
itemC->decAutoUpdate();
itemsInUse.insert( urlStr + ":" + urlReferenceStr, itemC );
itemU = itemC;
bool oldState = lister->d->complete;
lister->d->complete = false;
emit lister->started( _url );
if ( !lister->d->rootFileItem && lister->d->url == _url ) {
lister->d->rootFileItem = itemC->rootItem;
}
lister->addNewItems( *(itemC->lstItems) );
lister->emitItems();
Q_ASSERT( !urlsCurrentlyHeld[urlStr + ":" + urlReferenceStr] );
TQPtrList<KDirLister> *list = new TQPtrList<KDirLister>;
list->append( lister );
urlsCurrentlyHeld.insert( urlStr + ":" + urlReferenceStr, list );
lister->d->complete = oldState;
lister->emitCompleted( _url );
if ( lister->d->complete ) {
emit lister->completed();
}
if ( !itemC->complete ) {
updateDirectory( _url );
}
}
else // dir not in cache or _reload is true
{
kdDebug(7004) << "listDir: Entry not in cache or reloaded: " << _url << endl;
TQPtrList<KDirLister> *list = new TQPtrList<KDirLister>;
list->append( lister );
urlsCurrentlyListed.insert( urlStr + ":" + urlReferenceStr, list );
itemsCached.remove( urlStr );
itemU = new DirItem( _url );
itemsInUse.insert( urlStr + ":" + urlReferenceStr, itemU );
// // we have a limit of MAX_JOBS_PER_LISTER concurrently running jobs
// if ( lister->numJobs() >= MAX_JOBS_PER_LISTER )
// {
// lstPendingUpdates.append( _url );
// }
// else
// {
if ( lister->d->url == _url ) {
lister->d->rootFileItem = 0;
}
TDEIO::ListJob* job = TDEIO::listDir( _url, false /* no default GUI */ );
jobs.insert( job, TQValueList<TDEIO::UDSEntry>() );
lister->jobStarted( job );
lister->connectJob( job );
if ( lister->d->window ) {
job->setWindow( lister->d->window );
}
connect( job, TQ_SIGNAL( entries( TDEIO::Job *, const TDEIO::UDSEntryList & ) ),
this, TQ_SLOT( slotEntries( TDEIO::Job *, const TDEIO::UDSEntryList & ) ) );
connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ),
this, TQ_SLOT( slotResult( TDEIO::Job * ) ) );
connect( job, TQ_SIGNAL( redirection( TDEIO::Job *, const KURL & ) ),
this, TQ_SLOT( slotRedirection( TDEIO::Job *, const KURL & ) ) );
emit lister->started( _url );
// }
}
}
else
{
kdDebug(7004) << "listDir: Entry currently being listed: " << _url << endl;
emit lister->started( _url );
urlsCurrentlyListed[urlStr + ":" + urlReferenceStr]->append( lister );
TDEIO::ListJob *job = jobForUrl( urlStr + ":" + urlReferenceStr );
Q_ASSERT( job );
lister->jobStarted( job );
lister->connectJob( job );
Q_ASSERT( itemU );
if ( !lister->d->rootFileItem && lister->d->url == _url ) {
lister->d->rootFileItem = itemU->rootItem;
}
lister->addNewItems( *(itemU->lstItems) );
lister->emitItems();
}
// automatic updating of directories
if ( lister->d->autoUpdate ) {
itemU->incAutoUpdate();
}
return true;
}
bool KDirListerCache::validURL( const KDirLister *lister, const KURL& url ) const
{
if ( !url.isValid() )
{
if ( lister->d->autoErrorHandling )
{
TQString tmp = i18n("Malformed URL\n%1").arg( url.prettyURL() );
KMessageBox::error( lister->d->errorParent, tmp );
}
return false;
}
if ( !KProtocolInfo::supportsListing( url ) )
{
if ( lister->d->autoErrorHandling )
{
// TODO: this message should be changed during next string unfreeze!
TQString tmp = i18n("Malformed URL\n%1").arg( url.prettyURL() );
KMessageBox::error( lister->d->errorParent, tmp );
}
return false;
}
return true;
}
void KDirListerCache::stop( KDirLister *lister )
{
#ifdef DEBUG_CACHE
printDebug();
#endif
kdDebug(7004) << k_funcinfo << "lister: " << lister << endl;
bool stopped = false;
TQDictIterator< TQPtrList<KDirLister> > it( urlsCurrentlyListed );
TQPtrList<KDirLister> *listers;
int curIndex;
while ( (listers = it.current()) )
{
curIndex = listers->findRef( lister );
if ( curIndex > -1 )
{
// lister is listing url
TQString url = it.currentKey();
KDirLister* curLister = listers->at( curIndex );
//kdDebug(7004) << k_funcinfo << " found lister in list - for " << url << endl;
bool ret = listers->removeRef( lister );
Q_ASSERT( ret );
TDEIO::ListJob *job = jobForUrl( url );
if ( job ) {
lister->jobDone( job );
}
// move lister to urlsCurrentlyHeld
TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[url];
if ( !holders )
{
holders = new TQPtrList<KDirLister>;
urlsCurrentlyHeld.insert( url, holders );
}
holders->append( lister );
emit lister->canceled( curLister->d->url );
//kdDebug(7004) << k_funcinfo << "remaining list: " << listers->count() << " listers" << endl;
if ( listers->isEmpty() )
{
// kill the job since it isn't used any more
if ( job ) {
killJob( job );
}
urlsCurrentlyListed.remove( url );
}
stopped = true;
}
else
++it;
}
if ( stopped )
{
emit lister->canceled();
lister->d->complete = true;
}
// this is wrong if there is still an update running!
//Q_ASSERT( lister->d->complete );
}
void KDirListerCache::stop( KDirLister *lister, const KURL& _u )
{
TQString urlStr( _u.url(-1) );
TQString urlReferenceStr = _u.internalReferenceURL();
KURL _url( urlStr );
// TODO: consider to stop all the "child jobs" of _url as well
kdDebug(7004) << k_funcinfo << lister << " url=" << _url << endl;
TQPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr + ":" + urlReferenceStr];
if ( !listers || !listers->removeRef( lister ) )
return;
// move lister to urlsCurrentlyHeld
TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr + ":" + urlReferenceStr];
if ( !holders )
{
holders = new TQPtrList<KDirLister>;
urlsCurrentlyHeld.insert( urlStr + ":" + urlReferenceStr, holders );
}
holders->append( lister );
TDEIO::ListJob *job = jobForUrl( urlStr + ":" + urlReferenceStr );
if ( job ) {
lister->jobDone( job );
}
emit lister->canceled( _url );
if ( listers->isEmpty() )
{
// kill the job since it isn't used any more
if ( job )
killJob( job );
urlsCurrentlyListed.remove( urlStr + ":" + urlReferenceStr );
}
if ( lister->numJobs() == 0 )
{
lister->d->complete = true;
// we killed the last job for lister
emit lister->canceled();
}
}
void KDirListerCache::setAutoUpdate( KDirLister *lister, bool enable )
{
// IMPORTANT: this method does not check for the current autoUpdate state!
for ( KURL::List::Iterator it = lister->d->lstDirs.begin();
it != lister->d->lstDirs.end(); ++it )
{
if ( enable ) {
itemsInUse[(*it).url() + ":" + (*it).internalReferenceURL()]->incAutoUpdate();
}
else {
itemsInUse[(*it).url() + ":" + (*it).internalReferenceURL()]->decAutoUpdate();
}
}
}
void KDirListerCache::forgetDirs( KDirLister *lister )
{
kdDebug(7004) << k_funcinfo << lister << endl;
emit lister->clear();
// forgetDirs() will modify lstDirs, make a copy first
KURL::List lstDirsCopy = lister->d->lstDirs;
for ( KURL::List::Iterator it = lstDirsCopy.begin();
it != lstDirsCopy.end(); ++it )
{
forgetDirs( lister, *it, false );
}
}
void KDirListerCache::forgetDirs( KDirLister *lister, const KURL& _url, bool notify )
{
kdDebug(7004) << k_funcinfo << lister << " _url: " << _url << endl;
KURL url( _url );
url.adjustPath( -1 );
TQString urlStr = url.url();
TQString urlReferenceStr = url.internalReferenceURL();
TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr + ":" + urlReferenceStr];
//Q_ASSERT( holders );
if ( holders )
{
holders->removeRef( lister );
}
// remove the dir from lister->d->lstDirs so that it doesn't contain things
// that itemsInUse doesn't. When emitting the canceled signals lstDirs must
// not contain anything that itemsInUse does not contain. (otherwise it
// might crash in findByName()).
lister->d->lstDirs.remove( lister->d->lstDirs.find( url ) );
DirItem *item = itemsInUse[urlStr + ":" + urlReferenceStr];
if ( holders && holders->isEmpty() )
{
urlsCurrentlyHeld.remove( urlStr + ":" + urlReferenceStr ); // this deletes the (empty) holders list
if ( !urlsCurrentlyListed[urlStr + ":" + urlReferenceStr] )
{
// item not in use anymore -> move into cache if complete
itemsInUse.remove( urlStr + ":" + urlReferenceStr );
// this job is a running update
TDEIO::ListJob *job = jobForUrl( urlStr + ":" + urlReferenceStr );
if ( job )
{
lister->jobDone( job );
killJob( job );
kdDebug(7004) << k_funcinfo << "Killing update job for " << urlStr << endl;
emit lister->canceled( url );
if ( lister->numJobs() == 0 )
{
lister->d->complete = true;
emit lister->canceled();
}
}
if ( notify )
emit lister->clear( url );
if ( item && item->complete )
{
kdDebug(7004) << k_funcinfo << lister << " item moved into cache: " << url << endl;
itemsCached.insert( urlStr, item ); // TODO: may return false!!
// Should we forget the dir for good, or keep a watch on it?
// Generally keep a watch, except when it would prevent
// unmounting a removable device (#37780)
const bool isLocal = item->url.isLocalFile();
const bool isManuallyMounted = isLocal && TDEIO::manually_mounted( item->url.path() );
bool containsManuallyMounted = false;
if ( !isManuallyMounted && item->lstItems && isLocal )
{
// Look for a manually-mounted directory inside
// If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM
// I hope this isn't too slow (manually_mounted caches the last device so most
// of the time this is just a stat per subdir)
KFileItemListIterator kit( *item->lstItems );
for ( ; kit.current() && !containsManuallyMounted; ++kit )
if ( (*kit)->isDir() && TDEIO::manually_mounted( (*kit)->url().path() ) )
containsManuallyMounted = true;
}
if ( isManuallyMounted || containsManuallyMounted )
{
kdDebug(7004) << "Not adding a watch on " << item->url << " because it " <<
( isManuallyMounted ? "is manually mounted" : "contains a manually mounted subdir" ) << endl;
item->complete = false; // set to "dirty"
}
else
item->incAutoUpdate(); // keep watch
}
else
{
delete item;
item = 0;
}
}
}
if ( item && lister->d->autoUpdate )
item->decAutoUpdate();
}
void KDirListerCache::updateDirectory( const KURL& _dir )
{
kdDebug(7004) << k_funcinfo << _dir << endl;
TQString urlStr = _dir.url(-1);
TQString urlReferenceStr = _dir.internalReferenceURL();
if ( !checkUpdate( _dir, -1 ) ) {
return;
}
// A job can be running to
// - only list a new directory: the listers are in urlsCurrentlyListed
// - only update a directory: the listers are in urlsCurrentlyHeld
// - update a currently running listing: the listers are in urlsCurrentlyListed
// and urlsCurrentlyHeld
TQPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr + ":" + urlReferenceStr];
TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr + ":" + urlReferenceStr];
// restart the job for _dir if it is running already
bool killed = false;
TQWidget *window = 0;
TDEIO::ListJob *job = jobForUrl( urlStr + ":" + urlReferenceStr );
if ( job )
{
window = job->window();
killJob( job );
killed = true;
if ( listers ) {
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->jobDone( job );
}
}
if ( holders ) {
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) {
kdl->jobDone( job );
}
}
}
kdDebug(7004) << k_funcinfo << "Killed = " << killed << endl;
// we don't need to emit canceled signals since we only replaced the job,
// the listing is continuing.
Q_ASSERT( !listers || (listers && killed) );
job = TDEIO::listDir( _dir, false /* no default GUI */ );
jobs.insert( job, TQValueList<TDEIO::UDSEntry>() );
connect( job, TQ_SIGNAL(entries( TDEIO::Job *, const TDEIO::UDSEntryList & )),
this, TQ_SLOT(slotUpdateEntries( TDEIO::Job *, const TDEIO::UDSEntryList & )) );
connect( job, TQ_SIGNAL(result( TDEIO::Job * )),
this, TQ_SLOT(slotUpdateResult( TDEIO::Job * )) );
kdDebug(7004) << k_funcinfo << "update started in " << _dir << endl;
if ( listers ) {
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->jobStarted( job );
}
}
if ( holders )
{
if ( !killed )
{
bool first = true;
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
{
kdl->jobStarted( job );
if ( first && kdl->d->window )
{
first = false;
job->setWindow( kdl->d->window );
}
emit kdl->started( _dir );
}
}
else
{
job->setWindow( window );
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() ) {
kdl->jobStarted( job );
}
}
}
}
bool KDirListerCache::checkUpdate( const KURL& _dir, int truncationMode )
{
if ( !itemsInUse[_dir.url(truncationMode) + ":" + _dir.internalReferenceURL()] )
{
DirItem *item = itemsCached[_dir.url(truncationMode)];
if ( item && item->complete )
{
item->complete = false;
item->decAutoUpdate();
// Hmm, this debug output might include login/password from the _dir URL.
//kdDebug(7004) << k_funcinfo << "directory " << _dir << " not in use, marked dirty." << endl;
}
//else
//kdDebug(7004) << k_funcinfo << "aborted, directory " << _dir << " not in cache." << endl;
return false;
}
else
return true;
}
KFileItemList *KDirListerCache::itemsForDir( const KURL &_dir ) const
{
TQString urlStr = _dir.url(-1);
TQString urlReferenceStr = _dir.internalReferenceURL();
DirItem *item = itemsInUse[ urlStr + ":" + urlReferenceStr ];
if ( !item ) {
item = itemsCached[ urlStr ];
}
return item ? item->lstItems : 0;
}
KFileItem *KDirListerCache::findByName( const KDirLister *lister, const TQString& _name ) const
{
Q_ASSERT( lister );
for ( KURL::List::Iterator it = lister->d->lstDirs.begin();
it != lister->d->lstDirs.end(); ++it )
{
KFileItemListIterator kit( *itemsInUse[(*it).url() + ":" + (*it).internalReferenceURL()]->lstItems );
for ( ; kit.current(); ++kit )
if ( (*kit)->name() == _name )
return (*kit);
}
return 0L;
}
KFileItem *KDirListerCache::findByURL( const KDirLister *lister, const KURL& _u ) const
{
KURL _url = _u;
_url.adjustPath(-1);
KURL parentDir( _url );
parentDir.setPath( parentDir.directory() );
// If lister is set, check that it contains this dir
if ( lister && !lister->d->lstDirs.contains( parentDir ) )
return 0L;
KFileItemList *itemList = itemsForDir( parentDir );
if ( itemList )
{
KFileItemListIterator kit( *itemList );
for ( ; kit.current(); ++kit )
if ( (*kit)->url() == _url )
return (*kit);
}
return 0L;
}
void KDirListerCache::FilesAdded( const KURL &dir )
{
kdDebug(7004) << k_funcinfo << dir << endl;
updateDirectory( dir );
}
void KDirListerCache::FilesRemoved( const KURL::List &fileList )
{
kdDebug(7004) << k_funcinfo << endl;
KURL::List::ConstIterator it = fileList.begin();
for ( ; it != fileList.end() ; ++it )
{
// emit the deleteItem signal if this file was shown in any view
KFileItem *fileitem = 0L;
KURL parentDir( *it );
parentDir.setPath( parentDir.directory() );
KFileItemList *lstItems = itemsForDir( parentDir );
if ( lstItems )
{
KFileItem *fit = lstItems->first();
for ( ; fit; fit = lstItems->next() )
if ( fit->url() == *it ) {
fileitem = fit;
lstItems->take(); // remove fileitem from list
break;
}
}
// Tell the views about it before deleting the KFileItems. They might need the subdirs'
// file items (see the dirtree).
if ( fileitem )
{
TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDir.url() + ":" + parentDir.internalReferenceURL()];
if ( listers ) {
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->emitDeleteItem( fileitem );
}
}
}
// If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case.
if ( !fileitem || fileitem->isDir() )
{
// in case of a dir, check if we have any known children, there's much to do in that case
// (stopping jobs, removing dirs from cache etc.)
deleteDir( *it );
}
// now remove the item itself
delete fileitem;
}
}
void KDirListerCache::FilesChanged( const KURL::List &fileList )
{
KURL::List dirsToUpdate;
kdDebug(7004) << k_funcinfo << "only half implemented" << endl;
KURL::List::ConstIterator it = fileList.begin();
for ( ; it != fileList.end() ; ++it )
{
if ( ( *it ).isLocalFile() )
{
kdDebug(7004) << "KDirListerCache::FilesChanged " << *it << endl;
KFileItem *fileitem = findByURL( 0, *it );
if ( fileitem )
{
// we need to refresh the item, because e.g. the permissions can have changed.
aboutToRefreshItem( fileitem );
fileitem->refresh();
emitRefreshItem( fileitem );
}
else {
kdDebug(7004) << "item not found" << endl;
}
} else {
// For remote files, refresh() won't be able to figure out the new information.
// Let's update the dir.
KURL dir( *it );
dir.setPath( dir.directory( true ) );
if ( dirsToUpdate.find( dir ) == dirsToUpdate.end() ) {
dirsToUpdate.prepend( dir );
}
}
}
KURL::List::ConstIterator itdir = dirsToUpdate.begin();
for ( ; itdir != dirsToUpdate.end() ; ++itdir ) {
updateDirectory( *itdir );
}
// ## TODO problems with current jobs listing/updating that dir
// ( see kde-2.2.2's kdirlister )
}
void KDirListerCache::FileRenamed( const KURL &src, const KURL &dst )
{
kdDebug(7004) << k_funcinfo << src.prettyURL() << " -> " << dst.prettyURL() << endl;
#ifdef DEBUG_CACHE
printDebug();
#endif
// Somehow this should only be called if src is a dir. But how could we know if it is?
// (Note that looking into itemsInUse isn't good enough. One could rename a subdir in a view.)
renameDir( src, dst );
// Now update the KFileItem representing that file or dir (not exclusive with the above!)
KURL oldurl( src );
oldurl.adjustPath( -1 );
KFileItem *fileitem = findByURL( 0, oldurl );
if ( fileitem )
{
if ( !fileitem->isLocalFile() && !fileitem->localPath().isEmpty() ) // it uses UDS_LOCAL_PATH? ouch, needs an update then
FilesChanged( src );
else
{
aboutToRefreshItem( fileitem );
fileitem->setURL( dst );
fileitem->refreshMimeType();
emitRefreshItem( fileitem );
}
}
#ifdef DEBUG_CACHE
printDebug();
#endif
}
void KDirListerCache::aboutToRefreshItem( KFileItem *fileitem )
{
// Look whether this item was shown in any view, i.e. held by any dirlister
KURL parentDir( fileitem->url() );
parentDir.setPath( parentDir.directory() );
TQString parentDirURL = parentDir.url();
TQString parentDirReferenceURL = parentDir.internalReferenceURL();
TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL + ":" + parentDirReferenceURL];
if ( listers )
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
kdl->aboutToRefreshItem( fileitem );
// Also look in urlsCurrentlyListed, in case the user manages to rename during a listing
listers = urlsCurrentlyListed[parentDirURL + ":" + parentDirReferenceURL];
if ( listers )
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
kdl->aboutToRefreshItem( fileitem );
}
void KDirListerCache::emitRefreshItem( KFileItem *fileitem )
{
// Look whether this item was shown in any view, i.e. held by any dirlister
KURL parentDir( fileitem->url() );
parentDir.setPath( parentDir.directory() );
TQString parentDirURL = parentDir.url();
TQString parentDirReferenceURL = parentDir.internalReferenceURL();
TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL + ":" + parentDirReferenceURL];
if ( listers )
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
{
kdl->addRefreshItem( fileitem );
kdl->emitItems();
}
// Also look in urlsCurrentlyListed, in case the user manages to rename during a listing
listers = urlsCurrentlyListed[parentDirURL + ":" + parentDirReferenceURL];
if ( listers )
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
{
kdl->addRefreshItem( fileitem );
kdl->emitItems();
}
}
KDirListerCache* KDirListerCache::self()
{
if ( !s_pSelf )
s_pSelf = sd_KDirListerCache.setObject( s_pSelf, new KDirListerCache );
return s_pSelf;
}
bool KDirListerCache::exists()
{
return s_pSelf != 0;
}
// private slots
// _file can also be a directory being currently held!
void KDirListerCache::slotFileDirty( const KURL& _url )
{
kdDebug(7004) << k_funcinfo << _url << endl;
if ( !pendingUpdates[_url.path()] )
{
KURL dir;
dir.setPath( _url.path() );
dir.setInternalReferenceURL(_url.internalReferenceURL());
if ( checkUpdate( dir, -1 ) ) {
updateDirectory( _url );
}
// the parent directory of _url.path()
dir.setPath( dir.directory() );
dir.setInternalReferenceURL(_url.internalReferenceURL());
if ( checkUpdate( dir ) )
{
// Nice hack to save memory: use the qt object name to store the filename
TQTimer *timer = new TQTimer( this, _url.path().utf8() );
connect( timer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotFileDirtyDelayed()) );
pendingUpdates.insert( _url.path(), timer );
timer->start( 500, true );
}
}
}
// delayed updating of files, FAM is flooding us with events
void KDirListerCache::slotFileDirtyDelayed()
{
TQString file = TQString::fromUtf8( sender()->name() );
kdDebug(7004) << k_funcinfo << file << endl;
// TODO: do it better: don't always create/delete the TQTimer but reuse it.
// Delete the timer after the parent directory is removed from the cache.
pendingUpdates.remove( file );
KURL u;
u.setPath( file );
KFileItem *item = findByURL( 0, u ); // search all items
if ( item )
{
// we need to refresh the item, because e.g. the permissions can have changed.
aboutToRefreshItem( item );
item->refresh();
emitRefreshItem( item );
}
}
void KDirListerCache::slotFileCreated( const TQString& _file )
{
kdDebug(7004) << k_funcinfo << _file << endl;
// XXX: how to avoid a complete rescan here?
KURL u;
u.setPath( _file );
u.setPath( u.directory() );
FilesAdded( u );
}
void KDirListerCache::slotFileDeleted( const TQString& _file )
{
kdDebug(7004) << k_funcinfo << _file << endl;
KURL u;
u.setPath( _file );
FilesRemoved( u );
}
void KDirListerCache::slotEntries( TDEIO::Job *job, const TDEIO::UDSEntryList &entries )
{
KURL url = joburl( static_cast<TDEIO::ListJob *>(job) );
url.adjustPath(-1);
TQString urlStr = url.url();
TQString urlReferenceStr = url.internalReferenceURL();
kdDebug(7004) << k_funcinfo << "new entries for " << url << endl;
DirItem *dir = itemsInUse[urlStr + ":" + urlReferenceStr];
Q_ASSERT( dir );
TQPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr + ":" + urlReferenceStr];
Q_ASSERT( listers );
Q_ASSERT( !listers->isEmpty() );
// check if anyone wants the mimetypes immediately
bool delayedMimeTypes = true;
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
delayedMimeTypes = delayedMimeTypes && kdl->d->delayedMimeTypes;
}
// avoid creating these QStrings again and again
static const TQString& dot = TDEGlobal::staticQString(".");
static const TQString& dotdot = TDEGlobal::staticQString("..");
TDEIO::UDSEntryListConstIterator it = entries.begin();
TDEIO::UDSEntryListConstIterator end = entries.end();
for ( ; it != end; ++it )
{
TQString name;
// find out about the name
TDEIO::UDSEntry::ConstIterator entit = (*it).begin();
for( ; entit != (*it).end(); ++entit ) {
if ( (*entit).m_uds == TDEIO::UDS_NAME ) {
name = (*entit).m_str;
break;
}
}
Q_ASSERT( !name.isEmpty() );
if ( name.isEmpty() ) {
continue;
}
if ( name == dot )
{
Q_ASSERT( !dir->rootItem );
dir->rootItem = new KFileItem( *it, url, delayedMimeTypes, true );
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
if ( !kdl->d->rootFileItem && kdl->d->url == url ) {
kdl->d->rootFileItem = dir->rootItem;
}
}
}
else if ( name != dotdot )
{
KFileItem* item = new KFileItem( *it, url, delayedMimeTypes, true );
Q_ASSERT( item );
//kdDebug(7004)<< "Adding item: " << item->url() << endl;
dir->lstItems->append( item );
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->addNewItem( item );
}
}
}
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->emitItems();
}
}
void KDirListerCache::slotResult( TDEIO::Job *j )
{
Q_ASSERT( j );
TDEIO::ListJob *job = static_cast<TDEIO::ListJob *>( j );
jobs.remove( job );
KURL jobUrl = joburl( job );
jobUrl.adjustPath(-1); // need remove trailing slashes again, in case of redirections
TQString jobUrlStr = jobUrl.url();
TQString jobReferenceUrlStr = jobUrl.internalReferenceURL();
kdDebug(7004) << k_funcinfo << "finished listing " << jobUrl << endl;
#ifdef DEBUG_CACHE
printDebug();
#endif
TQPtrList<KDirLister> *listers = urlsCurrentlyListed.take( jobUrlStr + ":" + jobReferenceUrlStr );
Q_ASSERT( listers );
// move the directory to the held directories, do it before emitting
// the signals to make sure it exists in KDirListerCache in case someone
// calls listDir during the signal emission
Q_ASSERT( !urlsCurrentlyHeld[jobUrlStr + ":" + jobReferenceUrlStr] );
urlsCurrentlyHeld.insert( jobUrlStr + ":" + jobReferenceUrlStr, listers );
KDirLister *kdl;
if ( job->error() )
{
for ( kdl = listers->first(); kdl; kdl = listers->next() )
{
kdl->jobDone( job );
kdl->handleError( job );
emit kdl->canceled( jobUrl );
if ( kdl->numJobs() == 0 )
{
kdl->d->complete = true;
emit kdl->canceled();
}
}
}
else
{
DirItem *dir = itemsInUse[jobUrlStr + ":" + jobReferenceUrlStr];
Q_ASSERT( dir );
dir->complete = true;
for ( kdl = listers->first(); kdl; kdl = listers->next() )
{
kdl->jobDone( job );
kdl->emitCompleted( jobUrl );
if ( kdl->numJobs() == 0 )
{
kdl->d->complete = true;
emit kdl->completed();
}
}
}
// TODO: hmm, if there was an error and job is a parent of one or more
// of the pending urls we should cancel it/them as well
processPendingUpdates();
#ifdef DEBUG_CACHE
printDebug();
#endif
}
void KDirListerCache::slotRedirection( TDEIO::Job *j, const KURL& url )
{
Q_ASSERT( j );
TDEIO::ListJob *job = static_cast<TDEIO::ListJob *>( j );
KURL oldUrl = job->url(); // here we really need the old url!
KURL newUrl = url;
// strip trailing slashes
oldUrl.adjustPath(-1);
newUrl.adjustPath(-1);
if ( oldUrl == newUrl )
{
kdDebug(7004) << k_funcinfo << "New redirection url same as old, giving up." << endl;
return;
}
kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
#ifdef DEBUG_CACHE
printDebug();
#endif
// I don't think there can be dirItems that are childs of oldUrl.
// Am I wrong here? And even if so, we don't need to delete them, right?
// DF: redirection happens before listDir emits any item. Makes little sense otherwise.
// oldUrl cannot be in itemsCached because only completed items are moved there
DirItem *dir = itemsInUse.take( oldUrl.url() + ":" + oldUrl.internalReferenceURL() );
Q_ASSERT( dir );
TQPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrl.url() + ":" + oldUrl.internalReferenceURL() );
Q_ASSERT( listers );
Q_ASSERT( !listers->isEmpty() );
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
{
// TODO: put in own method?
if ( kdl->d->url.equals( oldUrl, true ) )
{
kdl->d->rootFileItem = 0;
kdl->d->url = newUrl;
}
*kdl->d->lstDirs.find( oldUrl ) = newUrl;
if ( kdl->d->lstDirs.count() == 1 )
{
emit kdl->clear();
emit kdl->redirection( newUrl );
emit kdl->redirection( oldUrl, newUrl );
}
else
{
emit kdl->clear( oldUrl );
emit kdl->redirection( oldUrl, newUrl );
}
}
// when a lister was stopped before the job emits the redirection signal, the old url will
// also be in urlsCurrentlyHeld
TQPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrl.url() + ":" + oldUrl.internalReferenceURL() );
if ( holders )
{
Q_ASSERT( !holders->isEmpty() );
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
{
kdl->jobStarted( job );
// do it like when starting a new list-job that will redirect later
emit kdl->started( oldUrl );
// TODO: maybe don't emit started if there's an update running for newUrl already?
if ( kdl->d->url.equals( oldUrl, true ) )
{
kdl->d->rootFileItem = 0;
kdl->d->url = newUrl;
}
*kdl->d->lstDirs.find( oldUrl ) = newUrl;
if ( kdl->d->lstDirs.count() == 1 )
{
emit kdl->clear();
emit kdl->redirection( newUrl );
emit kdl->redirection( oldUrl, newUrl );
}
else
{
emit kdl->clear( oldUrl );
emit kdl->redirection( oldUrl, newUrl );
}
}
}
DirItem *newDir = itemsInUse[newUrl.url() + ":" + newUrl.internalReferenceURL()];
if ( newDir )
{
kdDebug(7004) << "slotRedirection: " << newUrl.url() << " already in use" << endl;
// only in this case there can newUrl already be in urlsCurrentlyListed or urlsCurrentlyHeld
delete dir;
// get the job if one's running for newUrl already (can be a list-job or an update-job), but
// do not return this 'job', which would happen because of the use of redirectionURL()
TDEIO::ListJob *oldJob = jobForUrl( newUrl.url() + ":" + newUrl.internalReferenceURL(), job );
// listers of newUrl with oldJob: forget about the oldJob and use the already running one
// which will be converted to an updateJob
TQPtrList<KDirLister> *curListers = urlsCurrentlyListed[newUrl.url() + ":" + newUrl.internalReferenceURL()];
if ( curListers )
{
kdDebug(7004) << "slotRedirection: and it is currently listed" << endl;
Q_ASSERT( oldJob ); // ?!
for ( KDirLister *kdl = curListers->first(); kdl; kdl = curListers->next() ) // listers of newUrl
{
kdl->jobDone( oldJob );
kdl->jobStarted( job );
kdl->connectJob( job );
}
// append listers of oldUrl with newJob to listers of newUrl with oldJob
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
curListers->append( kdl );
}
else {
urlsCurrentlyListed.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), listers );
}
if ( oldJob ) // kill the old job, be it a list-job or an update-job
killJob( oldJob );
// holders of newUrl: use the already running job which will be converted to an updateJob
TQPtrList<KDirLister> *curHolders = urlsCurrentlyHeld[newUrl.url() + ":" + newUrl.internalReferenceURL()];
if ( curHolders )
{
kdDebug(7004) << "slotRedirection: and it is currently held." << endl;
for ( KDirLister *kdl = curHolders->first(); kdl; kdl = curHolders->next() ) // holders of newUrl
{
kdl->jobStarted( job );
emit kdl->started( newUrl );
}
// append holders of oldUrl to holders of newUrl
if ( holders )
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
curHolders->append( kdl );
}
else if ( holders )
urlsCurrentlyHeld.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), holders );
// emit old items: listers, holders. NOT: newUrlListers/newUrlHolders, they already have them listed
// TODO: make this a separate method?
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
{
if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
kdl->d->rootFileItem = newDir->rootItem;
kdl->addNewItems( *(newDir->lstItems) );
kdl->emitItems();
}
if ( holders )
{
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
{
if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
kdl->d->rootFileItem = newDir->rootItem;
kdl->addNewItems( *(newDir->lstItems) );
kdl->emitItems();
}
}
}
else if ( (newDir = itemsCached.take( newUrl.url() )) )
{
kdDebug(7004) << "slotRedirection: " << newUrl.url() << " is unused, but already in the cache." << endl;
delete dir;
itemsInUse.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), newDir );
urlsCurrentlyListed.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), listers );
if ( holders )
urlsCurrentlyHeld.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), holders );
// emit old items: listers, holders
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
{
if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
kdl->d->rootFileItem = newDir->rootItem;
kdl->addNewItems( *(newDir->lstItems) );
kdl->emitItems();
}
if ( holders )
{
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
{
if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
kdl->d->rootFileItem = newDir->rootItem;
kdl->addNewItems( *(newDir->lstItems) );
kdl->emitItems();
}
}
}
else
{
kdDebug(7004) << "slotRedirection: " << newUrl.url() << " has not been listed yet." << endl;
delete dir->rootItem;
dir->rootItem = 0;
dir->lstItems->clear();
dir->redirect( newUrl );
itemsInUse.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), dir );
urlsCurrentlyListed.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), listers );
if ( holders )
urlsCurrentlyHeld.insert( newUrl.url() + ":" + newUrl.internalReferenceURL(), holders );
else
{
#ifdef DEBUG_CACHE
printDebug();
#endif
return; // only in this case the job doesn't need to be converted,
}
}
// make the job an update job
job->disconnect( this );
connect( job, TQ_SIGNAL(entries( TDEIO::Job *, const TDEIO::UDSEntryList & )),
this, TQ_SLOT(slotUpdateEntries( TDEIO::Job *, const TDEIO::UDSEntryList & )) );
connect( job, TQ_SIGNAL(result( TDEIO::Job * )),
this, TQ_SLOT(slotUpdateResult( TDEIO::Job * )) );
// FIXME: autoUpdate-Counts!!
#ifdef DEBUG_CACHE
printDebug();
#endif
}
void KDirListerCache::renameDir( const KURL &oldUrl, const KURL &newUrl )
{
kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
TQString oldUrlStr = oldUrl.url(-1);
TQString newUrlStr = newUrl.url(-1);
// Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
//DirItem *dir = itemsInUse.take( oldUrlStr );
//emitRedirections( oldUrl, url );
// Look at all dirs being listed/shown
TQDictIterator<DirItem> itu( itemsInUse );
bool goNext;
while ( itu.current() )
{
goNext = true;
DirItem *dir = itu.current();
TQString oldDirURLIndep = itu.currentKey();
oldDirURLIndep.truncate(oldDirURLIndep.length() - (dir->url.internalReferenceURL().length()+strlen(":")));
KURL oldDirUrl ( oldDirURLIndep );
//kdDebug(7004) << "itemInUse: " << oldDirUrl.prettyURL() << endl;
// Check if this dir is oldUrl, or a subfolder of it
if ( oldUrl.isParentOf( oldDirUrl ) )
{
// TODO should use KURL::cleanpath like isParentOf does
TQString relPath = oldDirUrl.path().mid( oldUrl.path().length() );
KURL newDirUrl( newUrl ); // take new base
if ( !relPath.isEmpty() ) {
newDirUrl.addPath( relPath ); // add unchanged relative path
}
//kdDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl.prettyURL() << endl;
// Update URL in dir item and in itemsInUse
dir->redirect( newDirUrl );
itemsInUse.remove( itu.currentKey() ); // implies ++itu
itemsInUse.insert( newDirUrl.url(-1), dir );
goNext = false; // because of the implied ++itu above
if ( dir->lstItems )
{
// Rename all items under that dir
KFileItemListIterator kit( *dir->lstItems );
for ( ; kit.current(); ++kit )
{
KURL oldItemUrl = (*kit)->url();
TQString oldItemUrlStr( oldItemUrl.url(-1) );
KURL newItemUrl( oldItemUrl );
newItemUrl.setPath( newDirUrl.path() );
newItemUrl.addPath( oldItemUrl.fileName() );
kdDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url() << endl;
(*kit)->setURL( newItemUrl );
}
}
emitRedirections( oldDirUrl, newDirUrl );
}
if ( goNext )
++itu;
}
// Is oldUrl a directory in the cache?
// Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
removeDirFromCache( oldUrl );
// TODO rename, instead.
}
void KDirListerCache::emitRedirections( const KURL &oldUrl, const KURL &url )
{
kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << url.prettyURL() << endl;
TQString oldUrlStr = oldUrl.url(-1);
TQString urlStr = url.url(-1);
TQString oldReferenceUrlStr = oldUrl.internalReferenceURL();
TQString urlReferenceStr = url.internalReferenceURL();
TDEIO::ListJob *job = jobForUrl( oldUrlStr + ":" + oldReferenceUrlStr );
if ( job )
killJob( job );
// Check if we were listing this dir. Need to abort and restart with new name in that case.
TQPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrlStr + ":" + oldReferenceUrlStr );
if ( listers )
{
// Tell the world that the job listing the old url is dead.
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
{
if ( job )
kdl->jobDone( job );
emit kdl->canceled( oldUrl );
}
urlsCurrentlyListed.insert( urlStr + ":" + urlReferenceStr, listers );
}
// Check if we are currently displaying this directory (odds opposite wrt above)
// Update urlsCurrentlyHeld dict with new URL
TQPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrlStr + ":" + oldReferenceUrlStr );
if ( holders )
{
if ( job )
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
kdl->jobDone( job );
urlsCurrentlyHeld.insert( urlStr + ":" + urlReferenceStr, holders );
}
if ( listers )
{
updateDirectory( url );
// Tell the world about the new url
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
emit kdl->started( url );
}
if ( holders )
{
// And notify the dirlisters of the redirection
for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
{
*kdl->d->lstDirs.find( oldUrl ) = url;
if ( kdl->d->lstDirs.count() == 1 )
emit kdl->redirection( url );
emit kdl->redirection( oldUrl, url );
}
}
}
void KDirListerCache::removeDirFromCache( const KURL& dir )
{
kdDebug(7004) << "KDirListerCache::removeDirFromCache " << dir.prettyURL() << endl;
TQCacheIterator<DirItem> itc( itemsCached );
while ( itc.current() )
{
if ( dir.isParentOf( KURL( itc.currentKey() ) ) )
itemsCached.remove( itc.currentKey() );
else
++itc;
}
}
void KDirListerCache::slotUpdateEntries( TDEIO::Job* job, const TDEIO::UDSEntryList& list )
{
jobs[static_cast<TDEIO::ListJob*>(job)] += list;
}
void KDirListerCache::slotUpdateResult( TDEIO::Job * j )
{
Q_ASSERT( j );
TDEIO::ListJob *job = static_cast<TDEIO::ListJob *>( j );
KURL jobUrl = joburl( job );
jobUrl.adjustPath(-1); // need remove trailing slashes again, in case of redirections
TQString jobUrlStr = jobUrl.url();
TQString jobReferenceUrlStr = jobUrl.internalReferenceURL();
kdDebug(7004) << k_funcinfo << "finished update " << jobUrl << endl;
KDirLister *kdl;
TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[jobUrlStr + ":" + jobReferenceUrlStr];
TQPtrList<KDirLister> *tmpLst = urlsCurrentlyListed.take( jobUrlStr + ":" + jobReferenceUrlStr );
if ( tmpLst )
{
if ( listers )
for ( kdl = tmpLst->first(); kdl; kdl = tmpLst->next() )
{
Q_ASSERT( listers->containsRef( kdl ) == 0 );
listers->append( kdl );
}
else
{
listers = tmpLst;
urlsCurrentlyHeld.insert( jobUrlStr + ":" + jobReferenceUrlStr, listers );
}
}
// once we are updating dirs that are only in the cache this will fail!
Q_ASSERT( listers );
if ( job->error() )
{
for ( kdl = listers->first(); kdl; kdl = listers->next() )
{
kdl->jobDone( job );
//don't bother the user
//kdl->handleError( job );
emit kdl->canceled( jobUrl );
if ( kdl->numJobs() == 0 )
{
kdl->d->complete = true;
emit kdl->canceled();
}
}
jobs.remove( job );
// TODO: if job is a parent of one or more
// of the pending urls we should cancel them
processPendingUpdates();
return;
}
DirItem *dir = itemsInUse[jobUrlStr + ":" + jobReferenceUrlStr];
dir->complete = true;
// check if anyone wants the mimetypes immediately
bool delayedMimeTypes = true;
for ( kdl = listers->first(); kdl; kdl = listers->next() ) {
delayedMimeTypes = delayedMimeTypes && kdl->d->delayedMimeTypes;
}
// should be enough to get reasonable speed in most cases
TQDict<KFileItem> fileItems( 9973 );
KFileItemListIterator kit ( *(dir->lstItems) );
// Unmark all items in url
for ( ; kit.current(); ++kit )
{
(*kit)->unmark();
if (!((*kit)->listerURL().isEmpty())) {
fileItems.insert( (*kit)->listerURL().url(), *kit );
}
else {
fileItems.insert( (*kit)->url().url(), *kit );
}
}
static const TQString& dot = TDEGlobal::staticQString(".");
static const TQString& dotdot = TDEGlobal::staticQString("..");
KFileItem *item = 0, *tmp;
TQValueList<TDEIO::UDSEntry> buf = jobs[job];
TQValueListIterator<TDEIO::UDSEntry> it = buf.begin();
for ( ; it != buf.end(); ++it )
{
// Form the complete url
if ( !item ) {
item = new KFileItem( *it, jobUrl, delayedMimeTypes, true );
}
else {
item->setUDSEntry( *it, jobUrl, delayedMimeTypes, true );
}
// Find out about the name
TQString name = item->name();
Q_ASSERT( !name.isEmpty() );
// we duplicate the check for dotdot here, to avoid iterating over
// all items again and checking in matchesFilter() that way.
if ( name.isEmpty() || name == dotdot ) {
continue;
}
if ( name == dot )
{
// if the update was started before finishing the original listing
// there is no root item yet
if ( !dir->rootItem )
{
dir->rootItem = item;
item = 0;
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl ) {
kdl->d->rootFileItem = dir->rootItem;
}
}
}
continue;
}
// Find this item
if ( (tmp = fileItems[item->url().url()]) )
{
tmp->mark();
// check if something changed for this file
if ( !tmp->cmp( *item ) )
{
for ( kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->aboutToRefreshItem( tmp );
}
//kdDebug(7004) << "slotUpdateResult: file changed: " << tmp->name() << endl;
tmp->assign( *item );
for ( kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->addRefreshItem( tmp );
}
}
}
else // this is a new file
{
//kdDebug(7004) << "slotUpdateResult: new file: " << name << endl;
item->mark();
dir->lstItems->append( item );
for ( kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->addNewItem( item );
}
// item used, we need a new one for the next iteration
item = 0;
}
}
if ( item ) {
delete item;
}
jobs.remove( job );
deleteUnmarkedItems( listers, dir->lstItems );
for ( kdl = listers->first(); kdl; kdl = listers->next() )
{
kdl->emitItems();
kdl->jobDone( job );
kdl->emitCompleted( jobUrl );
if ( kdl->numJobs() == 0 )
{
kdl->d->complete = true;
emit kdl->completed();
}
}
// TODO: hmm, if there was an error and job is a parent of one or more
// of the pending urls we should cancel it/them as well
processPendingUpdates();
}
// private
TDEIO::ListJob *KDirListerCache::jobForUrl( const TQString& url, TDEIO::ListJob *not_job )
{
TDEIO::ListJob *job;
TQMap< TDEIO::ListJob *, TQValueList<TDEIO::UDSEntry> >::Iterator it = jobs.begin();
while ( it != jobs.end() )
{
job = it.key();
KURL itjoburl = joburl( job );
if ( ((itjoburl.url(-1) + ":" + itjoburl.internalReferenceURL()) == url) && (job != not_job) ) {
return job;
}
++it;
}
return 0;
}
const KURL& KDirListerCache::joburl( TDEIO::ListJob *job )
{
if ( job->redirectionURL().isValid() ) {
return job->redirectionURL();
}
else {
return job->url();
}
}
void KDirListerCache::killJob( TDEIO::ListJob *job )
{
jobs.remove( job );
job->disconnect( this );
job->kill();
}
void KDirListerCache::deleteUnmarkedItems( TQPtrList<KDirLister> *listers, KFileItemList *lstItems )
{
// Find all unmarked items and delete them
KFileItem* item;
lstItems->first();
while ( (item = lstItems->current()) )
if ( !item->isMarked() )
{
//kdDebug() << k_funcinfo << item->name() << endl;
for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() ) {
kdl->emitDeleteItem( item );
}
if ( item->isDir() ) {
deleteDir( item->url() );
}
// finally actually delete the item
lstItems->take();
delete item;
}
else
lstItems->next();
}
void KDirListerCache::deleteDir( const KURL& dirUrl )
{
//kdDebug() << k_funcinfo << dirUrl.prettyURL() << endl;
// unregister and remove the childs of the deleted item.
// Idea: tell all the KDirListers that they should forget the dir
// and then remove it from the cache.
TQDictIterator<DirItem> itu( itemsInUse );
while ( itu.current() )
{
TQString deletedUrlIndep = itu.currentKey();
deletedUrlIndep.truncate(deletedUrlIndep.length() - ((*itu)->url.internalReferenceURL().length()+strlen(":")));
KURL deletedUrl( deletedUrlIndep );
if ( dirUrl.isParentOf( deletedUrl ) )
{
// stop all jobs for deletedUrl
TQPtrList<KDirLister> *kdls = urlsCurrentlyListed[deletedUrl.url() + ":" + deletedUrl.internalReferenceURL()];
if ( kdls ) // yeah, I lack good names
{
// we need a copy because stop modifies the list
kdls = new TQPtrList<KDirLister>( *kdls );
for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
stop( kdl, deletedUrl );
delete kdls;
}
// tell listers holding deletedUrl to forget about it
// this will stop running updates for deletedUrl as well
kdls = urlsCurrentlyHeld[deletedUrl.url() + ":" + deletedUrl.internalReferenceURL()];
if ( kdls )
{
// we need a copy because forgetDirs modifies the list
kdls = new TQPtrList<KDirLister>( *kdls );
for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
{
// lister's root is the deleted item
if ( kdl->d->url == deletedUrl )
{
// tell the view first. It might need the subdirs' items (which forgetDirs will delete)
if ( kdl->d->rootFileItem )
emit kdl->deleteItem( kdl->d->rootFileItem );
forgetDirs( kdl );
kdl->d->rootFileItem = 0;
}
else
{
bool treeview = kdl->d->lstDirs.count() > 1;
if ( !treeview )
emit kdl->clear();
forgetDirs( kdl, deletedUrl, treeview );
}
}
delete kdls;
}
// delete the entry for deletedUrl - should not be needed, it's in
// items cached now
DirItem *dir = itemsInUse.take( deletedUrl.url() + ":" + deletedUrl.internalReferenceURL() );
Q_ASSERT( !dir );
if ( !dir ) // take didn't find it - move on
++itu;
}
else
++itu;
}
// remove the children from the cache
removeDirFromCache( dirUrl );
}
void KDirListerCache::processPendingUpdates()
{
// TODO
}
#ifndef NDEBUG
void KDirListerCache::printDebug()
{
kdDebug(7004) << "Items in use: " << endl;
TQDictIterator<DirItem> itu( itemsInUse );
for ( ; itu.current() ; ++itu ) {
kdDebug(7004) << " " << itu.currentKey() << " URL: " << itu.current()->url
<< " rootItem: " << ( itu.current()->rootItem ? itu.current()->rootItem->url() : KURL() )
<< " autoUpdates refcount: " << itu.current()->autoUpdates
<< " complete: " << itu.current()->complete
<< ( itu.current()->lstItems ? TQString(" with %1 items.").arg(itu.current()->lstItems->count()) : TQString(" lstItems=NULL") ) << endl;
}
kdDebug(7004) << "urlsCurrentlyHeld: " << endl;
TQDictIterator< TQPtrList<KDirLister> > it( urlsCurrentlyHeld );
for ( ; it.current() ; ++it )
{
TQString list;
for ( TQPtrListIterator<KDirLister> listit( *it.current() ); listit.current(); ++listit )
list += " 0x" + TQString::number( (long)listit.current(), 16 );
kdDebug(7004) << " " << it.currentKey() << " " << it.current()->count() << " listers: " << list << endl;
}
kdDebug(7004) << "urlsCurrentlyListed: " << endl;
TQDictIterator< TQPtrList<KDirLister> > it2( urlsCurrentlyListed );
for ( ; it2.current() ; ++it2 )
{
TQString list;
for ( TQPtrListIterator<KDirLister> listit( *it2.current() ); listit.current(); ++listit )
list += " 0x" + TQString::number( (long)listit.current(), 16 );
kdDebug(7004) << " " << it2.currentKey() << " " << it2.current()->count() << " listers: " << list << endl;
}
TQMap< TDEIO::ListJob *, TQValueList<TDEIO::UDSEntry> >::Iterator jit = jobs.begin();
kdDebug(7004) << "Jobs: " << endl;
for ( ; jit != jobs.end() ; ++jit )
kdDebug(7004) << " " << jit.key() << " listing " << joburl( jit.key() ).prettyURL() << ": " << (*jit).count() << " entries." << endl;
kdDebug(7004) << "Items in cache: " << endl;
TQCacheIterator<DirItem> itc( itemsCached );
for ( ; itc.current() ; ++itc )
kdDebug(7004) << " " << itc.currentKey() << " rootItem: "
<< ( itc.current()->rootItem ? itc.current()->rootItem->url().prettyURL() : TQString("NULL") )
<< ( itc.current()->lstItems ? TQString(" with %1 items.").arg(itc.current()->lstItems->count()) : TQString(" lstItems=NULL") ) << endl;
}
#endif
/*********************** -- The new KDirLister -- ************************/
KDirLister::KDirLister( bool _delayedMimeTypes )
{
kdDebug(7003) << "+KDirLister" << endl;
d = new KDirListerPrivate;
d->complete = true;
d->delayedMimeTypes = _delayedMimeTypes;
setAutoUpdate( true );
setDirOnlyMode( false );
setShowingDotFiles( false );
setAutoErrorHandlingEnabled( true, 0 );
}
KDirLister::~KDirLister()
{
kdDebug(7003) << "-KDirLister" << endl;
if ( KDirListerCache::exists() )
{
// Stop all running jobs
stop();
s_pCache->forgetDirs( this );
}
delete d;
}
bool KDirLister::openURL( const KURL& _url, bool _keep, bool _reload )
{
kdDebug(7003) << k_funcinfo << _url.prettyURL()
<< " keep=" << _keep << " reload=" << _reload << endl;
// emit the current changes made to avoid an inconsistent treeview
if ( d->changes != NONE && _keep ) {
emitChanges();
}
d->changes = NONE;
if ( !validURL( _url ) ) {
return false;
}
// Some ioslaves like media:/ or home:/ can provide a local url istead of a remote one
// If a local path is available, monitor that instead of the given remote URL...
if (!_url.isLocalFile()) {
TDEIO::LocalURLJob* localURLJob = TDEIO::localURL(_url);
if (localURLJob) {
d->openURLContext[localURLJob] = KDirListerPrivate::OpenURLContext{_url, _keep, _reload};
connect(localURLJob, TQ_SIGNAL(localURL(TDEIO::LocalURLJob*, const KURL&, bool)),
this, TQ_SLOT(slotOpenURLGotLocalURL(TDEIO::LocalURLJob*, const KURL&, bool)));
connect(localURLJob, TQ_SIGNAL(destroyed()), this, TQ_SLOT(slotLocalURLKIODestroyed()));
d->complete = false;
return true;
}
}
return s_pCache->listDir( this, _url, _keep, _reload );
}
void KDirLister::slotOpenURLGotLocalURL(TDEIO::LocalURLJob *job, const KURL& url, bool isLocal) {
auto jobIt = d->openURLContext.find(job);
Q_ASSERT( jobIt != d->openURLContext.end() );
auto ctx = jobIt.data();
KURL realURL = ctx.url;
if (isLocal) {
realURL = url;
realURL.setInternalReferenceURL(ctx.url.url());
d->m_referenceURLMap[ctx.url.url()] = url.path();
}
d->openURLContext.remove(jobIt);
d->complete = d->jobData.isEmpty() && d->openURLContext.isEmpty();
s_pCache->listDir( this, realURL, ctx.keep, ctx.reload );
}
void KDirLister::slotLocalURLKIODestroyed() {
TDEIO::Job* terminatedJob = const_cast<TDEIO::Job*>(static_cast<const TDEIO::Job*>(sender()));
auto jobIt = d->openURLContext.find(terminatedJob);
if (jobIt != d->openURLContext.end()) {
auto ctx = jobIt.data();
d->openURLContext.remove(jobIt);
d->complete = d->jobData.isEmpty() && d->openURLContext.isEmpty();
s_pCache->listDir( this, ctx.url, ctx.keep, ctx.reload );
}
}
void KDirLister::stop()
{
kdDebug(7003) << k_funcinfo << endl;
s_pCache->stop( this );
for( auto it = d->openURLContext.begin(); it != d->openURLContext.end(); ++it ) {
it.key()->disconnect( this );
it.key()->kill();
}
d->openURLContext.clear();
d->m_referenceURLMap.clear();
}
void KDirLister::stop( const KURL& _url )
{
kdDebug(7003) << k_funcinfo << _url.prettyURL() << endl;
s_pCache->stop( this, _url );
TQPtrList<TDEIO::Job> toRemove;
for( auto it = d->openURLContext.begin(); it != d->openURLContext.end(); ++it ) {
if( it.data().url ==_url ) {
it.key()->disconnect( this );
it.key()->kill();
toRemove.append(it.key());
}
}
for(const auto &job: toRemove) {
d->openURLContext.remove(job);
}
d->m_referenceURLMap.remove(_url.url());
}
bool KDirLister::autoUpdate() const
{
return d->autoUpdate;
}
void KDirLister::setAutoUpdate( bool _enable )
{
if ( d->autoUpdate == _enable )
return;
d->autoUpdate = _enable;
s_pCache->setAutoUpdate( this, _enable );
}
bool KDirLister::showingDotFiles() const
{
return d->isShowingDotFiles;
}
void KDirLister::setShowingDotFiles( bool _showDotFiles )
{
if ( d->isShowingDotFiles == _showDotFiles )
return;
d->isShowingDotFiles = _showDotFiles;
d->changes ^= DOT_FILES;
}
bool KDirLister::dirOnlyMode() const
{
return d->dirOnlyMode;
}
void KDirLister::setDirOnlyMode( bool _dirsOnly )
{
if ( d->dirOnlyMode == _dirsOnly )
return;
d->dirOnlyMode = _dirsOnly;
d->changes ^= DIR_ONLY_MODE;
}
bool KDirLister::autoErrorHandlingEnabled() const
{
return d->autoErrorHandling;
}
void KDirLister::setAutoErrorHandlingEnabled( bool enable, TQWidget* parent )
{
d->autoErrorHandling = enable;
d->errorParent = parent;
}
const KURL& KDirLister::url() const
{
return d->url;
}
const KURL::List& KDirLister::directories() const
{
return d->lstDirs;
}
void KDirLister::emitChanges()
{
if ( d->changes == NONE )
return;
static const TQString& dot = TDEGlobal::staticQString(".");
static const TQString& dotdot = TDEGlobal::staticQString("..");
for ( KURL::List::Iterator it = d->lstDirs.begin();
it != d->lstDirs.end(); ++it )
{
KFileItemListIterator kit( *s_pCache->itemsForDir( *it ) );
for ( ; kit.current(); ++kit )
{
if ( (*kit)->text() == dot || (*kit)->text() == dotdot )
continue;
bool oldMime = true, newMime = true;
if ( d->changes & MIME_FILTER )
{
oldMime = doMimeFilter( (*kit)->mimetype(), d->oldMimeFilter )
&& doMimeExcludeFilter( (*kit)->mimetype(), d->oldMimeExcludeFilter );
newMime = doMimeFilter( (*kit)->mimetype(), d->mimeFilter )
&& doMimeExcludeFilter( (*kit)->mimetype(), d->mimeExcludeFilter );
if ( oldMime && !newMime )
{
emit deleteItem( *kit );
continue;
}
}
if ( d->changes & DIR_ONLY_MODE )
{
// the lister switched to dirOnlyMode
if ( d->dirOnlyMode )
{
if ( !(*kit)->isDir() )
emit deleteItem( *kit );
}
else if ( !(*kit)->isDir() ) {
addNewItem( *kit );
}
continue;
}
if ( (*kit)->isHidden() )
{
if ( d->changes & DOT_FILES )
{
// the lister switched to dot files mode
if ( d->isShowingDotFiles ) {
addNewItem( *kit );
}
else {
emit deleteItem( *kit );
}
continue;
}
}
else if ( d->changes & NAME_FILTER )
{
bool oldName = (*kit)->isDir() ||
d->oldFilters.isEmpty() ||
doNameFilter( (*kit)->text(), d->oldFilters );
bool newName = (*kit)->isDir() ||
d->lstFilters.isEmpty() ||
doNameFilter( (*kit)->text(), d->lstFilters );
if ( oldName && !newName )
{
emit deleteItem( *kit );
continue;
}
else if ( !oldName && newName ) {
addNewItem( *kit );
}
}
if ( (d->changes & MIME_FILTER) && !oldMime && newMime ) {
addNewItem( *kit );
}
}
emitItems();
}
d->changes = NONE;
}
void KDirLister::updateDirectory( const KURL& _u )
{
s_pCache->updateDirectory( _u );
}
bool KDirLister::isFinished() const
{
return d->complete;
}
KFileItem *KDirLister::rootItem() const
{
return d->rootFileItem;
}
KFileItem *KDirLister::findByURL( const KURL& _url ) const
{
return s_pCache->findByURL( this, _url );
}
KFileItem *KDirLister::findByName( const TQString& _name ) const
{
return s_pCache->findByName( this, _name );
}
#ifndef KDE_NO_COMPAT
KFileItem *KDirLister::find( const KURL& _url ) const
{
return findByURL( _url );
}
#endif
// ================ public filter methods ================ //
void KDirLister::setNameFilter( const TQString& nameFilter )
{
if ( !(d->changes & NAME_FILTER) )
{
d->oldFilters = d->lstFilters;
d->lstFilters.setAutoDelete( false );
}
d->lstFilters.clear();
d->lstFilters.setAutoDelete( true );
d->nameFilter = nameFilter;
// Split on white space
TQStringList list = TQStringList::split( ' ', nameFilter );
for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it )
d->lstFilters.append( new TQRegExp(*it, false, true ) );
d->changes |= NAME_FILTER;
}
const TQString& KDirLister::nameFilter() const
{
return d->nameFilter;
}
void KDirLister::setMimeFilter( const TQStringList& mimeFilter )
{
if ( !(d->changes & MIME_FILTER) )
d->oldMimeFilter = d->mimeFilter;
if ( mimeFilter.find("all/allfiles") != mimeFilter.end() ||
mimeFilter.find("all/all") != mimeFilter.end() )
d->mimeFilter.clear();
else
d->mimeFilter = mimeFilter;
d->changes |= MIME_FILTER;
}
void KDirLister::setMimeExcludeFilter( const TQStringList& mimeExcludeFilter )
{
if ( !(d->changes & MIME_FILTER) )
d->oldMimeExcludeFilter = d->mimeExcludeFilter;
d->mimeExcludeFilter = mimeExcludeFilter;
d->changes |= MIME_FILTER;
}
void KDirLister::clearMimeFilter()
{
if ( !(d->changes & MIME_FILTER) )
{
d->oldMimeFilter = d->mimeFilter;
d->oldMimeExcludeFilter = d->mimeExcludeFilter;
}
d->mimeFilter.clear();
d->mimeExcludeFilter.clear();
d->changes |= MIME_FILTER;
}
const TQStringList& KDirLister::mimeFilters() const
{
return d->mimeFilter;
}
bool KDirLister::matchesFilter( const TQString& name ) const
{
return doNameFilter( name, d->lstFilters );
}
bool KDirLister::matchesMimeFilter( const TQString& mime ) const
{
return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter);
}
// ================ protected methods ================ //
bool KDirLister::matchesFilter( const KFileItem *item ) const
{
Q_ASSERT( item );
static const TQString& dotdot = TDEGlobal::staticQString("..");
if ( item->text() == dotdot )
return false;
if ( !d->isShowingDotFiles && item->isHidden() )
return false;
if ( item->isDir() || d->lstFilters.isEmpty() )
return true;
return matchesFilter( item->text() );
}
bool KDirLister::matchesMimeFilter( const KFileItem *item ) const
{
Q_ASSERT( item );
// Don't lose time determining the mimetype if there is no filter
if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() )
return true;
return matchesMimeFilter( item->mimetype() );
}
bool KDirLister::doNameFilter( const TQString& name, const TQPtrList<TQRegExp>& filters ) const
{
for ( TQPtrListIterator<TQRegExp> it( filters ); it.current(); ++it )
if ( it.current()->exactMatch( name ) )
return true;
return false;
}
bool KDirLister::doMimeFilter( const TQString& mime, const TQStringList& filters ) const
{
if ( filters.isEmpty() )
return true;
KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
//kdDebug(7004) << "doMimeFilter: investigating: "<<mimeptr->name()<<endl;
TQStringList::ConstIterator it = filters.begin();
for ( ; it != filters.end(); ++it )
if ( mimeptr->is(*it) )
return true;
//else kdDebug(7004) << "doMimeFilter: compared without result to "<<*it<<endl;
return false;
}
bool KDirLister::doMimeExcludeFilter( const TQString& mime, const TQStringList& filters ) const
{
if ( filters.isEmpty() )
return true;
TQStringList::ConstIterator it = filters.begin();
for ( ; it != filters.end(); ++it )
if ( (*it) == mime )
return false;
return true;
}
bool KDirLister::validURL( const KURL& _url ) const
{
return s_pCache->validURL( this, _url );
}
void KDirLister::handleError( TDEIO::Job *job )
{
if ( d->autoErrorHandling ) {
job->showErrorDialog( d->errorParent );
}
}
// ================= private methods ================= //
void KDirLister::addNewItem( const KFileItem *item )
{
TQString refURL = item->url().internalReferenceURL();
if (!refURL.isEmpty() && d->m_referenceURLMap.contains(refURL) &&
item->url().path().startsWith(d->m_referenceURLMap[refURL]))
{
// Likely a media:/ tdeioslave URL or similar
// Rewrite the URL to ensure that the user remains within the media:/ tree!
if (!refURL.endsWith("/"))
{
refURL.append("/");
}
KURL newItemURL(refURL);
newItemURL.addPath(item->url().fileName());
const_cast<KFileItem*>(item)->setListerURL(newItemURL);
const_cast<KFileItem*>(item)->setURL(newItemURL);
}
if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) {
return; // No reason to continue... bailing out here prevents a mimetype scan.
}
if ( matchesMimeFilter( item ) )
{
if ( !d->lstNewItems ) {
d->lstNewItems = new KFileItemList;
}
d->lstNewItems->append( item ); // items not filtered
}
else
{
if ( !d->lstMimeFilteredItems ) {
d->lstMimeFilteredItems = new KFileItemList;
}
d->lstMimeFilteredItems->append( item ); // only filtered by mime
}
}
void KDirLister::addNewItems( const KFileItemList& items )
{
// TODO: make this faster - test if we have a filter at all first
// DF: was this profiled? The matchesFoo() functions should be fast, w/o filters...
// Of course if there is no filter and we can do a range-insertion instead of a loop, that might be good.
for ( KFileItemListIterator kit( items ); kit.current(); ++kit ) {
addNewItem( *kit );
}
}
void KDirLister::aboutToRefreshItem( const KFileItem *item )
{
// The code here follows the logic in addNewItem
if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) )
d->refreshItemWasFiltered = true;
else if ( !matchesMimeFilter( item ) )
d->refreshItemWasFiltered = true;
else
d->refreshItemWasFiltered = false;
}
void KDirLister::addRefreshItem( const KFileItem *item )
{
bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
TQString refURL = item->url().internalReferenceURL();
if (!refURL.isEmpty() && d->m_referenceURLMap.contains(refURL) &&
item->url().path().startsWith(d->m_referenceURLMap[refURL]))
{
// Likely a media:/ tdeioslave URL or similar
// Rewrite the URL to ensure that the user remains within the media:/ tree!
if (!refURL.endsWith("/"))
{
refURL.append("/");
}
KURL newItemURL(refURL);
newItemURL.addPath(item->url().fileName());
const_cast<KFileItem*>(item)->setListerURL(newItemURL);
const_cast<KFileItem*>(item)->setURL(newItemURL);
}
if ( !isExcluded && matchesMimeFilter( item ) )
{
if ( d->refreshItemWasFiltered )
{
if ( !d->lstNewItems ) {
d->lstNewItems = new KFileItemList;
}
d->lstNewItems->append( item );
}
else
{
if ( !d->lstRefreshItems ) {
d->lstRefreshItems = new KFileItemList;
}
d->lstRefreshItems->append( item );
}
}
else if ( !d->refreshItemWasFiltered )
{
if ( !d->lstRemoveItems ) {
d->lstRemoveItems = new KFileItemList;
}
// notify the user that the mimetype of a file changed that doesn't match
// a filter or does match an exclude filter
d->lstRemoveItems->append( item );
}
}
void KDirLister::emitItems()
{
KFileItemList *tmpNew = d->lstNewItems;
d->lstNewItems = 0;
KFileItemList *tmpMime = d->lstMimeFilteredItems;
d->lstMimeFilteredItems = 0;
KFileItemList *tmpRefresh = d->lstRefreshItems;
d->lstRefreshItems = 0;
KFileItemList *tmpRemove = d->lstRemoveItems;
d->lstRemoveItems = 0;
if ( tmpNew )
{
// For historical reasons, items with different protocols and/or prefixes must be emitted separately
TQString protocol;
TQString prefix;
TQString prevProtocol;
TQString prevPrefix;
KFileItemList emitList;
for ( KFileItemListIterator kit( *tmpNew ); kit.current(); ++kit )
{
KFileItem *item = *kit;
protocol = item->url().protocol();
prefix = TQStringList::split("/", item->url().path())[0];
if ((protocol != prevProtocol) || (prefix != prevPrefix)) {
if (emitList.count() > 0) {
emit newItems( emitList );
emitList.clear();
}
}
emitList.append(item);
prevProtocol = protocol;
prevPrefix = prefix;
}
if (emitList.count() > 0) {
emit newItems( emitList );
}
delete tmpNew;
}
if ( tmpMime )
{
emit itemsFilteredByMime( *tmpMime );
delete tmpMime;
}
if ( tmpRefresh )
{
emit refreshItems( *tmpRefresh );
delete tmpRefresh;
}
if ( tmpRemove )
{
for ( KFileItem *tmp = tmpRemove->first(); tmp; tmp = tmpRemove->next() ) {
emit deleteItem( tmp );
}
delete tmpRemove;
}
}
void KDirLister::emitDeleteItem( KFileItem *item )
{
if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) {
return; // No reason to continue... bailing out here prevents a mimetype scan.
}
if ( matchesMimeFilter( item ) ) {
emit deleteItem( item );
}
}
// ================ private slots ================ //
void KDirLister::slotInfoMessage( TDEIO::Job *, const TQString& message )
{
emit infoMessage( message );
}
void KDirLister::slotPercent( TDEIO::Job *job, unsigned long pcnt )
{
d->jobData[static_cast<TDEIO::ListJob *>(job)].percent = pcnt;
int result = 0;
TDEIO::filesize_t size = 0;
TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
while ( dataIt != d->jobData.end() )
{
result += (*dataIt).percent * (*dataIt).totalSize;
size += (*dataIt).totalSize;
++dataIt;
}
if ( size != 0 )
result /= size;
else
result = 100;
emit percent( result );
}
void KDirLister::slotTotalSize( TDEIO::Job *job, TDEIO::filesize_t size )
{
d->jobData[static_cast<TDEIO::ListJob *>(job)].totalSize = size;
TDEIO::filesize_t result = 0;
TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
while ( dataIt != d->jobData.end() )
{
result += (*dataIt).totalSize;
++dataIt;
}
emit totalSize( result );
}
void KDirLister::slotProcessedSize( TDEIO::Job *job, TDEIO::filesize_t size )
{
d->jobData[static_cast<TDEIO::ListJob *>(job)].processedSize = size;
TDEIO::filesize_t result = 0;
TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
while ( dataIt != d->jobData.end() )
{
result += (*dataIt).processedSize;
++dataIt;
}
emit processedSize( result );
}
void KDirLister::slotSpeed( TDEIO::Job *job, unsigned long spd )
{
d->jobData[static_cast<TDEIO::ListJob *>(job)].speed = spd;
int result = 0;
TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
while ( dataIt != d->jobData.end() )
{
result += (*dataIt).speed;
++dataIt;
}
emit speed( result );
}
uint KDirLister::numJobs()
{
return d->jobData.count();
}
void KDirLister::jobDone( TDEIO::ListJob *job )
{
d->jobData.remove( job );
}
void KDirLister::jobStarted( TDEIO::ListJob *job )
{
KDirListerPrivate::JobData jobData;
jobData.speed = 0;
jobData.percent = 0;
jobData.processedSize = 0;
jobData.totalSize = 0;
d->jobData.insert( job, jobData );
d->complete = false;
}
void KDirLister::connectJob( TDEIO::ListJob *job )
{
connect( job, TQ_SIGNAL(infoMessage( TDEIO::Job *, const TQString& )),
this, TQ_SLOT(slotInfoMessage( TDEIO::Job *, const TQString& )) );
connect( job, TQ_SIGNAL(percent( TDEIO::Job *, unsigned long )),
this, TQ_SLOT(slotPercent( TDEIO::Job *, unsigned long )) );
connect( job, TQ_SIGNAL(totalSize( TDEIO::Job *, TDEIO::filesize_t )),
this, TQ_SLOT(slotTotalSize( TDEIO::Job *, TDEIO::filesize_t )) );
connect( job, TQ_SIGNAL(processedSize( TDEIO::Job *, TDEIO::filesize_t )),
this, TQ_SLOT(slotProcessedSize( TDEIO::Job *, TDEIO::filesize_t )) );
connect( job, TQ_SIGNAL(speed( TDEIO::Job *, unsigned long )),
this, TQ_SLOT(slotSpeed( TDEIO::Job *, unsigned long )) );
}
void KDirLister::emitCompleted( const KURL& _url )
{
TQString refURL = _url.internalReferenceURL();
if (!refURL.isEmpty() && d->m_referenceURLMap.contains(refURL) &&
_url.path().startsWith(d->m_referenceURLMap[refURL]))
{
// Likely a media:/ tdeioslave URL or similar
// Rewrite the URL to ensure that the user remains within the media:/ tree!
if (!refURL.endsWith("/"))
{
refURL.append("/");
}
KURL newItemURL(refURL);
newItemURL.addPath(_url.fileName());
emit completed(newItemURL);
}
else
{
emit completed(_url);
}
}
void KDirLister::setMainWindow( TQWidget *window )
{
d->window = window;
}
TQWidget *KDirLister::mainWindow()
{
return d->window;
}
KFileItemList KDirLister::items( WhichItems which ) const
{
return itemsForDir( url(), which );
}
KFileItemList KDirLister::itemsForDir( const KURL& dir, WhichItems which ) const
{
KFileItemList result;
KFileItemList *allItems = s_pCache->itemsForDir( dir );
if ( !allItems ) {
return result;
}
if ( which == AllItems ) {
result = *allItems; // shallow copy
}
else // only items passing the filters
{
for ( KFileItemListIterator kit( *allItems ); kit.current(); ++kit )
{
KFileItem *item = *kit;
bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
if ( !isExcluded && matchesMimeFilter( item ) ) {
result.append( item );
}
}
}
return result;
}
// to keep BC changes
void KDirLister::virtual_hook( int, void * )
{ /*BASE::virtual_hook( id, data );*/ }
#include "kdirlister.moc"
#include "kdirlister_p.moc"