curseSimpleOutput.cpp 17 KB

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