curseSimpleOutput.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /**
  2. * curseOutput.cpp for jsonstroller
  3. *
  4. * Author: isundil <isundill@gmail.com>
  5. **/
  6. #include <sys/ioctl.h>
  7. #include <unistd.h>
  8. #include <signal.h>
  9. #include "curseSimpleOutput.hh"
  10. #include "searchPattern.hh"
  11. #include "jsonPrimitive.hh"
  12. #include "jsonObject.hh"
  13. #include "jsonArray.hh"
  14. CurseSimpleOutput::CurseSimpleOutput(const Params &p): CurseOutput(p)
  15. {
  16. init();
  17. }
  18. CurseSimpleOutput::~CurseSimpleOutput()
  19. {
  20. shutdown();
  21. }
  22. void CurseSimpleOutput::run(JSonElement *root, const std::string &i)
  23. {
  24. scrollTop = 0;
  25. selection = data = root;
  26. inputName = i;
  27. loop(nullptr);
  28. }
  29. bool CurseSimpleOutput::redraw()
  30. {
  31. t_Cursor cursor(0, 1);
  32. /**
  33. * Will be true if the json's last item is visible
  34. **/
  35. bool result;
  36. select_up = select_down = nullptr;
  37. selectFound = selectIsLast = false;
  38. clear();
  39. writeTopLine(inputName, OutputFlag::SPECIAL_ACTIVEINPUTNAME);
  40. try {
  41. result = redraw(cursor, data);
  42. }
  43. catch (SelectionOutOfRange &e)
  44. {
  45. return false;
  46. }
  47. if (!result && !selectFound)
  48. {
  49. scrollTop++;
  50. return false;
  51. }
  52. if (!result && !select_down)
  53. selectIsLast = true;
  54. if (!select_down)
  55. {
  56. const JSonContainer *pselect = dynamic_cast<const JSonContainer*>(selection);
  57. if (pselect && !pselect->empty() && collapsed.find(pselect) == collapsed.cend())
  58. select_down = *(pselect->cbegin());
  59. else
  60. {
  61. const JSonElement *next = selection->findNext();
  62. select_down = next ? next : selection;
  63. }
  64. }
  65. if (!select_up)
  66. select_up = selection;
  67. refresh();
  68. return true;
  69. }
  70. inputResult CurseSimpleOutput::selectUp()
  71. {
  72. selection = select_up;
  73. return inputResult::redraw;
  74. }
  75. inputResult CurseSimpleOutput::selectDown()
  76. {
  77. if (selectIsLast)
  78. scrollTop += 2;
  79. else if (selection != select_down)
  80. selection = select_down;
  81. else
  82. return inputResult::nextInput;
  83. return inputResult::redraw;
  84. }
  85. inputResult CurseSimpleOutput::selectPUp()
  86. {
  87. const JSonElement *_selection = selection;
  88. const JSonElement *brother = _selection->findPrev();
  89. if (brother == nullptr)
  90. {
  91. const JSonElement *parent = _selection->getParent();
  92. if (parent && dynamic_cast<const JSonContainer*>(parent))
  93. {
  94. selection = _selection = parent;
  95. if (_selection->getParent() && dynamic_cast<const JSonObjectEntry*> (_selection->getParent()))
  96. selection = _selection->getParent();
  97. }
  98. else
  99. return inputResult::nextInput;
  100. }
  101. else
  102. selection = brother;
  103. return inputResult::redraw;
  104. }
  105. inputResult CurseSimpleOutput::selectPDown()
  106. {
  107. const JSonElement *brother = selection->findNext();
  108. if (brother)
  109. {
  110. selection = brother;
  111. return inputResult::redraw;
  112. }
  113. return inputResult::nextInput;
  114. }
  115. inputResult CurseSimpleOutput::expandSelection()
  116. {
  117. const JSonElement *_selection = selection;
  118. if (dynamic_cast<const JSonObjectEntry*>(_selection))
  119. _selection = **((const JSonObjectEntry*)_selection);
  120. if (!dynamic_cast<const JSonContainer*>(_selection))
  121. return inputResult::nextInput;
  122. if (collapsed.erase((const JSonContainer *)_selection))
  123. return inputResult::redraw;
  124. if (!((const JSonContainer*)_selection)->size())
  125. return inputResult::nextInput;
  126. selection = select_down;
  127. return inputResult::redraw;
  128. }
  129. inputResult CurseSimpleOutput::collapseSelection()
  130. {
  131. const JSonElement *_selection = selection;
  132. if (dynamic_cast<const JSonObjectEntry*>(_selection))
  133. _selection = **((const JSonObjectEntry*)_selection);
  134. if (_selection->getParent() && (!dynamic_cast<const JSonContainer*>(_selection)
  135. || collapsed.find((const JSonContainer *)_selection) != collapsed.end()
  136. || (dynamic_cast<const JSonContainer*>(_selection) && ((const JSonContainer*)_selection)->size() == 0)))
  137. {
  138. selection = selection->getParent();
  139. if (selection->getParent() && dynamic_cast<const JSonObjectEntry*>(selection->getParent()))
  140. selection = selection->getParent();
  141. }
  142. else
  143. collapsed.insert((const JSonContainer *)_selection);
  144. return inputResult::redraw;
  145. }
  146. inputResult CurseSimpleOutput::initSearch()
  147. {
  148. const SearchPattern *search_pattern = inputSearch();
  149. if (!search_pattern)
  150. return inputResult::redraw;
  151. search_result.clear();
  152. if (search_pattern->isEmpty())
  153. return inputResult::redraw;
  154. search(*search_pattern, data);
  155. delete search_pattern;
  156. return nextResult();
  157. }
  158. inputResult CurseSimpleOutput::nextResult()
  159. {
  160. if (search_result.empty())
  161. CurseOutput::redraw("Pattern not found");
  162. else if (jumpToNextSearch())
  163. return inputResult::redraw;
  164. return inputResult::nextInput;
  165. }
  166. inputResult CurseSimpleOutput::changeWindow(char, bool)
  167. {
  168. //TODO tab mode ?
  169. return inputResult::nextInput;
  170. }
  171. bool CurseSimpleOutput::redraw(t_Cursor &cursor, JSonElement *item)
  172. {
  173. checkSelection(item, cursor);
  174. if (dynamic_cast<const JSonContainer*>(item))
  175. {
  176. if (!writeContainer(cursor, (const JSonContainer *) item))
  177. return false;
  178. }
  179. else
  180. {
  181. cursor.second += CurseOutput::write(cursor.first, cursor.second, item, screenSize.first, CurseSimpleOutput::getFlag(item));
  182. if (hasReachedBottom(cursor.second, scrollTop, screenSize.second))
  183. return false;
  184. }
  185. return true;
  186. }
  187. bool CurseSimpleOutput::writeContainer(t_Cursor &cursor, const JSonContainer *item)
  188. {
  189. char childDelimiter[2];
  190. if (dynamic_cast<const JSonObject *>(item))
  191. memcpy(childDelimiter, "{}", sizeof(*childDelimiter) * 2);
  192. else
  193. memcpy(childDelimiter, "[]", sizeof(*childDelimiter) * 2);
  194. if (collapsed.find((const JSonContainer *)item) != collapsed.end())
  195. {
  196. std::string ss;
  197. ss.append(&childDelimiter[0], 1).append(" ... ").append(&childDelimiter[1], 1);
  198. cursor.second += write(cursor.first, cursor.second, ss, 7, screenSize.first, CurseSimpleOutput::getFlag(item));
  199. }
  200. else
  201. {
  202. cursor.second += write(cursor.first, cursor.second, childDelimiter[0], screenSize.first, CurseSimpleOutput::getFlag(item));
  203. if (hasReachedBottom(cursor.second, scrollTop, screenSize.second))
  204. return false;
  205. if (!writeContent(cursor, (std::list<JSonElement *> *)item))
  206. return false;
  207. cursor.second += write(cursor.first, cursor.second, childDelimiter[1], screenSize.first, CurseSimpleOutput::getFlag(item));
  208. }
  209. return !hasReachedBottom(cursor.second, scrollTop, screenSize.second);
  210. }
  211. bool CurseSimpleOutput::writeContent(t_Cursor &cursor, std::list<JSonElement*> *_item)
  212. {
  213. JSonContainer *item = (JSonContainer *)_item;
  214. bool containerIsObject = (dynamic_cast<JSonObject *>(item) != nullptr);
  215. bool result = true;
  216. cursor.first += INDENT_LEVEL;
  217. for (JSonElement *i : *item)
  218. {
  219. result = false;
  220. if (containerIsObject)
  221. {
  222. JSonObjectEntry *ent = (JSonObjectEntry*) i;
  223. bool isContainer = (dynamic_cast<JSonContainer *>(**ent) != nullptr);
  224. std::string key = ent->stringify();
  225. checkSelection(ent, cursor);
  226. if (isContainer && collapsed.find((JSonContainer*)(**ent)) != collapsed.cend())
  227. {
  228. if (dynamic_cast<JSonObject *>(**ent))
  229. {
  230. if (!writeKey(key, ent->lazystrlen(), "{ ... }", cursor, CurseSimpleOutput::getFlag(ent)))
  231. break;
  232. }
  233. else if (!writeKey(key, ent->lazystrlen(), "[ ... ]", cursor, CurseSimpleOutput::getFlag(ent)))
  234. break;
  235. if (hasReachedBottom(cursor.second, scrollTop, screenSize.second))
  236. break;
  237. }
  238. else if (!isContainer)
  239. {
  240. JSonElement *eContent = **ent;
  241. if (!writeKey(key, ent->lazystrlen(), eContent->stringify(), eContent->lazystrlen(), cursor, CurseSimpleOutput::getFlag(ent))
  242. || hasReachedBottom(cursor.second, scrollTop, screenSize.second))
  243. break;
  244. }
  245. else if (((JSonContainer*)(**ent))->size() == 0)
  246. {
  247. if (dynamic_cast<const JSonObject *>(**ent) )
  248. {
  249. if (!writeKey(key, ent->lazystrlen(), "{ }", cursor, CurseSimpleOutput::getFlag(ent)))
  250. break;
  251. }
  252. else if (!writeKey(key, ent->lazystrlen(), "[ ]", cursor, CurseSimpleOutput::getFlag(ent)))
  253. break;
  254. if (hasReachedBottom(cursor.second, scrollTop, screenSize.second))
  255. break;
  256. }
  257. else
  258. {
  259. if (!writeKey(key, ent->lazystrlen(), cursor, CurseSimpleOutput::getFlag(ent)))
  260. break;
  261. const JSonElement *saveSelection = selection;
  262. if (selection == ent)
  263. selection = **ent;
  264. cursor.first += INDENT_LEVEL /2;
  265. if (!redraw(cursor, **ent))
  266. {
  267. selection = saveSelection;
  268. cursor.first -= INDENT_LEVEL /2;
  269. return false;
  270. }
  271. selection = saveSelection;
  272. cursor.first -= INDENT_LEVEL /2;
  273. }
  274. }
  275. else
  276. {
  277. if (!redraw(cursor, i))
  278. break;
  279. }
  280. result = true;
  281. }
  282. cursor.first -= INDENT_LEVEL;
  283. //result will be false if for loop break'd at some time, true otherwise
  284. return result;
  285. }
  286. bool CurseSimpleOutput::writeKey(const std::string &key, const size_t keylen, t_Cursor &cursor, OutputFlag flags, unsigned int extraLen)
  287. {
  288. if (cursor.second - scrollTop <= 0)
  289. {
  290. cursor.second++;
  291. return true;
  292. }
  293. char oldType = flags.type();
  294. flags.type(OutputFlag::TYPE_OBJKEY);
  295. cursor.second += write(cursor.first, cursor.second, key, keylen, screenSize.first -extraLen -2, flags);
  296. flags.type(OutputFlag::TYPE_OBJ);
  297. write(": ", flags);
  298. flags.type(oldType);
  299. return !hasReachedBottom(cursor.second, scrollTop, screenSize.second);
  300. }
  301. bool CurseSimpleOutput::writeKey(const std::string &key, const size_t keylen, const std::string &after, size_t afterlen, t_Cursor &cursor, OutputFlag flags)
  302. {
  303. if (cursor.second - scrollTop <= 0)
  304. {
  305. cursor.second++;
  306. return true;
  307. }
  308. char oldType = flags.type();
  309. flags.type(OutputFlag::TYPE_OBJKEY);
  310. write(cursor.first, cursor.second, key, 0, 1, flags);
  311. flags.type(OutputFlag::TYPE_OBJ);
  312. write(": ", flags);
  313. flags.type(oldType);
  314. write(after.c_str(), flags);
  315. cursor.second += getNbLines(cursor.first +keylen +2 +afterlen, screenSize.first);
  316. return !hasReachedBottom(cursor.second, scrollTop, screenSize.second);
  317. }
  318. bool CurseSimpleOutput::writeKey(const std::string &key, const size_t keylen, const std::string &after, t_Cursor &cursor, OutputFlag flags)
  319. {
  320. return writeKey(key, keylen, after, after.size(), cursor, flags);
  321. }
  322. unsigned int CurseSimpleOutput::write(const int &x, const int &y, const char item, unsigned int maxWidth, OutputFlag flags)
  323. {
  324. int offsetY = y - scrollTop;
  325. char color = OutputFlag::SPECIAL_NONE;
  326. if (offsetY <= 0)
  327. return 1;
  328. if (flags.selected())
  329. attron(A_REVERSE | A_BOLD);
  330. if (flags.searched())
  331. color = OutputFlag::SPECIAL_SEARCH;
  332. else if (colors.find(flags.type()) != colors.end())
  333. color = flags.type();
  334. if (color != OutputFlag::SPECIAL_NONE)
  335. attron(COLOR_PAIR(color));
  336. mvprintw(offsetY, x, "%c", item);
  337. attroff(A_REVERSE | A_BOLD);
  338. if (color != OutputFlag::SPECIAL_NONE)
  339. attroff(COLOR_PAIR(color));
  340. return getNbLines(x +1, maxWidth);
  341. }
  342. unsigned int CurseSimpleOutput::write(const int &x, const int &y, const std::string &str, const size_t strlen, unsigned int maxWidth, const OutputFlag flags)
  343. {
  344. int offsetY = y - scrollTop;
  345. if (offsetY <= 0)
  346. return 1;
  347. move(offsetY, x);
  348. write(str, flags);
  349. return getNbLines(strlen +x, maxWidth);
  350. }
  351. void CurseSimpleOutput::write(const std::string &str, const OutputFlag flags) const
  352. {
  353. char color = OutputFlag::SPECIAL_NONE;
  354. if (flags.selected())
  355. attron(A_REVERSE | A_BOLD);
  356. if (flags.searched())
  357. color = OutputFlag::SPECIAL_SEARCH;
  358. else if (colors.find(flags.type()) != colors.end())
  359. color = flags.type();
  360. if (color != OutputFlag::SPECIAL_NONE)
  361. attron(COLOR_PAIR(color));
  362. printw("%s", str.c_str());
  363. attroff(A_REVERSE | A_BOLD);
  364. if (color != OutputFlag::SPECIAL_NONE)
  365. attroff(COLOR_PAIR(color));
  366. }
  367. bool CurseSimpleOutput::jumpToNextSearch(const JSonElement *current, bool &selectFound)
  368. {
  369. const JSonContainer *container = dynamic_cast<const JSonContainer *> (current);
  370. const JSonObjectEntry *objEntry = dynamic_cast<const JSonObjectEntry *> (current);
  371. if (selection == current)
  372. selectFound = true;
  373. if (container)
  374. {
  375. if (!container->empty())
  376. for (const JSonElement *it : *container)
  377. if (jumpToNextSearch(it, selectFound))
  378. return true;
  379. }
  380. else
  381. {
  382. if (current && std::find(search_result.cbegin(), search_result.cend(), current) != search_result.cend() && current != selection && selectFound)
  383. {
  384. selection = current;
  385. return true;
  386. }
  387. if (objEntry)
  388. if (jumpToNextSearch(**objEntry, selectFound))
  389. return true;
  390. }
  391. return false;
  392. }
  393. const OutputFlag CurseSimpleOutput::getFlag(const JSonElement *e) const
  394. {
  395. return getFlag(e, selection);
  396. }
  397. const OutputFlag CurseSimpleOutput::getFlag(const JSonElement *item, const JSonElement *selection) const
  398. {
  399. OutputFlag res;
  400. const JSonElement *i = dynamic_cast<const JSonObjectEntry*>(item) ? **((const JSonObjectEntry*)item) : item;
  401. res.selected(item == selection);
  402. res.searched(std::find(search_result.cbegin(), search_result.cend(), item) != search_result.cend());
  403. if (dynamic_cast<const JSonPrimitive<std::string> *>(i))
  404. res.type(OutputFlag::TYPE_STRING);
  405. else if (dynamic_cast<const JSonPrimitive<bool> *>(i))
  406. res.type(OutputFlag::TYPE_BOOL);
  407. else if (dynamic_cast<const JSonPrimitive<Null> *>(i))
  408. res.type(OutputFlag::TYPE_NULL);
  409. else if (dynamic_cast<const AJSonPrimitive *>(i))
  410. res.type(OutputFlag::TYPE_NUMBER);
  411. else if (dynamic_cast<const JSonObject*>(i))
  412. res.type(OutputFlag::TYPE_OBJ);
  413. else if (dynamic_cast<const JSonArray*>(i))
  414. res.type(OutputFlag::TYPE_ARR);
  415. return res;
  416. }
  417. unsigned int CurseSimpleOutput::search(const SearchPattern &search_pattern, const JSonElement *current)
  418. {
  419. const JSonContainer *container = dynamic_cast<const JSonContainer *> (current);
  420. const JSonObjectEntry *objEntry = dynamic_cast<const JSonObjectEntry *> (current);
  421. unsigned int result =0;
  422. if (container)
  423. {
  424. if (!container->empty())
  425. for (const JSonElement *it : *container)
  426. result += search(search_pattern, it);
  427. }
  428. else
  429. {
  430. if (current && current->match(search_pattern))
  431. {
  432. if (current->getParent() && dynamic_cast<const JSonObjectEntry*>(current->getParent()))
  433. search_result.push_back(current->getParent());
  434. else
  435. search_result.push_back(current);
  436. result++;
  437. }
  438. if (objEntry)
  439. result += search(search_pattern, **objEntry);
  440. }
  441. result = search_result.size();
  442. return result;
  443. }
  444. bool CurseSimpleOutput::jumpToNextSearch()
  445. {
  446. bool selectFound = false;
  447. bool res = jumpToNextSearch(data, selectFound);
  448. if (!res)
  449. {
  450. selection = *(search_result.cbegin());
  451. unfold(selection);
  452. CurseOutput::redraw("Search hit BOTTOM, continuing at TOP");
  453. return false;
  454. }
  455. unfold(selection);
  456. return true;
  457. }
  458. void CurseSimpleOutput::checkSelection(const JSonElement *item, const t_Cursor &cursor)
  459. {
  460. if (!selectFound)
  461. {
  462. if (selection == item)
  463. {
  464. if (cursor.second < scrollTop) //Selection is above vp, move scroll pos to selection and start drawing
  465. scrollTop = cursor.second;
  466. selectFound = true;
  467. }
  468. else if (!item->getParent() || !dynamic_cast<const JSonObjectEntry*>(item->getParent()))
  469. select_up = item;
  470. }
  471. else if (!select_down)
  472. {
  473. const JSonElement *parent = item->getParent();
  474. if (!dynamic_cast<const JSonContainer*>(item) && parent && selection != parent && dynamic_cast<const JSonObjectEntry*>(parent))
  475. item = parent;
  476. if (!parent || !dynamic_cast<const JSonObjectEntry*>(parent))
  477. select_down = item;
  478. }
  479. }
  480. void CurseSimpleOutput::shutdown()
  481. {
  482. endwin();
  483. delscreen(screen);
  484. if (screen_fd)
  485. {
  486. fclose(screen_fd);
  487. screen_fd = nullptr;
  488. }
  489. screen = nullptr;
  490. }
  491. const t_Cursor CurseSimpleOutput::getScreenSize() const
  492. {
  493. return getScreenSizeUnsafe();
  494. }