/***************************************************************************
                         diskusage.cpp  -  description
                             -------------------
    copyright            : (C) 2004 + by Csaba Karai
    e-mail               : krusader@users.sourceforge.net
    web site             : http://krusader.sourceforge.net
 ---------------------------------------------------------------------------
  Description
 ***************************************************************************

  A

     db   dD d8888b. db    db .d8888.  .d8b.  d8888b. d88888b d8888b.
     88 ,8P' 88  `8D 88    88 88'  YP d8' `8b 88  `8D 88'     88  `8D
     88,8P   88oobY' 88    88 `8bo.   88ooo88 88   88 88ooooo 88oobY'
     88`8b   88`8b   88    88   `Y8b. 88~~~88 88   88 88~~~~~ 88`8b
     88 `88. 88 `88. 88b  d88 db   8D 88   88 88  .8D 88.     88 `88.
     YP   YD 88   YD ~Y8888P' `8888Y' YP   YP Y8888D' Y88888P 88   YD

                                                     S o u r c e    F i l e

 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <time.h>
#include <tqlayout.h>
#include <tdelocale.h>
#include <tdepopupmenu.h>
#include <kmimetype.h>
#include <tdemessagebox.h>
#include <tdeglobalsettings.h>
#include <tdeio/job.h>
#include <tqpushbutton.h>
#include <tqhbox.h>
#include <tqapplication.h>
#include <tqcursor.h>
#include <tqpixmapcache.h>
#include <tqgroupbox.h>
#include <tqguardedptr.h>
#include "diskusage.h"
#include "../VFS/krpermhandler.h"
#include "../VFS/krvfshandler.h"
#include "../kicons.h"
#include "../defaults.h"
#include "../krusader.h"
#include "../krusaderview.h"
#include "../Panel/listpanel.h"
#include "../Panel/panelfunc.h"
#include "filelightParts/Config.h"

#include "dulines.h"
#include "dulistview.h"
#include "dufilelight.h"

// these are the values that will exist in the menu
#define DELETE_ID            90
#define EXCLUDE_ID           91
#define PARENT_DIR_ID        92
#define NEW_SEARCH_ID        93
#define REFRESH_ID           94
#define STEP_INTO_ID         95
#define INCLUDE_ALL_ID       96
#define VIEW_POPUP_ID        97
#define LINES_VIEW_ID        98
#define DETAILED_VIEW_ID     99
#define FILELIGHT_VIEW_ID   100
#define NEXT_VIEW_ID        101
#define PREVIOUS_VIEW_ID    102
#define ADDITIONAL_POPUP_ID 103

#define MAX_FILENUM         100

LoaderWidget::LoaderWidget( TQWidget *parent, const char *name ) : TQScrollView( parent, name ), cancelled( false )
{
  viewport()->setEraseColor( TQt::white );
  widget = new TQWidget( parent );

  TQGridLayout *loaderLayout = new TQGridLayout( widget );
  loaderLayout->setSpacing( 0 );
  loaderLayout->setMargin( 0 );

  TQGroupBox *loaderBox = new TQGroupBox( widget, "loaderGroupBox" );
  loaderBox->setFrameShape( TQGroupBox::Box );
  loaderBox->setFrameShadow( TQGroupBox::Sunken );
  loaderBox->setColumnLayout(0, TQt::Vertical );
  loaderBox->layout()->setSpacing( 0 );
  loaderBox->layout()->setMargin( 0 );
  loaderBox->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed );
  loaderBox->setFrameStyle( TQFrame::Panel + TQFrame::Raised );
  loaderBox->setLineWidth( 2 );

  TQGridLayout *synchGrid = new TQGridLayout( loaderBox->layout() );
  synchGrid->setSpacing( 6 );
  synchGrid->setMargin( 11 );

  TQLabel *titleLabel = new TQLabel( i18n( "Loading Usage Information" ), loaderBox, "titleLabel" );
  titleLabel->setAlignment( TQt::AlignHCenter );
  synchGrid->addMultiCellWidget( titleLabel, 0, 0, 0, 1 );

  TQLabel *filesLabel = new TQLabel( i18n( "Files:" ), loaderBox, "filesLabel" );
  filesLabel->setFrameShape( TQLabel::StyledPanel );
  filesLabel->setFrameShadow( TQLabel::Sunken );
  synchGrid->addWidget( filesLabel, 1, 0 );

  TQLabel *directoriesLabel = new TQLabel( i18n( "Directories:" ), loaderBox, "directoriesLabel" );
  directoriesLabel->setFrameShape( TQLabel::StyledPanel );
  directoriesLabel->setFrameShadow( TQLabel::Sunken );
  synchGrid->addWidget( directoriesLabel, 2, 0 );

  TQLabel *totalSizeLabel = new TQLabel( i18n( "Total Size:" ), loaderBox, "totalSizeLabel" );
  totalSizeLabel->setFrameShape( TQLabel::StyledPanel );
  totalSizeLabel->setFrameShadow( TQLabel::Sunken );
  synchGrid->addWidget( totalSizeLabel, 3, 0 );

  files = new TQLabel( loaderBox, "files" );
  files->setFrameShape( TQLabel::StyledPanel );
  files->setFrameShadow( TQLabel::Sunken );
  files->setAlignment( TQt::AlignRight );
  synchGrid->addWidget( files, 1, 1 );

  directories = new TQLabel( loaderBox, "directories" );
  directories->setFrameShape( TQLabel::StyledPanel );
  directories->setFrameShadow( TQLabel::Sunken );
  directories->setAlignment( TQt::AlignRight );
  synchGrid->addWidget( directories, 2, 1 );

  totalSize = new TQLabel( loaderBox, "totalSize" );
  totalSize->setFrameShape( TQLabel::StyledPanel );
  totalSize->setFrameShadow( TQLabel::Sunken );
  totalSize->setAlignment( TQt::AlignRight );
  synchGrid->addWidget( totalSize, 3, 1 );

  int width;
  searchedDirectory = new KSqueezedTextLabel( loaderBox, "searchedDirectory" );
  searchedDirectory->setFrameShape( TQLabel::StyledPanel );
  searchedDirectory->setFrameShadow( TQLabel::Sunken );
  searchedDirectory->setMinimumWidth( width = TQFontMetrics(searchedDirectory->font()).width("W") * 30 );
  searchedDirectory->setMaximumWidth( width );
  synchGrid->addMultiCellWidget( searchedDirectory, 4, 4, 0, 1 );

  TQFrame *line = new TQFrame( loaderBox, "duLine" );
  line->setFrameStyle( TQFrame::HLine | TQFrame::Sunken );
  synchGrid->addMultiCellWidget( line, 5, 5, 0, 1 );

  TQHBox *hbox = new TQHBox( loaderBox, "hbox" );
  TQSpacerItem* spacer = new TQSpacerItem( 0, 0, TQSizePolicy::Minimum, TQSizePolicy::Expanding );
  hbox->layout()->addItem( spacer );
  TQPushButton *cancelButton = new TQPushButton( hbox, "cancelButton" );
  cancelButton->setText( i18n( "Cancel"  ) );
  synchGrid->addWidget( hbox, 6, 1 );

  loaderLayout->addWidget( loaderBox, 0, 0 );

  addChild( widget );

  connect( cancelButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotCancelled() ) );
}

void LoaderWidget::resizeEvent ( TQResizeEvent *e )
{
  TQScrollView::resizeEvent( e );

  int x = ( viewport()->width() - widget->width() ) / 2;
  int y = ( viewport()->height() - widget->height() ) / 2;
  if( x < 0 ) x=0;
  if( y < 0 ) y=0;

  moveChild( widget, x, y );
}

void LoaderWidget::init()
{
  cancelled = false;
}

void LoaderWidget::setCurrentURL( KURL url )
{
  searchedDirectory->setText( vfs::pathOrURL( url, 1) );
}

void LoaderWidget::setValues( int fileNum, int dirNum, TDEIO::filesize_t total )
{
  files->setText( TQString("%1").arg( fileNum ) );
  directories->setText( TQString("%1").arg( dirNum ) );
  totalSize->setText( TQString("%1").arg( KRpermHandler::parseSize( total ).stripWhiteSpace() ) );
}

void LoaderWidget::slotCancelled()
{
  cancelled = true;
}

DiskUsage::DiskUsage( TQString confGroup, TQWidget *parent, char *name ) : TQWidgetStack( parent, name ),
                      currentDirectory( 0 ), root( 0 ), configGroup( confGroup ), loading( false ),
                      abortLoading( false ), clearAfterAbort( false ), deleting( false ), searchVfs( 0 )
{
  listView = new DUListView( this, "DU ListView" );
  lineView = new DULines( this, "DU LineView" );
  filelightView = new DUFilelight( this, "Filelight canvas" );
  loaderView = new LoaderWidget( this, "Loading view" );

  addWidget( listView );
  addWidget( lineView );
  addWidget( filelightView );
  addWidget( loaderView );

  setView( VIEW_LINES );

  Filelight::Config::read();
  propertyMap.setAutoDelete( true );

  connect( &loadingTimer, TQ_SIGNAL( timeout() ), this, TQ_SLOT( slotLoadDirectory() ) );
}

DiskUsage::~DiskUsage()
{
  if( root )
    delete root;

  if( listView )         // don't remove these lines. The module will crash at exit if removed
    delete listView;
  if( lineView )
    delete lineView;
  if( filelightView )
    delete filelightView;
}

void DiskUsage::load( KURL baseDir )
{
  if( searchVfs && !searchVfs->vfs_canDelete() ) {
    return;
  }

  fileNum = dirNum = 0;
  currentSize = 0;

  emit status( i18n( "Loading the disk usage information..." ) );

  clear();

  baseURL = baseDir;
  baseURL.setPath( baseDir.path( -1 ) );

  root = new Directory( baseURL.fileName(), vfs::pathOrURL( baseDir ) );

  directoryStack.clear();
  parentStack.clear();

  directoryStack.push( "" );
  parentStack.push( root );

  if( searchVfs )
  {
    delete searchVfs;
    searchVfs = 0;
  }
  searchVfs = KrVfsHandler::getVfs( baseDir );
  if( searchVfs == 0 )
  {
    loading = abortLoading = clearAfterAbort = false;
    emit loadFinished( false );
    return;
  }

  searchVfs->vfs_setQuiet( true );
  currentVfile = 0;

  if( !loading )
  {
    viewBeforeLoad = activeView;
    setView( VIEW_LOADER );
  }

  loading = true;

  loaderView->init();
  loaderView->setCurrentURL( baseURL );
  loaderView->setValues( fileNum, dirNum, currentSize );

  loadingTimer.start( 0, true );
}

void DiskUsage::slotLoadDirectory()
{
  if( searchVfs && !searchVfs->vfs_canDelete() ) { // recursive call from slotLoadDirectory?
    loadingTimer.start( 100, true );               // as it can cause crash, ignore it and wait while
    return;                                        // the recursion finishes
  }
  if( ( currentVfile == 0 && directoryStack.isEmpty() ) || loaderView->wasCancelled() || abortLoading )
  {
    if( searchVfs )
      delete searchVfs;
    
    searchVfs = 0;
    currentVfile = 0;

    setView( viewBeforeLoad );

    if( clearAfterAbort )
      clear();
    else {
      calculateSizes();
      changeDirectory( root );
    }

    emit loadFinished( !( loaderView->wasCancelled() || abortLoading ) );

    loading = abortLoading = clearAfterAbort = false;
  }
  else if( loading )
  {
    for( int counter = 0; counter != MAX_FILENUM; counter ++ )
    {
      if( currentVfile == 0 )
      {
        if( directoryStack.isEmpty() )
          break;

        dirToCheck = directoryStack.pop();
        currentParent = parentStack.pop();

        contentMap.insert( dirToCheck, currentParent );

        KURL url = baseURL;

        if( !dirToCheck.isEmpty() )
          url.addPath( dirToCheck );

#if defined(BSD)
        if ( url.isLocalFile() && url.path().left( 7 ) == "/procfs" )
          break;
#else
        if ( url.isLocalFile() && url.path().left( 5 ) == "/proc" )
          break;
#endif

        loaderView->setCurrentURL( url );

        if( !searchVfs->vfs_refresh( url ) )
          break;

        dirNum++;

        currentVfile = searchVfs->vfs_getFirstFile();
      }
      else
      {
        fileNum++;
        File *newItem = 0;

        TQString mime = currentVfile->vfile_getMime(true); // fast == not using mimetype magic

        if( currentVfile->vfile_isDir() && !currentVfile->vfile_isSymLink() )
        {
          newItem = new Directory( currentParent, currentVfile->vfile_getName(), dirToCheck, currentVfile->vfile_getSize(),
                                   currentVfile->vfile_getMode(), currentVfile->vfile_getOwner(), currentVfile->vfile_getGroup(),
                                   currentVfile->vfile_getPerm(), currentVfile->vfile_getTime_t(), currentVfile->vfile_isSymLink(),
                                   mime );
          directoryStack.push( (dirToCheck.isEmpty() ? "" : dirToCheck + "/" )+ currentVfile->vfile_getName() );
          parentStack.push( dynamic_cast<Directory *>( newItem ) );
        }
        else
        {
          newItem = new File( currentParent, currentVfile->vfile_getName(), dirToCheck, currentVfile->vfile_getSize(),
                              currentVfile->vfile_getMode(), currentVfile->vfile_getOwner(), currentVfile->vfile_getGroup(),
                              currentVfile->vfile_getPerm(), currentVfile->vfile_getTime_t(), currentVfile->vfile_isSymLink(),
                              mime );
          currentSize += currentVfile->vfile_getSize();
        }
        currentParent->append( newItem );

        currentVfile = searchVfs->vfs_getNextFile();
      }
    }

    loaderView->setValues( fileNum, dirNum, currentSize );
    loadingTimer.start( 0, true );
  }
}

void DiskUsage::stopLoad()
{
  abortLoading = true;
}

void DiskUsage::close()
{
  if( loading )
  {
    abortLoading = true;
    clearAfterAbort = true;
  }
}

void DiskUsage::dirUp()
{
  if( currentDirectory != 0 )
  {
    if ( currentDirectory->parent() != 0 )
      changeDirectory( (Directory *)(currentDirectory->parent()) );
    else
    {
      KURL up = baseURL.upURL();

      if( KMessageBox::questionYesNo( this, i18n( "Stepping into the parent directory requires "
                                                  "loading the content of the \"%1\" URL. Do you wish "
                                                  "to continue?" )
                                            .arg( vfs::pathOrURL( up ) ),
                                            i18n( "Krusader::DiskUsage" ), KStdGuiItem::yes(),
                                            KStdGuiItem::no(), "DiskUsageLoadParentDir"
                                            ) == KMessageBox::Yes )
        load( up );
    }
  }
}

Directory * DiskUsage::getDirectory( TQString dir )
{
  while( dir.endsWith( "/" ) )
    dir.truncate( dir.length() - 1 );

  if( dir.isEmpty() )
    return root;

  return contentMap.find( dir );
}

File * DiskUsage::getFile( TQString path )
{
  if( path == "" )
    return root;

  TQString dir = path;

  int ndx = path.findRev( '/' );
  TQString file = path.mid( ndx + 1 );

  if( ndx == -1 )
    dir = "";
  else
    dir.truncate( ndx );

  Directory *dirEntry = getDirectory( dir );
  if( dirEntry == 0 )
    return 0;

  for( Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it )
    if( (*it)->name() == file )
      return *it;

  return 0;
}

void DiskUsage::clear()
{
  baseURL = KURL();
  emit clearing();
  propertyMap.clear();
  contentMap.clear();
  if( root )
    delete root;
  root = currentDirectory = 0;
}

int DiskUsage::calculateSizes( Directory *dirEntry, bool emitSig, int depth )
{
  int changeNr = 0;

  if( dirEntry == 0 )
    dirEntry = root;

  TDEIO::filesize_t own = 0, total = 0;

  for( Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it )
  {
    File * item = *it;

    if( !item->isExcluded() )
    {
      if( item->isDir() )
        changeNr += calculateSizes( dynamic_cast<Directory *>( item ), emitSig, depth + 1 );
      else
        own += item->size();

      total += item->size();
    }
  }

  TDEIO::filesize_t oldOwn = dirEntry->ownSize(), oldTotal = dirEntry->size();
  dirEntry->setSizes( total, own );

  if( dirEntry == currentDirectory )
    currentSize = total;

  if( emitSig && ( own != oldOwn || total != oldTotal ) ) {
    emit changed( dirEntry );
    changeNr++;
  }

  if( depth == 0 && changeNr != 0 )
    emit changeFinished();
  return changeNr;
}

int DiskUsage::exclude( File *file, bool calcPercents, int depth )
{
  int changeNr = 0;

  if( !file->isExcluded() )
  {
    file->exclude( true );
    emit changed( file );
    changeNr++;

    if( file->isDir() )
    {
      Directory *dir = dynamic_cast<Directory *>( file );
      for( Iterator<File> it = dir->iterator(); it != dir->end(); ++it )
        changeNr += exclude( *it, false, depth + 1 );
    }
  }

  if( calcPercents )
  {
    calculateSizes( root, true );
    calculatePercents( true );
    createStatus();
  }

  if( depth == 0 && changeNr != 0 )
    emit changeFinished();

  return changeNr;
}

int DiskUsage::include( Directory *dir, int depth )
{
  int changeNr = 0;

  if( dir == 0 )
    return 0;

  for( Iterator<File> it = dir->iterator(); it != dir->end(); ++it )
  {
    File *item = *it;

    if( item->isDir() )
      changeNr += include( dynamic_cast<Directory *>( item ), depth + 1 );

    if( item->isExcluded() )
    {
      item->exclude( false );
      emit changed( item );
      changeNr++;
    }
  }

  if( depth == 0 && changeNr != 0 )
    emit changeFinished();

  return changeNr;
}

void DiskUsage::includeAll()
{
  include( root );
  calculateSizes( root, true );
  calculatePercents( true );
  createStatus();
}

int DiskUsage::del( File *file, bool calcPercents, int depth )
{
  int deleteNr = 0;

  if( file == root )
    return 0;

  krConfig->setGroup( "General" );
  bool trash = krConfig->readBoolEntry( "Move To Trash", _MoveToTrash );
  KURL url = vfs::fromPathOrURL( file->fullPath() );

  if( calcPercents )
  {
    // now ask the user if he want to delete:
    krConfig->setGroup( "Advanced" );
    if ( krConfig->readBoolEntry( "Confirm Delete", _ConfirmDelete ) ) {
      TQString s, b;
      if ( trash && url.isLocalFile() ) {
        s = i18n( "Do you really want to move this item to the trash?" );
        b = i18n( "&Trash" );
      } else {
        s = i18n( "Do you really want to delete this item?" );
        b = i18n( "&Delete" );
      }

      TQStringList name;
      name.append( file->fullPath() );
      // show message
      // note: i'm using continue and not yes/no because the yes/no has cancel as default button
      if ( KMessageBox::warningContinueCancelList( krApp, s, name, i18n( "Warning" ), b ) != KMessageBox::Continue )
        return 0;
    }

    emit status( i18n( "Deleting %1..." ).arg( file->name() ) );
  }

  if( file == currentDirectory )
    dirUp();

  if( file->isDir() )
  {
    Directory *dir = dynamic_cast<Directory *>( file );

    Iterator<File> it;
    while( ( it = dir->iterator() ) != dir->end() )
      deleteNr += del( *it, false, depth + 1 );

    TQString path;
    for( const Directory *d = (Directory*)file; d != root && d && d->parent() != 0; d = d->parent() )
    {
      if( !path.isEmpty() )
        path = "/" + path;

      path = d->name() + path;
    }

    contentMap.remove( path );
  }

  emit deleted( file );
  deleteNr++;

  TQGuardedPtr<TDEIO::Job> job;

  if( trash )
  {
#if KDE_IS_VERSION(3,4,0)
    job = TDEIO::trash( url, true );
#else
    job = new TDEIO::CopyJob( url,TDEGlobalSettings::trashPath(),TDEIO::CopyJob::Move,false,true );
#endif
    connect(job,TQ_SIGNAL(result(TDEIO::Job*)),krApp,TQ_SLOT(changeTrashIcon()));
  }
  else
  {
    job = new TDEIO::DeleteJob( vfs::fromPathOrURL( file->fullPath() ), false, false);
  }

  deleting = true;    // during tqApp->processEvent strange things can occur
  grabMouse();        // that's why we disable the mouse and keyboard events
  grabKeyboard();

  while( !job.isNull() )
    tqApp->processEvents();

  releaseMouse();
  releaseKeyboard(); 
  deleting = false;

  ((Directory *)(file->parent()))->remove( file );
  delete file;

  if( depth == 0 )
    createStatus();

  if( calcPercents )
  {
    calculateSizes( root, true );
    calculatePercents( true );
    createStatus();
    emit enteringDirectory( currentDirectory );
  }

  if( depth == 0 && deleteNr != 0 )
    emit deleteFinished();

  return deleteNr;
}

void * DiskUsage::getProperty( File *item, TQString key )
{
  Properties * props = propertyMap.find( item );
  if( props == 0 )
    return 0;
  return props->find( key );
}

void DiskUsage::addProperty( File *item, TQString key, void * prop )
{
  Properties * props = propertyMap.find( item );
  if( props == 0 )
  {
    props = new Properties();
    propertyMap.insert( item, props );
  }
  props->insert( key, prop );
}

void DiskUsage::removeProperty( File *item, TQString key )
{
  Properties * props = propertyMap.find( item );
  if( props == 0 )
    return;
  props->remove( key );
  if( props->count() == 0 )
    propertyMap.remove( item );
}

void DiskUsage::createStatus()
{
  Directory *dirEntry = currentDirectory;

  if( dirEntry == 0 )
    return;

  KURL url = baseURL;
  if( dirEntry != root )
      url.addPath( dirEntry->directory() );

  emit status( i18n( "Current directory:%1,  Total size:%2,  Own size:%3" )
               .arg( vfs::pathOrURL( url, -1 ) )
               .arg( " "+KRpermHandler::parseSize( dirEntry->size() ) )
               .arg( " "+KRpermHandler::parseSize( dirEntry->ownSize() ) ) );
}

void DiskUsage::changeDirectory( Directory *dir )
{
  currentDirectory = dir;

  currentSize = dir->size();
  calculatePercents( true, dir );

  createStatus();
  emit enteringDirectory( dir );
}

Directory* DiskUsage::getCurrentDir()
{
  return currentDirectory;
}

void DiskUsage::rightClickMenu( File *fileItem, TDEPopupMenu *addPopup, TQString addPopupName )
{
  TDEPopupMenu popup( this );

  popup.insertTitle( i18n("Disk Usage"));

  if( fileItem != 0 )
  {
    popup.insertItem(  i18n("Delete"),          DELETE_ID);
    popup.setAccel( Key_Delete, DELETE_ID );
    popup.insertItem(  i18n("Exclude"),         EXCLUDE_ID);
    popup.setAccel( CTRL + Key_E, EXCLUDE_ID );
    popup.insertSeparator();
  }

  popup.insertItem(  i18n("Up one directory"),  PARENT_DIR_ID);
  popup.setAccel( SHIFT + Key_Up, PARENT_DIR_ID );
  popup.insertItem(  i18n("New search"),        NEW_SEARCH_ID);
  popup.setAccel( CTRL + Key_N, NEW_SEARCH_ID );
  popup.insertItem(  i18n("Refresh"),           REFRESH_ID);
  popup.setAccel( CTRL + Key_R, REFRESH_ID );
  popup.insertItem(  i18n("Include all"),       INCLUDE_ALL_ID);
  popup.setAccel( CTRL + Key_I, INCLUDE_ALL_ID );
  popup.insertItem(  i18n("Step into"),         STEP_INTO_ID);
  popup.setAccel( SHIFT + Key_Down, STEP_INTO_ID );
  popup.insertSeparator();


  if( addPopup != 0 )
  {
    popup.insertItem( TQPixmap(), addPopup, ADDITIONAL_POPUP_ID );
    popup.changeItem( ADDITIONAL_POPUP_ID, addPopupName );
  }

  TDEPopupMenu viewPopup;
  viewPopup.insertItem(i18n("Lines"),      LINES_VIEW_ID);
  viewPopup.setAccel( CTRL + Key_L, LINES_VIEW_ID );
  viewPopup.insertItem(i18n("Detailed"),   DETAILED_VIEW_ID);
  viewPopup.setAccel( CTRL + Key_D, DETAILED_VIEW_ID );
  viewPopup.insertItem(i18n("Filelight"),  FILELIGHT_VIEW_ID);
  viewPopup.setAccel( CTRL + Key_F, FILELIGHT_VIEW_ID );
  viewPopup.insertSeparator();
  viewPopup.insertItem(i18n("Next"),       NEXT_VIEW_ID);
  viewPopup.setAccel( SHIFT + Key_Right, NEXT_VIEW_ID );
  viewPopup.insertItem(i18n("Previous"),   PREVIOUS_VIEW_ID);
  viewPopup.setAccel( SHIFT + Key_Left, PREVIOUS_VIEW_ID );

  popup.insertItem( TQPixmap(), &viewPopup, VIEW_POPUP_ID );
  popup.changeItem( VIEW_POPUP_ID, i18n( "View" ) );

  int result=popup.exec(TQCursor::pos());

  executeAction( result, fileItem );
}

void DiskUsage::executeAction( int action, File * fileItem )
{
  // check out the user's option
  switch ( action )
  {
  case DELETE_ID:
    if( fileItem )
      del( fileItem );
    break;
  case EXCLUDE_ID:
    if( fileItem )
      exclude( fileItem );
    break;
  case PARENT_DIR_ID:
    dirUp();
    break;
  case NEW_SEARCH_ID:
    emit newSearch();
    break;
  case REFRESH_ID:
    load( baseURL );
    break;
  case INCLUDE_ALL_ID:
    includeAll();
    break;
  case STEP_INTO_ID:
    {
      TQString uri;
      if( fileItem && fileItem->isDir() )
        uri = fileItem->fullPath();
      else
        uri = currentDirectory->fullPath();
      ACTIVE_FUNC->openUrl(vfs::fromPathOrURL( uri ));
    }
    break;
  case LINES_VIEW_ID:
    setView( VIEW_LINES );
    break;
  case DETAILED_VIEW_ID:
    setView( VIEW_DETAILED );
    break;
  case FILELIGHT_VIEW_ID:
    setView( VIEW_FILELIGHT );
    break;
  case NEXT_VIEW_ID:
    setView( ( activeView + 1 ) % 3 );
    break;
  case PREVIOUS_VIEW_ID:
    setView( ( activeView + 2 ) % 3 );
    break;
  }
  visibleWidget()->setFocus();
}

void DiskUsage::keyPressEvent( TQKeyEvent *e )
{
  if( activeView != VIEW_LOADER )
  {
    switch ( e->key() )
    {
    case Key_E:
      if( e->state() == ControlButton )
      {
        executeAction( EXCLUDE_ID, getCurrentFile() );
        return;
      }
    case Key_D:
      if( e->state() == ControlButton )
      {
        executeAction( DETAILED_VIEW_ID );
        return;
      }
    case Key_F:
      if( e->state() == ControlButton )
      {
        executeAction( FILELIGHT_VIEW_ID );
        return;
      }
    case Key_I:
      if( e->state() == ControlButton )
      {
        executeAction( INCLUDE_ALL_ID );
        return;
      }
      break;
    case Key_L:
      if( e->state() == ControlButton )
      {
        executeAction( LINES_VIEW_ID );
        return;
      }
    case Key_N:
      if( e->state() == ControlButton )
      {
        executeAction( NEW_SEARCH_ID );
        return;
      }
      break;
    case Key_R:
      if( e->state() == ControlButton )
      {
        executeAction( REFRESH_ID );
        return;
      }
      break;
    case Key_Up:
      if( e->state() == ShiftButton )
      {
        executeAction( PARENT_DIR_ID );
        return;
      }
      break;
    case Key_Down:
      if( e->state() == ShiftButton )
      {
        executeAction( STEP_INTO_ID );
        return;
      }
      break;
    case Key_Left:
      if( e->state() == ShiftButton )
      {
        executeAction( PREVIOUS_VIEW_ID );
        return;
      }
      break;
    case Key_Right:
      if( e->state() == ShiftButton )
      {
        executeAction( NEXT_VIEW_ID );
        return;
      }
      break;
    case Key_Delete:
      if( !e->state() )
      {
        executeAction( DELETE_ID, getCurrentFile() );
        return;
      }
    case Key_Plus:
      if( activeView == VIEW_FILELIGHT )
      {
        filelightView->zoomIn();
        return;
      }
      break;
    case Key_Minus:
      if( activeView == VIEW_FILELIGHT )
      {
        filelightView->zoomOut();
        return;
      }
      break;
    }
  }
  TQWidgetStack::keyPressEvent( e );
}

TQPixmap DiskUsage::getIcon( TQString mime )
{
  TQPixmap icon;

  if ( !TQPixmapCache::find( mime, icon ) )
  {
    // get the icon.
    if ( mime == "Broken Link !" )
      icon = FL_LOADICON( "file_broken" );
    else
      icon = FL_LOADICON( KMimeType::mimeType( mime ) ->icon( TQString(), true ) );

    // insert it into the cache
    TQPixmapCache::insert( mime, icon );
  }
  return icon;
}

int DiskUsage::calculatePercents( bool emitSig, Directory *dirEntry, int depth )
{
  int changeNr = 0;

  if( dirEntry == 0 )
    dirEntry = root;

  for( Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it )
  {
    File *item = *it;

    if( !item->isExcluded() )
    {
      int newPerc;

      if( dirEntry->size() == 0 && item->size() == 0 )
        newPerc = 0;
      else if( dirEntry->size() == 0 )
        newPerc = -1;
      else
        newPerc = (int)((double)item->size() / (double)currentSize * 10000. + 0.5);

      int oldPerc = item->intPercent();
      item->setPercent( newPerc );

      if( emitSig && newPerc != oldPerc ) {
        emit changed( item );
        changeNr++;
      }

      if( item->isDir() )
        changeNr += calculatePercents( emitSig, dynamic_cast<Directory *>( item ), depth + 1 );
    }
  }

  if( depth == 0 && changeNr != 0 )
    emit changeFinished();
  return changeNr;
}

TQString DiskUsage::getToolTip( File *item )
{
  KMimeType::Ptr mimePtr = KMimeType::mimeType( item->mime() );
  TQString mime = mimePtr->comment();

  time_t tma = item->time();
  struct tm* t=localtime((time_t *)&tma);
  TQDateTime tmp(TQDate(t->tm_year+1900, t->tm_mon+1, t->tm_mday), TQTime(t->tm_hour, t->tm_min));
  TQString date = TDEGlobal::locale()->formatDateTime(tmp);

  TQString str = "<qt><h5><table><tr><td>" + i18n( "Name:" ) +  "</td><td>" + item->name() + "</td></tr>"+
                "<tr><td>" + i18n( "Type:" ) +  "</td><td>" + mime + "</td></tr>"+
                "<tr><td>" + i18n( "Size:" ) +  "</td><td>" + KRpermHandler::parseSize( item->size() ) + "</td></tr>";

  if( item->isDir() )
    str +=      "<tr><td>" + i18n( "Own size:" ) +  "</td><td>" + KRpermHandler::parseSize( item->ownSize() ) + "</td></tr>";

  str +=        "<tr><td>" + i18n( "Last modified:" ) +  "</td><td>" + date + "</td></tr>"+
                "<tr><td>" + i18n( "Permissions:" ) +  "</td><td>" + item->perm() + "</td></tr>"+
                "<tr><td>" + i18n( "Owner:" ) +  "</td><td>" + item->owner() + " - " + item->group() + "</td></tr>"+
                "</table></h5></qt>";
  str.replace( " ", "&nbsp;" );
  return str;
}

void DiskUsage::setView( int view )
{
  switch( view )
  {
  case VIEW_LINES:
    raiseWidget( lineView );
    break;
  case VIEW_DETAILED:
    raiseWidget( listView );
    break;
  case VIEW_FILELIGHT:
    raiseWidget( filelightView );
    break;
  case VIEW_LOADER:
    raiseWidget( loaderView );
    break;
  }

  visibleWidget()->setFocus();
  emit viewChanged( activeView = view );
}

File * DiskUsage::getCurrentFile()
{
  File * file = 0;

  switch( activeView )
  {
  case VIEW_LINES:
    file = lineView->getCurrentFile();
    break;
  case VIEW_DETAILED:
    file = listView->getCurrentFile();
    break;
  case VIEW_FILELIGHT:
    file = filelightView->getCurrentFile();
    break;
  }

  return file;
}

bool DiskUsage::event( TQEvent * e )
{
  if( deleting ) {                       // if we are deleting, disable the mouse and
    switch( e->type() ) {                // keyboard events
    case TQEvent::MouseButtonPress:
    case TQEvent::MouseButtonRelease:
    case TQEvent::MouseButtonDblClick:
    case TQEvent::MouseMove:
    case TQEvent::KeyPress:
    case TQEvent::KeyRelease:
      return true;
    default:
      break;
    }
  }

  if ( e->type() == TQEvent::AccelOverride )
  {
    TQKeyEvent* ke = (TQKeyEvent*) e;

    if ( ke->state() == TQt::NoButton || ke->state() == Keypad )
    {
      switch ( ke->key() )
      {
        case Key_Delete:
        case Key_Plus:
        case Key_Minus:
          ke->accept();
          break;
      }
    }else if( ke->state() == ShiftButton )
    {
      switch ( ke->key() )
      {
        case Key_Left:
        case Key_Right:
        case Key_Up:
        case Key_Down:
          ke->accept();
          break;
      }
    }else if ( ke->state() & ControlButton )
    {
      switch ( ke->key() )
      {
        case Key_D:
        case Key_E:
        case Key_F:
        case Key_I:
        case Key_L:
        case Key_N:
        case Key_R:
          ke->accept();
          break;
      }
    }
  }
  return TQWidgetStack::event( e );
}

#include "diskusage.moc"
