curseOutput.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /**
  2. * curseOutput.cpp for jsonstroller
  3. *
  4. * Author: isundil <isundill@gmail.com>
  5. **/
  6. #include <iostream>
  7. #include <sys/ioctl.h>
  8. #include <algorithm>
  9. #include <unistd.h>
  10. #include <signal.h>
  11. #include <string.h>
  12. #include "searchPattern.hh"
  13. #include "curseOutput.hh"
  14. #include "jsonPrimitive.hh"
  15. #include "jsonObject.hh"
  16. #include "jsonArray.hh"
  17. #include "except.hh"
  18. #include "streamConsumer.hh"
  19. static CurseOutput *runningInst = nullptr;
  20. CurseOutput::CurseOutput(const Params &p): params(p)
  21. {
  22. runningInst = this;
  23. }
  24. CurseOutput::~CurseOutput()
  25. {
  26. runningInst = nullptr;
  27. }
  28. void CurseOutput::loop()
  29. {
  30. breakLoop = false;
  31. do
  32. {
  33. while (!redraw()) //TODO opti going down
  34. ;
  35. } while(readInput());
  36. }
  37. bool CurseOutput::onsig(int signo)
  38. {
  39. struct winsize size;
  40. switch (signo)
  41. {
  42. case SIGWINCH:
  43. if (ioctl(fileno(screen_fd ? screen_fd : stdout), TIOCGWINSZ, &size) == 0)
  44. resize_term(size.ws_row, size.ws_col);
  45. clear();
  46. while (!redraw());
  47. break;
  48. case SIGKILL:
  49. case SIGINT:
  50. case SIGTERM:
  51. breakLoop = true;
  52. break;
  53. default:
  54. return false;
  55. }
  56. return true;
  57. }
  58. void _resizeFnc(int signo)
  59. {
  60. if (!runningInst)
  61. return;
  62. runningInst->onsig(signo);
  63. }
  64. /**
  65. * Read input and expect signal
  66. * @Return true on:
  67. * - Windows resized
  68. * - Key press and need redraw
  69. * false on:
  70. * - exit signal
  71. **/
  72. inputResult CurseOutput::readInput()
  73. {
  74. while (!breakLoop)
  75. {
  76. inputResult r = evalKey(InputSequence::read());
  77. if (r != inputResult::nextInput)
  78. return r;
  79. }
  80. return inputResult::quit;
  81. }
  82. inputResult CurseOutput::evalKey(const InputSequence &c)
  83. {
  84. const std::string key = c.key();
  85. if (key == "Q")
  86. return inputResult::quit;
  87. else if (key == "K" || key == "KEY_UP")
  88. return selectUp();
  89. else if (key == "J" || key == "KEY_DOWN")
  90. return selectDown();
  91. else if (key == "KEY_PPAGE")
  92. return selectPUp();
  93. else if (key == "KEY_NPAGE")
  94. return selectPDown();
  95. else if (key == "L" || key == "KEY_RIGHT")
  96. return expandSelection();
  97. else if (key == "H" || key == "KEY_LEFT")
  98. return collapseSelection();
  99. else if (key == "/")
  100. return initSearch();
  101. else if (key == "N")
  102. return nextResult();
  103. else if (key == "^W-W")
  104. return changeWindow(1, true);
  105. else if (key == "^W-KEY_RIGHT" || key == "^W-L")
  106. return changeWindow(1, false);
  107. else if (key == "^W-KEY_LEFT" || key == "^W-H")
  108. return changeWindow(-1, false);
  109. return inputResult::nextInput;
  110. }
  111. bool CurseOutput::redraw(const std::string &errorMsg)
  112. {
  113. bool result = redraw();
  114. writeBottomLine(errorMsg, OutputFlag::SPECIAL_ERROR);
  115. return result;
  116. }
  117. bool CurseOutput::writeKey(const std::string &key, const size_t keylen, const std::string &after, std::pair<int, int> &cursor, const std::pair<unsigned int, unsigned int> &maxSize, OutputFlag flags)
  118. {
  119. return writeKey(key, keylen, after, after.size(), cursor, maxSize, flags);
  120. }
  121. unsigned int CurseOutput::write(const int &x, const int &y, JSonElement *item, unsigned int maxWidth, OutputFlag flags)
  122. {
  123. return write(x, y, item->stringify(), item->lazystrlen(), maxWidth, flags);
  124. }
  125. unsigned int CurseOutput::getNbLines(const size_t nbChar, unsigned int maxWidth)
  126. {
  127. double nLine = (double) nbChar / maxWidth;
  128. if (nLine == (unsigned int) nLine)
  129. return nLine;
  130. return nLine +1;
  131. }
  132. const std::pair<unsigned int, unsigned int> CurseOutput::getScreenSize() const
  133. {
  134. std::pair<int, int> bs;
  135. std::pair<int, int> sc;
  136. getmaxyx(stdscr, sc.second, sc.first);
  137. getbegyx(stdscr, bs.second, bs.first);
  138. sc.first -= bs.first;
  139. sc.second -= bs.second;
  140. return sc;
  141. }
  142. void CurseOutput::unfold(const JSonElement *item)
  143. {
  144. while (item->getParent())
  145. {
  146. collapsed.erase((const JSonContainer*)item->getParent());
  147. item = item->getParent();
  148. }
  149. }
  150. const SearchPattern *CurseOutput::inputSearch()
  151. {
  152. std::wstring buffer;
  153. bool abort = false;
  154. curs_set(true);
  155. wtimeout(stdscr, -1);
  156. while (!abort)
  157. {
  158. int c;
  159. writeBottomLine(L'/' +buffer, OutputFlag::SPECIAL_SEARCH);
  160. refresh();
  161. c = getwchar();
  162. if (c == L'\r')
  163. break;
  164. else if (c == L'\b' || c == 127)
  165. {
  166. if (!buffer.empty())
  167. buffer.pop_back();
  168. }
  169. else if (c == 27)
  170. abort = true;
  171. else
  172. buffer += c;
  173. }
  174. wtimeout(stdscr, 150);
  175. curs_set(false);
  176. {
  177. const size_t size = buffer.size();
  178. char bytesString[size * sizeof(wchar_t)];
  179. wcstombs(&bytesString[0], buffer.c_str(), sizeof(bytesString));
  180. std::string str;
  181. if (params.isIgnoringUnicode())
  182. str = bytesString;
  183. else
  184. str = StreamConsumer::extractUnicode(bytesString);
  185. return abort ? nullptr : new SearchPattern(str);
  186. }
  187. }
  188. void CurseOutput::writeBottomLine(const std::string &buffer, short color) const
  189. {
  190. const std::pair<unsigned int, unsigned int> screenSize = getScreenSize();
  191. const size_t bufsize = buffer.size();
  192. if (params.colorEnabled())
  193. attron(COLOR_PAIR(color));
  194. mvprintw(screenSize.second -1, 0, "%s%*c", buffer.c_str(), screenSize.first - bufsize, ' ');
  195. move(screenSize.second -1, bufsize);
  196. if (params.colorEnabled())
  197. attroff(COLOR_PAIR(color));
  198. }
  199. void CurseOutput::writeBottomLine(const std::wstring &buffer, short color) const
  200. {
  201. const std::pair<unsigned int, unsigned int> screenSize = getScreenSize();
  202. const size_t bufsize = buffer.size();
  203. if (params.colorEnabled())
  204. attron(COLOR_PAIR(color));
  205. mvprintw(screenSize.second -1, 0, "%S%*c", buffer.c_str(), screenSize.first - bufsize, ' ');
  206. move(screenSize.second -1, bufsize);
  207. if (params.colorEnabled())
  208. attroff(COLOR_PAIR(color));
  209. }