diff options
author | Michele Calgaro <michele.calgaro@yahoo.it> | 2025-08-28 22:44:34 +0900 |
---|---|---|
committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2025-08-31 23:30:34 +0900 |
commit | f9abd9d505434c9244c03eac708e29a0ca042f6b (patch) | |
tree | 30a197ab4c413849188bc131ff859212e636c821 /src/app/DiskUsage | |
parent | 14d42d284de233f9937becf3fc9ee0dabede3b21 (diff) | |
download | krusader-r14.1.x.tar.gz krusader-r14.1.x.zip |
Restructure source foldersr14.1.x
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
(cherry picked from commit 086012dcad8a976a0dabbb7cbc20c9cb612cdfa9)
Diffstat (limited to 'src/app/DiskUsage')
29 files changed, 5328 insertions, 0 deletions
diff --git a/src/app/DiskUsage/Makefile.am b/src/app/DiskUsage/Makefile.am new file mode 100644 index 0000000..691d045 --- /dev/null +++ b/src/app/DiskUsage/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = \ + radialMap \ + filelightParts + + +noinst_LIBRARIES = libDiskUsage.a + +INCLUDES = $(all_includes) + +libDiskUsage_a_METASOURCES = AUTO + +libDiskUsage_a_SOURCES = \ + diskusagegui.cpp \ + diskusage.cpp \ + dulistview.cpp \ + dulines.cpp \ + dufilelight.cpp diff --git a/src/app/DiskUsage/diskusage.cpp b/src/app/DiskUsage/diskusage.cpp new file mode 100644 index 0000000..c372e8c --- /dev/null +++ b/src/app/DiskUsage/diskusage.cpp @@ -0,0 +1,1147 @@ +/*************************************************************************** + 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( " ", " " ); + 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" diff --git a/src/app/DiskUsage/diskusage.h b/src/app/DiskUsage/diskusage.h new file mode 100644 index 0000000..ae70bdd --- /dev/null +++ b/src/app/DiskUsage/diskusage.h @@ -0,0 +1,206 @@ +/*************************************************************************** + diskusage.h - 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 + + H e a d e r 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. * + * * + ***************************************************************************/ + +#ifndef __DISK_USAGE_H__ +#define __DISK_USAGE_H__ + +#include "../VFS/vfs.h" +#include "filelightParts/fileTree.h" + +#include <tqdialog.h> +#include <tqlabel.h> +#include <tqdict.h> +#include <tqptrlist.h> +#include <tqptrdict.h> +#include <tqvaluestack.h> +#include <tqptrstack.h> +#include <kurl.h> +#include <ksqueezedtextlabel.h> +#include <tqwidgetstack.h> +#include <tqscrollview.h> +#include <tqtimer.h> + +#define VIEW_LINES 0 +#define VIEW_DETAILED 1 +#define VIEW_FILELIGHT 2 +#define VIEW_LOADER 3 + +typedef TQDict<void> Properties; + +class DUListView; +class DULines; +class DUFilelight; +class TDEPopupMenu; +class LoaderWidget; + +class DiskUsage : public TQWidgetStack +{ + TQ_OBJECT + + +public: + DiskUsage( TQString confGroup, TQWidget *parent = 0, char *name = 0); + ~DiskUsage(); + + void load( KURL dirName ); + void close(); + void stopLoad(); + bool isLoading() { return loading; } + + void setView( int view ); + int getActiveView() { return activeView; } + + Directory* getDirectory( TQString path ); + File * getFile( TQString path ); + + TQString getConfigGroup() { return configGroup; } + + void * getProperty( File *, TQString ); + void addProperty( File *, TQString, void * ); + void removeProperty( File *, TQString ); + + int exclude( File *file, bool calcPercents = true, int depth = 0 ); + void includeAll(); + + int del( File *file, bool calcPercents = true, int depth = 0 ); + + TQString getToolTip( File * ); + + void rightClickMenu( File *, TDEPopupMenu * = 0, TQString = TQString() ); + + void changeDirectory( Directory *dir ); + + Directory* getCurrentDir(); + File* getCurrentFile(); + + TQPixmap getIcon( TQString mime ); + + KURL getBaseURL() { return baseURL; } + +public slots: + void dirUp(); + void clear(); + +signals: + void enteringDirectory( Directory * ); + void clearing(); + void changed( File * ); + void changeFinished(); + void deleted( File * ); + void deleteFinished(); + void status( TQString ); + void viewChanged( int ); + void loadFinished( bool ); + void newSearch(); + +protected slots: + void slotLoadDirectory(); + +protected: + TQDict< Directory > contentMap; + TQPtrDict<Properties> propertyMap; + + Directory* currentDirectory; + TDEIO::filesize_t currentSize; + + virtual void keyPressEvent( TQKeyEvent * ); + virtual bool event( TQEvent * ); + + int calculateSizes( Directory *dir = 0, bool emitSig = false, int depth = 0 ); + int calculatePercents( bool emitSig = false, Directory *dir = 0 , int depth = 0 ); + int include( Directory *dir, int depth = 0 ); + void createStatus(); + void executeAction( int, File * = 0 ); + + KURL baseURL; //< the base URL of loading + + DUListView *listView; + DULines *lineView; + DUFilelight *filelightView; + LoaderWidget *loaderView; + + Directory *root; + + int activeView; + + TQString configGroup; + + bool first; + bool loading; + bool abortLoading; + bool clearAfterAbort; + bool deleting; + + TQValueStack<TQString> directoryStack; + TQPtrStack<Directory> parentStack; + + vfs * searchVfs; + vfile * currentVfile; + Directory * currentParent; + TQString dirToCheck; + + int fileNum; + int dirNum; + int viewBeforeLoad; + + TQTimer loadingTimer; +}; + + +class LoaderWidget : public TQScrollView +{ + TQ_OBJECT + + +public: + LoaderWidget( TQWidget *parent = 0, const char *name = 0 ); + + void init(); + void setCurrentURL( KURL url ); + void setValues( int fileNum, int dirNum, TDEIO::filesize_t total ); + bool wasCancelled() { return cancelled; } + +public slots: + void slotCancelled(); + +protected: + virtual void resizeEvent ( TQResizeEvent *e ); + + TQLabel *totalSize; + TQLabel *files; + TQLabel *directories; + + KSqueezedTextLabel *searchedDirectory; + TQWidget *widget; + + bool cancelled; +}; + +#endif /* __DISK_USAGE_GUI_H__ */ diff --git a/src/app/DiskUsage/diskusagegui.cpp b/src/app/DiskUsage/diskusagegui.cpp new file mode 100644 index 0000000..28b52ce --- /dev/null +++ b/src/app/DiskUsage/diskusagegui.cpp @@ -0,0 +1,227 @@ +/*************************************************************************** + diskusagegui.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 "diskusagegui.h" +#include "../kicons.h" +#include "../krusader.h" +#include "../VFS/vfs.h" +#include "../Dialogs/krdialogs.h" + +#include <tqtimer.h> +#include <tqhbox.h> +#include <tdelocale.h> +#include <tqtooltip.h> + +DiskUsageGUI::DiskUsageGUI( KURL openDir, TQWidget* parent, const char *name ) + : TQDialog( parent, name, false, 0 ), exitAtFailure( true ) +{ + setCaption( i18n("Krusader::Disk Usage") ); + + baseDirectory = openDir; + if( !newSearch() ) + return; + + TQGridLayout *duGrid = new TQGridLayout( this ); + duGrid->setSpacing( 6 ); + duGrid->setMargin( 11 ); + + TQHBox *duTools = new TQHBox( this, "duTools" ); + duTools->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ); + + btnNewSearch = new TQToolButton( duTools, "btnNewSearch" ); + btnNewSearch->setIconSet( TQIconSet(krLoader->loadIcon("document-open",TDEIcon::Desktop)) ); + TQToolTip::add( btnNewSearch, i18n( "Start new disk usage search" ) ); + + btnRefresh = new TQToolButton( duTools, "btnRefresh" ); + btnRefresh->setIconSet( TQIconSet(krLoader->loadIcon("reload",TDEIcon::Desktop)) ); + TQToolTip::add( btnRefresh, i18n( "Refresh" ) ); + + btnDirUp = new TQToolButton( duTools, "btnDirUp" ); + btnDirUp->setIconSet( TQIconSet(krLoader->loadIcon("go-up",TDEIcon::Desktop)) ); + TQToolTip::add( btnDirUp, i18n( "Parent directory" ) ); + + TQWidget * separatorWidget = new TQWidget( duTools, "separatorWidget" ); + separatorWidget->setMinimumWidth( 10 ); + + btnLines = new TQToolButton( duTools, "btnLines" ); + btnLines->setIconSet( TQIconSet(krLoader->loadIcon("format-justify-left",TDEIcon::Desktop)) ); + btnLines->setToggleButton( true ); + TQToolTip::add( btnLines, i18n( "Line view" ) ); + + btnDetailed = new TQToolButton( duTools, "btnDetailed" ); + btnDetailed->setIconSet( TQIconSet(krLoader->loadIcon("view_detailed",TDEIcon::Desktop)) ); + btnDetailed->setToggleButton( true ); + TQToolTip::add( btnDetailed, i18n( "Detailed view" ) ); + + btnFilelight = new TQToolButton( duTools, "btnFilelight" ); + btnFilelight->setIconSet( TQIconSet(krLoader->loadIcon("kr_diskusage",TDEIcon::Desktop)) ); + btnFilelight->setToggleButton( true ); + TQToolTip::add( btnFilelight, i18n( "Filelight view" ) ); + + TQWidget *spacerWidget = new TQWidget( duTools, "spacerWidget" ); + TQHBoxLayout *hboxlayout = new TQHBoxLayout( spacerWidget ); + TQSpacerItem* spacer = new TQSpacerItem( 0, 0, TQSizePolicy::Expanding, TQSizePolicy::Fixed ); + hboxlayout->addItem( spacer ); + + duGrid->addWidget( duTools, 0, 0 ); + + diskUsage = new DiskUsage( "DiskUsage", this ); + duGrid->addWidget( diskUsage, 1, 0 ); + + status = new KSqueezedTextLabel( this ); + status->setFrameShape( TQLabel::StyledPanel ); + status->setFrameShadow( TQLabel::Sunken ); + duGrid->addWidget( status, 2, 0 ); + + connect( diskUsage, TQ_SIGNAL( status( TQString ) ), this, TQ_SLOT( setStatus( TQString ) ) ); + connect( diskUsage, TQ_SIGNAL( viewChanged( int ) ), this, TQ_SLOT( slotViewChanged( int ) ) ); + connect( diskUsage, TQ_SIGNAL( newSearch() ), this, TQ_SLOT( newSearch() ) ); + connect( diskUsage, TQ_SIGNAL( loadFinished( bool ) ), this, TQ_SLOT( slotLoadFinished( bool ) ) ); + connect( btnNewSearch, TQ_SIGNAL( clicked() ), this, TQ_SLOT( newSearch() ) ); + connect( btnRefresh, TQ_SIGNAL( clicked() ), this, TQ_SLOT( loadUsageInfo() ) ); + connect( btnDirUp, TQ_SIGNAL( clicked() ), diskUsage, TQ_SLOT( dirUp() ) ); + connect( btnLines, TQ_SIGNAL( clicked() ), this, TQ_SLOT( selectLinesView() ) ); + connect( btnDetailed, TQ_SIGNAL( clicked() ), this, TQ_SLOT( selectListView() ) ); + connect( btnFilelight, TQ_SIGNAL( clicked() ), this, TQ_SLOT( selectFilelightView() ) ); + + krConfig->setGroup( "DiskUsage" ); + + int view = krConfig->readNumEntry( "View", VIEW_LINES ); + if( view < VIEW_LINES || view > VIEW_FILELIGHT ) + view = VIEW_LINES; + diskUsage->setView( view ); + + sizeX = krConfig->readNumEntry( "Window Width", TQFontMetrics(font()).width("W") * 70 ); + sizeY = krConfig->readNumEntry( "Window Height", TQFontMetrics(font()).height() * 25 ); + resize( sizeX, sizeY ); + + if( krConfig->readBoolEntry( "Window Maximized", false ) ) + showMaximized(); + else + show(); + + exec(); +} + +DiskUsageGUI::~DiskUsageGUI() +{ +} + +void DiskUsageGUI::slotLoadFinished( bool result ) +{ + if( exitAtFailure && !result ) + reject(); + else + exitAtFailure = false; +} + +void DiskUsageGUI::enableButtons( bool isOn ) +{ + btnNewSearch->setEnabled( isOn ); + btnRefresh->setEnabled( isOn ); + btnDirUp->setEnabled( isOn ); + btnLines->setEnabled( isOn ); + btnDetailed->setEnabled( isOn ); + btnFilelight->setEnabled( isOn ); +} + +void DiskUsageGUI::resizeEvent( TQResizeEvent *e ) +{ + if( !isMaximized() ) + { + sizeX = e->size().width(); + sizeY = e->size().height(); + } + TQDialog::resizeEvent( e ); +} + +void DiskUsageGUI::reject() +{ + krConfig->setGroup( "DiskUsage" ); + krConfig->writeEntry("Window Width", sizeX ); + krConfig->writeEntry("Window Height", sizeY ); + krConfig->writeEntry("Window Maximized", isMaximized() ); + krConfig->writeEntry("View", diskUsage->getActiveView() ); + + TQDialog::reject(); +} + +void DiskUsageGUI::loadUsageInfo() +{ + diskUsage->load( baseDirectory ); +} + +void DiskUsageGUI::setStatus( TQString stat ) +{ + status->setText( stat ); +} + +void DiskUsageGUI::slotViewChanged( int view ) +{ + if( view == VIEW_LOADER ) + { + enableButtons( false ); + return; + } + enableButtons( true ); + + btnLines->setOn( false ); + btnDetailed->setOn( false ); + btnFilelight->setOn( false ); + + switch( view ) + { + case VIEW_LINES: + btnLines->setOn( true ); + break; + case VIEW_DETAILED: + btnDetailed->setOn( true ); + break; + case VIEW_FILELIGHT: + btnFilelight->setOn( true ); + break; + case VIEW_LOADER: + break; + } +} + +bool DiskUsageGUI::newSearch() +{ + // ask the user for the copy dest + + KURL tmp = KChooseDir::getDir(i18n( "Viewing the usage of directory:" ), baseDirectory, baseDirectory); + if (tmp.isEmpty()) return false; + baseDirectory = tmp; + + TQTimer::singleShot( 0, this, TQ_SLOT( loadUsageInfo() ) ); + return true; +} + +#include "diskusagegui.moc" diff --git a/src/app/DiskUsage/diskusagegui.h b/src/app/DiskUsage/diskusagegui.h new file mode 100644 index 0000000..1c75a79 --- /dev/null +++ b/src/app/DiskUsage/diskusagegui.h @@ -0,0 +1,90 @@ +/*************************************************************************** + diskusagegui.h - 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 + + H e a d e r 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. * + * * + ***************************************************************************/ + +#ifndef __DISK_USAGE_GUI_H__ +#define __DISK_USAGE_GUI_H__ + +#include <tqdialog.h> +#include <tqlayout.h> +#include <tqtoolbutton.h> +#include <kurl.h> +#include <ksqueezedtextlabel.h> + +#include "diskusage.h" + +class DiskUsageGUI : public TQDialog +{ + TQ_OBJECT + + +public: + DiskUsageGUI( KURL openDir, TQWidget* parent=0, const char *name = 0 ); + ~DiskUsageGUI(); + + +public slots: + void loadUsageInfo(); + bool newSearch(); + void setStatus( TQString ); + + void selectLinesView() { diskUsage->setView( VIEW_LINES ); } + void selectListView() { diskUsage->setView( VIEW_DETAILED ); } + void selectFilelightView() { diskUsage->setView( VIEW_FILELIGHT ); } + +protected slots: + virtual void reject(); + void slotViewChanged( int view ); + void enableButtons( bool ); + void slotLoadFinished( bool ); + +protected: + virtual void resizeEvent( TQResizeEvent *e ); + + DiskUsage *diskUsage; + KURL baseDirectory; + + KSqueezedTextLabel *status; + + TQToolButton *btnNewSearch; + TQToolButton *btnRefresh; + TQToolButton *btnDirUp; + + TQToolButton *btnLines; + TQToolButton *btnDetailed; + TQToolButton *btnFilelight; + + int sizeX; + int sizeY; + + bool exitAtFailure; +}; + +#endif /* __DISK_USAGE_GUI_H__ */ + diff --git a/src/app/DiskUsage/dufilelight.cpp b/src/app/DiskUsage/dufilelight.cpp new file mode 100644 index 0000000..debdb06 --- /dev/null +++ b/src/app/DiskUsage/dufilelight.cpp @@ -0,0 +1,236 @@ +/*************************************************************************** + dufilelight.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 "dufilelight.h" +#include "radialMap/radialMap.h" +#include <tdepopupmenu.h> +#include <tdelocale.h> +#include <kinputdialog.h> + +#define SCHEME_POPUP_ID 6730 + +DUFilelight::DUFilelight( DiskUsage *usage, const char *name ) + : RadialMap::Widget( usage, name ), diskUsage( usage ), currentDir( 0 ), refreshNeeded( true ) +{ + setFocusPolicy( TQWidget::StrongFocus ); + + connect( diskUsage, TQ_SIGNAL( enteringDirectory( Directory * ) ), this, TQ_SLOT( slotDirChanged( Directory * ) ) ); + connect( diskUsage, TQ_SIGNAL( clearing() ), this, TQ_SLOT( clear() ) ); + connect( diskUsage, TQ_SIGNAL( changed( File * ) ), this, TQ_SLOT( slotChanged( File * ) ) ); + connect( diskUsage, TQ_SIGNAL( deleted( File * ) ), this, TQ_SLOT( slotChanged( File * ) ) ); + connect( diskUsage, TQ_SIGNAL( changeFinished() ), this, TQ_SLOT( slotRefresh() ) ); + connect( diskUsage, TQ_SIGNAL( deleteFinished() ), this, TQ_SLOT( slotRefresh() ) ); + connect( diskUsage, TQ_SIGNAL( aboutToShow( TQWidget * ) ), this, TQ_SLOT( slotAboutToShow( TQWidget * ) ) ); +} + +void DUFilelight::slotDirChanged( Directory *dir ) +{ + if( diskUsage->visibleWidget() != this ) + return; + + if( currentDir != dir ) + { + currentDir = dir; + + invalidate( false ); + create( dir ); + refreshNeeded = false; + } +} + +void DUFilelight::clear() +{ + invalidate( false ); + currentDir = 0; +} + +File * DUFilelight::getCurrentFile() +{ + const RadialMap::Segment * focus = focusSegment(); + + if( !focus || focus->isFake() || focus->file() == currentDir ) + return 0; + + return (File *)focus->file(); +} + +void DUFilelight::mousePressEvent( TQMouseEvent *event ) +{ + if( event->button() == TQt::RightButton ) + { + File * file = 0; + + const RadialMap::Segment * focus = focusSegment(); + + if( focus && !focus->isFake() && focus->file() != currentDir ) + file = (File *)focus->file(); + + TDEPopupMenu filelightPopup; + filelightPopup.insertItem( i18n("Zoom In"), this, TQ_SLOT( zoomIn() ), Key_Plus ); + filelightPopup.insertItem( i18n("Zoom Out"), this, TQ_SLOT( zoomOut() ), Key_Minus ); + + TDEPopupMenu schemePopup; + schemePopup.insertItem( i18n("Rainbow"), this, TQ_SLOT( schemeRainbow() ) ); + schemePopup.insertItem( i18n("High Contrast"), this, TQ_SLOT( schemeHighContrast() ) ); + schemePopup.insertItem( i18n("TDE"), this, TQ_SLOT( schemeKDE() ) ); + + filelightPopup.insertItem( TQPixmap(), &schemePopup, SCHEME_POPUP_ID ); + filelightPopup.changeItem( SCHEME_POPUP_ID, i18n( "Scheme" ) ); + + filelightPopup.insertItem( i18n("Increase contrast"), this, TQ_SLOT( increaseContrast() ) ); + filelightPopup.insertItem( i18n("Decrease contrast"), this, TQ_SLOT( decreaseContrast() ) ); + + int aid = filelightPopup.insertItem( i18n("Use anti-aliasing" ), this, TQ_SLOT( changeAntiAlias() ) ); + filelightPopup.setItemChecked( aid, Filelight::Config::antiAliasFactor > 1 ); + + int sid = filelightPopup.insertItem( i18n("Show small files" ), this, TQ_SLOT( showSmallFiles() ) ); + filelightPopup.setItemChecked( sid, Filelight::Config::showSmallFiles ); + + int vid = filelightPopup.insertItem( i18n("Vary label font sizes" ), this, TQ_SLOT( varyLabelFontSizes() ) ); + filelightPopup.setItemChecked( vid, Filelight::Config::varyLabelFontSizes ); + + filelightPopup.insertItem( i18n("Minimum font size"), this, TQ_SLOT( minFontSize() ) ); + + diskUsage->rightClickMenu( file, &filelightPopup, i18n( "Filelight" ) ); + return; + }else if( event->button() == TQt::LeftButton ) + { + const RadialMap::Segment * focus = focusSegment(); + + if( focus && !focus->isFake() && focus->file() == currentDir ) + { + diskUsage->dirUp(); + return; + } + else if( focus && !focus->isFake() && focus->file()->isDir() ) + { + diskUsage->changeDirectory( (Directory *)focus->file() ); + return; + } + } + + RadialMap::Widget::mousePressEvent( event ); +} + +void DUFilelight::setScheme( Filelight::MapScheme scheme ) +{ + Filelight::Config::scheme = scheme; + Filelight::Config::write(); + slotRefresh(); +} + +void DUFilelight::increaseContrast() +{ + if( ( Filelight::Config::contrast += 10 ) > 100 ) + Filelight::Config::contrast = 100; + + Filelight::Config::write(); + slotRefresh(); +} + +void DUFilelight::decreaseContrast() +{ + if( ( Filelight::Config::contrast -= 10 ) > 100 ) + Filelight::Config::contrast = 0; + + Filelight::Config::write(); + slotRefresh(); +} + +void DUFilelight::changeAntiAlias() +{ + Filelight::Config::antiAliasFactor = 1 + ( Filelight::Config::antiAliasFactor == 1 ); + Filelight::Config::write(); + slotRefresh(); +} + +void DUFilelight::showSmallFiles() +{ + Filelight::Config::showSmallFiles = !Filelight::Config::showSmallFiles; + Filelight::Config::write(); + slotRefresh(); +} + +void DUFilelight::varyLabelFontSizes() +{ + Filelight::Config::varyLabelFontSizes = !Filelight::Config::varyLabelFontSizes; + Filelight::Config::write(); + slotRefresh(); +} + +void DUFilelight::minFontSize() +{ + bool ok = false; + + int result = KInputDialog::getInteger( i18n( "Krusader::Filelight" ), + i18n( "Minimum font size" ), (int)Filelight::Config::minFontPitch, 1, 100, 1, &ok, this ); + + if ( ok ) + { + Filelight::Config::minFontPitch = (uint)result; + + Filelight::Config::write(); + slotRefresh(); + } +} + +void DUFilelight::slotAboutToShow( TQWidget *widget ) +{ + if( widget == this && ( diskUsage->getCurrentDir() != currentDir || refreshNeeded ) ) + { + refreshNeeded = false; + if( ( currentDir = diskUsage->getCurrentDir() ) != 0 ) + { + invalidate( false ); + create( currentDir ); + } + } +} + +void DUFilelight::slotRefresh() +{ + if( diskUsage->visibleWidget() != this ) + return; + + refreshNeeded = false; + if( currentDir && currentDir == diskUsage->getCurrentDir() ) + { + invalidate( false ); + create( currentDir ); + } +} + +void DUFilelight::slotChanged( File * ) +{ + if( !refreshNeeded ) + refreshNeeded = true; +} + +#include "dufilelight.moc" diff --git a/src/app/DiskUsage/dufilelight.h b/src/app/DiskUsage/dufilelight.h new file mode 100644 index 0000000..9ef8ccb --- /dev/null +++ b/src/app/DiskUsage/dufilelight.h @@ -0,0 +1,81 @@ +/*************************************************************************** + dufilelight.h - 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 + + H e a d e r 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. * + * * + ***************************************************************************/ + +#ifndef __DU_FILELIGHT_H__ +#define __DU_FILELIGHT_H__ + +#include "diskusage.h" +#include "radialMap/widget.h" +#include "filelightParts/Config.h" + +class DUFilelight : public RadialMap::Widget +{ + TQ_OBJECT + + +public: + DUFilelight( DiskUsage *usage, const char *name ); + + File * getCurrentFile(); + +public slots: + void slotDirChanged( Directory * ); + void clear(); + void slotChanged( File * ); + void slotRefresh(); + +protected slots: + void slotAboutToShow( TQWidget *widget ); + + void schemeRainbow() { setScheme( Filelight::Rainbow ); } + void schemeHighContrast() { setScheme( Filelight::HighContrast ); } + void schemeKDE() { setScheme( Filelight::KDE ); } + + void increaseContrast(); + void decreaseContrast(); + void changeAntiAlias(); + void showSmallFiles(); + void varyLabelFontSizes(); + void minFontSize(); + +protected: + virtual void mousePressEvent( TQMouseEvent* ); + + void setScheme( Filelight::MapScheme ); + + DiskUsage *diskUsage; + Directory *currentDir; + +private: + bool refreshNeeded; +}; + +#endif /* __DU_FILELIGHT_H__ */ + diff --git a/src/app/DiskUsage/dulines.cpp b/src/app/DiskUsage/dulines.cpp new file mode 100644 index 0000000..ba10a04 --- /dev/null +++ b/src/app/DiskUsage/dulines.cpp @@ -0,0 +1,522 @@ +/*************************************************************************** + dulines.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 "dulines.h" +#include "../kicons.h" +#include "../krusader.h" +#include "../VFS/krpermhandler.h" +#include <tqheader.h> +#include <tdelocale.h> +#include <tqpen.h> +#include <tqpainter.h> +#include <tqfontmetrics.h> +#include <tqtimer.h> +#include <tqtooltip.h> +#include <tdepopupmenu.h> + +class DULinesItem : public TQListViewItem +{ +public: + DULinesItem( DiskUsage *diskUsageIn, File *fileItem, TQListView * parent, TQString label1, + TQString label2, TQString label3, unsigned int italicPos ) : TQListViewItem( parent, label1, label2, label3 ), + diskUsage( diskUsageIn ), file( fileItem ), isTruncated( false ), italicTextPos( italicPos ) {} + DULinesItem( DiskUsage *diskUsageIn, File *fileItem, TQListView * parent, TQListViewItem * after, + TQString label1, TQString label2, TQString label3, unsigned int italicPos ) : TQListViewItem( parent, after, label1, + label2, label3 ), diskUsage( diskUsageIn ), file( fileItem ), isTruncated( false ), italicTextPos( italicPos ) {} + + virtual int compare ( TQListViewItem * i, int col, bool ascending ) const + { + if( text(0) == ".." ) return ascending ? -1 : 1; + if( i->text(0) == "..") return ascending ? 1 : -1; + + DULinesItem *compWith = dynamic_cast< DULinesItem * >( i ); + + TQString buf1,buf2; + + switch( col ) + { + case 0: + case 1: + buf1.sprintf("%025llu",file->size()); + buf2.sprintf("%025llu",compWith->file->size()); + return -TQString::compare( buf1, buf2 ); + default: + return TQListViewItem::compare( i, col, ascending ); + } + } + + virtual void paintCell( TQPainter * p, const TQColorGroup & cg, int column, int width, int align ) + { + if( column == 2 ) + { + if ( isSelected() ) + p->fillRect( 0, 0, width, height(), cg.brush( TQColorGroup::Highlight ) ); + else + p->fillRect( 0, 0, width, height(), cg.brush( TQColorGroup::Base ) ); + + TQListView *lv = listView(); + + int pos = lv->itemMargin(); + + const TQPixmap *icon = pixmap( column ); + if( icon ) + { + int iconWidth = icon->width() + lv->itemMargin(); + int xo = pos; + int yo = ( height() - icon->height() ) / 2; + + p->drawPixmap( xo, yo, *icon ); + + pos += iconWidth; + } + + TQFontMetrics fm( p->fontMetrics() ); + + if( isSelected() ) + p->setPen( cg.highlightedText() ); + else + p->setPen( cg.text() ); + + TQString t = text( column ); + TQString b; + + if( t.length() > italicTextPos ) + { + b = t.mid( italicTextPos ); + t.truncate( italicTextPos ); + } + + isTruncated = false; + if( !t.isEmpty() ) + { + int remWidth = width-pos; + + if( fm.width( t ) > remWidth ) + { + while( !t.isEmpty() ) + { + t.truncate( t.length() - 1 ); + if( fm.width( t + "..." ) <= remWidth ) + break; + } + t += "..."; + isTruncated = true; + } + + p->drawText( pos, 0, width, height(), align, t ); + pos += fm.width( t ); + } + + if( !b.isEmpty() && !isTruncated ) + { + TQFont font( p->font() ); + font.setItalic( true ); + p->setFont( font ); + + TQFontMetrics fm2( p->fontMetrics() ); + + int remWidth = width-pos; + + if( fm2.width( b ) > remWidth ) + { + while( !b.isEmpty() ) + { + b.truncate( b.length() - 1 ); + if( fm2.width( b + "..." ) <= remWidth ) + break; + } + b += "..."; + isTruncated = true; + } + + p->drawText( pos, 0, width, height(), align, b ); + } + } + else + TQListViewItem::paintCell( p, cg, column, width, align ); + } + + inline File * getFile() { return file; } + +private: + DiskUsage *diskUsage; + File *file; + + bool isTruncated; + unsigned int italicTextPos; +}; + +class DULinesToolTip : public TQToolTip +{ +public: + DULinesToolTip( DiskUsage *usage, TQWidget *parent, TQListView *lv ); + void maybeTip( const TQPoint &pos ); + + virtual ~DULinesToolTip() {} +private: + TQListView *view; + DiskUsage *diskUsage; +}; + +DULinesToolTip::DULinesToolTip( DiskUsage *usage, TQWidget *parent, TQListView *lv ) + : TQToolTip( parent ), view( lv ), diskUsage( usage ) +{ +} + +void DULinesToolTip::maybeTip( const TQPoint &pos ) +{ + TQListViewItem *item = view->itemAt( pos ); + TQPoint contentsPos = view->viewportToContents( pos ); + if ( !item ) + return; + + int col = view->header()->sectionAt( contentsPos.x() ); + + int width = item->width( TQFontMetrics( view->font() ), view, col ); + + TQRect r = view->itemRect( item ); + int headerPos = view->header()->sectionPos( col ); + r.setLeft( headerPos ); + r.setRight( headerPos + view->header()->sectionSize( col ) ); + + if( col != 0 && width > view->columnWidth( col ) ) + tip( r, item->text( col ) ); + else if( col == 1 && item->text( 0 ) != ".." ) + { + File *fileItem = ((DULinesItem *)item)->getFile(); + tip( r, diskUsage->getToolTip( fileItem ) ); + } +} + +DULines::DULines( DiskUsage *usage, const char *name ) + : TQListView( usage, name ), diskUsage( usage ), refreshNeeded( false ) +{ + setAllColumnsShowFocus(true); + setVScrollBarMode(TQScrollView::Auto); + setHScrollBarMode(TQScrollView::Auto); + setShowSortIndicator(true); + setTreeStepSize( 10 ); + + int defaultSize = TQFontMetrics(font()).width("W"); + + krConfig->setGroup( diskUsage->getConfigGroup() ); + + showFileSize = krConfig->readBoolEntry( "L Show File Size", true ); + + int lineWidth = krConfig->readNumEntry("L Line Width", defaultSize * 20 ); + addColumn( i18n("Line View"), lineWidth ); + setColumnWidthMode(0,TQListView::Manual); + int precentWidth = krConfig->readNumEntry("L Percent Width", defaultSize * 6 ); + addColumn( i18n("Percent"), precentWidth ); + setColumnWidthMode(1,TQListView::Manual); + int nameWidth = krConfig->readNumEntry("L Name Width", defaultSize * 20 ); + addColumn( i18n("Name"), nameWidth ); + setColumnWidthMode(2,TQListView::Manual); + + setColumnAlignment( 1, TQt::AlignRight ); + + header()->setStretchEnabled( true, 0 ); + + setSorting( 1 ); + + toolTip = new DULinesToolTip( diskUsage, viewport(), this ); + + connect( diskUsage, TQ_SIGNAL( enteringDirectory( Directory * ) ), this, TQ_SLOT( slotDirChanged( Directory * ) ) ); + connect( diskUsage, TQ_SIGNAL( clearing() ), this, TQ_SLOT( clear() ) ); + + connect( header(), TQ_SIGNAL( sizeChange( int, int, int ) ), this, TQ_SLOT( sectionResized( int ) ) ); + + connect( this, TQ_SIGNAL(rightButtonPressed(TQListViewItem *, const TQPoint &, int)), + this, TQ_SLOT( slotRightClicked(TQListViewItem *) ) ); + connect( diskUsage, TQ_SIGNAL( changed( File * ) ), this, TQ_SLOT( slotChanged( File * ) ) ); + connect( diskUsage, TQ_SIGNAL( deleted( File * ) ), this, TQ_SLOT( slotDeleted( File * ) ) ); +} + +DULines::~DULines() +{ + krConfig->setGroup( diskUsage->getConfigGroup() ); + krConfig->writeEntry("L Line Width", columnWidth( 0 ) ); + krConfig->writeEntry("L Percent Width", columnWidth( 1 ) ); + krConfig->writeEntry("L Name Width", columnWidth( 2 ) ); + krConfig->writeEntry("L Show File Size", showFileSize ); + + delete toolTip; +} + +void DULines::slotDirChanged( Directory *dirEntry ) +{ + clear(); + + TQListViewItem * lastItem = 0; + + if( ! ( dirEntry->parent() == 0 ) ) + { + lastItem = new TQListViewItem( this, ".." ); + lastItem->setPixmap( 0, FL_LOADICON( "go-up" ) ); + lastItem->setSelectable( false ); + } + + int maxPercent = -1; + for( Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it ) + { + File *item = *it; + if( !item->isExcluded() && item->intPercent() > maxPercent ) + maxPercent = item->intPercent(); + } + + for( Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it ) + { + File *item = *it; + + TQString fileName = item->name(); + + unsigned int italicStart = fileName.length(); + + if( showFileSize ) + fileName += " [" + TDEIO::convertSize( item->size() ) + "]"; + + if( lastItem == 0 ) + lastItem = new DULinesItem( diskUsage, item, this, "", item->percent() + " ", fileName, italicStart ); + else + lastItem = new DULinesItem( diskUsage, item, this, lastItem, "", item->percent() + " ", fileName, italicStart ); + + if( item->isExcluded() ) + lastItem->setVisible( false ); + + lastItem->setPixmap( 2, diskUsage->getIcon( item->mime() ) ); + lastItem->setPixmap( 0, createPixmap( item->intPercent(), maxPercent, columnWidth( 0 ) - itemMargin() ) ); + } + + setCurrentItem( firstChild() ); +} + +TQPixmap DULines::createPixmap( int percent, int maxPercent, int maxWidth ) +{ + if( percent < 0 || percent > maxPercent || maxWidth < 2 || maxPercent == 0 ) + return TQPixmap(); + maxWidth -= 2; + + int actualWidth = maxWidth*percent/maxPercent; + if( actualWidth == 0 ) + return TQPixmap(); + + TQPen pen; + pen.setColor( TQt::black ); + TQPainter painter; + + int size = TQFontMetrics(font()).height()-2; + TQRect rect( 0, 0, actualWidth, size ); + TQPixmap pixmap( rect.width(), rect.height() ); + + painter.begin( &pixmap ); + painter.setPen( pen ); + + for( int i = 1; i < actualWidth - 1; i++ ) + { + int color = (511*i/maxWidth); + if( color < 256 ) + pen.setColor( TQColor( 255-color, 255, 0 ) ); + else + pen.setColor( TQColor( color-256, 511-color, 0 ) ); + + painter.setPen( pen ); + painter.drawLine( i, 1, i, size-1 ); + } + + pen.setColor( TQt::black ); + painter.setPen( pen ); + painter.drawRect( rect ); + painter.end(); + pixmap.detach(); + return pixmap; +} + +void DULines::sectionResized( int column ) +{ + if( childCount() == 0 || column != 0 ) + return; + + Directory * currentDir = diskUsage->getCurrentDir(); + if( currentDir == 0 ) + return; + + int maxPercent = -1; + for( Iterator<File> it = currentDir->iterator(); it != currentDir->end(); ++it ) + { + File *item = *it; + + if( !item->isExcluded() && item->intPercent() > maxPercent ) + maxPercent = item->intPercent(); + } + + DULinesItem *duItem = (DULinesItem *)firstChild(); + while( duItem ) + { + if( duItem->text( 0 ) != ".." ) + duItem->setPixmap( 0, createPixmap( duItem->getFile()->intPercent(), maxPercent, columnWidth( 0 ) ) ); + duItem = (DULinesItem *)duItem->nextSibling(); + } +} + +bool DULines::doubleClicked( TQListViewItem * item ) +{ + if( item ) + { + if( item->text( 0 ) != ".." ) + { + File *fileItem = ((DULinesItem *)item)->getFile(); + if( fileItem->isDir() ) + diskUsage->changeDirectory( dynamic_cast<Directory *> ( fileItem ) ); + return true; + } + else + { + Directory *upDir = (Directory *)diskUsage->getCurrentDir()->parent(); + + if( upDir ) + diskUsage->changeDirectory( upDir ); + return true; + } + } + return false; +} + +void DULines::contentsMouseDoubleClickEvent ( TQMouseEvent * e ) +{ + if ( e || e->button() == TQt::LeftButton ) + { + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem * item = itemAt( vp ); + + if( doubleClicked( item ) ) + return; + + } + TQListView::contentsMouseDoubleClickEvent( e ); +} + + +void DULines::keyPressEvent( TQKeyEvent *e ) +{ + switch ( e->key() ) + { + case Key_Return : + case Key_Enter : + if( doubleClicked( currentItem() ) ) + return; + break; + case Key_Left : + case Key_Right : + case Key_Up : + case Key_Down : + if( e->state() == ShiftButton ) + { + e->ignore(); + return; + } + break; + case Key_Delete : + e->ignore(); + return; + } + TQListView::keyPressEvent( e ); +} + +void DULines::slotRightClicked( TQListViewItem *item ) +{ + File * file = 0; + + if ( item && item->text( 0 ) != ".." ) + file = ((DULinesItem *)item)->getFile(); + + TDEPopupMenu linesPopup; + int lid = linesPopup.insertItem( i18n("Show file sizes"), this, TQ_SLOT( slotShowFileSizes() ) ); + linesPopup.setItemChecked( lid, showFileSize ); + + diskUsage->rightClickMenu( file, &linesPopup, i18n( "Lines" ) ); +} + +void DULines::slotShowFileSizes() +{ + showFileSize = !showFileSize; + slotDirChanged( diskUsage->getCurrentDir() ); +} + +File * DULines::getCurrentFile() +{ + TQListViewItem *item = currentItem(); + + if( item == 0 || item->text( 0 ) == ".." ) + return 0; + + return ((DULinesItem *)item)->getFile(); +} + +void DULines::slotChanged( File * item ) +{ + TQListViewItem *lvitem = firstChild(); + while( lvitem ) + { + if( lvitem->text( 0 ) != ".." ) { + DULinesItem *duItem = (DULinesItem *)( lvitem ); + if( duItem->getFile() == item ) + { + duItem->setVisible( !item->isExcluded() ); + duItem->setText( 1, item->percent() ); + if( !refreshNeeded ) + { + refreshNeeded = true; + TQTimer::singleShot( 0, this, TQ_SLOT( slotRefresh() ) ); + } + break; + } + } + lvitem = lvitem->nextSibling(); + } +} + +void DULines::slotDeleted( File * item ) +{ + TQListViewItem *lvitem = firstChild(); + while( lvitem ) + { + if( lvitem->text( 0 ) != ".." ) { + DULinesItem *duItem = (DULinesItem *)( lvitem ); + if( duItem->getFile() == item ) + { + delete duItem; + break; + } + } + lvitem = lvitem->nextSibling(); + } +} + +#include "dulines.moc" diff --git a/src/app/DiskUsage/dulines.h b/src/app/DiskUsage/dulines.h new file mode 100644 index 0000000..7531c86 --- /dev/null +++ b/src/app/DiskUsage/dulines.h @@ -0,0 +1,79 @@ +/*************************************************************************** + dulines.h - 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 + + H e a d e r 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. * + * * + ***************************************************************************/ + +#ifndef __DU_LINES_H__ +#define __DU_LINES_H__ + +#include <tqlistview.h> +#include <tqpixmap.h> +#include "diskusage.h" + +class DULinesToolTip; + +class DULines : public TQListView +{ + TQ_OBJECT + + +public: + DULines( DiskUsage *usage, const char *name ); + ~DULines(); + + File * getCurrentFile(); + +public slots: + void slotDirChanged( Directory *dirEntry ); + void sectionResized( int ); + void slotRightClicked(TQListViewItem *); + void slotChanged( File * ); + void slotDeleted( File * ); + void slotShowFileSizes(); + void slotRefresh() { refreshNeeded = false; sectionResized( 0 ); } + +protected: + DiskUsage *diskUsage; + + virtual void contentsMouseDoubleClickEvent ( TQMouseEvent * e ); + virtual void keyPressEvent( TQKeyEvent *e ); + +private: + TQPixmap createPixmap( int percent, int maxPercent, int maxWidth ); + + bool doubleClicked( TQListViewItem * item ); + + bool refreshNeeded; + + bool showFileSize; + + DULinesToolTip *toolTip; +}; + +#endif /* __DU_LINES_H__ */ + diff --git a/src/app/DiskUsage/dulistview.cpp b/src/app/DiskUsage/dulistview.cpp new file mode 100644 index 0000000..dc40690 --- /dev/null +++ b/src/app/DiskUsage/dulistview.cpp @@ -0,0 +1,293 @@ +/*************************************************************************** + dulistview.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 "dulistview.h" +#include "../krusader.h" +#include "../kicons.h" +#include "../VFS/krpermhandler.h" +#include <tqfontmetrics.h> +#include <tdelocale.h> +#include <kmimetype.h> +#include <tdeglobal.h> +#include <tqheader.h> +#include <time.h> + +DUListView::DUListView( DiskUsage *usage, const char *name ) + : TQListView( usage, name ), diskUsage( usage ) +{ + setAllColumnsShowFocus(true); + setVScrollBarMode(TQScrollView::Auto); + setHScrollBarMode(TQScrollView::Auto); + setShowSortIndicator(true); + setRootIsDecorated( true ); + setTreeStepSize( 10 ); + + int defaultSize = TQFontMetrics(font()).width("W"); + + krConfig->setGroup( diskUsage->getConfigGroup() ); + int nameWidth = krConfig->readNumEntry("D Name Width", defaultSize * 20 ); + addColumn( i18n("Name"), nameWidth ); + setColumnWidthMode(0,TQListView::Manual); + int percentWidth = krConfig->readNumEntry("D Percent Width", defaultSize * 5 ); + addColumn( i18n("Percent"), percentWidth ); + setColumnWidthMode(1,TQListView::Manual); + int totalSizeWidth = krConfig->readNumEntry("D Total Size Width", defaultSize * 10 ); + addColumn( i18n("Total size"), totalSizeWidth ); + setColumnWidthMode(1,TQListView::Manual); + int ownSizeWidth = krConfig->readNumEntry("D Own Size Width", defaultSize * 10 ); + addColumn( i18n("Own size"), ownSizeWidth ); + setColumnWidthMode(2,TQListView::Manual); + int typeWidth = krConfig->readNumEntry("D Type Width", defaultSize * 10 ); + addColumn( i18n("Type"), typeWidth ); + setColumnWidthMode(3,TQListView::Manual); + int dateWidth = krConfig->readNumEntry("D Date Width", defaultSize * 10 ); + addColumn( i18n("Date"), dateWidth ); + setColumnWidthMode(4,TQListView::Manual); + int permissionsWidth = krConfig->readNumEntry("D Permissions Width", defaultSize * 6 ); + addColumn( i18n("Permissions"), permissionsWidth ); + setColumnWidthMode(5,TQListView::Manual); + int ownerWidth = krConfig->readNumEntry("D Owner Width", defaultSize * 5 ); + addColumn( i18n("Owner"), ownerWidth ); + setColumnWidthMode(6,TQListView::Manual); + int groupWidth = krConfig->readNumEntry("D Group Width", defaultSize * 5 ); + addColumn( i18n("Group"), groupWidth ); + setColumnWidthMode(7,TQListView::Manual); + + setColumnAlignment( 1, TQt::AlignRight ); + setColumnAlignment( 2, TQt::AlignRight ); + setColumnAlignment( 3, TQt::AlignRight ); + + setSorting( 2 ); + + connect( diskUsage, TQ_SIGNAL( enteringDirectory( Directory * ) ), this, TQ_SLOT( slotDirChanged( Directory * ) ) ); + connect( diskUsage, TQ_SIGNAL( clearing() ), this, TQ_SLOT( clear() ) ); + connect( diskUsage, TQ_SIGNAL( changed( File * ) ), this, TQ_SLOT( slotChanged( File * ) ) ); + connect( diskUsage, TQ_SIGNAL( deleted( File * ) ), this, TQ_SLOT( slotDeleted( File * ) ) ); + + connect( this, TQ_SIGNAL(rightButtonPressed(TQListViewItem *, const TQPoint &, int)), + this, TQ_SLOT( slotRightClicked(TQListViewItem *) ) ); + connect( this, TQ_SIGNAL( expanded ( TQListViewItem * ) ), + this, TQ_SLOT( slotExpanded( TQListViewItem * ) ) ); +} + +DUListView::~ DUListView() +{ + krConfig->setGroup( diskUsage->getConfigGroup() ); + krConfig->writeEntry("D Name Width", columnWidth( 0 ) ); + krConfig->writeEntry("D Percent Width", columnWidth( 1 ) ); + krConfig->writeEntry("D Total Size Width", columnWidth( 2 ) ); + krConfig->writeEntry("D Own Size Width", columnWidth( 3 ) ); + krConfig->writeEntry("D Type Width", columnWidth( 4 ) ); + krConfig->writeEntry("D Date Width", columnWidth( 5 ) ); + krConfig->writeEntry("D Permissions Width", columnWidth( 6 ) ); + krConfig->writeEntry("D Owner Width", columnWidth( 7 ) ); + krConfig->writeEntry("D Group Width", columnWidth( 8 ) ); +} + +void DUListView::addDirectory( Directory *dirEntry, TQListViewItem *parent ) +{ + TQListViewItem * lastItem = 0; + + if( parent == 0 && ! ( dirEntry->parent() == 0 ) ) + { + lastItem = new TQListViewItem( this, ".." ); + lastItem->setPixmap( 0, FL_LOADICON( "go-up" ) ); + lastItem->setSelectable( false ); + } + + for( Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it ) + { + File *item = *it; + + 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 totalSize = KRpermHandler::parseSize( item->size() ) + " "; + TQString ownSize = KRpermHandler::parseSize( item->ownSize() ) + " "; + TQString percent = item->percent(); + + if( lastItem == 0 && parent == 0 ) + lastItem = new DUListViewItem( diskUsage, item, this, item->name(), percent, totalSize, ownSize, + mime, date, item->perm(), item->owner(), item->group() ); + else if ( lastItem == 0 ) + lastItem = new DUListViewItem( diskUsage, item, parent, item->name(), percent, totalSize, ownSize, + mime, date, item->perm(), item->owner(), item->group() ); + else if ( parent == 0 ) + lastItem = new DUListViewItem( diskUsage, item, this, lastItem, item->name(), percent, totalSize, + ownSize, mime, date, item->perm(), item->owner(), item->group() ); + else + lastItem = new DUListViewItem( diskUsage, item, parent, lastItem, item->name(), percent, totalSize, + ownSize, mime, date, item->perm(), item->owner(), item->group() ); + + if( item->isExcluded() ) + lastItem->setVisible( false ); + + lastItem->setPixmap( 0, diskUsage->getIcon( item->mime() ) ); + + if( item->isDir() && !item->isSymLink() ) + lastItem->setExpandable( true ); + } + + TQListViewItem *first = firstChild(); + if( first ) + setCurrentItem( first ); +} + +void DUListView::slotDirChanged( Directory *dirEntry ) +{ + clear(); + addDirectory( dirEntry, 0 ); +} + +File * DUListView::getCurrentFile() +{ + TQListViewItem *item = currentItem(); + + if( item == 0 || item->text( 0 ) == ".." ) + return 0; + + return ((DUListViewItem *)item)->getFile(); +} + +void DUListView::slotChanged( File * item ) +{ + void * itemPtr = diskUsage->getProperty( item, "ListView-Ref" ); + if( itemPtr == 0 ) + return; + + DUListViewItem *duItem = (DUListViewItem *)itemPtr; + duItem->setVisible( !item->isExcluded() ); + duItem->setText( 1, item->percent() ); + duItem->setText( 2, KRpermHandler::parseSize( item->size() ) + " " ); + duItem->setText( 3, KRpermHandler::parseSize( item->ownSize() ) + " " ); +} + +void DUListView::slotDeleted( File * item ) +{ + void * itemPtr = diskUsage->getProperty( item, "ListView-Ref" ); + if( itemPtr == 0 ) + return; + + DUListViewItem *duItem = (DUListViewItem *)itemPtr; + delete duItem; +} + +void DUListView::slotRightClicked( TQListViewItem *item ) +{ + File * file = 0; + + if ( item && item->text( 0 ) != ".." ) + file = ((DUListViewItem *)item)->getFile(); + + diskUsage->rightClickMenu( file ); +} + +bool DUListView::doubleClicked( TQListViewItem * item ) +{ + if( item ) + { + if( item->text( 0 ) != ".." ) + { + File *fileItem = ((DUListViewItem *)item)->getFile(); + if( fileItem->isDir() ) + diskUsage->changeDirectory( dynamic_cast<Directory *> ( fileItem ) ); + return true; + } + else + { + Directory *upDir = (Directory *)diskUsage->getCurrentDir()->parent(); + + if( upDir ) + diskUsage->changeDirectory( upDir ); + return true; + } + } + return false; +} + +void DUListView::contentsMouseDoubleClickEvent ( TQMouseEvent * e ) +{ + if ( e || e->button() == TQt::LeftButton ) + { + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem * item = itemAt( vp ); + + if( doubleClicked( item ) ) + return; + + } + TQListView::contentsMouseDoubleClickEvent( e ); +} + +void DUListView::keyPressEvent( TQKeyEvent *e ) +{ + switch ( e->key() ) + { + case Key_Return : + case Key_Enter : + if( doubleClicked( currentItem() ) ) + return; + break; + case Key_Left : + case Key_Right : + case Key_Up : + case Key_Down : + if( e->state() == ShiftButton ) + { + e->ignore(); + return; + } + break; + case Key_Delete : + e->ignore(); + return; + } + TQListView::keyPressEvent( e ); +} + +void DUListView::slotExpanded( TQListViewItem *item ) +{ + if( item == 0 || item->text( 0 ) == ".." ) + return; + + if( item->childCount() == 0 ) + { + File *fileItem = ((DUListViewItem *)item)->getFile(); + if( fileItem->isDir() ) + addDirectory( dynamic_cast<Directory *>( fileItem ), item ); + } +} + +#include "dulistview.moc" diff --git a/src/app/DiskUsage/dulistview.h b/src/app/DiskUsage/dulistview.h new file mode 100644 index 0000000..9e9fdb4 --- /dev/null +++ b/src/app/DiskUsage/dulistview.h @@ -0,0 +1,145 @@ +/*************************************************************************** + dulistview.h - 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 + + H e a d e r 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. * + * * + ***************************************************************************/ + +#ifndef __DU_LISTVIEW_H__ +#define __DU_LISTVIEW_H__ + +#include <tqlistview.h> +#include "diskusage.h" + +class DUListViewItem : public TQListViewItem +{ +public: + DUListViewItem( DiskUsage *diskUsageIn, File *fileIn, TQListView * parent, TQString label1, + TQString label2, TQString label3, TQString label4, TQString label5, TQString label6, + TQString label7, TQString label8, TQString label9 ) + : TQListViewItem( parent, label1, label2, label3, label4, label5, label6, label7, label8), + diskUsage( diskUsageIn ), file( fileIn ) + { + setText( 8, label9 ); + diskUsage->addProperty( file, "ListView-Ref", this ); + } + DUListViewItem( DiskUsage *diskUsageIn, File *fileIn, TQListViewItem * parent, TQString label1, + TQString label2, TQString label3, TQString label4, TQString label5, TQString label6, + TQString label7, TQString label8, TQString label9 ) + : TQListViewItem( parent, label1, label2, label3, label4, label5, label6, label7, label8), + diskUsage( diskUsageIn ), file( fileIn ) + { + setText( 8, label9 ); + diskUsage->addProperty( file, "ListView-Ref", this ); + } + DUListViewItem( DiskUsage *diskUsageIn, File *fileIn, TQListView * parent, TQListViewItem * after, + TQString label1, TQString label2, TQString label3, TQString label4, TQString label5, + TQString label6, TQString label7, TQString label8, TQString label9 ) + : TQListViewItem( parent, after, label1, label2, label3, label4, label5, label6, label7, label8), + diskUsage( diskUsageIn ), file( fileIn ) + { + setText( 8, label9 ); + diskUsage->addProperty( file, "ListView-Ref", this ); + } + DUListViewItem( DiskUsage *diskUsageIn, File *fileIn, TQListViewItem * parent, TQListViewItem * after, + TQString label1, TQString label2, TQString label3, TQString label4, TQString label5, + TQString label6, TQString label7, TQString label8, TQString label9 ) + : TQListViewItem( parent, after, label1, label2, label3, label4, label5, label6, label7, label8), + diskUsage( diskUsageIn ), file( fileIn ) + { + setText( 8, label9 ); + diskUsage->addProperty( file, "ListView-Ref", this ); + } + ~DUListViewItem() + { + diskUsage->removeProperty( file, "ListView-Ref" ); + } + + virtual int compare ( TQListViewItem * i, int col, bool ascending ) const + { + if( text(0) == ".." ) return ascending ? -1 : 1; + if( i->text(0) == "..") return ascending ? 1 : -1; + + DUListViewItem *compWith = dynamic_cast< DUListViewItem * >( i ); + + TQString buf1,buf2; + + switch( col ) + { + case 1: + case 2: + buf1.sprintf("%025llu",file->size()); + buf2.sprintf("%025llu",compWith->file->size()); + return -TQString::compare( buf1, buf2 ); + case 3: + buf1.sprintf("%025llu",file->ownSize()); + buf2.sprintf("%025llu",compWith->file->ownSize()); + return -TQString::compare( buf1, buf2 ); + case 5: + return TQListViewItem::compare( i, col, !ascending ); + default: + return TQListViewItem::compare( i, col, ascending ); + } + } + + inline File * getFile() { return file; } + +private: + DiskUsage *diskUsage; + File *file; +}; + +class DUListView : public TQListView +{ + TQ_OBJECT + + +public: + DUListView( DiskUsage *usage, const char *name ); + ~DUListView(); + + File * getCurrentFile(); + +public slots: + void slotDirChanged( Directory * ); + void slotChanged( File * ); + void slotDeleted( File * ); + void slotRightClicked(TQListViewItem *); + void slotExpanded( TQListViewItem * ); + +protected: + DiskUsage *diskUsage; + + virtual void contentsMouseDoubleClickEvent ( TQMouseEvent * e ); + virtual void keyPressEvent( TQKeyEvent *e ); + +private: + void addDirectory( Directory *dirEntry, TQListViewItem *parent ); + bool doubleClicked( TQListViewItem * item ); +}; + +#endif /* __DU_LISTVIEW_H__ */ + diff --git a/src/app/DiskUsage/filelightParts/Config.cpp b/src/app/DiskUsage/filelightParts/Config.cpp new file mode 100644 index 0000000..bf25a29 --- /dev/null +++ b/src/app/DiskUsage/filelightParts/Config.cpp @@ -0,0 +1,50 @@ + +#include "Config.h" +#include <tdeconfig.h> +#include <tdeglobal.h> + + +bool Config::varyLabelFontSizes = true; +bool Config::showSmallFiles = false; +uint Config::contrast = 50; +uint Config::antiAliasFactor = 2; +uint Config::minFontPitch = 10; +uint Config::defaultRingDepth = 4; +Filelight::MapScheme Config::scheme; + + +inline TDEConfig& +Filelight::Config::tdeconfig() +{ + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "DiskUsage" ); + return *config; +} + +void +Filelight::Config::read() +{ + const TDEConfig &config = tdeconfig(); + + varyLabelFontSizes = config.readBoolEntry( "varyLabelFontSizes", true ); + showSmallFiles = config.readBoolEntry( "showSmallFiles", false ); + contrast = config.readNumEntry( "contrast", 50 ); + antiAliasFactor = config.readNumEntry( "antiAliasFactor", 2 ); + minFontPitch = config.readNumEntry( "minFontPitch", TQFont().pointSize() - 3); + scheme = (MapScheme) config.readNumEntry( "scheme", 0 ); + + defaultRingDepth = 4; +} + +void +Filelight::Config::write() +{ + TDEConfig &config = tdeconfig(); + + config.writeEntry( "varyLabelFontSizes", varyLabelFontSizes ); + config.writeEntry( "showSmallFiles", showSmallFiles); + config.writeEntry( "contrast", contrast ); + config.writeEntry( "antiAliasFactor", antiAliasFactor ); + config.writeEntry( "minFontPitch", minFontPitch ); + config.writeEntry( "scheme", scheme ); +} diff --git a/src/app/DiskUsage/filelightParts/Config.h b/src/app/DiskUsage/filelightParts/Config.h new file mode 100644 index 0000000..52a98b7 --- /dev/null +++ b/src/app/DiskUsage/filelightParts/Config.h @@ -0,0 +1,37 @@ + +#ifndef Config_H +#define Config_H + +#include <tqstringlist.h> + +class TDEConfig; + + +namespace Filelight +{ + enum MapScheme { Rainbow, HighContrast, KDE, FileDensity, ModTime }; + + class Config + { + static TDEConfig& tdeconfig(); + + public: + static void read(); + static void write(); + + //keep everything positive, avoid using DON'T, NOT or NO + + static bool varyLabelFontSizes; + static bool showSmallFiles; + static uint contrast; + static uint antiAliasFactor; + static uint minFontPitch; + static uint defaultRingDepth; + + static MapScheme scheme; + }; +} + +using Filelight::Config; + +#endif diff --git a/src/app/DiskUsage/filelightParts/Makefile.am b/src/app/DiskUsage/filelightParts/Makefile.am new file mode 100644 index 0000000..002f461 --- /dev/null +++ b/src/app/DiskUsage/filelightParts/Makefile.am @@ -0,0 +1,9 @@ +noinst_LIBRARIES = libfilelightparts.a + +INCLUDES = $(all_includes) + +METASOURCES = AUTO + +libfilelightparts_a_SOURCES = \ + Config.cpp \ + fileTree.cpp diff --git a/src/app/DiskUsage/filelightParts/debug.h b/src/app/DiskUsage/filelightParts/debug.h new file mode 100644 index 0000000..252a001 --- /dev/null +++ b/src/app/DiskUsage/filelightParts/debug.h @@ -0,0 +1,14 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef DEBUG_H +#define DEBUG_H + +#include <kdebug.h> + +#define debug kdDebug +#define error kdError +#define fatal kdFatal +#define warning kdWarning + +#endif diff --git a/src/app/DiskUsage/filelightParts/fileTree.cpp b/src/app/DiskUsage/filelightParts/fileTree.cpp new file mode 100644 index 0000000..0afd4d4 --- /dev/null +++ b/src/app/DiskUsage/filelightParts/fileTree.cpp @@ -0,0 +1,78 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2004 +//Copyright: See COPYING file that comes with this distribution + +#include "fileTree.h" +#include <tdeglobal.h> +#include <tdelocale.h> +#include <tqstring.h> + +//static definitions +const FileSize File::DENOMINATOR[4] = { 1ull, 1ull<<10, 1ull<<20, 1ull<<30 }; +const char File::PREFIX[5][2] = { "", "K", "M", "G", "T" }; + +TQString +File::fullPath( const Directory *root /*= 0*/ ) const +{ + TQString path; + + if( root == this ) root = 0; //prevent returning empty string when there is something we could return + + const File *d; + + for( d = this; d != root && d && d->parent() != 0; d = d->parent() ) + { + if( !path.isEmpty() ) + path = "/" + path; + + path = d->name() + path; + } + + if( d ) + { + while( d->parent() ) + d = d->parent(); + + if( d->directory().endsWith( "/" ) ) + return d->directory() + path; + else + return d->directory() + "/" + path; + } + else + return path; +} + +TQString +File::humanReadableSize( UnitPrefix key /*= mega*/ ) const //FIXME inline +{ + return humanReadableSize( m_size, key ); +} + +TQString +File::humanReadableSize( FileSize size, UnitPrefix key /*= mega*/ ) //static +{ + TQString s; + double prettySize = (double)size / (double)DENOMINATOR[key]; + const TDELocale &locale = *TDEGlobal::locale(); + + if( prettySize >= 0.01 ) + { + if( prettySize < 1 ) s = locale.formatNumber( prettySize, 2 ); + else if( prettySize < 100 ) s = locale.formatNumber( prettySize, 1 ); + else s = locale.formatNumber( prettySize, 0 ); + + s += ' '; + s += PREFIX[key]; + s += 'B'; + } + + if( prettySize < 0.1 ) + { + s += " ("; + s += locale.formatNumber( size / DENOMINATOR[ key ? key - 1 : 0 ], 0 ); + s += ' '; + s += PREFIX[key]; + s += "B)"; + } + + return s; +} diff --git a/src/app/DiskUsage/filelightParts/fileTree.h b/src/app/DiskUsage/filelightParts/fileTree.h new file mode 100644 index 0000000..2ba79ae --- /dev/null +++ b/src/app/DiskUsage/filelightParts/fileTree.h @@ -0,0 +1,299 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2004 +//Copyright: See COPYING file that comes with this distribution + +#ifndef FILETREE_H +#define FILETREE_H + +#include <stdlib.h> +#include <sys/types.h> +#include <tdeio/global.h> + +//TODO these are pointlessly general purpose now, make them incredibly specific + + + +typedef TDEIO::filesize_t FileSize; + +template <class T> class Iterator; +template <class T> class ConstIterator; +template <class T> class Chain; + +template <class T> +class Link +{ +public: + Link( T* const t ) : prev( this ), next( this ), data( t ) {} + Link() : prev( this ), next( this ), data( 0 ) {} + + //TODO unlinking is slow and you don't use it very much in this context. + // ** Perhaps you can make a faster deletion system that doesn't bother tidying up first + // ** and then you MUST call some kind of detach() function when you remove elements otherwise + ~Link() { delete data; unlink(); } + + friend class Iterator<T>; + friend class ConstIterator<T>; + friend class Chain<T>; + +private: + void unlink() { prev->next = next; next->prev = prev; prev = next = this; } + + Link<T>* prev; + Link<T>* next; + + T* data; //ensure only iterators have access to this +}; + + +template <class T> +class Iterator +{ +public: + Iterator() : link( 0 ) { } //**** remove this, remove this REMOVE THIS!!! dangerous as your implementation doesn't test for null links, always assumes they can be derefenced + Iterator( Link<T> *p ) : link( p ) { } + + bool operator==( const Iterator<T>& it ) const { return link == it.link; } + bool operator!=( const Iterator<T>& it ) const { return link != it.link; } + bool operator!=( const Link<T> *p ) const { return p != link; } + + //here we have a choice, really I should make two classes one const the other not + const T* operator*() const { return link->data; } + T* operator*() { return link->data; } + + Iterator<T>& operator++() { link = link->next; return *this; } //**** does it waste time returning in places where we don't use the retval? + + bool isNull() const { return (link == 0); } //REMOVE WITH ABOVE REMOVAL you don't want null iterators to be possible + + void transferTo( Chain<T> &chain ) + { + chain.append( remove() ); + } + + T* const remove() //remove from list, delete Link, data is returned NOT deleted + { + T* const d = link->data; + Link<T>* const p = link->prev; + + link->data = 0; + delete link; + link = p; //make iterator point to previous element, YOU must check this points to an element + + return d; + } + +private: + Link<T> *link; +}; + + +template <class T> +class ConstIterator +{ +public: + ConstIterator( Link<T> *p ) : link( p ) { } + + bool operator==( const Iterator<T>& it ) const { return link == it.link; } + bool operator!=( const Iterator<T>& it ) const { return link != it.link; } + bool operator!=( const Link<T> *p ) const { return p != link; } + + const T* operator*() const { return link->data; } + + ConstIterator<T>& operator++() { link = link->next; return *this; } + +private: + const Link<T> *link; +}; + +//**** try to make a generic list class and then a brief full list template that inlines +// thus reducing code bloat +template <class T> +class Chain +{ +public: + Chain() { } + virtual ~Chain() { empty(); } + + void append( T* const data ) + { + Link<T>* const link = new Link<T>( data ); + + link->prev = head.prev; + link->next = &head; + + head.prev->next = link; + head.prev = link; + } + + void transferTo( Chain &c ) + { + if( isEmpty() ) return; + + Link<T>* const first = head.next; + Link<T>* const last = head.prev; + + head.unlink(); + + first->prev = c.head.prev; + c.head.prev->next = first; + + last->next = &c.head; + c.head.prev = last; + } + + void empty() { while( head.next != &head ) { delete head.next; } } + + Iterator<T> iterator() const { return Iterator<T>( head.next ); } + ConstIterator<T> constIterator() const { return ConstIterator<T>( head.next ); } + const Link<T> *end() const { return &head; } + bool isEmpty() const { return ( head.next == &head ); } + +private: + Link<T> head; + void operator=( const Chain& ) {} +}; + + +class Directory; +class TQString; +class KURL; + +class File +{ +protected: + Directory *m_parent; //0 if this is treeRoot + TQString m_name; //< file name + TQString m_directory;//< the directory of the file + FileSize m_size; //< size with subdirectories + FileSize m_ownSize; //< size without subdirectories + mode_t m_mode; //< file mode + TQString m_owner; //< file owner name + TQString m_group; //< file group name + TQString m_perm; //< file permissions string + time_t m_time; //< file modification in time_t format + bool m_symLink; //< true if the file is a symlink + TQString m_mimeType; //< file mimetype + bool m_excluded; //< flag if the file is excluded from du + int m_percent; //< percent flag + +public: + File( Directory *parentIn, const TQString &nameIn, const TQString &dir, FileSize sizeIn, mode_t modeIn, + const TQString &ownerIn, const TQString &groupIn, const TQString &permIn, time_t timeIn, bool symLinkIn, + const TQString &mimeTypeIn ) + : m_parent( parentIn ), m_name( nameIn ), m_directory( dir ), m_size( sizeIn ), m_ownSize( sizeIn ), m_mode( modeIn ), + m_owner( ownerIn ), m_group( groupIn ), m_perm( permIn ), m_time( timeIn ), m_symLink( symLinkIn ), + m_mimeType( mimeTypeIn ), m_excluded( false ), m_percent( -1 ) {} + + File( const TQString &nameIn, FileSize sizeIn ) + : m_parent( 0 ), m_name( nameIn ), m_directory( TQString() ), m_size( sizeIn ), m_ownSize( sizeIn ), m_mode( 0 ), + m_owner( TQString() ), m_group( TQString() ), m_perm( TQString() ), m_time( -1 ), + m_symLink( false ), m_mimeType( TQString() ), m_excluded( false ), m_percent( -1 ) + { + } + + virtual ~File() {} + + inline const TQString & name() const {return m_name;} + inline const TQString & directory() const {return m_directory;} + inline const FileSize size() const {return m_excluded ? 0 : m_size;} + inline const FileSize ownSize() const {return m_excluded ? 0 : m_ownSize;} + inline const mode_t mode() const {return m_mode;} + inline const TQString & owner() const {return m_owner;} + inline const TQString & group() const {return m_group;} + inline const TQString & perm() const {return m_perm;} + inline const time_t time() const {return m_time;} + inline const TQString & mime() const {return m_mimeType;} + inline const bool isSymLink() const {return m_symLink;} + virtual const bool isDir() const {return false;} + inline const bool isExcluded() const {return m_excluded;} + inline void exclude( bool flag ) {m_excluded = flag;} + inline const int intPercent() const {return m_percent;} + inline const TQString percent() const {if( m_percent < 0 ) + return "INV"; + TQString buf; + buf.sprintf( "%d.%02d%%", m_percent / 100, m_percent % 100 ); + return buf;} + inline void setPercent( int p ) {m_percent = p;} + inline const Directory* parent() const {return m_parent;} + + inline void setSizes( TDEIO::filesize_t totalSize, TDEIO::filesize_t ownSize ) + { + m_ownSize = ownSize; + m_size = totalSize; + } + + enum UnitPrefix { kilo, mega, giga, tera }; + + static const FileSize DENOMINATOR[4]; + static const char PREFIX[5][2]; + + TQString fullPath( const Directory* = 0 ) const; + TQString humanReadableSize( UnitPrefix key = mega ) const; + + static TQString humanReadableSize( FileSize size, UnitPrefix Key = mega ); + + friend class Directory; +}; + + +//TODO when you modify this to take into account hardlinks you should make the Chain layered not inherited +class Directory : public Chain<File>, public File +{ +public: + Directory( Directory *parentIn, const TQString &nameIn, const TQString &dir, FileSize sizeIn, mode_t modeIn, + const TQString &ownerIn, const TQString &groupIn, const TQString &permIn, time_t timeIn, bool symLinkIn, + const TQString &mimeTypeIn ) + : File( parentIn, nameIn, dir, sizeIn, modeIn, ownerIn, groupIn, permIn, timeIn, symLinkIn, mimeTypeIn ), + m_fileCount( 0 ) + {} + + Directory( const TQString &name, TQString url ) : File( name, 0 ), m_fileCount( 0 ) + { + m_directory = url; + } + + virtual ~Directory() {} + virtual const bool isDir() const {return true;} + + void append( File *p ) + { + ++m_fileCount; + + Directory *parent = m_parent; + while( parent ) + { + parent->m_fileCount++; + parent = parent->m_parent; + } + + Chain<File>::append( p ); + p->m_parent = this; + } + + void remove( File *p ) + { + for( Iterator<File> it = Chain<File>::iterator(); it != Chain<File>::end(); ++it ) + if( (*it) == p ) + { + --m_fileCount; + + Directory *parent = m_parent; + while( parent ) + { + parent->m_fileCount--; + parent = parent->m_parent; + } + + it.remove(); + break; + } + } + + uint fileCount() const { return m_fileCount; } + +private: + Directory( const Directory& ); + void operator=( const Directory& ); + + uint m_fileCount; +}; + +#endif diff --git a/src/app/DiskUsage/radialMap/Makefile.am b/src/app/DiskUsage/radialMap/Makefile.am new file mode 100644 index 0000000..ab5effd --- /dev/null +++ b/src/app/DiskUsage/radialMap/Makefile.am @@ -0,0 +1,13 @@ +noinst_LIBRARIES = libradialmap.a + +INCLUDES = -I$(top_srcdir)/src/app/DiskUsage/filelightParts $(all_includes) + +METASOURCES = AUTO + +libradialmap_a_SOURCES = \ + widget.cpp \ + builder.cpp \ + map.cpp \ + widgetEvents.cpp \ + labels.cpp \ + segmentTip.cpp diff --git a/src/app/DiskUsage/radialMap/builder.cpp b/src/app/DiskUsage/radialMap/builder.cpp new file mode 100644 index 0000000..8edb70c --- /dev/null +++ b/src/app/DiskUsage/radialMap/builder.cpp @@ -0,0 +1,139 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include "builder.h" +#include "Config.h" +#include "fileTree.h" +#include <tdeglobal.h> //locale object +#include <tdelocale.h> +#include "widget.h" + + +//**** REMOVE NEED FOR the +1 with MAX_RING_DEPTH uses +//**** add some angle bounds checking (possibly in Segment ctor? can I delete in a ctor?) +//**** this class is a mess + +RadialMap::Builder::Builder( RadialMap::Map *m, const Directory* const d, bool fast ) + : m_map( m ) + , m_root( d ) + , m_minSize( static_cast<FileSize>((d->size() * 3) / (PI * m->height() - m->MAP_2MARGIN )) ) + , m_depth( &m->m_visibleDepth ) +{ + m_signature = new Chain<Segment> [*m_depth + 1]; + + if( !fast )//|| *m_depth == 0 ) //depth 0 is special case usability-wise //**** WHY?! + { + //determine depth rather than use old one + findVisibleDepth( d ); //sets m_depth + } + + m_map->setRingBreadth(); + setLimits( m_map->m_ringBreadth ); + build( d ); + + m_map->m_signature = m_signature; + + delete []m_limits; +} + + +void +RadialMap::Builder::findVisibleDepth( const Directory* const dir, const unsigned int depth ) +{ + //**** because I don't use the same minimumSize criteria as in the visual function + // this can lead to incorrect visual representation + //**** BUT, you can't set those limits until you know m_depth! + + //**** also this function doesn't check to see if anything is actually visible + // it just assumes that when it reaches a new level everything in it is visible + // automatically. This isn't right especially as there might be no files in the + // dir provided to this function! + + static uint stopDepth = 0; + + if( dir == m_root ) + { + stopDepth = *m_depth; + *m_depth = 0; + } + + if( *m_depth < depth ) *m_depth = depth; + if( *m_depth >= stopDepth ) return; + + for( ConstIterator<File> it = dir->constIterator(); it != dir->end(); ++it ) + if( (*it)->isDir() && (*it)->size() > m_minSize ) + findVisibleDepth( (Directory *)*it, depth + 1 ); //if no files greater than min size the depth is still recorded +} + +void +RadialMap::Builder::setLimits( const uint &b ) //b = breadth? +{ + double size3 = m_root->size() * 3; + double pi2B = PI * 2 * b; + + m_limits = new FileSize [*m_depth + 1]; //FIXME delete! + + for( unsigned int d = 0; d <= *m_depth; ++d ) + m_limits[d] = (FileSize)(size3 / (double)(pi2B * (d + 1))); //min is angle that gives 3px outer diameter for that depth +} + + +//**** segments currently overlap at edges (i.e. end of first is start of next) +bool +RadialMap::Builder::build( const Directory* const dir, const unsigned int depth, unsigned int a_start, const unsigned int a_end ) +{ + //first iteration: dir == m_root + + if( dir->fileCount() == 0 ) //we do fileCount rather than size to avoid chance of divide by zero later + return false; + + FileSize hiddenSize = 0; + uint hiddenFileCount = 0; + + for( ConstIterator<File> it = dir->constIterator(); it != dir->end(); ++it ) + { + if( (*it)->size() > m_limits[depth] ) + { + unsigned int a_len = (unsigned int)(5760 * ((double)(*it)->size() / (double)m_root->size())); + + Segment *s = new Segment( *it, a_start, a_len ); + + (m_signature + depth)->append( s ); + + if( (*it)->isDir() ) + { + if( depth != *m_depth ) + { + //recurse + s->m_hasHiddenChildren = build( (Directory*)*it, depth + 1, a_start, a_start + a_len ); + } + else s->m_hasHiddenChildren = true; + } + + a_start += a_len; //**** should we add 1? + + } else { + + hiddenSize += (*it)->size(); + + if( (*it)->isDir() ) //**** considered virtual, but dir wouldn't count itself! + hiddenFileCount += static_cast<const Directory*>(*it)->fileCount(); //need to add one to count the dir as well + + ++hiddenFileCount; + } + } + + if( hiddenFileCount == dir->fileCount() && !Config::showSmallFiles ) + + return true; + + else if( (Config::showSmallFiles && hiddenSize > m_limits[depth]) || (depth == 0 && (hiddenSize > dir->size()/8)) /*|| > size() * 0.75*/ ) + { + //append a segment for unrepresented space - a "fake" segment + + const TQString s = i18n( "%1 files: ~ %2" ).arg( TDEGlobal::locale()->formatNumber( hiddenFileCount, 0 ) ).arg( File::humanReadableSize( hiddenSize/hiddenFileCount ) ); + (m_signature + depth)->append( new Segment( new File( s, hiddenSize ), a_start, a_end - a_start, true ) ); + } + + return false; +} diff --git a/src/app/DiskUsage/radialMap/builder.h b/src/app/DiskUsage/radialMap/builder.h new file mode 100644 index 0000000..2fddbe0 --- /dev/null +++ b/src/app/DiskUsage/radialMap/builder.h @@ -0,0 +1,37 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef BUILDER_H +#define BUILDER_H + +#include "radialMap.h" //Segment, defines +#include "fileTree.h" + +template <class T> class Chain; + +namespace RadialMap +{ + class Map; + + //temporary class that builds the Map signature + + class Builder + { + public: + Builder( Map*, const Directory* const, bool fast=false ); + + private: + void findVisibleDepth( const Directory* const dir, const uint=0 ); + void setLimits( const uint& ); + bool build( const Directory* const, const uint=0, uint=0, const uint=5760 ); + + Map *m_map; + const Directory* const m_root; + const FileSize m_minSize; + uint *m_depth; + Chain<Segment> *m_signature; + FileSize *m_limits; + }; +} + +#endif diff --git a/src/app/DiskUsage/radialMap/labels.cpp b/src/app/DiskUsage/radialMap/labels.cpp new file mode 100644 index 0000000..5d61b7a --- /dev/null +++ b/src/app/DiskUsage/radialMap/labels.cpp @@ -0,0 +1,342 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include <kstringhandler.h> +#include <tqfont.h> +#include <tqfontmetrics.h> +#include <tqpainter.h> +#include <tqptrlist.h> + +#include "Config.h" +#include "fileTree.h" +#include "radialMap.h" +#include "sincos.h" +#include "widget.h" + + + +namespace RadialMap +{ + struct Label + { + Label( const RadialMap::Segment *s, int l ) : segment( s ), lvl( l ), a( segment->start() + (segment->length() / 2) ) { } + + bool tooClose( const int &aa ) const { return ( a > aa - LABEL_ANGLE_MARGIN && a < aa + LABEL_ANGLE_MARGIN ); } + + const RadialMap::Segment *segment; + const unsigned int lvl; + const int a; + + int x1, y1, x2, y2, x3; + int tx, ty; + + TQString qs; + }; + + class LabelList : public TQPtrList<Label> + { + protected: + int compareItems( TQPtrCollection::Item item1, TQPtrCollection::Item item2 ) + { + //you add 1440 to work round the fact that later you want the circle split vertically + //and as it is you start at 3 o' clock. It's to do with rightPrevY, stops annoying bug + + int a1 = ((Label*)item1)->a + 1440; + int a2 = ((Label*)item2)->a + 1440; + + if( a1 == a2 ) + return 0; + + if( a1 > 5760 ) a1 -= 5760; + if( a2 > 5760 ) a2 -= 5760; + + if( a1 > a2 ) + return 1; + + return -1; + } + }; +} + + +void +RadialMap::Widget::paintExplodedLabels( TQPainter &paint ) const +{ + //we are a friend of RadialMap::Map + + LabelList list; list.setAutoDelete( true ); + TQPtrListIterator<Label> it( list ); + unsigned int startLevel = 0; + + + //1. Create list of labels sorted in the order they will be rendered + + if( m_focus != NULL && m_focus->file() != m_tree ) //separate behavior for selected vs unselected segments + { + //don't bother with files + if( m_focus->file() == 0 || !m_focus->file()->isDir() ) return; + + //find the range of levels we will be potentially drawing labels for + for( const Directory *p = (const Directory *)m_focus->file(); + p != m_tree; + ++startLevel ) //startLevel is the level above whatever m_focus is in + { + p = p->parent(); + } + + //range=2 means 2 levels to draw labels for + + unsigned int a1, a2, minAngle; + + a1 = m_focus->start(); + a2 = m_focus->end(); //boundry angles + minAngle = int(m_focus->length() * LABEL_MIN_ANGLE_FACTOR); + + + #define segment (*it) + #define ring (m_map.m_signature + i) + + //**** Levels should be on a scale starting with 0 + //**** range is a useless parameter + //**** keep a topblock var which is the lowestLevel OR startLevel for identation purposes + for( unsigned int i = startLevel; i <= m_map.m_visibleDepth; ++i ) + { + for( Iterator<Segment> it = ring->iterator(); it != ring->end(); ++it ) + if( segment->start() >= a1 && segment->end() <= a2 ) + if( segment->length() > minAngle ) + list.inSort( new Label( segment, i ) ); + } + + #undef ring + #undef segment + + } else { + + #define ring m_map.m_signature + + for( Iterator<Segment> it = ring->iterator(); it != ring->end(); ++it ) + if( (*it)->length() > 288 ) + list.inSort( new Label( (*it), 0 ) ); + + #undef ring + + } + + //2. Check to see if any adjacent labels are too close together + // if so, remove the least significant labels + + it.toFirst(); + TQPtrListIterator<Label> jt( it ); + ++jt; + + while( jt ) //**** no need to check _it_ as jt will be NULL if _it_ was too + { + //this method is fairly efficient + + if( (*it)->tooClose( (*jt)->a ) ) + { + if( (*it)->lvl > (*jt)->lvl ) + { + list.remove( *it ); + it = jt; + } + else + list.remove( *jt ); + } + else + ++it; + + jt = it; + ++jt; + } + + //used in next two steps + bool varySizes; + //**** should perhaps use doubles + int *sizes = new int [ m_map.m_visibleDepth + 1 ]; //**** make sizes an array of floats I think instead (or doubles) + + do + { + //3. Calculate font sizes + + { + //determine current range of levels to draw for + unsigned int range = 0; + + for( it.toFirst(); it != 0; ++it ) + { + unsigned int lvl = (*it)->lvl; + if( lvl > range ) + range = lvl; + + //**** better way would just be to assign if nothing is range + } + + range -= startLevel; //range 0 means 1 level of labels + + varySizes = Config::varyLabelFontSizes && (range != 0); + + if( varySizes ) + { + //create an array of font sizes for various levels + //will exceed normal font pitch automatically if necessary, but not minPitch + //**** this needs to be checked lots + + //**** what if this is negative (min size gtr than default size) + unsigned int step = (paint.font().pointSize() - Config::minFontPitch) / range; + if( step == 0 ) step = 1; + + for( unsigned int x = range + startLevel, y = Config::minFontPitch; + x >= startLevel; + y += step, --x ) + { + sizes[x] = y; + } + } + } + + //4. determine label co-ordinates + + int x1, y1, x2, y2, x3, tx, ty; //coords + double sinra, cosra, ra; //angles + + int cx = m_map.width() / 2 + m_offset.x(); //centre relative to canvas + int cy = m_map.height() / 2 + m_offset.y(); + + int spacer, preSpacer = int(m_map.m_ringBreadth * 0.5) + m_map.m_innerRadius; + int fullStrutLength = ( m_map.width() - m_map.MAP_2MARGIN ) / 2 + LABEL_MAP_SPACER; //full length of a strut from map center + + int prevLeftY = 0; + int prevRightY = height(); + + bool rightSide; + + TQFont font; + + for( it.toFirst(); it != 0; ++it ) + { + //** bear in mind that text is drawn with TQPoint param as BOTTOM left corner of text box + if( varySizes ) font.setPointSize( sizes[(*it)->lvl] ); + TQFontMetrics fm( font ); + int fmh = fm.height(); //used to ensure label texts don't overlap + int fmhD4 = fmh / 4; + + fmh += LABEL_TEXT_VMARGIN; + + rightSide = ( (*it)->a < 1440 || (*it)->a > 4320 ); + + ra = M_PI/2880 * (*it)->a; //convert to radians +#if 0 + sincos( ra, &sinra, &cosra ); +#endif + sinra = sin(ra); cosra = cos(ra); + + spacer = preSpacer + m_map.m_ringBreadth * (*it)->lvl; + + x1 = cx + (int)(cosra * spacer); + y1 = cy - (int)(sinra * spacer); + y2 = y1 - (int)(sinra * (fullStrutLength - spacer)); + + if( rightSide ) { //righthand side, going upwards + + if( y2 > prevRightY /*- fmh*/ ) //then it is too low, needs to be drawn higher + y2 = prevRightY /*- fmh*/; + + } else { //lefthand side, going downwards + + if( y2 < prevLeftY/* + fmh*/ ) //then we're too high, need to be drawn lower + y2 = prevLeftY /*+ fmh*/; + } + + x2 = x1 - int(double(y2 - y1) / tan( ra )); + ty = y2 + fmhD4; + + TQString qs; + if( rightSide ) { + + if( x2 > width() || ty < fmh || x2 < x1 ) + { + //skip this strut + //**** don't duplicate this code + list.remove( *it ); //will delete the label and set it to list.current() which _should_ be the next ptr + break; + } + + prevRightY = ty - fmh - fmhD4; //must be after above's "continue" + + qs = KStringHandler::cPixelSqueeze( (*it)->segment->file()->name(), fm, width() - x2 ); + + x3 = width() - fm.width( qs ) + - LABEL_HMARGIN //outer margin + - LABEL_TEXT_HMARGIN //margin between strut and text + //- ((*it)->lvl - startLevel) * LABEL_HMARGIN //indentation + ; + if( x3 < x2 ) x3 = x2; + tx = x3 + LABEL_TEXT_HMARGIN; + + } else { + + if( x2 < 0 || ty > height() || x2 > x1 ) + { + //skip this strut + list.remove( *it ); //will delete the label and set it to list.current() which _should_ be the next ptr + break; + } + + prevLeftY = ty + fmh - fmhD4; + + qs = KStringHandler::cPixelSqueeze( (*it)->segment->file()->name(), fm, x2 ); + + //**** needs a little tweaking: + + tx = fm.width( qs ) + LABEL_HMARGIN/* + ((*it)->lvl - startLevel) * LABEL_HMARGIN*/; + if( tx > x2 ) { //text is too long + tx = LABEL_HMARGIN + x2 - tx; //some text will be lost from sight + x3 = x2; //no text margin (right side of text here) + } else { + x3 = tx + LABEL_TEXT_HMARGIN; + tx = LABEL_HMARGIN /* + ((*it)->lvl - startLevel) * LABEL_HMARGIN*/; + } + } + + (*it)->x1 = x1; + (*it)->y1 = y1; + (*it)->x2 = x2; + (*it)->y2 = y2; + (*it)->x3 = x3; + (*it)->tx = tx; + (*it)->ty = ty; + (*it)->qs = qs; + } + + //if an element is deleted at this stage, we need to do this whole + //iteration again, thus the following loop + //**** in rare case that deleted label was last label in top level + // and last in labelList too, this will not work as expected (not critical) + + } while( it != 0 ); + + + //5. Render labels + + paint.setPen( TQPen( TQt::black, 1 ) ); + + for( it.toFirst(); it != 0; ++it ) + { + if( varySizes ) + { + //**** how much overhead in making new TQFont each time? + // (implicate sharing remember) + TQFont font = paint.font(); + font.setPointSize( sizes[(*it)->lvl] ); + paint.setFont( font ); + } + + paint.drawEllipse( (*it)->x1 - 3, (*it)->y1 - 3, 7, 7 ); //**** CPU intensive! better to use a pixmap + paint.drawLine( (*it)->x1, (*it)->y1, (*it)->x2, (*it)->y2 ); + paint.drawLine( (*it)->x2, (*it)->y2, (*it)->x3, (*it)->y2); + paint.drawText( (*it)->tx, (*it)->ty, (*it)->qs ); + } + + delete [] sizes; +} diff --git a/src/app/DiskUsage/radialMap/map.cpp b/src/app/DiskUsage/radialMap/map.cpp new file mode 100644 index 0000000..329b8bc --- /dev/null +++ b/src/app/DiskUsage/radialMap/map.cpp @@ -0,0 +1,432 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include <kcursor.h> //make() +#include <tdeglobalsettings.h> //kdeColours +#include <kimageeffect.h> //desaturate() +#include <tqapplication.h> //make() +#include <tqimage.h> //make() & paint() +#include <tqfont.h> //ctor +#include <tqfontmetrics.h> //ctor +#include <tqpainter.h> + +#include "builder.h" +#include "Config.h" +#include "fileTree.h" +#include "sincos.h" +#include "widget.h" + +#define COLOR_GREY TQColor( 0, 0, 140, TQColor::Hsv ) + + +RadialMap::Map::Map() + : m_signature( 0 ) + , m_ringBreadth( MIN_RING_BREADTH ) + , m_innerRadius( 0 ) + , m_visibleDepth( DEFAULT_RING_DEPTH ) +{ + //FIXME this is all broken. No longer is a maximum depth! + const int fmh = TQFontMetrics( TQFont() ).height(); + const int fmhD4 = fmh / 4; + MAP_2MARGIN = 2 * ( fmh - (fmhD4 - LABEL_MAP_SPACER) ); //margin is dependent on fitting in labels at top and bottom +} + +RadialMap::Map::~Map() +{ + delete [] m_signature; +} + +void +RadialMap::Map::invalidate( const bool desaturateTheImage ) +{ + delete [] m_signature; + m_signature = 0; + + if( desaturateTheImage ) + { + TQImage img = this->convertToImage(); + + KImageEffect::desaturate( img, 0.7 ); + KImageEffect::toGray( img, true ); + + this->convertFromImage( img ); + } + + m_visibleDepth = Config::defaultRingDepth; +} + +void +RadialMap::Map::make( const Directory *tree, bool refresh ) +{ + //**** determineText seems pointless optimisation + // but is it good to keep the text consistent? + // even if it makes it a lie? + + //slow operation so set the wait cursor + TQApplication::setOverrideCursor( KCursor::waitCursor() ); + + { + //build a signature of visible components + delete [] m_signature; + Builder builder( this, tree, refresh ); + } + + //colour the segments + colorise(); + + //determine centerText + if( !refresh ) + { + int i; + + for( i = 3; i > 0; --i ) + if( tree->size() > File::DENOMINATOR[i] ) + break; + + m_centerText = tree->humanReadableSize( (File::UnitPrefix)i ); + } + + //paint the pixmap + aaPaint(); + + TQApplication::restoreOverrideCursor(); +} + +void +RadialMap::Map::setRingBreadth() +{ + m_ringBreadth = (height() - MAP_2MARGIN) / (2 * m_visibleDepth + 4); + + if( m_ringBreadth < MIN_RING_BREADTH ) m_ringBreadth = MIN_RING_BREADTH; + else if( m_ringBreadth > MAX_RING_BREADTH ) m_ringBreadth = MAX_RING_BREADTH; +} + +bool +RadialMap::Map::resize( const TQRect &rect ) +{ + //there's a MAP_2MARGIN border + + #define mw width() + #define mh height() + #define cw rect.width() + #define ch rect.height() + + if( cw < mw || ch < mh || (cw > mw && ch > mh) ) + { + uint size = (( cw < ch ) ? cw : ch) - MAP_2MARGIN; + + //this also causes uneven sizes to always resize when resizing but map is small in that dimension + //size -= size % 2; //even sizes mean less staggered non-antialiased resizing + + { + const uint minSize = MIN_RING_BREADTH * 2 * (m_visibleDepth + 2); + const uint mD2 = MAP_2MARGIN / 2; + + if( size < minSize ) size = minSize; + + //this TQRect is used by paint() + m_rect.setRect( mD2, mD2, size, size ); + } + + //resize the pixmap + size += MAP_2MARGIN; + KPixmap::resize( size, size ); + + if( m_signature != NULL ) + { + setRingBreadth(); + paint(); + } + else fill(); //FIXME I don't like having to do this.. + + return true; + } + + #undef mw + #undef mh + #undef cw + #undef ch + + return false; +} + +void +RadialMap::Map::colorise() +{ + TQColor cp, cb; + double darkness = 1; + double contrast = (double)Config::contrast / (double)100; + int h, s1, s2, v1, v2; + + TQColor kdeColour[2] = { TDEGlobalSettings::inactiveTitleColor(), TDEGlobalSettings::activeTitleColor() }; + + double deltaRed = (double)(kdeColour[0].red() - kdeColour[1].red()) / 2880; //2880 for semicircle + double deltaGreen = (double)(kdeColour[0].green() - kdeColour[1].green()) / 2880; + double deltaBlue = (double)(kdeColour[0].blue() - kdeColour[1].blue()) / 2880; + + for( uint i = 0; i <= m_visibleDepth; ++i, darkness += 0.04 ) + { + for( Iterator<Segment> it = m_signature[i].iterator(); it != m_signature[i].end(); ++it ) + { + switch( Config::scheme ) + { + case 2000: //HACK for summary view + + if( (*it)->file()->name() == "Used" ) { + cb = TQApplication::palette().active().color( TQColorGroup::Highlight ); + cb.hsv( &h, &s1, &v1 ); + + if( s1 > 80 ) + s1 = 80; + + v2 = v1 - int(contrast * v1); + s2 = s1 + int(contrast * (255 - s1)); + + cb.setHsv( h, s1, v1 ); + cp.setHsv( h, s2, v2 ); + } + else { + cp = TQt::gray; + cb = TQt::white; + } + + (*it)->setPalette( cp, cb ); + + continue; + case Filelight::KDE: + { + //gradient will work by figuring out rgb delta values for 360 degrees + //then each component is angle*delta + + int a = (*it)->start(); + + if( a > 2880 ) a = 2880 - (a - 2880); + + h = (int)(deltaRed * a) + kdeColour[1].red(); + s1 = (int)(deltaGreen * a) + kdeColour[1].green(); + v1 = (int)(deltaBlue * a) + kdeColour[1].blue(); + + cb.setRgb( h, s1, v1 ); + cb.hsv( &h, &s1, &v1 ); + + break; + } + + case Filelight::HighContrast: + + cp.setHsv( 0, 0, 0 ); //values of h, s and v are irrelevant + cb.setHsv( 180, 0, int(255.0 * contrast) ); + (*it)->setPalette( cp, cb ); + continue; + + default: + h = int((*it)->start() / 16); + s1 = 160; + v1 = (int)(255.0 / darkness); //****doing this more often than once seems daft! + } + + v2 = v1 - int(contrast * v1); + s2 = s1 + int(contrast * (255 - s1)); + + if( s1 < 80 ) s1 = 80; //can fall too low and makes contrast between the files hard to discern + + if( (*it)->isFake() ) //multi-file + { + cb.setHsv( h, s2, (v2 < 90) ? 90 : v2 ); //too dark if < 100 + cp.setHsv( h, 17, v1 ); + } + else if( !(*it)->file()->isDir() ) //file + { + cb.setHsv( h, 17, v1 ); + cp.setHsv( h, 17, v2 ); + } + else //directory + { + cb.setHsv( h, s1, v1 ); //v was 225 + cp.setHsv( h, s2, v2 ); //v was 225 - delta + } + + (*it)->setPalette( cp, cb ); + + //**** may be better to store KDE colours as H and S and vary V as others + //**** perhaps make saturation difference for s2 dependent on contrast too + //**** fake segments don't work with highContrast + //**** may work better with cp = cb rather than TQt::white + //**** you have to ensure the grey of files is sufficient, currently it works only with rainbow (perhaps use contrast there too) + //**** change v1,v2 to vp, vb etc. + //**** using percentages is not strictly correct as the eye doesn't work like that + //**** darkness factor is not done for kde_colour scheme, and also value for files is incorrect really for files in this scheme as it is not set like rainbow one is + } + } +} + +void +RadialMap::Map::aaPaint() +{ + //paint() is called during continuous processes + //aaPaint() is not and is slower so set overidecursor (make sets it too) + TQApplication::setOverrideCursor( KCursor::waitCursor() ); + paint( Config::antiAliasFactor ); + TQApplication::restoreOverrideCursor(); +} + +void +RadialMap::Map::paint( unsigned int scaleFactor ) +{ + if( scaleFactor == 0 ) //just in case + scaleFactor = 1; + + TQPainter paint; + TQRect rect = m_rect; + int step = m_ringBreadth; + int excess = -1; + + //scale the pixmap, or do intelligent distribution of excess to prevent nasty resizing + if( scaleFactor > 1 ) + { + int x1, y1, x2, y2; + rect.coords( &x1, &y1, &x2, &y2 ); + x1 *= scaleFactor; + y1 *= scaleFactor; + x2 *= scaleFactor; + y2 *= scaleFactor; + rect.setCoords( x1, y1, x2, y2 ); + + step *= scaleFactor; + KPixmap::resize( this->size() * (int)scaleFactor ); + } + else if( m_ringBreadth != MAX_RING_BREADTH && m_ringBreadth != MIN_RING_BREADTH ) { + excess = rect.width() % m_ringBreadth; + ++step; + } + + //**** best option you can think of is to make the circles slightly less perfect, + // ** i.e. slightly eliptic when resizing inbetween + + + paint.begin( this ); + + fill(); //erase background + + for( int x = m_visibleDepth; x >= 0; --x ) + { + int width = rect.width() / 2; + //clever geometric trick to find largest angle that will give biggest arrow head + int a_max = int(acos( (double)width / double((width + 5) * scaleFactor) ) * (180*16 / M_PI)); + + for( ConstIterator<Segment> it = m_signature[x].constIterator(); it != m_signature[x].end(); ++it ) + { + //draw the pie segments, most of this code is concerned with drawing the little + //arrows on the ends of segments when they have hidden files + + paint.setPen( (*it)->pen() ); + + if( (*it)->hasHiddenChildren() ) + { + //draw arrow head to indicate undisplayed files/directories + TQPointArray pts( 3 ); + TQPoint pos, cpos = rect.center(); + uint a[3] = { (*it)->start(), (*it)->length(), 0 }; + + a[2] = a[0] + (a[1] / 2); //assign to halfway between + if( a[1] > a_max ) + { + a[1] = a_max; + a[0] = a[2] - a_max / 2; + } + + a[1] += a[0]; + + for( int i = 0, radius = width; i < 3; ++i ) + { + double ra = M_PI/(180*16) * a[i], sinra, cosra; + + if( i == 2 ) + radius += 5 * scaleFactor; +#if 0 + sincos( ra, &sinra, &cosra ); +#endif + sinra = sin(ra); cosra = cos(ra); + pos.rx() = cpos.x() + static_cast<int>(cosra * radius); + pos.ry() = cpos.y() - static_cast<int>(sinra * radius); + pts.setPoint( i, pos ); + } + + paint.setBrush( (*it)->pen() ); + paint.drawPolygon( pts ); + } + + paint.setBrush( (*it)->brush() ); + paint.drawPie( rect, (*it)->start(), (*it)->length() ); + + if( (*it)->hasHiddenChildren() ) + { + //**** code is bloated! + paint.save(); + TQPen pen = paint.pen(); + int width = 2 * scaleFactor; + pen.setWidth( width ); + paint.setPen( pen ); + TQRect rect2 = rect; + width /= 2; + rect2.addCoords( width, width, -width, -width ); + paint.drawArc( rect2, (*it)->start(), (*it)->length() ); + paint.restore(); + } + } + + if( excess >= 0 ) { //excess allows us to resize more smoothly (still crud tho) + if( excess < 2 ) //only decrease rect by more if even number of excesses left + --step; + excess -= 2; + } + + rect.addCoords( step, step, -step, -step ); + } + + // if( excess > 0 ) rect.addCoords( excess, excess, 0, 0 ); //ugly + + paint.setPen( COLOR_GREY ); + paint.setBrush( TQt::white ); + paint.drawEllipse( rect ); + + if( scaleFactor > 1 ) + { + //have to end in order to smoothscale() + paint.end(); + + int x1, y1, x2, y2; + rect.coords( &x1, &y1, &x2, &y2 ); + x1 /= scaleFactor; + y1 /= scaleFactor; + x2 /= scaleFactor; + y2 /= scaleFactor; + rect.setCoords( x1, y1, x2, y2 ); + + TQImage img = this->convertToImage(); + img = img.smoothScale( this->size() / (int)scaleFactor ); + this->convertFromImage( img ); + + paint.begin( this ); + paint.setPen( COLOR_GREY ); + paint.setBrush( TQt::white ); + } + + paint.drawText( rect, TQt::AlignCenter, m_centerText ); + + m_innerRadius = rect.width() / 2; //rect.width should be multiple of 2 + + paint.end(); +} +#if 0 +#if __GLIBC__ < 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 + + void + sincos( double angleRadians, double *Sin, double *Cos ) + { + *Sin = sin( angleRadians ); + *Cos = cos( angleRadians ); + } + +#endif +#endif diff --git a/src/app/DiskUsage/radialMap/radialMap.h b/src/app/DiskUsage/radialMap/radialMap.h new file mode 100644 index 0000000..c5edf65 --- /dev/null +++ b/src/app/DiskUsage/radialMap/radialMap.h @@ -0,0 +1,71 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef RADIALMAP_H +#define RADIALMAP_H + +#include <tqcolor.h> + +class File; + + +namespace RadialMap +{ + class Segment //all angles are in 16ths of degrees + { + public: + Segment( const File *f, uint s, uint l, bool isFake = false ) + : m_angleStart( s ) + , m_angleSegment( l ) + , m_file( f ) + , m_hasHiddenChildren( false ) + , m_fake( isFake ) {} + ~Segment(); + + uint start() const { return m_angleStart; } + uint length() const { return m_angleSegment; } + uint end() const { return m_angleStart + m_angleSegment; } + const File *file() const { return m_file; } + const TQColor& pen() const { return m_pen; } + const TQColor& brush() const { return m_brush; } + + bool isFake() const { return m_fake; } + bool hasHiddenChildren() const { return m_hasHiddenChildren; } + + bool intersects( uint a ) const { return ( ( a >= start() ) && ( a < end() ) ); } + + friend class Map; + friend class Builder; + + private: + void setPalette( const TQColor &p, const TQColor &b ) { m_pen = p; m_brush = b; } + + const uint m_angleStart, m_angleSegment; + const File* const m_file; + TQColor m_pen, m_brush; + bool m_hasHiddenChildren; + const bool m_fake; + }; +} + + +#ifndef PI +#define PI 3.141592653589793 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327 +#endif + +#define MIN_RING_BREADTH 20 +#define MAX_RING_BREADTH 60 +#define DEFAULT_RING_DEPTH 4 //first level = 0 +#define MIN_RING_DEPTH 0 + +#define LABEL_MAP_SPACER 7 +#define LABEL_HMARGIN 10 +#define LABEL_TEXT_HMARGIN 5 +#define LABEL_TEXT_VMARGIN 0 +#define LABEL_ANGLE_MARGIN 32 +#define LABEL_MIN_ANGLE_FACTOR 0.05 + +#endif diff --git a/src/app/DiskUsage/radialMap/segmentTip.cpp b/src/app/DiskUsage/radialMap/segmentTip.cpp new file mode 100644 index 0000000..2f2b31c --- /dev/null +++ b/src/app/DiskUsage/radialMap/segmentTip.cpp @@ -0,0 +1,163 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include "fileTree.h" +#include "segmentTip.h" + +#include <tdeapplication.h> //installing eventFilters +#include <tdeglobal.h> +#include <tdeglobalsettings.h> +#include <tdelocale.h> +#include <kpixmapeffect.h> +#include <tqpainter.h> +#include <tqtooltip.h> //for its palette + + + +namespace RadialMap { + +SegmentTip::SegmentTip( uint h ) + : TQWidget( 0, 0, WNoAutoErase | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WStyle_StaysOnTop | WX11BypassWM ) + , m_cursorHeight( -h ) +{ + setBackgroundMode( TQt::NoBackground ); +} + +void +SegmentTip::moveto( TQPoint p, const TQWidget &canvas, bool placeAbove ) +{ + //**** this function is very slow and seems to be visibly influenced by operations like mapFromGlobal() (who knows why!) + // ** so any improvements are much desired + + //TODO uints could improve the class + p.rx() -= rect().center().x(); + p.ry() -= (placeAbove ? 8 + height() : m_cursorHeight - 8); + + const TQRect screen = TDEGlobalSettings::desktopGeometry( parentWidget() ); + + const int x = p.x(); + const int y = p.y(); + const int x2 = x + width(); + const int y2 = y + height(); //how's it ever gunna get below screen height?! (well you never know I spose) + const int sw = screen.width(); + const int sh = screen.height(); + + if( x < 0 ) p.setX( 0 ); + if( y < 0 ) p.setY( 0 ); + if( x2 > sw ) p.rx() -= x2 - sw; + if( y2 > sh ) p.ry() -= y2 - sh; + + + //I'm using this TQPoint to determine where to offset the bitBlt in m_pixmap + TQPoint offset = canvas.mapToGlobal( TQPoint() ) - p; + if( offset.x() < 0 ) offset.setX( 0 ); + if( offset.y() < 0 ) offset.setY( 0 ); + + + const TQRect alphaMaskRect( canvas.mapFromGlobal( p ), size() ); + const TQRect intersection( alphaMaskRect.intersect( canvas.rect() ) ); + + m_pixmap.resize( size() ); //move to updateTip once you are sure it can never be null + bitBlt( &m_pixmap, offset, &canvas, intersection, TQt::CopyROP ); + + TQPainter paint( &m_pixmap ); + paint.setPen( TQt::black ); + paint.setBrush( TQt::NoBrush ); + paint.drawRect( rect() ); + paint.end(); + + m_pixmap = KPixmapEffect::fade( m_pixmap, 0.6, TQToolTip::palette().color( TQPalette::Active, TQColorGroup::Background ) ); + + paint.begin( &m_pixmap ); + paint.drawText( rect(), AlignCenter, m_text ); + paint.end(); + + p += screen.topLeft(); //for Xinerama users + + move( x, y ); + show(); + update(); +} + +void +SegmentTip::updateTip( const File* const file, const Directory* const root ) +{ + const TQString s1 = file->fullPath( root ); + TQString s2 = file->humanReadableSize(); + TDELocale *loc = TDEGlobal::locale(); + const uint MARGIN = 3; + const uint pc = 100 * file->size() / root->size(); + uint maxw = 0; + uint h = fontMetrics().height()*2 + 2*MARGIN; + + if( pc > 0 ) s2 += TQString( " (%1%)" ).arg( loc->formatNumber( pc, 0 ) ); + + m_text = s1; + m_text += '\n'; + m_text += s2; + + if( file->isDir() ) + { + double files = static_cast<const Directory*>(file)->fileCount(); + const uint pc = uint((100 * files) / (double)root->fileCount()); + TQString s3 = i18n( "Files: %1" ).arg( loc->formatNumber( files, 0 ) ); + + if( pc > 0 ) s3 += TQString( " (%1%)" ).arg( loc->formatNumber( pc, 0 ) ); + + maxw = fontMetrics().width( s3 ); + h += fontMetrics().height(); + m_text += '\n'; + m_text += s3; + } + + uint + w = fontMetrics().width( s1 ); if( w > maxw ) maxw = w; + w = fontMetrics().width( s2 ); if( w > maxw ) maxw = w; + + resize( maxw + 2 * MARGIN, h ); +} + +bool +SegmentTip::event( TQEvent *e ) +{ + switch( e->type() ) + { + case TQEvent::Show: + kapp->installEventFilter( this ); + break; + case TQEvent::Hide: + kapp->removeEventFilter( this ); + break; + case TQEvent::Paint: + { + //TQPainter( this ).drawPixmap( 0, 0, m_pixmap ); + bitBlt( this, 0, 0, &m_pixmap ); + return true; + } + default: + ; + } + + return false/*TQWidget::event( e )*/; +} + +bool +SegmentTip::eventFilter( TQObject*, TQEvent *e ) +{ + switch ( e->type() ) + { + case TQEvent::Leave: +// case TQEvent::MouseButtonPress: +// case TQEvent::MouseButtonRelease: + case TQEvent::KeyPress: + case TQEvent::KeyRelease: + case TQEvent::FocusIn: + case TQEvent::FocusOut: + case TQEvent::Wheel: + hide(); //FALL THROUGH + default: + return false; //allow this event to passed to target + } +} + +} //namespace RadialMap diff --git a/src/app/DiskUsage/radialMap/segmentTip.h b/src/app/DiskUsage/radialMap/segmentTip.h new file mode 100644 index 0000000..7f279cb --- /dev/null +++ b/src/app/DiskUsage/radialMap/segmentTip.h @@ -0,0 +1,33 @@ +// Author: Max Howell <max.howell@methylblue.com>, (C) 2004 +// Copyright: See COPYING file that comes with this distribution + +#ifndef SEGMENTTIP_H +#define SEGMENTTIP_H + +#include <kpixmap.h> +#include <tqwidget.h> + +class File; +class Directory; + +namespace RadialMap +{ + class SegmentTip : public TQWidget + { + public: + SegmentTip( uint ); + + void updateTip( const File*, const Directory* ); + void moveto( TQPoint, const TQWidget&, bool ); + + private: + virtual bool eventFilter( TQObject*, TQEvent* ); + virtual bool event( TQEvent* ); + + uint m_cursorHeight; + KPixmap m_pixmap; + TQString m_text; + }; +} + +#endif diff --git a/src/app/DiskUsage/radialMap/sincos.h b/src/app/DiskUsage/radialMap/sincos.h new file mode 100644 index 0000000..5e66e54 --- /dev/null +++ b/src/app/DiskUsage/radialMap/sincos.h @@ -0,0 +1,17 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef SINCOS_H +#define SINCOS_H + +#include <math.h> +#if 0 +#if __GLIBC__ < 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 + + void + sincos( double angleRadians, double *Sin, double *Cos ); + +#endif + +#endif +#endif diff --git a/src/app/DiskUsage/radialMap/widget.cpp b/src/app/DiskUsage/radialMap/widget.cpp new file mode 100644 index 0000000..4ed038f --- /dev/null +++ b/src/app/DiskUsage/radialMap/widget.cpp @@ -0,0 +1,199 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include <kcursor.h> //ctor +#include <tdelocale.h> +#include <kurl.h> +#include <tqapplication.h> //sendEvent +#include <tqbitmap.h> //ctor - finding cursor size +#include <tqcursor.h> //slotPostMouseEvent() +#include <tqtimer.h> //member + +#include "Config.h" +#include "debug.h" +#include "fileTree.h" +#include "radialMap.h" //constants +#include "widget.h" + + + +RadialMap::Widget::Widget( TQWidget *parent, const char *name ) + : TQWidget( parent, name, TQt::WNoAutoErase ) + , m_tree( 0 ) + , m_focus( 0 ) + , m_tip( KCursor::handCursor().bitmap()->height() ) //needs to know cursor height + , m_rootSegment( 0 ) //TODO we don't delete it, *shrug* +{ + setBackgroundColor( TQt::white ); + + connect( this, TQ_SIGNAL(created( const Directory* )), TQ_SLOT(sendFakeMouseEvent()) ); + connect( this, TQ_SIGNAL(created( const Directory* )), TQ_SLOT(update()) ); + connect( &m_timer, TQ_SIGNAL(timeout()), TQ_SLOT(resizeTimeout()) ); +} + +TQString +RadialMap::Widget::path() const +{ + if( m_tree == 0 ) + return TQString(); + return m_tree->fullPath(); +} + +KURL +RadialMap::Widget::url( File const * const file ) const +{ + if( file == 0 && m_tree == 0 ) + return KURL(); + + return KURL::fromPathOrURL( file ? file->fullPath() : m_tree->fullPath() ); +} + +void +RadialMap::Widget::invalidate( const bool b ) +{ + if( isValid() ) + { + //**** have to check that only way to invalidate is this function frankly + //**** otherwise you may get bugs.. + + //disable mouse tracking + setMouseTracking( false ); + + KURL urlInv = url(); + + //ensure this class won't think we have a map still + m_tree = 0; + m_focus = 0; + + delete m_rootSegment; + m_rootSegment = 0; + + //FIXME move this disablement thing no? + // it is confusing in other areas, like the whole createFromCache() thing + m_map.invalidate( b ); //b signifies whether the pixmap is made to look disabled or not + if( b ) + update(); + + //tell rest of Filelight + emit invalidated( urlInv ); + } +} + +void +RadialMap::Widget::create( const Directory *tree ) +{ + //it is not the responsibility of create() to invalidate first + //skip invalidation at your own risk + + //FIXME make it the responsibility of create to invalidate first + + if( tree ) + { + m_focus = 0; + //generate the filemap image + m_map.make( tree ); + + //this is the inner circle in the center + m_rootSegment = new Segment( tree, 0, 16*360 ); + + setMouseTracking( true ); + } + + m_tree = tree; + + //tell rest of Filelight + emit created( tree ); +} + +void +RadialMap::Widget::createFromCache( const Directory *tree ) +{ + //no scan was necessary, use cached tree, however we MUST still emit invalidate + invalidate( false ); + create( tree ); +} + +void +RadialMap::Widget::sendFakeMouseEvent() //slot +{ + TQMouseEvent me( TQEvent::MouseMove, mapFromGlobal( TQCursor::pos() ), TQt::NoButton, TQt::NoButton ); + TQApplication::sendEvent( this, &me ); +} + +void +RadialMap::Widget::resizeTimeout() //slot +{ + // the segments are about to erased! + // this was a horrid bug, and proves the OO programming should be obeyed always! + m_focus = 0; + if( m_tree ) + m_map.make( m_tree, true ); + update(); +} + +void +RadialMap::Widget::refresh( int filth ) +{ + //TODO consider a more direct connection + + if( !m_map.isNull() ) + { + switch( filth ) + { + case 1: + m_focus = 0; + if( m_tree ) + m_map.make( m_tree, true ); //true means refresh only + break; + + case 2: + m_map.aaPaint(); + break; + + case 3: + m_map.colorise(); //FALL THROUGH! + case 4: + m_map.paint(); + + default: + break; + } + + update(); + } +} + +void +RadialMap::Widget::zoomIn() //slot +{ + if( m_map.m_visibleDepth > MIN_RING_DEPTH ) + { + m_focus = 0; + --m_map.m_visibleDepth; + if( m_tree ) + m_map.make( m_tree ); + Config::defaultRingDepth = m_map.m_visibleDepth; + update(); + } +} + +void +RadialMap::Widget::zoomOut() //slot +{ + m_focus = 0; + ++m_map.m_visibleDepth; + if( m_tree ) + m_map.make( m_tree ); + if( m_map.m_visibleDepth > Config::defaultRingDepth ) + Config::defaultRingDepth = m_map.m_visibleDepth; + update(); +} + + +RadialMap::Segment::~Segment() +{ + if( isFake() ) + delete m_file; //created by us in Builder::build() +} + +#include "widget.moc" diff --git a/src/app/DiskUsage/radialMap/widget.h b/src/app/DiskUsage/radialMap/widget.h new file mode 100644 index 0000000..83c6baa --- /dev/null +++ b/src/app/DiskUsage/radialMap/widget.h @@ -0,0 +1,111 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2004 +//Copyright: See COPYING file that comes with this distribution + +#ifndef WIDGET_H +#define WIDGET_H + +#include <kurl.h> +#include <tqtimer.h> +#include "segmentTip.h" + +template <class T> class Chain; +class Directory; +class File; +namespace TDEIO { class Job; } +class KURL; + +namespace RadialMap +{ + class Segment; + + class Map : public KPixmap + { + public: + Map(); + ~Map(); + + void make( const Directory *, bool = false ); + bool resize( const TQRect& ); + + bool isNull() const { return ( m_signature == 0 ); } + void invalidate( const bool ); + + friend class Builder; + friend class Widget; + + private: + void paint( uint = 1 ); + void aaPaint(); + void colorise(); + void setRingBreadth(); + + Chain<Segment> *m_signature; + + TQRect m_rect; + uint m_ringBreadth; ///ring breadth + uint m_innerRadius; ///radius of inner circle + uint m_visibleDepth; ///visible level depth of system + TQString m_centerText; + + uint MAP_2MARGIN; + }; + + class Widget : public TQWidget + { + TQ_OBJECT + + + public: + Widget( TQWidget* = 0, const char* = 0 ); + + TQString path() const; + KURL url( File const * const = 0 ) const; + + bool isValid() const { return m_tree != 0; } + + friend class Label; //FIXME badness + + public slots: + void zoomIn(); + void zoomOut(); + void create( const Directory* ); + void invalidate( const bool = true ); + void refresh( int ); + + private slots: + void resizeTimeout(); + void sendFakeMouseEvent(); + void deleteJobFinished( TDEIO::Job* ); + void createFromCache( const Directory* ); + + signals: + void activated( const KURL& ); + void invalidated( const KURL& ); + void created( const Directory* ); + void mouseHover( const TQString& ); + + protected: + virtual void paintEvent( TQPaintEvent* ); + virtual void resizeEvent( TQResizeEvent* ); + virtual void mouseMoveEvent( TQMouseEvent* ); + virtual void mousePressEvent( TQMouseEvent* ); + + protected: + const Segment *segmentAt( TQPoint& ) const; //FIXME const reference for a library others can use + const Segment *rootSegment() const { return m_rootSegment; } ///never == 0 + const Segment *focusSegment() const { return m_focus; } ///0 == nothing in focus + + private: + void paintExplodedLabels( TQPainter& ) const; + + const Directory *m_tree; + const Segment *m_focus; + TQPoint m_offset; + TQTimer m_timer; + Map m_map; + SegmentTip m_tip; + Segment *m_rootSegment; + }; +} + +#endif diff --git a/src/app/DiskUsage/radialMap/widgetEvents.cpp b/src/app/DiskUsage/radialMap/widgetEvents.cpp new file mode 100644 index 0000000..0cc11af --- /dev/null +++ b/src/app/DiskUsage/radialMap/widgetEvents.cpp @@ -0,0 +1,241 @@ +//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include "fileTree.h" +#include "radialMap.h" //class Segment +#include "widget.h" + +#include <kcursor.h> //::mouseMoveEvent() +#include <kiconeffect.h> //::mousePressEvent() +#include <kiconloader.h> //::mousePressEvent() +#include <tdeio/job.h> //::mousePressEvent() +#include <tdelocale.h> +#include <tdemessagebox.h> //::mousePressEvent() +#include <tdepopupmenu.h> //::mousePressEvent() +#include <krun.h> //::mousePressEvent() +#include <math.h> //::segmentAt() +#include <tqapplication.h>//TQApplication::setOverrideCursor() +#include <tqpainter.h> +#include <tqtimer.h> //::resizeEvent() + + + +void +RadialMap::Widget::resizeEvent( TQResizeEvent* ) +{ + if( m_map.resize( rect() ) ) + m_timer.start( 500, true ); //will cause signature to rebuild for new size + + //always do these as they need to be initialised on creation + m_offset.rx() = (width() - m_map.width()) / 2; + m_offset.ry() = (height() - m_map.height()) / 2; +} + +void +RadialMap::Widget::paintEvent( TQPaintEvent* ) +{ + //bltBit for some TQt setups will bitBlt _after_ the labels are painted. Which buggers things up! + //shame as bitBlt is faster, possibly TQt bug? Should report the bug? - seems to be race condition + //bitBlt( this, m_offset, &m_map ); + + TQPainter paint( this ); + + paint.drawPixmap( m_offset, m_map ); + + //vertical strips + if( m_map.width() < width() ) + { + paint.eraseRect( 0, 0, m_offset.x(), height() ); + paint.eraseRect( m_map.width() + m_offset.x(), 0, m_offset.x() + 1, height() ); + } + //horizontal strips + if( m_map.height() < height() ) + { + paint.eraseRect( 0, 0, width(), m_offset.y() ); + paint.eraseRect( 0, m_map.height() + m_offset.y(), width(), m_offset.y() + 1 ); + } + + //exploded labels + if( !m_map.isNull() && !m_timer.isActive() ) + paintExplodedLabels( paint ); +} + +const RadialMap::Segment* +RadialMap::Widget::segmentAt( TQPoint &e ) const +{ + //determine which segment TQPoint e is above + + e -= m_offset; + + if( e.x() <= m_map.width() && e.y() <= m_map.height() ) + { + //transform to cartesian coords + e.rx() -= m_map.width() / 2; //should be an int + e.ry() = m_map.height() / 2 - e.y(); + + double length = hypot( e.x(), e.y() ); + + if( length >= m_map.m_innerRadius ) //not hovering over inner circle + { + uint depth = ((int)length - m_map.m_innerRadius) / m_map.m_ringBreadth; + + if( depth <= m_map.m_visibleDepth ) //**** do earlier since you can //** check not outside of range + { + //vector calculation, reduces to simple trigonometry + //cos angle = (aibi + ajbj) / albl + //ai = x, bi=1, aj=y, bj=0 + //cos angle = x / (length) + + uint a = (uint)(acos( (double)e.x() / length ) * 916.736); //916.7324722 = #radians in circle * 16 + + //acos only understands 0-180 degrees + if( e.y() < 0 ) a = 5760 - a; + + #define ring (m_map.m_signature + depth) + for( ConstIterator<Segment> it = ring->constIterator(); it != ring->end(); ++it ) + if( (*it)->intersects( a ) ) + return *it; + #undef ring + } + } + else return m_rootSegment; //hovering over inner circle + } + + return 0; +} + +void +RadialMap::Widget::mouseMoveEvent( TQMouseEvent *e ) +{ + //set m_focus to what we hover over, update UI if it's a new segment + + Segment const * const oldFocus = m_focus; + TQPoint p = e->pos(); + + m_focus = segmentAt( p ); //NOTE p is passed by non-const reference + + if( m_focus && m_focus->file() != m_tree ) + { + if( m_focus != oldFocus ) //if not same as last time + { + setCursor( KCursor::handCursor() ); + m_tip.updateTip( m_focus->file(), m_tree ); + emit mouseHover( m_focus->file()->fullPath() ); + + //repaint required to update labels now before transparency is generated + repaint( false ); + } + + m_tip.moveto( e->globalPos(), *this, ( p.y() < 0 ) ); //updates tooltip psuedo-tranparent background + } + else if( oldFocus && oldFocus->file() != m_tree ) + { + unsetCursor(); + m_tip.hide(); + update(); + + emit mouseHover( TQString() ); + } +} + +void +RadialMap::Widget::mousePressEvent( TQMouseEvent *e ) +{ + //m_tip is hidden already by event filter + //m_focus is set correctly (I've been strict, I assure you it is correct!) + + if( m_focus && !m_focus->isFake() ) + { + const KURL url = Widget::url( m_focus->file() ); + const bool isDir = m_focus->file()->isDir(); + + if( e->button() == TQt::RightButton ) + { + TDEPopupMenu popup; + popup.insertTitle( m_focus->file()->fullPath( m_tree ) ); + + if( isDir ) + { + popup.insertItem( SmallIconSet( "konqueror" ), i18n( "Open &Konqueror Here" ), 0 ); + if( url.protocol() == "file" ) + popup.insertItem( SmallIconSet( "konsole" ), i18n( "Open &Konsole Here" ), 1 ); + + if( m_focus->file() != m_tree ) + { + popup.insertSeparator(); + popup.insertItem( SmallIconSet( "viewmag" ), i18n( "&Center Map Here" ), 2 ); + } + } + else popup.insertItem( SmallIconSet( "document-open" ), i18n( "&Open" ), 3 ); + + popup.insertSeparator(); + popup.insertItem( SmallIconSet( "edit-delete" ), i18n( "&Delete" ), 4 ); + + switch( popup.exec( e->globalPos(), 1 ) ) { + case 0: + //KRun::runCommand will show an error message if there was trouble + KRun::runCommand( TQString( "kfmclient openURL '%1'" ).arg( url.url() ) ); + break; + + case 1: + KRun::runCommand( TQString( "konsole --workdir '%1'" ).arg( url.url() ) ); + break; + + case 2: + case 3: + goto sectionTwo; + + case 4: + { + const KURL url = Widget::url( m_focus->file() ); + const TQString message = ( m_focus->file()->isDir() + ? i18n( "<qt>The directory at <i>'%1'</i> will be <b>recursively</b> and <b>permanently</b> deleted!</qt>" ) + : i18n( "<qt><i>'%1'</i> will be <b>permanently</b> deleted!</qt>" )).arg( url.prettyURL() ); + const int userIntention = KMessageBox::warningContinueCancel( this, message, TQString(), KGuiItem( i18n("&Delete"), "edit-delete" ) ); + + if( userIntention == KMessageBox::Continue ) { + TDEIO::Job *job = TDEIO::del( url ); + job->setWindow( this ); + connect( job, TQ_SIGNAL(result( TDEIO::Job* )), TQ_SLOT(deleteJobFinished( TDEIO::Job* )) ); + TQApplication::setOverrideCursor( KCursor::workingCursor() ); + } + } + + default: + //ensure m_focus is set for new mouse position + sendFakeMouseEvent(); + } + + } else { + + sectionTwo: + + const TQRect rect( e->x() - 20, e->y() - 20, 40, 40 ); + + m_tip.hide(); //user expects this + + if( !isDir || e->button() == TQt::MidButton ) + { + TDEIconEffect::visualActivate( this, rect ); + new KRun( url, this, true ); //FIXME see above + } + else if( m_focus->file() != m_tree ) //is left mouse button + { + TDEIconEffect::visualActivate( this, rect ); + emit activated( url ); //activate first, this will cause UI to prepare itself + if( m_focus ) + createFromCache( (Directory *)m_focus->file() ); + } + } + } +} + +void +RadialMap::Widget::deleteJobFinished( TDEIO::Job *job ) +{ + TQApplication::restoreOverrideCursor(); + if( !job->error() ) + invalidate(); + else + job->showErrorDialog( this ); +} |