Logo Search packages:      
Sourcecode: k4dirstat version File versions  Download package

kdirtree.cpp

/*
 *   File name:   kdirtree.cpp
 *   Summary:     Support classes for KDirStat
 *   License:     LGPL - See file COPYING.LIB for details.
 *   Author:      Stefan Hundhammer <sh@suse.de>
 *              Joshua Hodosh <kdirstat@grumpypenguin.org>
 *
 *   Updated:     2010-02-01
 */


#ifdef HAVE_CONFIG_H
#   include <config.h>
#endif

#include <kapplication.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include "kdirtree.h"
#include "kdirreadjob.h"
#include "kdirtreecache.h"

using namespace KDirStat;



00027 KDirTree::KDirTree()
    : QObject()
{
    _root         = 0;
    _selection          = 0;
    _isFileProtocol     = false;
    _isBusy       = false;
    _readMethod         = KDirReadUnknown;

    readConfig();

    connect( & _jobQueue, SIGNAL( finished()     ),
           this,        SLOT  ( slotFinished() ) );
}


00043 KDirTree::~KDirTree()
{
    selectItem( 0 );

    if ( _root )
      delete _root;
}


void
00053 KDirTree::readConfig()
{
    KConfigGroup config = KGlobal::config()->group("Directory Reading");

    _crossFileSystems         = config.readEntry( "CrossFileSystems",     false );
    _enableLocalDirReader     = config.readEntry( "EnableLocalDirReader", true  );
}


void
00063 KDirTree::setRoot( KFileInfo *newRoot )
{
    if ( _root )
    {
      selectItem( 0 );
      emit deletingChild( _root );
      delete _root;
      emit childDeleted();
    }

    _root = newRoot;
}


void
00078 KDirTree::clear( bool sendSignals )
{
    _jobQueue.clear();

    if ( _root )
    {
      selectItem( 0 );

      if ( sendSignals )
          emit deletingChild( _root );

      delete _root;
      _root = 0;

      if ( sendSignals )
          emit childDeleted();
    }

    _isBusy = false;
}


void
00101 KDirTree::startReading( const KUrl & url )
{
    // kdDebug() << k_funcinfo << " " << url.url() << endl;

#if 0
    kdDebug() << "url: "            << url.url()            << endl;
    kdDebug() << "path: "           << url.path()           << endl;
    kdDebug() << "filename: "       << url.filename()       << endl;
    kdDebug() << "protocol: "       << url.protocol()       << endl;
    kdDebug() << "isValid: "        << url.isValid()  << endl;
    kdDebug() << "isMalformed: "    << url.isMalformed()    << endl;
    kdDebug() << "isLocalFile: "    << url.isLocalFile()    << endl;
#endif

    _isBusy = true;
    emit startingReading();

    setRoot( 0 );
    readConfig();
    _isFileProtocol = url.isLocalFile();

    if ( _isFileProtocol && _enableLocalDirReader )
    {
      // kdDebug() << "Using local directory reader for " << url.url() << endl;
      _readMethod = KDirReadLocal;
      _root       = KLocalDirReadJob::stat( url, this );
    }
    else
    {
      // kdDebug() << "Using KIO methods for " << url.url() << endl;
      KUrl cleanUrl( url );
      cleanUrl.cleanPath();   // Resolve relative paths, get rid of multiple '/'
      _readMethod = KDirReadKIO;
      _root             = KioDirReadJob::stat( cleanUrl, this );
    }

    if ( _root )
    {
      childAddedNotify( _root );

      if ( _root->isDir() )
      {
          KDirInfo *dir = (KDirInfo *) _root;

          if ( _readMethod == KDirReadLocal )
            addJob( new KLocalDirReadJob( this, dir ) );
          else
            addJob( new KioDirReadJob( this, dir ) );
      }
      else
      {
          _isBusy = false;
          emit finished();
      }
    }
    else    // stat() failed
    {
      // kdWarning() << "stat(" << url.url() << ") failed" << endl;
      _isBusy = false;
      emit finished();
      emit finalizeLocal( 0 );
    }
}


void
00167 KDirTree::refresh( KFileInfo *subtree )
{
    if ( ! _root )
      return;

    if ( ! subtree || ! subtree->parent() )     // Refresh all (from root)
    {
      startReading( fixedUrl( _root->url() ) );
    }
    else    // Refresh subtree
    {
      // Save some values from the old subtree.

      KUrl url          = subtree->url();
      KDirInfo * parent = subtree->parent();


      // Select nothing if the current selection is to be deleted

      if ( _selection && _selection->isInSubtree( subtree ) )
          selectItem( 0 );


      // Clear any old "excluded" status

      subtree->setExcluded( false );

      
      // Get rid of the old subtree.

      emit deletingChild( subtree );

      // kdDebug() << "Deleting subtree " << subtree << endl;

      /**
       * This may sound stupid, but the parent must be told to unlink its
       * child from the children list. The child cannot simply do this by
       * itself in its destructor since at this point important parts of the
       * object may already be destroyed, e.g., the virtual table -
       * i.e. virtual methods won't work any more.
       *
       * I just found that out the hard way by several hours of debugging. ;-}
       **/
      parent->deletingChild( subtree );
      delete subtree;
      emit childDeleted();

      _isBusy = true;
      emit startingReading();
      
      // Create new subtree root.

      subtree = ( _readMethod == KDirReadLocal ) ?
          KLocalDirReadJob::stat( url, this, parent ) : KioDirReadJob::stat( url, this, parent );

      // kdDebug() << "New subtree: " << subtree << endl;

      if ( subtree )
      {
          // Insert new subtree root into the tree hierarchy.

          parent->insertChild( subtree );
          childAddedNotify( subtree );

          if ( subtree->isDir() )
          {
            // Prepare reading this subtree's contents.

            KDirInfo *dir = (KDirInfo *) subtree;

            if ( _readMethod == KDirReadLocal )
                addJob( new KLocalDirReadJob( this, dir ) );
            else
                addJob( new KioDirReadJob( this, dir ) );
          }
          else
          {
            _isBusy = false;
            emit finished();
          }
      }
    }
}


void
00253 KDirTree::abortReading()
{
    if ( _jobQueue.isEmpty() )
      return;

    _jobQueue.abort();

    _isBusy = false;
    emit aborted();
}


void
00266 KDirTree::slotFinished()
{
    _isBusy = false;
    emit finished();
}


void
00274 KDirTree::childAddedNotify( KFileInfo *newChild )
{
    emit childAdded( newChild );

    if ( newChild->dotEntry() )
      emit childAdded( newChild->dotEntry() );
}


void
00284 KDirTree::deletingChildNotify( KFileInfo *deletedChild )
{
    emit deletingChild( deletedChild );

    // Only now check for selection and root: Give connected objects
    // (i.e. views) a chance to change either while handling the signal.

    if ( _selection && _selection->isInSubtree( deletedChild ) )
       selectItem( 0 );

    if ( deletedChild == _root )
      _root = 0;
}


void
00300 KDirTree::childDeletedNotify()
{
    emit childDeleted();
}


void
00307 KDirTree::deleteSubtree( KFileInfo *subtree )
{
    // kdDebug() << "Deleting subtree " << subtree << endl;
    KDirInfo *parent = subtree->parent();

    if ( parent )
    {
      // Give the parent of the child to be deleted a chance to unlink the
      // child from its children list and take care of internal summary
      // fields
      parent->deletingChild( subtree );
    }

    // Send notification to anybody interested (e.g., to attached views)
    deletingChildNotify( subtree );

    if ( parent )
    {
      if ( parent->isDotEntry() && ! parent->hasChildren() )
          // This was the last child of a dot entry
      {
          // Get rid of that now empty and useless dot entry

          if ( parent->parent() )
          {
            if ( parent->parent()->isFinished() )
            {
                // kdDebug() << "Removing empty dot entry " << parent << endl;

                deletingChildNotify( parent );
                parent->parent()->setDotEntry( 0 );

                delete parent;
            }
          }
          else    // no parent - this should never happen (?)
          {
            kdError() << "Internal error: Killing dot entry without parent " << parent << endl;

            // Better leave that dot entry alone - we shouldn't have come
            // here in the first place. Who knows what will happen if this
            // thing is deleted now?!
            //
            // Intentionally NOT calling:
            //     delete parent;
          }
      }
    }

    delete subtree;

    if ( subtree == _root )
    {
      selectItem( 0 );
      _root = 0;
    }

    emit childDeleted();
}


void
00369 KDirTree::addJob( KDirReadJob * job )
{
    _jobQueue.enqueue( job );
}


void
00376 KDirTree::sendProgressInfo( const QString &infoLine )
{
    emit progressInfo( infoLine );
}


void
00383 KDirTree::sendFinalizeLocal( KDirInfo *dir )
{
    emit finalizeLocal( dir );
}


void
00390 KDirTree::sendStartingReading()
{
    emit startingReading();
}


void
00397 KDirTree::sendFinished()
{
    emit finished();
}


void
00404 KDirTree::sendAborted()
{
    emit aborted();
}


void
00411 KDirTree::selectItem( KFileInfo *newSelection )
{
    if ( newSelection == _selection )
      return;

#if 0
    if ( newSelection )
      kdDebug() << k_funcinfo << " selecting " << newSelection << endl;
    else
      kdDebug() << k_funcinfo << " selecting nothing" << endl;
#endif

    _selection = newSelection;
    emit selectionChanged( _selection );
}


bool
00429 KDirTree::writeCache( const QString & cacheFileName )
{
    KCacheWriter writer( cacheFileName, this );
    return writer.ok();
}


void
00437 KDirTree::readCache( const QString & cacheFileName )
{
    _isBusy = true;
    emit startingReading();
    addJob( new KCacheReadJob( this, 0, cacheFileName ) );
}



// EOF

Generated by  Doxygen 1.6.0   Back to index