curseSplitOutput.cpp 29 KB

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