diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 01:15:27 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 01:15:27 +0000 |
commit | b6e09a3a8ea5f1338d089a29eb6e08f00f03f1aa (patch) | |
tree | 7dee2cbb5c94d3371357f796d42e344e1305ce9c /kdirstat/kdirtreeview.cpp | |
download | kdirstat-b6e09a3a8ea5f1338d089a29eb6e08f00f03f1aa.tar.gz kdirstat-b6e09a3a8ea5f1338d089a29eb6e08f00f03f1aa.zip |
Added abandoned KDE3 version of kdirstat
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kdirstat@1088039 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdirstat/kdirtreeview.cpp')
-rw-r--r-- | kdirstat/kdirtreeview.cpp | 1956 |
1 files changed, 1956 insertions, 0 deletions
diff --git a/kdirstat/kdirtreeview.cpp b/kdirstat/kdirtreeview.cpp new file mode 100644 index 0000000..3283efe --- /dev/null +++ b/kdirstat/kdirtreeview.cpp @@ -0,0 +1,1956 @@ +/* + * File name: kdirtreeview.cpp + * Summary: High level classes for KDirStat + * License: LGPL - See file COPYING.LIB for details. + * Author: Stefan Hundhammer <sh@suse.de> + * + * Updated: 2005-01-07 + */ + + +#include <time.h> +#include <stdlib.h> + +#include <qtimer.h> +#include <qcolor.h> +#include <qheader.h> +#include <qpopupmenu.h> + +#include <kapp.h> +#include <klocale.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kicontheme.h> +#include <kiconloader.h> + +#include "kdirtreeview.h" +#include "kdirtreeiterators.h" +#include "kpacman.h" + +#define SEPARATE_READ_JOBS_COL 0 +#define VERBOSE_PROGRESS_INFO 0 + +using namespace KDirStat; + + +KDirTreeView::KDirTreeView( QWidget * parent ) + : KDirTreeViewParentClass( parent ) +{ + _tree = 0; + _updateTimer = 0; + _selection = 0; + _openLevel = 1; + _doLazyClone = true; + _doPacManAnimation = false; + _updateInterval = 333; // millisec + _sortCol = -1; + + for ( int i=0; i < DEBUG_COUNTERS; i++ ) + _debugCount[i] = 0; + + setDebugFunc( 1, "KDirTreeViewItem::init()" ); + setDebugFunc( 2, "KDirTreeViewItem::updateSummary()" ); + setDebugFunc( 3, "KDirTreeViewItem::deferredClone()" ); + setDebugFunc( 4, "KDirTreeViewItem::compare()" ); + setDebugFunc( 5, "KDirTreeViewItem::paintCell()" ); + +#if SEPARATE_READ_JOBS_COL + _readJobsCol = -1; +#endif + setRootIsDecorated( false ); + + int numCol = 0; + addColumn( i18n( "Name" ) ); _nameCol = numCol; + _iconCol = numCol++; + addColumn( i18n( "Subtree Percentage" ) ); _percentBarCol = numCol++; + addColumn( i18n( "Percentage" ) ); _percentNumCol = numCol++; + addColumn( i18n( "Subtree Total" ) ); _totalSizeCol = numCol++; + _workingStatusCol = _totalSizeCol; + addColumn( i18n( "Own Size" ) ); _ownSizeCol = numCol++; + addColumn( i18n( "Items" ) ); _totalItemsCol = numCol++; + addColumn( i18n( "Files" ) ); _totalFilesCol = numCol++; + addColumn( i18n( "Subdirs" ) ); _totalSubDirsCol = numCol++; + addColumn( i18n( "Last Change" ) ); _latestMtimeCol = numCol++; + +#if ! SEPARATE_READ_JOBS_COL + _readJobsCol = _percentBarCol; +#endif + + setColumnAlignment ( _totalSizeCol, AlignRight ); + setColumnAlignment ( _percentNumCol, AlignRight ); + setColumnAlignment ( _ownSizeCol, AlignRight ); + setColumnAlignment ( _totalItemsCol, AlignRight ); + setColumnAlignment ( _totalFilesCol, AlignRight ); + setColumnAlignment ( _totalSubDirsCol, AlignRight ); + setColumnAlignment ( _readJobsCol, AlignRight ); + + + setSorting( _totalSizeCol ); + + +#define loadIcon(ICON) KGlobal::iconLoader()->loadIcon( (ICON), KIcon::Small ) + + _openDirIcon = loadIcon( "folder_open" ); + _closedDirIcon = loadIcon( "folder" ); + _openDotEntryIcon = loadIcon( "folder_orange_open"); + _closedDotEntryIcon = loadIcon( "folder_orange" ); + _unreadableDirIcon = loadIcon( "folder_locked" ); + _mountPointIcon = loadIcon( "hdd_mount" ); + _fileIcon = loadIcon( "mime_empty" ); + _symLinkIcon = loadIcon( "symlink" ); // The KDE standard link icon is ugly! + _blockDevIcon = loadIcon( "blockdevice" ); + _charDevIcon = loadIcon( "chardevice" ); + _fifoIcon = loadIcon( "socket" ); + _stopIcon = loadIcon( "stop" ); + _readyIcon = QPixmap(); + +#undef loadIcon + + setDefaultFillColors(); + readConfig(); + ensureContrast(); + + + connect( kapp, SIGNAL( kdisplayPaletteChanged() ), + this, SLOT ( paletteChanged() ) ); + + connect( this, SIGNAL( selectionChanged ( QListViewItem * ) ), + this, SLOT ( selectItem ( QListViewItem * ) ) ); + + connect( this, SIGNAL( rightButtonPressed ( QListViewItem *, const QPoint &, int ) ), + this, SLOT ( popupContextMenu ( QListViewItem *, const QPoint &, int ) ) ); + + connect( header(), SIGNAL( sizeChange ( int, int, int ) ), + this, SLOT ( columnResized( int, int, int ) ) ); + + _contextInfo = new QPopupMenu; + _idContextInfo = _contextInfo->insertItem ( "dummy" ); +} + + +KDirTreeView::~KDirTreeView() +{ + if ( _tree ) + delete _tree; + + /* + * Don't delete _updateTimer here, it's already automatically deleted by Qt! + * (Since it's derived from QObject and has a QObject parent). + */ +} + + +void +KDirTreeView::setDebugFunc( int i, const QString & functionName ) +{ + if ( i > 0 && i < DEBUG_COUNTERS ) + _debugFunc[i] = functionName; +} + + +void +KDirTreeView::incDebugCount( int i ) +{ + if ( i > 0 && i < DEBUG_COUNTERS ) + _debugCount[i]++; +} + + +void +KDirTreeView::busyDisplay() +{ +#if SEPARATE_READ_JOBS_COL + if ( _readJobsCol < 0 ) + { + _readJobsCol = header()->count(); + addColumn( i18n( "Read Jobs" ) ); + setColumnAlignment( _readJobsCol, AlignRight ); + } +#else + _readJobsCol = _percentBarCol; +#endif +} + + +void +KDirTreeView::idleDisplay() +{ +#if SEPARATE_READ_JOBS_COL + if ( _readJobsCol >= 0 ) + { + removeColumn( _readJobsCol ); + } +#else + if ( _sortCol == _readJobsCol && _sortCol >= 0 ) + { + // A pathological case: The user requested sorting by read jobs, and + // now that everything is read, the items are still in that sort order. + // Not only is that sort order now useless (since all read jobs are + // done), it is contrary to the (now changed) semantics of this + // column. Calling QListView::sort() might do the trick, but we can + // never know just how clever that QListView widget tries to be and + // maybe avoid another sorting by the same column - so let's use the + // easy way out and sort by another column that has the same sorting + // semantics like the percentage bar column (that had doubled as the + // read job column while reading) now has. + + setSorting( _percentNumCol ); + } +#endif + + _readJobsCol = -1; +} + + +void +KDirTreeView::openURL( KURL url ) +{ + // Clean up any old leftovers + + clear(); + _currentDir = ""; + + if ( _tree ) + delete _tree; + + + // Create new (empty) dir tree + + _tree = new KDirTree(); + + + // Connect signals + + connect( _tree, SIGNAL( progressInfo ( const QString & ) ), + this, SLOT ( sendProgressInfo( const QString & ) ) ); + + connect( _tree, SIGNAL( childAdded( KFileInfo * ) ), + this, SLOT ( addChild ( KFileInfo * ) ) ); + + connect( _tree, SIGNAL( deletingChild( KFileInfo * ) ), + this, SLOT ( deleteChild ( KFileInfo * ) ) ); + + connect( _tree, SIGNAL( startingReading() ), + this, SLOT ( prepareReading() ) ); + + connect( _tree, SIGNAL( finished() ), + this, SLOT ( slotFinished() ) ); + + connect( _tree, SIGNAL( aborted() ), + this, SLOT ( slotAborted() ) ); + + connect( _tree, SIGNAL( finalizeLocal( KDirInfo * ) ), + this, SLOT ( finalizeLocal( KDirInfo * ) ) ); + + connect( this, SIGNAL( selectionChanged( KFileInfo * ) ), + _tree, SLOT ( selectItem ( KFileInfo * ) ) ); + + connect( _tree, SIGNAL( selectionChanged( KFileInfo * ) ), + this, SLOT ( selectItem ( KFileInfo * ) ) ); + + // Implicitly calling prepareReading() via the tree's startingReading() signal + _tree->startReading( url ); + + logActivity( 30 ); +} + + +void +KDirTreeView::prepareReading() +{ + + // Prepare cyclic update + + if ( _updateTimer ) + delete _updateTimer; + + _updateTimer = new QTimer( this ); + + if ( _updateTimer ) + { + _updateTimer->changeInterval( _updateInterval ); + connect( _updateTimer, SIGNAL( timeout() ), + this, SLOT ( updateSummary() ) ); + + connect( _updateTimer, SIGNAL( timeout() ), + this, SLOT ( sendProgressInfo() ) ); + } + + + // Change display to busy state + + setSorting( _totalSizeCol ); + busyDisplay(); + emit startingReading(); + + + // Actually do something + + _stopWatch.start(); +} + + +void +KDirTreeView::refreshAll() +{ + if ( _tree && _tree->root() ) + { + clear(); + // Implicitly calling prepareReading() via the tree's startingReading() signal + _tree->refresh( 0 ); + } +} + + +void +KDirTreeView::refreshSelected() +{ + if ( _tree && _tree->root() && _selection ) + { + // Implicitly calling prepareReading() via the tree's startingReading() signal + _tree->refresh( _selection->orig() ); + } + + logActivity( 10 ); +} + + +void +KDirTreeView::abortReading() +{ + if ( _tree ) + _tree->abortReading(); +} + + +void +KDirTreeView::clear() +{ + clearSelection(); + KDirTreeViewParentClass::clear(); + + for ( int i=0; i < DEBUG_COUNTERS; i++ ) + _debugCount[i] = 0; +} + + +void +KDirTreeView::addChild( KFileInfo *newChild ) +{ + if ( newChild->parent() ) + { + KDirTreeViewItem *cloneParent = locate( newChild->parent(), + _doLazyClone, // lazy + true ); // doClone + + if ( cloneParent ) + { + if ( isOpen( cloneParent ) || ! _doLazyClone ) + { + // kdDebug() << "Immediately cloning " << newChild << endl; + new KDirTreeViewItem( this, cloneParent, newChild ); + } + } + else // Error + { + if ( ! _doLazyClone ) + { + kdError() << k_funcinfo << "Can't find parent view item for " + << newChild << endl; + } + } + } + else // No parent - top level item + { + // kdDebug() << "Immediately top level cloning " << newChild << endl; + new KDirTreeViewItem( this, newChild ); + } +} + + +void +KDirTreeView::deleteChild( KFileInfo *child ) +{ + KDirTreeViewItem *clone = locate( child, + false, // lazy + false ); // doClone + KDirTreeViewItem *nextSelection = 0; + + if ( clone ) + { + if ( clone == _selection ) + { + /** + * The selected item is about to be deleted. Select some other item + * so there is still something selected: Preferably the next item + * or the parent if there is no next. This cannot be done from + * outside because the order of items is not known to the outside; + * it might appear very random if the next item in the KFileInfo + * list would be selected. The order of that list is definitely + * different than the order of this view - which is what the user + * sees. So let's give the user a reasonable next selection so he + * can continue working without having to explicitly select another + * item. + * + * This is very useful if the user just activated a cleanup action + * that deleted an item: It makes sense to implicitly select the + * next item so he can clean up many items in a row. + **/ + + nextSelection = clone->next() ? clone->next() : clone->parent(); + // kdDebug() << k_funcinfo << " Next selection: " << nextSelection << endl; + } + + KDirTreeViewItem *parent = clone->parent(); + delete clone; + + while ( parent ) + { + parent->updateSummary(); + parent = parent->parent(); + } + + if ( nextSelection ) + selectItem( nextSelection ); + } +} + + +void +KDirTreeView::updateSummary() +{ + KDirTreeViewItem *child = firstChild(); + + while ( child ) + { + child->updateSummary(); + child = child->next(); + } +} + + +void +KDirTreeView::slotFinished() +{ + emit progressInfo( i18n( "Finished. Elapsed time: %1" ) + .arg( formatTime( _stopWatch.elapsed(), true ) ) ); + + if ( _updateTimer ) + { + delete _updateTimer; + _updateTimer = 0; + } + + idleDisplay(); + updateSummary(); + logActivity( 30 ); + +#if 0 + for ( int i=0; i < DEBUG_COUNTERS; i++ ) + { + kdDebug() << "Debug counter #" << i << ": " << _debugCount[i] + << "\t" << _debugFunc[i] + << endl; + } + kdDebug() << endl; +#endif + + emit finished(); +} + + +void +KDirTreeView::slotAborted() +{ + emit progressInfo( i18n( "Aborted. Elapsed time: %1" ) + .arg( formatTime( _stopWatch.elapsed(), true ) ) ); + + if ( _updateTimer ) + { + delete _updateTimer; + _updateTimer = 0; + } + + idleDisplay(); + updateSummary(); + + emit aborted(); +} + + +void +KDirTreeView::finalizeLocal( KDirInfo *dir ) +{ + if ( dir ) + { + KDirTreeViewItem *clone = locate( dir, + false, // lazy + false ); // doClone + if ( clone ) + clone->finalizeLocal(); + } +} + + +void +KDirTreeView::sendProgressInfo( const QString & newCurrentDir ) +{ + _currentDir = newCurrentDir; + +#if VERBOSE_PROGRESS_INFO + emit progressInfo( i18n( "Elapsed time: %1 reading directory %2" ) + .arg( formatTime( _stopWatch.elapsed() ) ) + .arg( _currentDir ) ); +#else + emit progressInfo( i18n( "Elapsed time: %1" ) + .arg( formatTime( _stopWatch.elapsed() ) ) ); +#endif +} + + +#if QT_VERSION < 300 +void +KDirTreeView::sendProgressInfo() +{ + sendProgressInfo( _currentDir ); +} +#endif + + +KDirTreeViewItem * +KDirTreeView::locate( KFileInfo *wanted, bool lazy, bool doClone ) +{ + KDirTreeViewItem *child = firstChild(); + + while ( child ) + { + KDirTreeViewItem *wantedChild = child->locate( wanted, lazy, doClone, 0 ); + + if ( wantedChild ) + return wantedChild; + else + child = child->next(); + } + + return 0; +} + + + +int +KDirTreeView::openCount() +{ + int count = 0; + KDirTreeViewItem *child = firstChild(); + + while ( child ) + { + count += child->openCount(); + child = child->next(); + } + + return count; +} + + +void +KDirTreeView::selectItem( QListViewItem *listViewItem ) +{ + _selection = dynamic_cast<KDirTreeViewItem *>( listViewItem ); + + if ( _selection ) + { + // kdDebug() << k_funcinfo << " Selecting item " << _selection << endl; + setSelected( _selection, true ); + } + else + { + // kdDebug() << k_funcinfo << " Clearing selection" << endl; + clearSelection(); + } + + + emit selectionChanged( _selection ); + emit selectionChanged( _selection ? _selection->orig() : (KFileInfo *) 0 ); +} + + +void +KDirTreeView::selectItem( KFileInfo *newSelection ) +{ + // Short-circuit for the most common case: The signal has been triggered by + // this view, and the KDirTree has sent it right back. + + if ( _selection && _selection->orig() == newSelection ) + return; + + if ( ! newSelection ) + clearSelection(); + else + { + _selection = locate( newSelection, + false, // lazy + true ); // doClone + if ( _selection ) + { + closeAllExcept( _selection ); + _selection->setOpen( false ); + ensureItemVisible( _selection ); + emit selectionChanged( _selection ); + setSelected( _selection, true ); + } + else + kdError() << "Couldn't clone item " << newSelection << endl; + } +} + + +void +KDirTreeView::clearSelection() +{ + // kdDebug() << k_funcinfo << endl; + _selection = 0; + QListView::clearSelection(); + + emit selectionChanged( (KDirTreeViewItem *) 0 ); + emit selectionChanged( (KFileInfo *) 0 ); +} + + +void +KDirTreeView::closeAllExcept( KDirTreeViewItem *except ) +{ + if ( ! except ) + { + kdError() << k_funcinfo << ": NULL pointer passed" << endl; + return; + } + + except->closeAllExceptThis(); +} + + +const QColor & +KDirTreeView::fillColor( int level ) const +{ + if ( level < 0 ) + { + level = 0; + kdWarning() << k_funcinfo << "Invalid argument: " << level << endl; + } + + return _fillColor [ level % _usedFillColors ]; +} + + +const QColor & +KDirTreeView::rawFillColor( int level ) const +{ + if ( level < 0 || level > KDirTreeViewMaxFillColor ) + { + level = 0; + kdWarning() << k_funcinfo << "Invalid argument: " << level << endl; + } + + return _fillColor [ level % KDirTreeViewMaxFillColor ]; +} + + +void +KDirTreeView::setFillColor( int level, + const QColor & color ) +{ + if ( level >= 0 && level < KDirTreeViewMaxFillColor ) + _fillColor[ level ] = color; +} + + + +void +KDirTreeView::setUsedFillColors( int usedFillColors ) +{ + if ( usedFillColors < 1 ) + { + kdWarning() << k_funcinfo << "Invalid argument: "<< usedFillColors << endl; + usedFillColors = 1; + } + else if ( usedFillColors >= KDirTreeViewMaxFillColor ) + { + kdWarning() << k_funcinfo << "Invalid argument: "<< usedFillColors + << " (max: " << KDirTreeViewMaxFillColor-1 << ")" << endl; + usedFillColors = KDirTreeViewMaxFillColor-1; + } + + _usedFillColors = usedFillColors; +} + + +void +KDirTreeView::setDefaultFillColors() +{ + int i; + + for ( i=0; i < KDirTreeViewMaxFillColor; i++ ) + { + _fillColor[i] = blue; + } + + i = 0; + _usedFillColors = 4; + + setFillColor ( i++, QColor ( 0, 0, 255 ) ); + setFillColor ( i++, QColor ( 128, 0, 128 ) ); + setFillColor ( i++, QColor ( 231, 147, 43 ) ); + setFillColor ( i++, QColor ( 4, 113, 0 ) ); + setFillColor ( i++, QColor ( 176, 0, 0 ) ); + setFillColor ( i++, QColor ( 204, 187, 0 ) ); + setFillColor ( i++, QColor ( 162, 98, 30 ) ); + setFillColor ( i++, QColor ( 0, 148, 146 ) ); + setFillColor ( i++, QColor ( 217, 94, 0 ) ); + setFillColor ( i++, QColor ( 0, 194, 65 ) ); + setFillColor ( i++, QColor ( 194, 108, 187 ) ); + setFillColor ( i++, QColor ( 0, 179, 255 ) ); +} + + +void +KDirTreeView::setTreeBackground( const QColor &color ) +{ + _treeBackground = color; + _percentageBarBackground = _treeBackground.dark( 115 ); + + QPalette pal = kapp->palette(); + pal.setBrush( QColorGroup::Base, _treeBackground ); + setPalette( pal ); +} + + +void +KDirTreeView::ensureContrast() +{ + if ( colorGroup().base() == white || + colorGroup().base() == black ) + { + setTreeBackground( colorGroup().midlight() ); + } + else + { + setTreeBackground( colorGroup().base() ); + } +} + + +void +KDirTreeView::paletteChanged() +{ + setTreeBackground( KGlobalSettings::baseColor() ); + ensureContrast(); +} + + +void +KDirTreeView::popupContextMenu( QListViewItem * listViewItem, + const QPoint & pos, + int column ) +{ + KDirTreeViewItem *item = (KDirTreeViewItem *) listViewItem; + + if ( ! item ) + return; + + if ( column == _nameCol || + column == _percentBarCol || + column == _percentNumCol ) + { + // Make the item the context menu is popping up over the current + // selection - all user operations refer to the current selection. + // Just right-clicking on an item does not make it the current + // item! + selectItem( item ); + + // Let somebody from outside pop up the context menu, if so desired. + emit contextMenu( item, pos ); + } + + + // If the column is one with a large size in kB/MB/GB, open a + // info popup with the exact number. + + if ( column == _ownSizeCol && ! item->orig()->isDotEntry() ) + { + KFileInfo * orig = item->orig(); + + if ( orig->isSparseFile() || ( orig->links() > 1 && orig->isFile() ) ) + { + QString text; + + if ( orig->isSparseFile() ) + { + text = i18n( "Sparse file: %1 (%2 Bytes) -- allocated: %3 (%4 Bytes)" ) + .arg( formatSize( orig->byteSize() ) ) + .arg( formatSizeLong( orig->byteSize() ) ) + .arg( formatSize( orig->allocatedSize() ) ) + .arg( formatSizeLong( orig->allocatedSize() ) ); + } + else + { + text = i18n( "%1 (%2 Bytes) with %3 hard links => effective size: %4 (%5 Bytes)" ) + .arg( formatSize( orig->byteSize() ) ) + .arg( formatSizeLong( orig->byteSize() ) ) + .arg( orig->links() ) + .arg( formatSize( orig->size() ) ) + .arg( formatSizeLong( orig->size() ) ); + } + + popupContextInfo( pos, text ); + } + else + { + popupContextSizeInfo( pos, orig->size() ); + } + } + + if ( column == _totalSizeCol && + ( item->orig()->isDir() || item->orig()->isDotEntry() ) ) + { + popupContextSizeInfo( pos, item->orig()->totalSize() ); + } + + + // Show alternate time / date format in time / date related columns. + + if ( column == _latestMtimeCol ) + { + popupContextInfo( pos, formatTimeDate( item->orig()->latestMtime() ) ); + } + + logActivity( 3 ); +} + + +void +KDirTreeView::popupContextSizeInfo( const QPoint & pos, + KFileSize size ) +{ + QString info; + + if ( size < 1024 ) + { + info = formatSizeLong( size ) + " " + i18n( "Bytes" ); + } + else + { + info = i18n( "%1 (%2 Bytes)" ) + .arg( formatSize( size ) ) + .arg( formatSizeLong( size ) ); + } + + popupContextInfo( pos, info ); +} + + +void +KDirTreeView::popupContextInfo( const QPoint & pos, + const QString & info ) +{ + _contextInfo->changeItem( info, _idContextInfo ); + _contextInfo->popup( pos ); +} + + +void +KDirTreeView::readConfig() +{ + KConfig *config = kapp->config(); + KConfigGroupSaver saver( config, "Tree Colors" ); + _usedFillColors = config->readNumEntry( "usedFillColors", -1 ); + + if ( _usedFillColors < 0 ) + { + /* + * No 'usedFillColors' in the config file? Better forget that + * file and use default values. Otherwise, all colors would very + * likely become blue - the default color. + */ + setDefaultFillColors(); + } + else + { + // Read the rest of the 'Tree Colors' section + + QColor defaultColor( blue ); + + for ( int i=0; i < KDirTreeViewMaxFillColor; i++ ) + { + QString name; + name.sprintf( "fillColor_%02d", i ); + _fillColor [i] = config->readColorEntry( name, &defaultColor ); + } + } + + if ( isVisible() ) + triggerUpdate(); +} + + +void +KDirTreeView::saveConfig() const +{ + KConfig *config = kapp->config(); + KConfigGroupSaver saver( config, "Tree Colors" ); + + config->writeEntry( "usedFillColors", _usedFillColors ); + + for ( int i=0; i < KDirTreeViewMaxFillColor; i++ ) + { + QString name; + name.sprintf( "fillColor_%02d", i ); + config->writeEntry ( name, _fillColor [i] ); + } +} + + +void +KDirTreeView::setSorting( int column, bool increasing ) +{ + _sortCol = column; + QListView::setSorting( column, increasing ); +} + + +void +KDirTreeView::logActivity( int points ) +{ + emit userActivity( points ); +} + + +void +KDirTreeView::columnResized( int column, int oldSize, int newSize ) +{ + NOT_USED( oldSize ); + NOT_USED( newSize ); + + if ( column == _percentBarCol ) + triggerUpdate(); +} + +void +KDirTreeView::sendMailToOwner() +{ + if ( ! _selection ) + { + kdError() << k_funcinfo << "Nothing selected!" << endl; + return; + } + + QString owner = KAnyDirReadJob::owner( fixedUrl( _selection->orig()->url() ) ); + QString subject = i18n( "Disk Usage" ); + QString body = + i18n("Please check your disk usage and clean up if you can. Thank you." ) + + "\n\n" + + _selection->asciiDump() + + "\n\n" + + i18n( "Disk usage report generated by KDirStat" ) + + "\nhttp://kdirstat.sourceforge.net/"; + + // kdDebug() << "owner: " << owner << endl; + // kdDebug() << "subject: " << subject << endl; + // kdDebug() << "body:\n" << body << endl; + + KURL mail; + mail.setProtocol( "mailto" ); + mail.setPath( owner ); + mail.setQuery( "?subject=" + KURL::encode_string( subject ) + + "&body=" + KURL::encode_string( body ) ); + + // TODO: Check for maximum command line length. + // + // The hard part with this is how to get this from all that 'autoconf' + // stuff into 'config.h' or some other include file without hardcoding + // anything - this is too system dependent. + + kapp->invokeMailer( mail ); + logActivity( 10 ); +} + + + + + + +KDirTreeViewItem::KDirTreeViewItem( KDirTreeView * view, + KFileInfo * orig ) + : QListViewItem( view ) +{ + init( view, 0, orig ); +} + + +KDirTreeViewItem::KDirTreeViewItem( KDirTreeView * view, + KDirTreeViewItem * parent, + KFileInfo * orig ) + : QListViewItem( parent ) +{ + CHECK_PTR( parent ); + init( view, parent, orig ); +} + + +void +KDirTreeViewItem::init( KDirTreeView * view, + KDirTreeViewItem * parent, + KFileInfo * orig ) +{ + _view = view; + _parent = parent; + _orig = orig; + _percent = 0.0; + _pacMan = 0; + _openCount = 0; + + // _view->incDebugCount(1); + // kdDebug() << "new KDirTreeViewItem for " << orig << endl; + + if ( _orig->isDotEntry() ) + { + setText( view->nameCol(), i18n( "<Files>" ) ); + QListViewItem::setOpen ( false ); + } + else + { + setText( view->nameCol(), QString::fromLocal8Bit(_orig->name()) ); + + if ( ! _orig->isDevice() ) + { + QString text; + + if ( _orig->isFile() && ( _orig->links() > 1 ) ) // Regular file with multiple links + { + if ( _orig->isSparseFile() ) + { + text = i18n( "%1 / %2 Links (allocated: %3)" ) + .arg( formatSize( _orig->byteSize() ) ) + .arg( formatSize( _orig->links() ) ) + .arg( formatSize( _orig->allocatedSize() ) ); + } + else + { + text = i18n( "%1 / %2 Links" ) + .arg( formatSize( _orig->byteSize() ) ) + .arg( _orig->links() ); + } + } + else // No multiple links or no regular file + { + if ( _orig->isSparseFile() ) + { + text = i18n( "%1 (allocated: %2)" ) + .arg( formatSize( _orig->byteSize() ) ) + .arg( formatSize( _orig->allocatedSize() ) ); + } + else + { + text = formatSize( _orig->size() ); + } + } + + setText( view->ownSizeCol(), text ); + } + + QListViewItem::setOpen ( _orig->treeLevel() < _view->openLevel() ); + /* + * Don't use KDirTreeViewItem::setOpen() here since this might call + * KDirTreeViewItem::deferredClone() which would confuse bookkeeping + * with addChild() signals that might arrive, too - resulting in double + * dot entries. + */ + } + + if ( _view->doLazyClone() && + ( _orig->isDir() || _orig->isDotEntry() ) ) + { + /* + * Determine whether or not this item can be opened. + * + * Normally, Qt handles this very well, but when lazy cloning is in + * effect, Qt cannot know whether or not there are children - they may + * only be in the original tree until the user tries to open this + * item. So let's assume there may be children as long as the directory + * is still being read. + */ + + if ( _orig->readState() == KDirQueued || + _orig->readState() == KDirReading ) + { + setExpandable( true ); + } + else // KDirFinished, KDirError, KDirAborted + { + setExpandable( _orig->hasChildren() ); + } + } + + if ( ! parent || parent->isOpen() ) + { + setIcon(); + } + + _openCount = isOpen() ? 1 : 0; +} + + +KDirTreeViewItem::~KDirTreeViewItem() +{ + if ( _pacMan ) + delete _pacMan; + + if ( this == _view->selection() ) + _view->clearSelection(); +} + + +void +KDirTreeViewItem::setIcon() +{ + QPixmap icon; + + if ( _orig->isDotEntry() ) + { + icon = isOpen() ? _view->openDotEntryIcon() : _view->closedDotEntryIcon(); + } + else if ( _orig->isDir() ) + { + if ( _orig->readState() == KDirAborted ) icon = _view->stopIcon(); + else if ( _orig->readState() == KDirError ) + { + icon = _view->unreadableDirIcon(); + setExpandable( false ); + } + else + { + if ( _orig->isMountPoint() ) + { + icon = _view->mountPointIcon(); + } + else + { + icon = isOpen() ? _view->openDirIcon() : _view->closedDirIcon(); + } + } + } + else if ( _orig->isFile() ) icon = _view->fileIcon(); + else if ( _orig->isSymLink() ) icon = _view->symLinkIcon(); + else if ( _orig->isBlockDevice() ) icon = _view->blockDevIcon(); + else if ( _orig->isCharDevice() ) icon = _view->charDevIcon(); + else if ( _orig->isSpecial() ) icon = _view->fifoIcon(); + + setPixmap( _view->iconCol(), icon ); +} + + +void +KDirTreeViewItem::updateSummary() +{ + // _view->incDebugCount(2); + + // Update this item + + setIcon(); + setText( _view->latestMtimeCol(), " " + localeTimeDate( _orig->latestMtime() ) ); + + if ( _orig->isDir() || _orig->isDotEntry() ) + { + QString prefix = " "; + + if ( _orig->readState() == KDirAborted ) + prefix = " >"; + + setText( _view->totalSizeCol(), prefix + formatSize( _orig->totalSize() ) ); + setText( _view->totalItemsCol(), prefix + formatCount( _orig->totalItems() ) ); + setText( _view->totalFilesCol(), prefix + formatCount( _orig->totalFiles() ) ); + + if ( _view->readJobsCol() >= 0 ) + { +#if SEPARATE_READ_JOBS_COL + setText( _view->readJobsCol(), " " + formatCount( _orig->pendingReadJobs(), true ) ); +#else + int jobs = _orig->pendingReadJobs(); + QString text = ""; + + if ( jobs > 0 ) + text = i18n( "[%1 Read Jobs]" ).arg( formatCount( _orig->pendingReadJobs(), true ) ); + setText( _view->readJobsCol(), text ); +#endif + } + } + + if ( _orig->isDir() ) + { + setText( _view->totalSubDirsCol(), " " + formatCount( _orig->totalSubDirs() ) ); + } + + + // Calculate and display percentage + + if ( _orig->parent() && // only if there is a parent as calculation base + _orig->parent()->pendingReadJobs() < 1 && // not before subtree is finished reading + _orig->parent()->totalSize() > 0 ) // avoid division by zero + { + _percent = ( 100.0 * _orig->totalSize() ) / (float) _orig->parent()->totalSize(); + setText( _view->percentNumCol(), formatPercent ( _percent ) ); + } + else + { + _percent = 0.0; + setText( _view->percentNumCol(), "" ); + } + + if ( _view->doPacManAnimation() && _orig->isBusy() ) + { + if ( ! _pacMan ) + _pacMan = new KPacManAnimation( _view, height()-4, true ); + + repaint(); + } + + + if ( ! isOpen() ) // Lazy update: Nobody can see the children + return; // -> don't update them. + + + // Update all children + + KDirTreeViewItem *child = firstChild(); + + while ( child ) + { + child->updateSummary(); + child = child->next(); + } +} + + +KDirTreeViewItem * +KDirTreeViewItem::locate( KFileInfo * wanted, + bool lazy, + bool doClone, + int level ) +{ + if ( lazy && ! isOpen() ) + { + /* + * In "lazy" mode, we don't bother searching all the children of this + * item if they are not visible (i.e. the branch is open) anyway. In + * this case, cloning that branch is deferred until the branch is + * actually opened - which in most cases will never happen anyway (most + * users don't manually open each and every subtree). If and when it + * happens, we'll probably be fast enough bringing the view tree in + * sync with the original tree since opening a branch requires manual + * interaction which is a whole lot slower than copying a couple of + * objects. + * + * Note that this mode is _independent_ of lazy cloning in general: The + * caller explicitly specifies if he wants to locate an item at all + * cost, even if that means deferred cloning children whose creation + * has been delayed until now. + */ + + // kdDebug() << "Too lazy to search for " << wanted << " from " << this << endl; + return 0; + } + + if ( _orig == wanted ) + { + return this; + } + + if ( level < 0 ) + level = _orig->treeLevel(); + + if ( wanted->urlPart( level ) == _orig->name() ) + { + // Search all children + + KDirTreeViewItem *child = firstChild(); + + if ( ! child && _orig->hasChildren() && doClone ) + { + // kdDebug() << "Deferred cloning " << this << " for children search of " << wanted << endl; + deferredClone(); + child = firstChild(); + } + + while ( child ) + { + KDirTreeViewItem *foundChild = child->locate( wanted, lazy, doClone, level+1 ); + + if ( foundChild ) + return foundChild; + else + child = child->next(); + } + } + + return 0; +} + + +void +KDirTreeViewItem::deferredClone() +{ + // _view->incDebugCount(3); + + if ( ! _orig->hasChildren() ) + { + // kdDebug() << k_funcinfo << "Oops, no children - sorry for bothering you!" << endl; + setExpandable( false ); + + return; + } + + + // Clone all normal children + + int level = _orig->treeLevel(); + bool startingClean = ! firstChild(); + KFileInfo *origChild = _orig->firstChild(); + + while ( origChild ) + { + if ( startingClean || + ! locate( origChild, + false, // lazy + true, // doClone + level ) ) + { + // kdDebug() << "Deferred cloning " << origChild << endl; + new KDirTreeViewItem( _view, this, origChild ); + } + + origChild = origChild->next(); + } + + + // Clone the dot entry + + if ( _orig->dotEntry() && + ( startingClean || + ! locate( _orig->dotEntry(), + false, // lazy + true, // doClone + level ) + ) + ) + { + // kdDebug() << "Deferred cloning dot entry for " << _orig << endl; + new KDirTreeViewItem( _view, this, _orig->dotEntry() ); + } +} + + +void +KDirTreeViewItem::finalizeLocal() +{ + // kdDebug() << k_funcinfo << _orig << endl; + cleanupDotEntries(); + + if ( _orig->totalItems() == 0 ) + // _orig->hasChildren() would give a wrong answer here since it counts + // the dot entry, too - which might be removed a moment later. + { + setExpandable( false ); + } +} + + +void +KDirTreeViewItem::cleanupDotEntries() +{ + if ( ! _orig->dotEntry() ) + return; + + KDirTreeViewItem *dotEntry = findDotEntry(); + + if ( ! dotEntry ) + return; + + + // Reparent dot entry children if there are no subdirectories on this level + + if ( ! _orig->firstChild() ) + { + // kdDebug() << "Removing solo dot entry clone " << _orig << endl; + KDirTreeViewItem *child = dotEntry->firstChild(); + + while ( child ) + { + KDirTreeViewItem *nextChild = child->next(); + + + // Reparent this child + + // kdDebug() << "Reparenting clone " << child << endl; + dotEntry->removeItem( child ); + insertItem( child ); + + child = nextChild; + } + + /* + * Immediately delete the (now emptied) dot entry. The algorithm for + * the original tree doesn't quite fit here - there, the dot entry is + * actually deleted in the step below. But the 'no children' check for + * this fails here since the original dot entry still _has_ its + * children - they will be deleted only after all clones have been + * processed. + * + * This had been the cause for a core that took me quite some time to + * track down. + */ + delete dotEntry; + dotEntry = 0; + } + + + // Delete dot entries without any children + + if ( ! _orig->dotEntry()->firstChild() && dotEntry ) + { + // kdDebug() << "Removing empty dot entry clone " << _orig << endl; + delete dotEntry; + } +} + + +KDirTreeViewItem * +KDirTreeViewItem::findDotEntry() const +{ + KDirTreeViewItem *child = firstChild(); + + while ( child ) + { + if ( child->orig()->isDotEntry() ) + return child; + + child = child->next(); + } + + return 0; +} + + +void +KDirTreeViewItem::setOpen( bool open ) +{ + if ( open && _view->doLazyClone() ) + { + // kdDebug() << "Opening " << this << endl; + deferredClone(); + } + + if ( isOpen() != open ) + { + openNotify( open ); + } + + QListViewItem::setOpen( open ); + setIcon(); + + if ( open ) + updateSummary(); + + // kdDebug() << _openCount << " open in " << this << endl; + + // _view->logActivity( 1 ); +} + + +void +KDirTreeViewItem::openNotify( bool open ) +{ + if ( open ) + _openCount++; + else + _openCount--; + + if ( _parent ) + _parent->openNotify( open ); +} + + +void +KDirTreeViewItem::openSubtree() +{ + if ( parent() ) + parent()->setOpen( true ); + + setOpen( true ); +} + + +void +KDirTreeViewItem::closeSubtree() +{ + setOpen( false ); + + if ( _openCount > 0 ) + { + KDirTreeViewItem * child = firstChild(); + + while ( child ) + { + child->closeSubtree(); + child = child->next(); + } + } + + _openCount = 0; // just to be sure +} + + +void +KDirTreeViewItem::closeAllExceptThis() +{ + KDirTreeViewItem *sibling = _parent ? + _parent->firstChild() : _view->firstChild(); + + while ( sibling ) + { + if ( sibling != this ) + sibling->closeSubtree(); // Recurse down + + sibling = sibling->next(); + } + + setOpen( true ); + + if ( _parent ) + _parent->closeAllExceptThis(); // Recurse up +} + + +QString +KDirTreeViewItem::asciiDump() +{ + QString dump; + + dump.sprintf( "%10s %s\n", + (const char *) formatSize( _orig->totalSize() ), + (const char *) _orig->debugUrl() ); + + if ( isOpen() ) + { + KDirTreeViewItem *child = firstChild(); + + while ( child ) + { + dump += child->asciiDump(); + child = child->next(); + } + } + + return dump; +} + + +/** + * Comparison function used for sorting the list. + * Returns: + * -1 if this < other + * 0 if this == other + * +1 if this > other + **/ +int +KDirTreeViewItem::compare( QListViewItem * otherListViewItem, + int column, + bool ascending ) const +{ + // _view->incDebugCount(4); + KDirTreeViewItem * other = dynamic_cast<KDirTreeViewItem *> (otherListViewItem); + + if ( other ) + { + KFileInfo * otherOrig = other->orig(); + +#if ! SEPARATE_READ_JOBS_COL + if ( column == _view->readJobsCol() ) return - compare( _orig->pendingReadJobs(), otherOrig->pendingReadJobs() ); + else +#endif + if ( column == _view->totalSizeCol() || + column == _view->percentNumCol() || + column == _view->percentBarCol() ) return - compare( _orig->totalSize(), otherOrig->totalSize() ); + + else if ( column == _view->ownSizeCol() ) return - compare( _orig->size(), otherOrig->size() ); + else if ( column == _view->totalItemsCol() ) return - compare( _orig->totalItems(), otherOrig->totalItems() ); + else if ( column == _view->totalFilesCol() ) return - compare( _orig->totalFiles(), otherOrig->totalFiles() ); + else if ( column == _view->totalSubDirsCol() ) return - compare( _orig->totalSubDirs(), otherOrig->totalSubDirs() ); + else if ( column == _view->latestMtimeCol() ) return - compare( _orig->latestMtime(), otherOrig->latestMtime() ); + else + { + if ( _orig->isDotEntry() ) // make sure dot entries are last in the list + return 1; + + if ( otherOrig->isDotEntry() ) + return -1; + } + } + + return QListViewItem::compare( otherListViewItem, column, ascending ); +} + + +void +KDirTreeViewItem::paintCell( QPainter * painter, + const QColorGroup & colorGroup, + int column, + int width, + int alignment ) +{ + // _view->incDebugCount(5); + + if ( column == _view->percentBarCol() ) + { + painter->setBackgroundColor( colorGroup.base() ); + + if ( _percent > 0.0 ) + { + if ( _pacMan ) + { + delete _pacMan; + _pacMan = 0; + } + + int level = _orig->treeLevel(); + paintPercentageBar ( _percent, + painter, + _view->treeStepSize() * ( level-1 ), + width, + _view->fillColor( level-1 ), + _view->percentageBarBackground() ); + } + else + { + if ( _pacMan && _orig->isBusy() ) + { + // kdDebug() << "Animating PacMan for " << _orig << endl; + // painter->setBackgroundColor( _view->treeBackground() ); + _pacMan->animate( painter, QRect( 0, 0, width, height() ) ); + } + else + { + if ( _view->percentBarCol() == _view->readJobsCol() + && ! _pacMan ) + { + QListViewItem::paintCell( painter, + colorGroup, + column, + width, + alignment ); + } + else + { + painter->eraseRect( 0, 0, width, height() ); + } + } + } + } + else + { + /* + * Call the parent's paintCell() method. We don't want to do + * all the hassle of drawing strings and pixmaps, regarding + * alignments etc. + */ + QListViewItem::paintCell( painter, + colorGroup, + column, + width, + alignment ); + } +} + + +void +KDirTreeViewItem::paintPercentageBar( float percent, + QPainter * painter, + int indent, + int width, + const QColor & fillColor, + const QColor & barBackground ) +{ + int penWidth = 2; + int extraMargin = 3; + int x = _view->itemMargin(); + int y = extraMargin; + int w = width - 2 * _view->itemMargin(); + int h = height() - 2 * extraMargin; + int fillWidth; + + painter->eraseRect( 0, 0, width, height() ); + w -= indent; + x += indent; + + if ( w > 0 ) + { + QPen pen( painter->pen() ); + pen.setWidth( 0 ); + painter->setPen( pen ); + painter->setBrush( NoBrush ); + fillWidth = (int) ( ( w - 2 * penWidth ) * percent / 100.0); + + + // Fill bar background. + + painter->fillRect( x + penWidth, y + penWidth, + w - 2 * penWidth + 1, h - 2 * penWidth + 1, + barBackground ); + /* + * Notice: The Xlib XDrawRectangle() function always fills one + * pixel less than specified. Altough this is very likely just a + * plain old bug, it is documented that way. Obviously, Qt just + * maps the fillRect() call directly to XDrawRectangle() so they + * inherited that bug (although the Qt doc stays silent about + * it). So it is really necessary to compensate for that missing + * pixel in each dimension. + * + * If you don't believe it, see for yourself. + * Hint: Try the xmag program to zoom into the drawn pixels. + **/ + + // Fill the desired percentage. + + painter->fillRect( x + penWidth, y + penWidth, + fillWidth+1, h - 2 * penWidth+1, + fillColor ); + + + // Draw 3D shadows. + + pen.setColor( contrastingColor ( Qt::black, + painter->backgroundColor() ) ); + painter->setPen( pen ); + painter->drawLine( x, y, x+w, y ); + painter->drawLine( x, y, x, y+h ); + + pen.setColor( contrastingColor( barBackground.dark(), + painter->backgroundColor() ) ); + painter->setPen( pen ); + painter->drawLine( x+1, y+1, x+w-1, y+1 ); + painter->drawLine( x+1, y+1, x+1, y+h-1 ); + + pen.setColor( contrastingColor( barBackground.light(), + painter->backgroundColor() ) ); + painter->setPen( pen ); + painter->drawLine( x+1, y+h, x+w, y+h ); + painter->drawLine( x+w, y, x+w, y+h ); + + pen.setColor( contrastingColor( Qt::white, + painter->backgroundColor() ) ); + painter->setPen( pen ); + painter->drawLine( x+2, y+h-1, x+w-1, y+h-1 ); + painter->drawLine( x+w-1, y+1, x+w-1, y+h-1 ); + } +} + + + + + + + + +QString +KDirStat::formatSizeLong( KFileSize size ) +{ + QString sizeText; + int count = 0; + + while ( size > 0 ) + { + sizeText = ( ( size % 10 ) + '0' ) + sizeText; + size /= 10; + + if ( ++count == 3 && size > 0 ) + { + sizeText = KGlobal::locale()->thousandsSeparator() + sizeText; + count = 0; + } + } + + return sizeText; +} + + +QString +KDirStat::hexKey( KFileSize size ) +{ + /** + * This is optimized for performance, not for aesthetics. + * And every now and then the old C hacker breaks through in most of us... + * ;-) + **/ + + static const char hexDigits[] = "0123456789ABCDEF"; + char key[ sizeof( KFileSize ) * 2 + 1 ]; // 2 hex digits per byte required + char *cptr = key + sizeof( key ) - 1; // now points to last char of key + + memset( key, '0', sizeof( key ) - 1 ); // fill with zeroes + *cptr-- = 0; // terminate string + + while ( size > 0 ) + { + *cptr-- = hexDigits[ size & 0xF ]; // same as size % 16 + size >>= 4; // same as size /= 16 + } + + return QString( key ); +} + + +QString +KDirStat::formatTime( long millisec, bool showMilliSeconds ) +{ + QString formattedTime; + int hours; + int min; + int sec; + + hours = millisec / 3600000L; // 60*60*1000 + millisec %= 3600000L; + + min = millisec / 60000L; // 60*1000 + millisec %= 60000L; + + sec = millisec / 1000L; + millisec %= 1000L; + + if ( showMilliSeconds ) + { + formattedTime.sprintf ( "%02d:%02d:%02d.%03ld", + hours, min, sec, millisec ); + } + else + { + formattedTime.sprintf ( "%02d:%02d:%02d", hours, min, sec ); + } + + return formattedTime; +} + + +QString +KDirStat::formatCount( int count, bool suppressZero ) +{ + if ( suppressZero && count == 0 ) + return ""; + + QString countString; + countString.setNum( count ); + + return countString; +} + + +QString +KDirStat::formatPercent( float percent ) +{ + QString percentString; + + percentString.sprintf( "%.1f%%", percent ); + + return percentString; +} + + +QString +KDirStat::formatTimeDate( time_t rawTime ) +{ + QString timeDateString; + struct tm *t = localtime( &rawTime ); + + /* + * Format this as "yyyy-mm-dd hh:mm:ss". + * + * This format may not be POSIX'ly correct, but it is the ONLY of all those + * brain-dead formats today's computer users are confronted with that makes + * any sense to the average human. + * + * Agreed, it takes some getting used to, too, but once you got that far, + * you won't want to miss it. + * + * Who the hell came up with those weird formats like described in the + * ctime() man page? Don't those people ever actually use that? + * + * What sense makes a format like "Wed Jun 30 21:49:08 1993" ? + * The weekday (of all things!) first, then a partial month name, then the + * day of month, then the time and then - at the very end - the year. + * IMHO this is maximum brain-dead. Not only can't you do any kind of + * decent sorting or automatic processing with that disinformation + * hodge-podge, your brain runs in circles trying to make sense of it. + * + * I could put up with crap like that if the Americans and Brits like it + * that way, but unfortunately I as a German am confronted with that + * bullshit, too, on a daily basis - either because some localization stuff + * didn't work out right (again) or because some jerk decided to emulate + * this stuff in the German translation, too. I am sick and tired with + * that, and since this is MY program I am going to use a format that makes + * sense to ME. + * + * No, no exceptions for Americans or Brits. I had to put up with their + * crap long enough, now it's time for them to put up with mine. + * Payback time - though luck, folks. + * ;-) + * + * Stefan Hundhammer <sh@suse.de> 2001-05-28 + * (in quite some fit of frustration) + */ + timeDateString.sprintf( "%4d-%02d-%02d %02d:%02d:%02d", + t->tm_year + 1900, + t->tm_mon + 1, // another brain-dead common pitfall - 0..11 + t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec ); + + return timeDateString; +} + + +QString +KDirStat::localeTimeDate( time_t rawTime ) +{ + QDateTime timeDate; + timeDate.setTime_t( rawTime ); + QString timeDateString = + KGlobal::locale()->formatDate( timeDate.date(), true ) + " " + // short format + KGlobal::locale()->formatTime( timeDate.time(), true ); // include seconds + + return timeDateString; +} + + +QColor +KDirStat::contrastingColor( const QColor &desiredColor, + const QColor &contrastColor ) +{ + if ( desiredColor != contrastColor ) + { + return desiredColor; + } + + if ( contrastColor != contrastColor.light() ) + { + // try a little lighter + return contrastColor.light(); + } + else + { + // try a little darker + return contrastColor.dark(); + } +} + + + + + +// EOF |