#include #include #include #include "curseOutput.hh" #include "jsonObject.hh" #include "jsonArray.hh" #include "jsonPrimitive.hh" #include "optional.hpp" CurseOutput::CurseOutput(JSonElement *root): data(root), selection(root), indentLevel(4) { } CurseOutput::~CurseOutput() { } void CurseOutput::run() { init(); do { redraw(); refresh(); } while(readInput()); shutdown(); } void CurseOutput::redraw() { std::pair screenSize; std::pair cursor(topleft.second->getLevel() * indentLevel, 0); select_up = select_down = nullptr; selectFound = false; getScreenSize(screenSize); redraw(cursor, screenSize, topleft.second); move(screenSize.second, screenSize.first); if (!select_down) select_down = selection; if (!select_up) select_up = selection; } void CurseOutput::getScreenSize(std::pair &ss) { getmaxyx(stdscr, ss.second, ss.first); } CurseOutput::t_nextKey CurseOutput::findNext(const JSonElement *item) { const JSonContainer *parent = item->getParent(); if (parent == nullptr) return t_nextKey::empty(); // Root node, can't have brothers if (dynamic_cast(parent) != nullptr) { const JSonObject *oParent = (const JSonObject *) parent; JSonObject::const_iterator it = oParent->cbegin(); while (it != oParent->cend()) { if ((*it).second == item) { it++; if (it == oParent->cend()) return t_nextKey::empty(); // Last item return t_nextKey(std::pair, const JSonElement *>((*it).first, (*it).second)); } it++; } return t_nextKey::empty(); } if (dynamic_cast(parent) != nullptr) { const JSonArray *aParent = (const JSonArray *) parent; JSonArray::const_iterator it = aParent->cbegin(); while (it != aParent->cend()) { if (*it == item) { it++; if (it == aParent->cend()) return t_nextKey::empty(); // Last item return t_nextKey(std::pair, const JSonElement *>(Optional::empty(), *it)); } it++; } return t_nextKey::empty(); } return t_nextKey::empty(); // Primitive, can't have child (impossible) } void CurseOutput::redraw(std::pair &cursor, const std::pair &maxSize, const JSonElement *item) { do { selected = false; if (dynamic_cast(item) != nullptr) { cursor.first += indentLevel /2; if (selection == item) selected = selectFound = true; else if (!selectFound) select_up = item; else if (!select_down) select_down = item; write(cursor.first, cursor.second, "{"); selected = false; cursor.second++; for (JSonObject::const_iterator i = ((JSonObject *)item)->cbegin(); i != ((JSonObject *)item)->cend(); ++i) { const std::pair ipair = *i; if (selection == ipair.second) selected = true; cursor.first += indentLevel /2; writeKey(ipair.first, cursor); cursor.first -= indentLevel /2; redraw(cursor, maxSize, ipair.second); selected = false; cursor.first -= indentLevel; } if (selection == item) selected = true; write(cursor.first, cursor.second, "}"); selected = false; cursor.first -= indentLevel /2; cursor.second++; } else if (dynamic_cast(item) != nullptr) { cursor.first += indentLevel /2; if (selection == item) selected = selectFound = true; else if (!selectFound) select_up = item; else if (!select_down) select_down = item; write(cursor.first, cursor.second, "["); selected = false; cursor.first += indentLevel /2; cursor.second++; for (JSonArray::const_iterator i = ((JSonArray *)item)->cbegin(); i != ((JSonArray *)item)->cend(); ++i) redraw(cursor, maxSize, *i); cursor.first -= indentLevel /2; if (selection == item) selected = true; write(cursor.first, cursor.second, "]"); selected = false; cursor.first -= indentLevel /2; cursor.second++; } else { if (item == selection) selectFound = selected = true; else if (!selectFound) select_up = item; else if (!select_down) select_down = item; write(cursor.first, cursor.second, item->stringify()); selected = false; cursor.second++; } t_nextKey next = findNext(item); if (next.isAbsent()) break; if (next.value().first.isPresent()) writeKey(next.value().first.value(), cursor); item = next.value().second; } while (true); } void CurseOutput::writeKey(const std::string &key, std::pair &cursor) { write(cursor.first, cursor.second, key +": "); cursor.first += indentLevel; cursor.second++; } void CurseOutput::write(const int &x, const int &y, JSonElement *item) { std::string str = item->stringify(); write(x, y, str); } void CurseOutput::write(const int &x, const int &y, const std::string &str) { if (selected) { attron(A_REVERSE | A_BOLD); mvprintw(y, x, str.c_str()); attroff(A_REVERSE | A_BOLD); } else mvprintw(y, x, str.c_str()); } /** * Read input and expect signal * @Return true on: * - Windows resized * - Key press and need redraw * false on: * - exit signal **/ bool CurseOutput::readInput() { while (1) { int c; while ((c = wgetch(stdscr)) == ERR); switch (c) { case 'q': case 'Q': return false; case KEY_UP: case 'K': case 'k': selection = select_up; return true; case KEY_DOWN: case 'j': case 'J': selection = select_down; return true; default: return true; } } return false; } void CurseOutput::init() { initscr(); noecho(); cbreak(); clear(); curs_set(false); nodelay(stdscr, true); if (!isatty(fileno(stdin)) || !isatty(fileno(stdout))) { screen_fd = fopen("/dev/tty", "r+"); setbuf(screen_fd, nullptr); screen = newterm(nullptr, screen_fd, screen_fd); } else screen = newterm(nullptr, stdout, stdin); keypad(stdscr, true); topleft.first = std::pair(0, 0); topleft.second = data; } void CurseOutput::shutdown() { delscreen(screen); endwin(); if (screen_fd) { fclose(screen_fd); screen_fd = nullptr; } }