curseSplitOutput.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. /**
  2. * curseSplitOutput.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 <curses.h>
  10. #include <alloca.h>
  11. #include "searchPattern.hh"
  12. #include "curseSplitOutput.hh"
  13. #include "jsonObject.hh"
  14. #include "jsonArray.hh"
  15. #include "jsonPrimitive.hh"
  16. #include "levenshteinMatrice.hpp"
  17. template<class T> const T &list_at(const std::list<T> &l, unsigned int pos)
  18. {
  19. typename std::list<T>::const_iterator it = l.cbegin();
  20. std::advance(it, pos);
  21. return *it;
  22. }
  23. CurseSplitOutput::CurseSplitOutput(const Params &p): CurseOutput(p)
  24. {
  25. diffMatrice = nullptr;
  26. init();
  27. }
  28. CurseSplitOutput::~CurseSplitOutput()
  29. {
  30. shutdown();
  31. if (diffMatrice)
  32. {
  33. delete diffMatrice;
  34. diffMatrice = nullptr;
  35. }
  36. }
  37. void CurseSplitOutput::run(const std::deque<std::string> &inputName, const std::deque<JSonElement*> &roots)
  38. {
  39. nbInputs = inputName.size();
  40. selectedWin = 0;
  41. destroyAllSubWin();
  42. subWindows.clear();
  43. screenSize = getScreenSize(); // screenSize is based on nbInputs, we must refresh it
  44. for (size_t i =0; i < nbInputs; ++i)
  45. {
  46. t_subWindow subwin;
  47. subwin.fileName = inputName.at(i);
  48. subwin.selection = subwin.lastSelection = subwin.root = roots.at(i);
  49. subwin.select_up = subwin.select_down = nullptr;
  50. subwin.innerWin = subwin.outerWin = nullptr;
  51. subwin.scrollTop = 0;
  52. subwin.outerWin = newwin(screenSize.second +2, screenSize.first, 0, i * (screenSize.first -1));
  53. subwin.innerWin = newwin(screenSize.second, screenSize.first -2, 1, i * (screenSize.first -1) +1);
  54. keypad(subwin.outerWin, true);
  55. subWindows.push_back(subwin);
  56. box(subwin.outerWin, 0, 0);
  57. wrefresh(subwin.outerWin);
  58. }
  59. computeDiff();
  60. loop(subWindows.at(0).outerWin);
  61. }
  62. void CurseSplitOutput::computeDiff()
  63. {
  64. //TODO diffMatrice should be LevenshteinMatrice_base[nbInputs -1]
  65. //And we should iterate such as diffMatrice[n] = diff(n, n+1) ?
  66. if (diffMatrice)
  67. delete diffMatrice;
  68. LevenshteinMatrice_base::Builder builder;
  69. if (nbInputs == 2)
  70. diffMatrice = builder.build(subWindows.at(0).root, subWindows.at(1).root);
  71. else if (nbInputs == 3)
  72. throw std::runtime_error("3-input diff not implemented");
  73. }
  74. inputResult CurseSplitOutput::selectUp()
  75. {
  76. setSelection(subWindows.at(selectedWin).select_up);
  77. return inputResult::redraw;
  78. }
  79. inputResult CurseSplitOutput::selectDown()
  80. {
  81. const JSonElement *newSelection = subWindows.at(selectedWin).select_down;
  82. size_t i = 0;
  83. for (t_subWindow &w : subWindows)
  84. {
  85. if (w.selectIsLast)
  86. w.scrollTop += 2;
  87. if (i == selectedWin)
  88. w.selection = w.lastSelection = newSelection;
  89. else
  90. {
  91. w.selection = diffMatrice->getEquivalence(newSelection);
  92. if (w.selection)
  93. w.lastSelection = w.selection;
  94. }
  95. ++i;
  96. }
  97. return inputResult::redraw;
  98. }
  99. void CurseSplitOutput::setSelection(const JSonElement *selection)
  100. {
  101. size_t i =0;
  102. for (t_subWindow &w : subWindows)
  103. {
  104. if (i == selectedWin)
  105. w.selection = w.lastSelection = selection;
  106. else
  107. {
  108. w.selection = diffMatrice->getEquivalence(selection);
  109. if (w.selection)
  110. w.lastSelection = w.selection;
  111. }
  112. ++i;
  113. }
  114. }
  115. inputResult CurseSplitOutput::selectPUp()
  116. {
  117. const JSonElement *_selection = subWindows.at(selectedWin).selection;
  118. const JSonElement *nextSelection = _selection->findPrev();
  119. if (nextSelection == nullptr)
  120. {
  121. const JSonElement *parent = _selection->getParent();
  122. if (parent && dynamic_cast<const JSonContainer*>(parent))
  123. {
  124. nextSelection = _selection = parent;
  125. if (_selection->getParent() && dynamic_cast<const JSonObjectEntry*> (_selection->getParent()))
  126. nextSelection = _selection->getParent();
  127. }
  128. else
  129. return inputResult::nextInput;
  130. }
  131. setSelection(nextSelection);
  132. return inputResult::redraw;
  133. }
  134. inputResult CurseSplitOutput::selectPDown()
  135. {
  136. const JSonElement *brother = subWindows.at(selectedWin).selection->findNext();
  137. if (brother)
  138. {
  139. setSelection(brother);
  140. return inputResult::redraw;
  141. }
  142. return inputResult::nextInput;
  143. }
  144. inputResult CurseSplitOutput::expandSelection()
  145. {
  146. const JSonElement *_selection = subWindows.at(selectedWin).selection;
  147. if (dynamic_cast<const JSonObjectEntry*>(_selection))
  148. _selection = **((const JSonObjectEntry*)_selection);
  149. if (!dynamic_cast<const JSonContainer*>(_selection))
  150. return inputResult::nextInput;
  151. if (collapsed.erase((const JSonContainer *)_selection))
  152. {
  153. _selection = diffMatrice->getEquivalence(_selection);
  154. collapsed.erase((const JSonContainer *)_selection);
  155. return inputResult::redraw;
  156. }
  157. if (!((const JSonContainer*)_selection)->size())
  158. return inputResult::nextInput;
  159. selectDown();
  160. return inputResult::redraw;
  161. }
  162. inputResult CurseSplitOutput::collapseSelection()
  163. {
  164. const JSonElement *_selection = subWindows.at(selectedWin).selection;
  165. if (dynamic_cast<const JSonObjectEntry*>(_selection))
  166. _selection = **((const JSonObjectEntry*)_selection);
  167. if (_selection->getParent() && (!dynamic_cast<const JSonContainer*>(_selection)
  168. || collapsed.find((const JSonContainer *)_selection) != collapsed.end()
  169. || (dynamic_cast<const JSonContainer*>(_selection) && ((const JSonContainer*)_selection)->size() == 0)))
  170. {
  171. _selection = subWindows.at(selectedWin).selection->getParent();
  172. if (_selection->getParent() && dynamic_cast<const JSonObjectEntry*>(_selection->getParent()))
  173. setSelection(_selection->getParent());
  174. else
  175. setSelection(_selection);
  176. }
  177. else
  178. {
  179. collapsed.insert((const JSonContainer *)_selection);
  180. _selection = diffMatrice->getEquivalence(_selection);
  181. if (_selection)
  182. collapsed.insert((const JSonContainer *)_selection);
  183. }
  184. return inputResult::redraw;
  185. }
  186. inputResult CurseSplitOutput::initSearch()
  187. {
  188. const SearchPattern *searchPattern = inputSearch();
  189. if (!searchPattern)
  190. return inputResult::redraw;
  191. for (t_subWindow &s : subWindows)
  192. s.searchResults.clear();
  193. if (searchPattern->isEmpty())
  194. return inputResult::redraw;
  195. search(*searchPattern);
  196. delete searchPattern;
  197. return nextResult();
  198. }
  199. inputResult CurseSplitOutput::nextResult()
  200. {
  201. if (subWindows.at(selectedWin).searchResults.empty())
  202. CurseOutput::redraw("Pattern not found");
  203. else if (jumpToNextSearch())
  204. return inputResult::redraw;
  205. return inputResult::nextInput;
  206. }
  207. inputResult CurseSplitOutput::changeWindow(char d, bool c)
  208. {
  209. if ((selectedWin +d < 0 || selectedWin +d >= nbInputs) && !c)
  210. return inputResult::nextInput;
  211. selectedWin = (selectedWin +d) % nbInputs;
  212. t_subWindow &w = subWindows.at(selectedWin);
  213. //Restore selection
  214. if (!w.selection)
  215. {
  216. size_t i =0;
  217. for (t_subWindow &it : subWindows)
  218. {
  219. if (i == selectedWin)
  220. it.selection = it.lastSelection;
  221. else
  222. {
  223. it.selection = diffMatrice->getEquivalence(w.lastSelection);
  224. if (it.selection)
  225. it.lastSelection = it.selection;
  226. }
  227. ++i;
  228. }
  229. }
  230. return inputResult::redrawAll;
  231. }
  232. void CurseSplitOutput::checkSelection(const JSonElement *item)
  233. {
  234. t_subWindow &w = subWindows.at(workingWin);
  235. if (!w.selectFound)
  236. {
  237. if (w.lastSelection == item)
  238. {
  239. if (w.cursor.second < w.scrollTop) //Selection is above vp, move scroll pos to selection and start drawing
  240. w.scrollTop = w.cursor.second;
  241. w.selectFound = true;
  242. }
  243. else if (!item->getParent() || !dynamic_cast<const JSonObjectEntry*>(item->getParent()))
  244. w.select_up = item;
  245. }
  246. else if (!w.select_down)
  247. {
  248. const JSonElement *parent = item->getParent();
  249. if (!dynamic_cast<const JSonContainer*>(item) &&
  250. parent &&
  251. w.lastSelection != parent &&
  252. dynamic_cast<const JSonObjectEntry*>(parent))
  253. item = parent;
  254. if (!item->getParent() || !dynamic_cast<const JSonObjectEntry*>(item->getParent()))
  255. w.select_down = item;
  256. }
  257. }
  258. bool CurseSplitOutput::jumpToNextSearch(const JSonElement *current, bool &selectFound)
  259. {
  260. const JSonContainer *container = dynamic_cast<const JSonContainer *> (current);
  261. const JSonObjectEntry *objEntry = dynamic_cast<const JSonObjectEntry *> (current);
  262. if (subWindows.at(selectedWin).lastSelection == current)
  263. selectFound = true;
  264. if (container)
  265. {
  266. if (!container->empty())
  267. for (const JSonElement *it : *container)
  268. if (jumpToNextSearch(it, selectFound))
  269. return true;
  270. }
  271. else
  272. {
  273. if (current &&
  274. std::find(subWindows.at(selectedWin).searchResults.cbegin(),
  275. subWindows.at(selectedWin).searchResults.cend(),
  276. current) != subWindows.at(selectedWin).searchResults.cend() &&
  277. current != subWindows.at(selectedWin).selection &&
  278. selectFound)
  279. {
  280. subWindows.at(selectedWin).lastSelection = current;
  281. return true;
  282. }
  283. if (objEntry)
  284. if (jumpToNextSearch(**objEntry, selectFound))
  285. return true;
  286. }
  287. return false;
  288. }
  289. unsigned int CurseSplitOutput::search(const SearchPattern &searchPattern)
  290. {
  291. unsigned int result =0;
  292. for (t_subWindow &w : subWindows)
  293. result += search(searchPattern, w.root);
  294. return result;
  295. }
  296. unsigned int CurseSplitOutput::search(const SearchPattern &searchPattern, const JSonElement *current)
  297. {
  298. const JSonContainer *container = dynamic_cast<const JSonContainer *> (current);
  299. const JSonObjectEntry *objEntry = dynamic_cast<const JSonObjectEntry *> (current);
  300. if (container)
  301. {
  302. if (!container->empty())
  303. for (const JSonElement *it : *container)
  304. search(searchPattern, it);
  305. }
  306. else
  307. {
  308. if (current && current->match(searchPattern))
  309. {
  310. if (current->getParent() && dynamic_cast<const JSonObjectEntry*>(current->getParent()))
  311. subWindows.at(workingWin).searchResults.push_back(current->getParent());
  312. else
  313. subWindows.at(workingWin).searchResults.push_back(current);
  314. }
  315. if (objEntry)
  316. search(searchPattern, **objEntry);
  317. }
  318. return subWindows.at(workingWin).searchResults.size();
  319. }
  320. bool CurseSplitOutput::jumpToNextSearch()
  321. {
  322. bool selectFound = false;
  323. bool res = jumpToNextSearch(subWindows.at(selectedWin).root, selectFound);
  324. if (!res)
  325. {
  326. subWindows.at(selectedWin).lastSelection = *(subWindows.at(selectedWin).searchResults.cbegin());
  327. unfold(subWindows.at(selectedWin).selection);
  328. CurseOutput::redraw("Search hit BOTTOM, continuing at TOP");
  329. return false;
  330. }
  331. unfold(subWindows.at(selectedWin).selection);
  332. return true;
  333. }
  334. const Optional<bool> CurseSplitOutput::redrawOneItemToWorkingWin(t_subWindow &w)
  335. {
  336. bool result;
  337. try {
  338. if (w.parentsIterators.empty())
  339. result = redraw(w, w.root);
  340. else
  341. result = redraw(w, w.parentsIterators.top());
  342. }
  343. catch (SelectionOutOfRange &e)
  344. {
  345. return Optional<bool>::empty;
  346. }
  347. if (!result || w.parentsIterators.empty())
  348. {
  349. if (!result)
  350. {
  351. if (!w.selectFound)
  352. {
  353. w.scrollTop++;
  354. return Optional<bool>::empty;
  355. }
  356. if (!w.select_down)
  357. w.selectIsLast = true;
  358. }
  359. if (!w.select_down)
  360. {
  361. const JSonContainer *pselect = dynamic_cast<const JSonContainer*>(w.selection);
  362. if (pselect && !pselect->empty() && collapsed.find(pselect) == collapsed.cend())
  363. w.select_down = *(pselect->cbegin());
  364. else
  365. {
  366. const JSonElement *next = w.lastSelection->findNext();
  367. w.select_down = next ? next : w.lastSelection;
  368. }
  369. }
  370. if (!w.select_up)
  371. w.select_up = subWindows.at(workingWin).lastSelection;
  372. return Optional<bool>::of(true);
  373. }
  374. return Optional<bool>::of(false);
  375. }
  376. void CurseSplitOutput::onResizeHandler()
  377. {
  378. size_t i =0;
  379. clear();
  380. for (t_subWindow &subwin: subWindows)
  381. {
  382. wresize(subwin.outerWin, screenSize.second +2, screenSize.first);
  383. wresize(subwin.innerWin, screenSize.second, screenSize.first -2);
  384. mvwin(subwin.outerWin, 0, i * (screenSize.first -1));
  385. mvwin(subwin.innerWin, 1, i * (screenSize.first -1) +1);
  386. box(subwin.outerWin, 0, 0);
  387. wrefresh(subwin.outerWin);
  388. ++i;
  389. }
  390. }
  391. bool CurseSplitOutput::redraw()
  392. {
  393. short writingDone = (1 << nbInputs) -1;
  394. unsigned int *cursorInit = (unsigned int *)alloca(sizeof(*cursorInit) * nbInputs);
  395. workingWin = 0;
  396. for (t_subWindow &w : subWindows)
  397. {
  398. w.cursor = t_Cursor(2, 1);
  399. w.select_up = w.select_down = nullptr;
  400. w.selectFound = w.selectIsLast = false;
  401. wclear(w.innerWin);
  402. writeTopLine(w.fileName,
  403. workingWin == selectedWin ? OutputFlag::SPECIAL_ACTIVEINPUTNAME : OutputFlag::SPECIAL_INPUTNAME);
  404. w.parentsIterators = std::stack<std::pair<int, JSonContainer*> >();
  405. ++workingWin;
  406. }
  407. while (writingDone)
  408. {
  409. // Display Gap (--)
  410. bool restart = false;
  411. unsigned int maxLines = 0;
  412. workingWin = 0;
  413. for (t_subWindow &w : subWindows)
  414. {
  415. if ((writingDone & (1 << workingWin)) &&
  416. ((!w.parentsIterators.empty() && isAdded(w.parentsIterators.top())) ||
  417. (w.parentsIterators.empty() && isAdded(w.root))))
  418. {
  419. const unsigned int startY = w.cursor.second;
  420. do
  421. {
  422. const Optional<bool> wrote = redrawOneItemToWorkingWin(w);
  423. if (wrote.absent())
  424. return false;
  425. if (wrote.get())
  426. {
  427. writingDone &= ~(1 << workingWin);
  428. break;
  429. }
  430. } while (isAdded(w.parentsIterators.top()));
  431. const unsigned int diffY = w.cursor.second - startY;
  432. unsigned int i = 0;
  433. for (t_subWindow &wi: subWindows)
  434. if (i++ != workingWin)
  435. for (unsigned int j = 0; j < diffY; ++j)
  436. displayDiffOp(wi.innerWin, (wi.cursor.second)++, eLevenshteinOperator::rem);
  437. restart = true;
  438. break;
  439. }
  440. ++workingWin;
  441. }
  442. if (restart)
  443. continue;
  444. // Actual display
  445. workingWin = 0;
  446. for (t_subWindow &w : subWindows)
  447. {
  448. cursorInit[workingWin] = w.cursor.second;
  449. if ((writingDone & (1 << workingWin)))
  450. {
  451. const Optional<bool> wrote = redrawOneItemToWorkingWin(w);
  452. if (wrote.absent())
  453. return false;
  454. if (wrote.get())
  455. writingDone &= ~(1 << workingWin);
  456. maxLines = std::max(maxLines, w.cursor.second - cursorInit[workingWin]);
  457. }
  458. ++workingWin;
  459. }
  460. // Display multi-lines gaps
  461. workingWin = 0;
  462. for (t_subWindow &w : subWindows)
  463. w.cursor.second = cursorInit[workingWin++] + maxLines;
  464. }
  465. for (t_subWindow &w : subWindows)
  466. wrefresh(w.innerWin);
  467. return true;
  468. }
  469. bool CurseSplitOutput::writeContainer(JSonContainer *item, bool opening)
  470. {
  471. char childDelimiter[2];
  472. t_subWindow &w = subWindows.at(workingWin);
  473. if (dynamic_cast<const JSonObject *>(item))
  474. memcpy(childDelimiter, "{}", sizeof(*childDelimiter) * 2);
  475. else
  476. memcpy(childDelimiter, "[]", sizeof(*childDelimiter) * 2);
  477. if (!opening) // Display close brackets
  478. {
  479. w.cursor.first -= INDENT_LEVEL;
  480. w.cursor.second += write(w.cursor.first, w.cursor.second, childDelimiter[1], screenSize.first -2, CurseSplitOutput::getFlag(item));
  481. }
  482. else if (collapsed.find((const JSonContainer *)item) != collapsed.end()) // inline collapsed
  483. {
  484. std::string ss;
  485. ss.append(&childDelimiter[0], 1).append(" ... ").append(&childDelimiter[1], 1);
  486. w.cursor.second += write(w.cursor.first, w.cursor.second, ss, 7, screenSize.first -2, CurseSplitOutput::getFlag(item));
  487. }
  488. else // Display open brackets
  489. {
  490. w.cursor.second += write(w.cursor.first, w.cursor.second, childDelimiter[0], screenSize.first -2, CurseSplitOutput::getFlag(item));
  491. if (hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second))
  492. return false;
  493. w.parentsIterators.push(std::pair<int, JSonContainer *>(-1, item));
  494. w.cursor.first += INDENT_LEVEL;
  495. return true;
  496. }
  497. return !hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second);
  498. }
  499. bool CurseSplitOutput::writeKey(t_subWindow &w, const std::string &key, const size_t keylen, OutputFlag flags, unsigned int extraLen)
  500. {
  501. if (w.cursor.second <= w.scrollTop)
  502. {
  503. w.cursor.second++;
  504. return true;
  505. }
  506. char oldType = flags.type();
  507. flags.type(OutputFlag::TYPE_OBJKEY);
  508. w.cursor.second += write(w.cursor.first, w.cursor.second, key, keylen, screenSize.first -extraLen -2 -2, flags);
  509. flags.type(OutputFlag::TYPE_OBJ);
  510. write(": ", flags);
  511. flags.type(oldType);
  512. return !hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second);
  513. }
  514. bool CurseSplitOutput::writeKey(t_subWindow &w, const std::string &key, const size_t keylen, const std::string &after, const size_t afterlen, t_Cursor &cursor, OutputFlag flags)
  515. {
  516. if (cursor.second <= w.scrollTop)
  517. {
  518. cursor.second++;
  519. return true;
  520. }
  521. char oldType = flags.type();
  522. flags.type(OutputFlag::TYPE_OBJKEY);
  523. write(cursor.first, cursor.second, key, 0, 1, flags);
  524. flags.type(OutputFlag::TYPE_OBJ);
  525. write(": ", flags);
  526. flags.type(oldType);
  527. write(after, flags);
  528. cursor.second += getNbLines(cursor.first +keylen +2 +afterlen, screenSize.first -2);
  529. return !hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second);
  530. }
  531. bool CurseSplitOutput::writeKey(t_subWindow &w, const std::string &key, const size_t keylen, const std::string &after, t_Cursor &cursor, OutputFlag flags)
  532. {
  533. return writeKey(w, key, keylen, after, after.length(), cursor, flags);
  534. }
  535. bool CurseSplitOutput::isAdded(const JSonElement *e) const
  536. {
  537. return diffMatrice->getEquivalence(e) == nullptr;
  538. }
  539. bool CurseSplitOutput::isAdded(const std::pair<int, JSonContainer *> &item) const
  540. {
  541. const JSonElement *e;
  542. if ((unsigned int) (item.first +1) >= item.second->size())
  543. e = item.second;
  544. else
  545. e = list_at<JSonElement*>(*(item.second), item.first +1);
  546. return isAdded(e);
  547. }
  548. bool CurseSplitOutput::redraw(t_subWindow &w, std::pair<int, JSonContainer *> &item)
  549. {
  550. JSonElement *currentItem;
  551. (item.first)++;
  552. if ((unsigned int) item.first == item.second->size())
  553. {
  554. w.parentsIterators.pop();
  555. return writeContainer(item.second, false);
  556. }
  557. currentItem = list_at<JSonElement*>(*(item.second), item.first);
  558. return redraw(w, currentItem);
  559. }
  560. bool CurseSplitOutput::redraw(t_subWindow &w, JSonElement *item)
  561. {
  562. checkSelection(item);
  563. if (dynamic_cast<const JSonContainer*>(item))
  564. {
  565. if (!writeContainer((JSonContainer *) item))
  566. return false;
  567. }
  568. else if (dynamic_cast<JSonObjectEntry*>(item))
  569. {
  570. return writeObjectEntry(w, (JSonObjectEntry*) item);
  571. }
  572. else
  573. {
  574. w.cursor.second += CurseOutput::write(w.cursor.first, w.cursor.second, item, screenSize.first -2, CurseSplitOutput::getFlag(item));
  575. if (hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second))
  576. return false;
  577. }
  578. return true;
  579. }
  580. bool CurseSplitOutput::writeObjectEntry(t_subWindow &w, JSonObjectEntry *ent)
  581. {
  582. bool isContainer = (dynamic_cast<JSonContainer *>(**ent) != nullptr);
  583. std::string key = ent->stringify();
  584. if (isContainer && collapsed.find((JSonContainer*)(**ent)) != collapsed.cend()) // inline collapsed container
  585. {
  586. if (dynamic_cast<JSonObject *>(**ent))
  587. {
  588. if (!writeKey(w, key, ent->lazystrlen(), "{ ... }", w.cursor, getFlag(ent)))
  589. return false;
  590. }
  591. else if (!writeKey(w, key, ent->lazystrlen(), "[ ... ]", w.cursor, getFlag(ent)))
  592. return false;
  593. if (hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second))
  594. return false;
  595. }
  596. else if (!isContainer) // inline value
  597. {
  598. JSonElement *eContent = **ent;
  599. if (!writeKey(w, key, ent->lazystrlen(), eContent->stringify(), eContent->lazystrlen(), w.cursor, getFlag(ent)))
  600. return false;
  601. }
  602. else if (((JSonContainer*)(**ent))->size() == 0) // inline empty
  603. {
  604. if (dynamic_cast<const JSonObject *>(**ent) )
  605. {
  606. if (!writeKey(w, key, ent->lazystrlen(), "{ }", w.cursor, getFlag(ent)))
  607. return false;
  608. }
  609. else if (!writeKey(w, key, ent->lazystrlen(), "[ ]", w.cursor, getFlag(ent)))
  610. return false;
  611. if (hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second))
  612. return false;
  613. }
  614. else // Container
  615. {
  616. if (!writeKey(w, key, ent->lazystrlen(), getFlag(ent)))
  617. return false;
  618. writeContainer((JSonContainer*)(**ent), true);
  619. }
  620. return !hasReachedBottom(w.cursor.second, w.scrollTop, screenSize.second);
  621. }
  622. unsigned int CurseSplitOutput::write(const unsigned int &x, const unsigned int &y, const char item, unsigned int maxWidth, OutputFlag flags)
  623. {
  624. unsigned int offsetY = y - subWindows.at(workingWin).scrollTop;
  625. WINDOW *currentWin = subWindows.at(workingWin).innerWin;
  626. char color = OutputFlag::SPECIAL_NONE;
  627. if (y <= subWindows.at(workingWin).scrollTop) // underflow / before top of screen
  628. return 1;
  629. if (flags.selected())
  630. wattron(currentWin, A_REVERSE | A_BOLD);
  631. if (flags.searched())
  632. color = OutputFlag::SPECIAL_SEARCH;
  633. else if (colors.find(flags.type()) != colors.end())
  634. color = flags.type();
  635. if (color != OutputFlag::SPECIAL_NONE)
  636. wattron(currentWin, COLOR_PAIR(color));
  637. mvwprintw(currentWin, offsetY, x, "%c", item);
  638. wattroff(currentWin, A_REVERSE | A_BOLD);
  639. if (color != OutputFlag::SPECIAL_NONE)
  640. wattroff(currentWin, COLOR_PAIR(color));
  641. displayDiffOp(currentWin, offsetY, flags.diffOp());
  642. return getNbLines(x +1, maxWidth);
  643. }
  644. void CurseSplitOutput::displayDiffOp(WINDOW *w, const int &y, const eLevenshteinOperator &op) const
  645. {
  646. switch (op)
  647. {
  648. case eLevenshteinOperator::add:
  649. wattron(w, A_REVERSE | A_BOLD);
  650. wattron(w, COLOR_PAIR(OutputFlag::DIFF_ADD));
  651. mvwprintw(w, y, 0, "++");
  652. wattroff(w, COLOR_PAIR(OutputFlag::DIFF_ADD));
  653. wattroff(w, A_REVERSE | A_BOLD);
  654. break;
  655. case eLevenshteinOperator::mod:
  656. wattron(w, A_REVERSE | A_BOLD);
  657. wattron(w, COLOR_PAIR(OutputFlag::DIFF_MOD));
  658. mvwprintw(w, y, 0, "!!");
  659. wattroff(w, COLOR_PAIR(OutputFlag::DIFF_MOD));
  660. wattroff(w, A_REVERSE | A_BOLD);
  661. break;
  662. case eLevenshteinOperator::rem:
  663. wattron(w, A_REVERSE | A_BOLD);
  664. wattron(w, COLOR_PAIR(OutputFlag::DIFF_REM));
  665. mvwprintw(w, y, 0, "--");
  666. wattroff(w, COLOR_PAIR(OutputFlag::DIFF_REM));
  667. wattroff(w, A_REVERSE | A_BOLD);
  668. break;
  669. case eLevenshteinOperator::equ: // skip
  670. break;
  671. }
  672. }
  673. unsigned int CurseSplitOutput::write(const unsigned int &x, const unsigned int &y, const std::string &str, const size_t strlen, unsigned int maxWidth, const OutputFlag flags)
  674. {
  675. const t_subWindow &w = subWindows.at(workingWin);
  676. WINDOW *currentWin = w.innerWin;
  677. if (y <= w.scrollTop)
  678. return 1;
  679. displayDiffOp(currentWin, y, flags.diffOp());
  680. wmove(subWindows.at(workingWin).innerWin, y - w.scrollTop, x);
  681. write(str, flags);
  682. return getNbLines(strlen +x, maxWidth);
  683. }
  684. void CurseSplitOutput::write(const std::string &str, const OutputFlag flags) const
  685. {
  686. char color = OutputFlag::SPECIAL_NONE;
  687. WINDOW *currentWin = subWindows.at(workingWin).innerWin;
  688. if (flags.selected())
  689. wattron(currentWin, A_REVERSE | A_BOLD);
  690. if (flags.searched())
  691. color = OutputFlag::SPECIAL_SEARCH;
  692. else if (colors.find(flags.type()) != colors.end())
  693. color = flags.type();
  694. if (color != OutputFlag::SPECIAL_NONE)
  695. wattron(currentWin, COLOR_PAIR(color));
  696. wprintw(currentWin, "%s", str.c_str());
  697. wattroff(currentWin, A_REVERSE | A_BOLD);
  698. if (color != OutputFlag::SPECIAL_NONE)
  699. wattroff(currentWin, COLOR_PAIR(color));
  700. }
  701. void CurseSplitOutput::destroyAllSubWin()
  702. {
  703. for (t_subWindow &w : subWindows)
  704. {
  705. if (w.outerWin)
  706. {
  707. wborder(w.outerWin, ' ', ' ', ' ',' ',' ',' ',' ',' ');
  708. wrefresh(w.outerWin);
  709. delwin(w.outerWin);
  710. w.outerWin = nullptr;
  711. }
  712. if (w.innerWin)
  713. {
  714. delwin(w.innerWin);
  715. w.innerWin = nullptr;
  716. }
  717. }
  718. }
  719. void CurseSplitOutput::writeTopLine(const std::string &buffer, short color) const
  720. {
  721. const std::string str = buffer.substr(0, screenSize.first -2);
  722. const size_t bufsize = str.size();
  723. WINDOW *currentWin = subWindows.at(workingWin).innerWin;
  724. if (params.colorEnabled())
  725. wattron(currentWin, COLOR_PAIR(color));
  726. if (bufsize == screenSize.first -2)
  727. mvwprintw(currentWin, 0, 0, "%s", str.c_str());
  728. else
  729. mvwprintw(currentWin, 0, 0, "%s%*c", str.c_str(), screenSize.first - bufsize -2, ' ');
  730. if (params.colorEnabled())
  731. wattroff(currentWin, COLOR_PAIR(color));
  732. }
  733. const t_Cursor CurseSplitOutput::getScreenSize() const
  734. {
  735. t_Cursor result = getScreenSizeUnsafe();
  736. return t_Cursor(result.first / nbInputs, result.second -2);
  737. }
  738. void CurseSplitOutput::shutdown()
  739. {
  740. destroyAllSubWin();
  741. endwin();
  742. delscreen(screen);
  743. if (screen_fd)
  744. {
  745. fclose(screen_fd);
  746. screen_fd = nullptr;
  747. }
  748. screen = nullptr;
  749. }
  750. const OutputFlag CurseSplitOutput::getFlag(const JSonElement *e) const
  751. {
  752. return getFlag(e, subWindows.at(workingWin).selection);
  753. }
  754. const OutputFlag CurseSplitOutput::getFlag(const JSonElement *item, const JSonElement *selection) const
  755. {
  756. OutputFlag res;
  757. const JSonElement *i = dynamic_cast<const JSonObjectEntry*>(item) ? **((const JSonObjectEntry*)item) : item;
  758. res.selected(item == selection
  759. || (item->getParent() && dynamic_cast<const JSonObjectEntry*>(item->getParent()) && selection == item->getParent()));
  760. res.searched(std::find(subWindows.at(selectedWin).searchResults.cbegin(),
  761. subWindows.at(selectedWin).searchResults.cend(),
  762. item) != subWindows.at(selectedWin).searchResults.cend());
  763. try {
  764. res.diffOp(diffMatrice->get(item));
  765. }
  766. catch (std::out_of_range &e) {
  767. res.diffOp(eLevenshteinOperator::add);
  768. }
  769. if (dynamic_cast<const JSonPrimitive<std::string> *>(i))
  770. res.type(OutputFlag::TYPE_STRING);
  771. else if (dynamic_cast<const JSonPrimitive<bool> *>(i))
  772. res.type(OutputFlag::TYPE_BOOL);
  773. else if (dynamic_cast<const JSonPrimitive<Null> *>(i))
  774. res.type(OutputFlag::TYPE_NULL);
  775. else if (dynamic_cast<const AJSonPrimitive *>(i))
  776. res.type(OutputFlag::TYPE_NUMBER);
  777. else if (dynamic_cast<const JSonObject*>(i))
  778. res.type(OutputFlag::TYPE_OBJ);
  779. else if (dynamic_cast<const JSonArray*>(i))
  780. res.type(OutputFlag::TYPE_ARR);
  781. return res;
  782. }