curseOutput.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #include<iostream>
  2. #include <unistd.h>
  3. #include <utility>
  4. #include "curseOutput.hh"
  5. #include "jsonObject.hh"
  6. #include "jsonArray.hh"
  7. #include "jsonPrimitive.hh"
  8. #include "optional.hpp"
  9. CurseOutput::CurseOutput(JSonElement *root): data(root), selection(root), indentLevel(4)
  10. { }
  11. CurseOutput::~CurseOutput()
  12. { }
  13. void CurseOutput::run()
  14. {
  15. init();
  16. do
  17. {
  18. redraw();
  19. refresh();
  20. } while(readInput());
  21. shutdown();
  22. }
  23. void CurseOutput::redraw()
  24. {
  25. std::pair<int, int> screenSize;
  26. std::pair<int, int> cursor(topleft.second->getLevel() * indentLevel, 0);
  27. select_up = select_down = nullptr;
  28. selectFound = false;
  29. getScreenSize(screenSize);
  30. redraw(cursor, screenSize, topleft.second);
  31. move(screenSize.second, screenSize.first);
  32. if (!select_down)
  33. select_down = selection;
  34. if (!select_up)
  35. select_up = selection;
  36. }
  37. void CurseOutput::getScreenSize(std::pair<int, int> &ss)
  38. {
  39. getmaxyx(stdscr, ss.second, ss.first);
  40. }
  41. CurseOutput::t_nextKey CurseOutput::findNext(const JSonElement *item)
  42. {
  43. const JSonContainer *parent = item->getParent();
  44. if (parent == nullptr)
  45. return t_nextKey::empty(); // Root node, can't have brothers
  46. if (dynamic_cast<const JSonObject *>(parent) != nullptr)
  47. {
  48. const JSonObject *oParent = (const JSonObject *) parent;
  49. JSonObject::const_iterator it = oParent->cbegin();
  50. while (it != oParent->cend())
  51. {
  52. if ((*it).second == item)
  53. {
  54. it++;
  55. if (it == oParent->cend())
  56. return t_nextKey::empty(); // Last item
  57. return t_nextKey(std::pair<Optional<const std::string>, const JSonElement *>((*it).first, (*it).second));
  58. }
  59. it++;
  60. }
  61. return t_nextKey::empty();
  62. }
  63. if (dynamic_cast<const JSonArray *>(parent) != nullptr)
  64. {
  65. const JSonArray *aParent = (const JSonArray *) parent;
  66. JSonArray::const_iterator it = aParent->cbegin();
  67. while (it != aParent->cend())
  68. {
  69. if (*it == item)
  70. {
  71. it++;
  72. if (it == aParent->cend())
  73. return t_nextKey::empty(); // Last item
  74. return t_nextKey(std::pair<Optional<const std::string>, const JSonElement *>(Optional<const std::string>::empty(), *it));
  75. }
  76. it++;
  77. }
  78. return t_nextKey::empty();
  79. }
  80. return t_nextKey::empty(); // Primitive, can't have child (impossible)
  81. }
  82. void CurseOutput::redraw(std::pair<int, int> &cursor, const std::pair<int, int> &maxSize, const JSonElement *item)
  83. {
  84. do
  85. {
  86. selected = false;
  87. if (dynamic_cast<const JSonObject*>(item) != nullptr)
  88. {
  89. cursor.first += indentLevel /2;
  90. if (selection == item)
  91. selected = selectFound = true;
  92. else if (!selectFound)
  93. select_up = item;
  94. else if (!select_down)
  95. select_down = item;
  96. write(cursor.first, cursor.second, "{");
  97. selected = false;
  98. cursor.second++;
  99. for (JSonObject::const_iterator i = ((JSonObject *)item)->cbegin(); i != ((JSonObject *)item)->cend(); ++i)
  100. {
  101. const std::pair<std::string, JSonElement *> ipair = *i;
  102. if (selection == ipair.second)
  103. selected = true;
  104. cursor.first += indentLevel /2;
  105. writeKey(ipair.first, cursor);
  106. cursor.first -= indentLevel /2;
  107. redraw(cursor, maxSize, ipair.second);
  108. selected = false;
  109. cursor.first -= indentLevel;
  110. }
  111. if (selection == item)
  112. selected = true;
  113. write(cursor.first, cursor.second, "}");
  114. selected = false;
  115. cursor.first -= indentLevel /2;
  116. cursor.second++;
  117. }
  118. else if (dynamic_cast<const JSonArray*>(item) != nullptr)
  119. {
  120. cursor.first += indentLevel /2;
  121. if (selection == item)
  122. selected = selectFound = true;
  123. else if (!selectFound)
  124. select_up = item;
  125. else if (!select_down)
  126. select_down = item;
  127. write(cursor.first, cursor.second, "[");
  128. selected = false;
  129. cursor.first += indentLevel /2;
  130. cursor.second++;
  131. for (JSonArray::const_iterator i = ((JSonArray *)item)->cbegin(); i != ((JSonArray *)item)->cend(); ++i)
  132. redraw(cursor, maxSize, *i);
  133. cursor.first -= indentLevel /2;
  134. if (selection == item)
  135. selected = true;
  136. write(cursor.first, cursor.second, "]");
  137. selected = false;
  138. cursor.first -= indentLevel /2;
  139. cursor.second++;
  140. }
  141. else
  142. {
  143. if (item == selection)
  144. selectFound = selected = true;
  145. else if (!selectFound)
  146. select_up = item;
  147. else if (!select_down)
  148. select_down = item;
  149. write(cursor.first, cursor.second, item->stringify());
  150. selected = false;
  151. cursor.second++;
  152. }
  153. t_nextKey next = findNext(item);
  154. if (next.isAbsent())
  155. break;
  156. if (next.value().first.isPresent())
  157. writeKey(next.value().first.value(), cursor);
  158. item = next.value().second;
  159. } while (true);
  160. }
  161. void CurseOutput::writeKey(const std::string &key, std::pair<int, int> &cursor)
  162. {
  163. write(cursor.first, cursor.second, key +": ");
  164. cursor.first += indentLevel;
  165. cursor.second++;
  166. }
  167. void CurseOutput::write(const int &x, const int &y, JSonElement *item)
  168. {
  169. std::string str = item->stringify();
  170. write(x, y, str);
  171. }
  172. void CurseOutput::write(const int &x, const int &y, const std::string &str)
  173. {
  174. if (selected)
  175. {
  176. attron(A_REVERSE | A_BOLD);
  177. mvprintw(y, x, str.c_str());
  178. attroff(A_REVERSE | A_BOLD);
  179. }
  180. else
  181. mvprintw(y, x, str.c_str());
  182. }
  183. /**
  184. * Read input and expect signal
  185. * @Return true on:
  186. * - Windows resized
  187. * - Key press and need redraw
  188. * false on:
  189. * - exit signal
  190. **/
  191. bool CurseOutput::readInput()
  192. {
  193. while (1)
  194. {
  195. int c;
  196. while ((c = wgetch(stdscr)) == ERR);
  197. switch (c)
  198. {
  199. case 'q':
  200. case 'Q':
  201. return false;
  202. case KEY_UP:
  203. case 'K':
  204. case 'k':
  205. selection = select_up;
  206. return true;
  207. case KEY_DOWN:
  208. case 'j':
  209. case 'J':
  210. selection = select_down;
  211. return true;
  212. default:
  213. return true;
  214. }
  215. }
  216. return false;
  217. }
  218. void CurseOutput::init()
  219. {
  220. initscr();
  221. noecho();
  222. cbreak();
  223. clear();
  224. curs_set(false);
  225. nodelay(stdscr, true);
  226. if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
  227. {
  228. screen_fd = fopen("/dev/tty", "r+");
  229. setbuf(screen_fd, nullptr);
  230. screen = newterm(nullptr, screen_fd, screen_fd);
  231. }
  232. else
  233. screen = newterm(nullptr, stdout, stdin);
  234. keypad(stdscr, true);
  235. topleft.first = std::pair<unsigned int, unsigned int>(0, 0);
  236. topleft.second = data;
  237. }
  238. void CurseOutput::shutdown()
  239. {
  240. delscreen(screen);
  241. endwin();
  242. if (screen_fd)
  243. {
  244. fclose(screen_fd);
  245. screen_fd = nullptr;
  246. }
  247. }