diff --git a/include/Display.hpp b/include/Display.hpp index 8cf8417..2e59195 100644 --- a/include/Display.hpp +++ b/include/Display.hpp @@ -1,16 +1,20 @@ #ifndef H_Display #define H_Display +#include #include #include #include #include +#include #include //ioctl() and TIOCGWINSZ #include // for STDOUT_FILENO #include +class TreeNodeDiskUsage; + class Display { public: @@ -46,6 +50,9 @@ class Display /// Highlights the line i by inverting its colors (black on white background). void HighlightLine(unsigned int i, bool highlight); + /// Fills the internal lines buffer with the formatted contents of the tree node. + void DisplayTreeNode(TreeNodeDiskUsage const& treeNode, size_t topLine = 0, bool SI_units = true); + /// Returns a winsize structure with size.ws_row is the number of rows, size.ws_col is the number of columns. static winsize GetTerminalSize(); @@ -74,6 +81,6 @@ std::string GenerateProgressBar(size_t width, size_t current, size_t total, bool /// Converts a size in bytes to a human readable size (To, Go, Mo, ko, o). /// If SI_units is false, the size will be displayed using powers of 1024 instead of 1000, producing Mio, kio etc. /// The suffix can be specified. Default is 'o' for 'octet'. Use 'b' for bits or 'B' for bytes. -std::string Bytes2HumanReadable(uint64_t size, bool SI_units = true, const char* suffix = "o"); +std::string Bytes2HumanReadable(uint64_t size, bool SI_units = true, std::string const& suffix = "o"); #endif diff --git a/include/EventManager.hpp b/include/EventManager.hpp index 402d484..2d8324f 100644 --- a/include/EventManager.hpp +++ b/include/EventManager.hpp @@ -37,8 +37,10 @@ class EventManager Display & display; TreeNodeDiskUsage & rootNode; - int currentLine; + int topLine; // +#include + +// DEBUG +#ifndef PRINT_VAR +#define PRINT_VAR(x); std::cout << #x << "\t= " << (x) << "\n"; +#endif + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) using std::cout; @@ -15,14 +23,14 @@ std::string const& Display::GetLine(unsigned int i) const { return lines[i]; } void Display::SetLine(unsigned int i, std::string const& str) { - if(str.size() <= Cols()) - lines[i] = str + std::string(Cols() - str.size(), ' '); + if(str.size() <= screenCols) + lines[i] = str + std::string(screenCols - str.size(), ' '); else - lines[i] = str.substr(0, Cols()); + lines[i] = str.substr(0, screenCols); } -void Display::SetLineCentered(unsigned int i, std::string const& str, char fillChar) { lines[i] = CenterString(str, Cols(), fillChar); } -void Display::SetLineRjustified(unsigned int i, std::string const& str, char fillChar) { lines[i] = RightJustify(str, Cols(), fillChar); } +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); } char & Display::GetPixel(unsigned int i, unsigned int j) { return lines[i][j]; } @@ -61,6 +69,81 @@ void Display::HighlightLine(unsigned int i, bool highlight) } } +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 +*/ + winsize Display::GetTerminalSize() { winsize size; @@ -128,7 +211,7 @@ std::string GenerateProgressBar(size_t width, size_t current, size_t total, bool return progressBar; } -std::string Bytes2HumanReadable(uint64_t size, bool SI_units, const char* suffix) +std::string Bytes2HumanReadable(uint64_t size, bool SI_units, std::string const& suffix) { const uint64_t kilo = (SI_units) ? 1000ULL : 1024ULL; const uint64_t mega = kilo*kilo; @@ -136,32 +219,27 @@ std::string Bytes2HumanReadable(uint64_t size, bool SI_units, const char* suffix const uint64_t tera = giga*kilo; const uint64_t peta = tera*kilo; const uint64_t exa = peta*kilo; - //const uint64_t zetta = exa*kilo; - //const uint64_t yotta = zetta*kilo; - char buf[16]; - std::string fullSuffix = (SI_units) ? std::string(suffix) : (std::string("i") + suffix); + std::string fullSuffix = (SI_units) ? suffix : (std::string("i") + suffix); - //if(size >= yotta) - // sprintf(buf, "%5.1f Y%s", (double)size/yotta, fullSuffix.c_str()); - //else if(size >= zetta) - // sprintf(buf, "%5.1f Z%s", (double)size/zetta, fullSuffix.c_str()); + std::stringstream out; + out.precision(1); + out << std::fixed << std::setw(5) << std::right; if(size >= exa) - sprintf(buf, "%5.1f E%s", (double)size/exa, fullSuffix.c_str()); + out << (double)size/exa << " E" << fullSuffix; else if(size >= peta) - sprintf(buf, "%5.1f P%s", (double)size/peta, fullSuffix.c_str()); + out << (double)size/peta << " P" << fullSuffix; else if(size >= tera) - sprintf(buf, "%5.1f T%s", (double)size/tera, fullSuffix.c_str()); + out << (double)size/tera << " T" << fullSuffix; else if(size >= giga) - sprintf(buf, "%5.1f G%s", (double)size/giga, fullSuffix.c_str()); + out << (double)size/giga << " G" << fullSuffix; else if(size >= mega) - sprintf(buf, "%5.1f M%s", (double)size/mega, fullSuffix.c_str()); + out << (double)size/mega << " M" << fullSuffix; else if(size >= kilo) - sprintf(buf, "%5.1f k%s", (double)size/kilo, fullSuffix.c_str()); + out << (double)size/kilo << " k" << fullSuffix; else - sprintf(buf, "%5.1f %s", (double)size, suffix); - - return std::string(buf); + out << (double)size << " " << suffix; + return out.str(); } diff --git a/src/EventManager.cpp b/src/EventManager.cpp index d9f490a..43df361 100644 --- a/src/EventManager.cpp +++ b/src/EventManager.cpp @@ -11,14 +11,17 @@ EventManager::EventManager(Display & display_, TreeNodeDiskUsage & rootNode_) : display(display_), - rootNode(rootNode_) -{} - -EventManager::~EventManager() + rootNode(rootNode_), + topLine(0), + currentLine(0), + currentNode(&rootNode), + parentNode(&rootNode) { } +EventManager::~EventManager() {} + void EventManager::MainEventLoop() { struct termios oldSettings, newSettings; @@ -46,7 +49,8 @@ void EventManager::MainEventLoop() if(res > 0) { char c; - read(fileno(stdin), &c, 1); + if(read(fileno(stdin), &c, 1) != 1) // read character from stdin + break; // std::cout << (int)c << " = \"" << c << "\"\n";// DEBUG // Parse event diff --git a/src/test.cpp b/src/test.cpp index 8b0fc4e..5872048 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -189,7 +189,7 @@ void runTests(int argc, char *argv[]) } } } - //if(0) + if(0) {// test EventManager Display display; TreeNodeDiskUsage tree(rootpath); @@ -197,4 +197,14 @@ void runTests(int argc, char *argv[]) EventManager eventManager(display, tree); eventManager.MainEventLoop(); } + //if(0) + {// test interactive display of tree node (without interactivity) + Display display; + TreeNodeDiskUsage tree(rootpath); + tree.BuildTree(false); + tree.SortBySizeDesc(); + display.DisplayTreeNode(tree, 0, true); + //Display::ClearScreen(); + display.DrawScreenLines(); + } }