2022-01-19 00:40:54 +01:00
# include <Display.hpp>
2022-01-22 21:13:04 +01:00
# include <TreeNodeDiskUsage.hpp>
// DEBUG
# ifndef PRINT_VAR
# define PRINT_VAR(x); std::cout << #x << "\t= " << (x) << "\n";
# endif
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
2022-01-19 00:40:54 +01:00
using std : : cout ;
Display : : Display ( )
2022-01-19 20:27:30 +01:00
{
UpdateScreenSize ( ) ;
ClearScreenLines ( ) ;
}
unsigned int Display : : Cols ( ) const { return screenCols ; }
unsigned int Display : : Rows ( ) const { return screenRows ; }
std : : string const & Display : : GetLine ( unsigned int i ) const { return lines [ i ] ; }
void Display : : SetLine ( unsigned int i , std : : string const & str )
{
2022-01-22 21:13:04 +01:00
if ( str . size ( ) < = screenCols )
lines [ i ] = str + std : : string ( screenCols - str . size ( ) , ' ' ) ;
2022-01-19 20:27:30 +01:00
else
2022-01-22 21:13:04 +01:00
lines [ i ] = str . substr ( 0 , screenCols ) ;
2022-01-19 20:27:30 +01:00
}
2022-01-22 21:13:04 +01:00
void Display : : SetLineCentered ( unsigned int i , std : : string const & str , char fillChar ) { lines [ i ] = CenterString ( str , screenCols , fillChar ) ; }
void Display : : SetLineRjustified ( unsigned int i , std : : string const & str , char fillChar ) { lines [ i ] = RightJustify ( str , screenCols , fillChar ) ; }
2022-01-19 22:23:28 +01:00
2022-01-19 20:27:30 +01:00
char & Display : : GetPixel ( unsigned int i , unsigned int j ) { return lines [ i ] [ j ] ; }
void Display : : ClearScreenLines ( )
{
lines . clear ( ) ;
std : : string emptyLine ( screenCols , ' ' ) ;
for ( unsigned int i = 0 ; i < screenRows ; i + + )
lines . push_back ( emptyLine ) ;
}
void Display : : DrawScreenLines ( )
{
for ( unsigned int i = 0 ; i < lines . size ( ) ; i + + )
cout < < lines [ i ] < < " \n " ;
}
void Display : : UpdateScreenSize ( )
2022-01-19 00:40:54 +01:00
{
winsize size = Display : : GetTerminalSize ( ) ;
screenCols = size . ws_col ;
screenRows = size . ws_row ;
}
2022-01-19 20:27:30 +01:00
void Display : : HighlightLine ( unsigned int i , bool highlight )
{
if ( highlight )
{
if ( lines [ i ] . substr ( 0 , 4 ) ! = C_INVERT_FG_BG )
lines [ i ] = C_INVERT_FG_BG + lines [ i ] + C_RESET ;
}
else
{
if ( lines [ i ] . substr ( 0 , 4 ) = = C_INVERT_FG_BG )
lines [ i ] = lines [ i ] . substr ( 4 , lines [ i ] . size ( ) - 4 - 4 ) ;
}
}
2022-01-19 00:40:54 +01:00
2022-01-22 21:13:04 +01:00
void Display : : DisplayTreeNode ( TreeNodeDiskUsage const & treeNode , size_t topLine , bool SI_units )
{
ClearScreenLines ( ) ;
// Write header
lines [ 0 ] = std : : string ( C_INVERT_FG_BG ) + CenterString ( " DiskUsageInteractive ~ Use the arrow keys to navigate, press ? or 'h' for help " , screenCols ) + C_RESET ;
// Write currently displayed directory using its absolute path. If the path resolution does not succeed, the relative path is displayed
char absoluteNodePathCstr [ 4096 + 1 ] ;
char * ptr = realpath ( treeNode . path . c_str ( ) , absoluteNodePathCstr ) ;
std : : string absoluteNodePath = ( ptr ! = NULL ) ? absoluteNodePathCstr : treeNode . path ;
lines [ 1 ] = " --- " + LeftJustify ( absoluteNodePath + " " , screenCols - 5 , ' - ' ) ;
size_t availableLines = screenRows - 3 ; // Number of available lines for the listing
size_t fieldWidth1 = 10 ;
size_t fieldWidth2 = screenCols / 3 ;
size_t fieldWidth3 = screenCols - fieldWidth1 - fieldWidth2 ;
if ( lines . size ( ) < 3 ) // Not enough space !
return ;
for ( size_t i = 0 ; i < MIN ( availableLines , treeNode . GetChildrenCount ( ) - topLine ) ; i + + )
{
TreeNodeDiskUsage const & node = treeNode . children [ i + topLine ] ;
std : : stringstream lineStream ;
lineStream < < RightJustify ( Bytes2HumanReadable ( node . totalSize , SI_units ) , fieldWidth1 ) ;
lineStream < < GenerateProgressBar ( fieldWidth2 , node . totalSize , treeNode . totalSize , true , " # " ) ;
lineStream < < " " < < LeftJustify ( node . GetNodeName ( ) + ( ( node . isFolder ) ? " / " : " " ) , fieldWidth3 - 1 ) ;
lines [ i + 2 ] = LeftJustify ( lineStream . str ( ) , screenCols ) ;
}
// Write footer
std : : stringstream footerSstream ; footerSstream < < " Total disk usage: " < < Bytes2HumanReadable ( treeNode . totalSizeOnDisk , SI_units )
< < " Apparent size: " < < Bytes2HumanReadable ( treeNode . totalSize , SI_units )
< < " Items: " < < treeNode . totalElements ;
lines [ screenRows - 1 ] = std : : string ( C_INVERT_FG_BG ) + LeftJustify ( footerSstream . str ( ) , screenCols ) + C_RESET ;
}
/*
ncdu 1.14 .1 ~ Use the arrow keys to navigate , press ? for help
- - - / home / jerome / codeserver / config / workspace / DiskUsageInteractive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
952.0 KiB [ # # # # # # # # # # ] / build
536.0 KiB [ # # # # # ] / . git
24.0 KiB [ ] / src
16.0 KiB [ ] / include
e 4.0 KiB [ ] / workspace
4.0 KiB [ ] Makefile
4.0 KiB [ ] . gitignore
@ 0.0 B [ ] DiskUsageInteractive
┌ ─ ─ ─ ncdu help ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 1 : Keys ─ ─ ─ 2 : Format ─ ─ ─ 3 : About ─ ─ ┐
│ │
│ up , k Move cursor up │
│ down , j Move cursor down │
│ right / enter Open selected directory │
│ left , < , h Open parent directory │
│ n Sort by name ( ascending / descending ) │
│ s Sort by size ( ascending / descending ) │
│ C Sort by items ( ascending / descending ) │
│ M Sort by mtime ( - e flag ) │
│ d Delete selected file or directory │
│ t Toggle dirs before files when sorting │
│ - - more - - │
│ Press q to close │
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
Total disk usage : 1.5 MiB Apparent size : 1.3 MiB Items : 131
*/
2022-01-19 00:40:54 +01:00
winsize Display : : GetTerminalSize ( )
{
winsize size ;
ioctl ( STDOUT_FILENO , TIOCGWINSZ , & size ) ;
return size ;
}
void Display : : ClearScreen ( ) { cout < < " \033 [2J \033 [1;1H " ; }
2022-01-19 23:46:41 +01:00
std : : string LeftJustify ( std : : string str , unsigned int width , char fillChar )
2022-01-19 22:23:28 +01:00
{
2022-01-19 23:46:41 +01:00
str . resize ( width , fillChar ) ;
return str ;
2022-01-19 22:23:28 +01:00
}
std : : string CenterString ( std : : string const & str , unsigned int width , char fillChar )
{
if ( str . size ( ) < = width )
{
unsigned int freeSpace = ( width - str . size ( ) ) / 2 ;
return std : : string ( freeSpace , fillChar ) + str + std : : string ( freeSpace , fillChar ) ;
}
else
return str . substr ( 0 , width ) ;
}
std : : string RightJustify ( std : : string const & str , unsigned int width , char fillChar )
{
if ( str . size ( ) < = width )
return std : : string ( width - str . size ( ) , fillChar ) + str ;
else
return str . substr ( 0 , width ) ;
}
2022-01-19 23:46:41 +01:00
std : : string GenerateProgressBar ( size_t width , size_t current , size_t total , bool showPercentage , std : : string const & fillChar )
2022-01-19 00:40:54 +01:00
{
std : : string progressBar ;
2022-01-19 23:46:41 +01:00
size_t freeWidth = width - 2 ;
2022-01-19 00:40:54 +01:00
char buf [ 8 ] ;
2022-01-19 13:23:52 +01:00
if ( total = = 0 ) // Protect against divisions by 0
total = 1 ;
2022-01-19 00:40:54 +01:00
2022-01-19 23:46:41 +01:00
if ( current > total ) // Protect against > 100 %
current = total ;
2022-01-19 00:40:54 +01:00
if ( showPercentage )
{
double percentCurrent = 100. * ( ( double ) current / ( double ) total ) ;
freeWidth - = 7 ;
sprintf ( buf , " %5.1f%% " , percentCurrent ) ;
progressBar + = buf ;
}
2022-01-19 23:46:41 +01:00
progressBar + = ' [ ' ;
2022-01-19 00:40:54 +01:00
2022-01-19 23:46:41 +01:00
size_t scaledCurrent = ( current * freeWidth ) / total ;
2022-01-19 00:40:54 +01:00
2022-01-19 23:46:41 +01:00
for ( size_t i = 0 ; i < scaledCurrent ; i + + )
2022-01-19 00:40:54 +01:00
progressBar + = fillChar ;
2022-01-19 23:46:41 +01:00
progressBar + = std : : string ( freeWidth - scaledCurrent , ' ' ) ;
2022-01-19 00:40:54 +01:00
progressBar + = ' ] ' ;
return progressBar ;
}
2022-01-19 13:23:52 +01:00
2022-01-22 21:13:04 +01:00
std : : string Bytes2HumanReadable ( uint64_t size , bool SI_units , std : : string const & suffix )
2022-01-19 13:23:52 +01:00
{
const uint64_t kilo = ( SI_units ) ? 1000ULL : 1024ULL ;
const uint64_t mega = kilo * kilo ;
const uint64_t giga = mega * kilo ;
const uint64_t tera = giga * kilo ;
const uint64_t peta = tera * kilo ;
const uint64_t exa = peta * kilo ;
2022-01-22 21:13:04 +01:00
std : : string fullSuffix = ( SI_units ) ? suffix : ( std : : string ( " i " ) + suffix ) ;
2022-01-19 13:23:52 +01:00
2022-01-22 21:13:04 +01:00
std : : stringstream out ;
out . precision ( 1 ) ;
out < < std : : fixed < < std : : setw ( 5 ) < < std : : right ;
2022-01-19 13:23:52 +01:00
if ( size > = exa )
2022-01-22 21:13:04 +01:00
out < < ( double ) size / exa < < " E " < < fullSuffix ;
2022-01-19 13:23:52 +01:00
else if ( size > = peta )
2022-01-22 21:13:04 +01:00
out < < ( double ) size / peta < < " P " < < fullSuffix ;
2022-01-19 13:23:52 +01:00
else if ( size > = tera )
2022-01-22 21:13:04 +01:00
out < < ( double ) size / tera < < " T " < < fullSuffix ;
2022-01-19 13:23:52 +01:00
else if ( size > = giga )
2022-01-22 21:13:04 +01:00
out < < ( double ) size / giga < < " G " < < fullSuffix ;
2022-01-19 13:23:52 +01:00
else if ( size > = mega )
2022-01-22 21:13:04 +01:00
out < < ( double ) size / mega < < " M " < < fullSuffix ;
2022-01-19 13:23:52 +01:00
else if ( size > = kilo )
2022-01-22 21:13:04 +01:00
out < < ( double ) size / kilo < < " k " < < fullSuffix ;
2022-01-19 13:23:52 +01:00
else
2022-01-22 21:13:04 +01:00
out < < ( double ) size < < " " < < suffix ;
return out . str ( ) ;
2022-01-19 13:23:52 +01:00
}