Panel.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. htop - Panel.c
  3. (C) 2004-2011 Hisham H. Muhammad
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "config.h" // IWYU pragma: keep
  8. #include "Panel.h"
  9. #include <assert.h>
  10. #include <ctype.h>
  11. #include <stdbool.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <strings.h>
  15. #include "CRT.h"
  16. #include "ListItem.h"
  17. #include "Macros.h"
  18. #include "ProvideCurses.h"
  19. #include "RichString.h"
  20. #include "XUtils.h"
  21. const PanelClass Panel_class = {
  22. .super = {
  23. .extends = Class(Object),
  24. .delete = Panel_delete
  25. },
  26. .eventHandler = Panel_selectByTyping,
  27. };
  28. Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
  29. Panel* this;
  30. this = xMalloc(sizeof(Panel));
  31. Object_setClass(this, Class(Panel));
  32. Panel_init(this, x, y, w, h, type, owner, fuBar);
  33. return this;
  34. }
  35. void Panel_delete(Object* cast) {
  36. Panel* this = (Panel*)cast;
  37. Panel_done(this);
  38. free(this);
  39. }
  40. void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
  41. this->x = x;
  42. this->y = y;
  43. this->w = w;
  44. this->h = h;
  45. this->cursorX = 0;
  46. this->cursorY = 0;
  47. this->eventHandlerState = NULL;
  48. this->items = Vector_new(type, owner, DEFAULT_SIZE);
  49. this->scrollV = 0;
  50. this->scrollH = 0;
  51. this->selected = 0;
  52. this->oldSelected = 0;
  53. this->selectedLen = 0;
  54. this->needsRedraw = true;
  55. this->cursorOn = false;
  56. this->wasFocus = false;
  57. RichString_beginAllocated(this->header);
  58. this->defaultBar = fuBar;
  59. this->currentBar = fuBar;
  60. this->selectionColorId = PANEL_SELECTION_FOCUS;
  61. }
  62. void Panel_done(Panel* this) {
  63. assert (this != NULL);
  64. free(this->eventHandlerState);
  65. Vector_delete(this->items);
  66. FunctionBar_delete(this->defaultBar);
  67. RichString_delete(&this->header);
  68. }
  69. void Panel_setCursorToSelection(Panel* this) {
  70. this->cursorY = this->y + this->selected - this->scrollV + 1;
  71. this->cursorX = this->x + this->selectedLen - this->scrollH;
  72. }
  73. void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
  74. this->selectionColorId = colorId;
  75. }
  76. inline void Panel_setHeader(Panel* this, const char* header) {
  77. RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
  78. this->needsRedraw = true;
  79. }
  80. void Panel_move(Panel* this, int x, int y) {
  81. assert (this != NULL);
  82. this->x = x;
  83. this->y = y;
  84. this->needsRedraw = true;
  85. }
  86. void Panel_resize(Panel* this, int w, int h) {
  87. assert (this != NULL);
  88. this->w = w;
  89. this->h = h;
  90. this->needsRedraw = true;
  91. }
  92. void Panel_prune(Panel* this) {
  93. assert (this != NULL);
  94. Vector_prune(this->items);
  95. this->scrollV = 0;
  96. this->selected = 0;
  97. this->oldSelected = 0;
  98. this->needsRedraw = true;
  99. }
  100. void Panel_add(Panel* this, Object* o) {
  101. assert (this != NULL);
  102. Vector_add(this->items, o);
  103. this->needsRedraw = true;
  104. }
  105. void Panel_insert(Panel* this, int i, Object* o) {
  106. assert (this != NULL);
  107. Vector_insert(this->items, i, o);
  108. this->needsRedraw = true;
  109. }
  110. void Panel_set(Panel* this, int i, Object* o) {
  111. assert (this != NULL);
  112. Vector_set(this->items, i, o);
  113. }
  114. Object* Panel_get(Panel* this, int i) {
  115. assert (this != NULL);
  116. return Vector_get(this->items, i);
  117. }
  118. Object* Panel_remove(Panel* this, int i) {
  119. assert (this != NULL);
  120. this->needsRedraw = true;
  121. Object* removed = Vector_remove(this->items, i);
  122. if (this->selected > 0 && this->selected >= Vector_size(this->items)) {
  123. this->selected--;
  124. }
  125. return removed;
  126. }
  127. Object* Panel_getSelected(Panel* this) {
  128. assert (this != NULL);
  129. if (Vector_size(this->items) > 0) {
  130. return Vector_get(this->items, this->selected);
  131. } else {
  132. return NULL;
  133. }
  134. }
  135. void Panel_moveSelectedUp(Panel* this) {
  136. assert (this != NULL);
  137. Vector_moveUp(this->items, this->selected);
  138. if (this->selected > 0) {
  139. this->selected--;
  140. }
  141. }
  142. void Panel_moveSelectedDown(Panel* this) {
  143. assert (this != NULL);
  144. Vector_moveDown(this->items, this->selected);
  145. if (this->selected + 1 < Vector_size(this->items)) {
  146. this->selected++;
  147. }
  148. }
  149. int Panel_getSelectedIndex(const Panel* this) {
  150. assert (this != NULL);
  151. return this->selected;
  152. }
  153. int Panel_size(const Panel* this) {
  154. assert (this != NULL);
  155. return Vector_size(this->items);
  156. }
  157. void Panel_setSelected(Panel* this, int selected) {
  158. assert (this != NULL);
  159. int size = Vector_size(this->items);
  160. if (selected >= size) {
  161. selected = size - 1;
  162. }
  163. if (selected < 0) {
  164. selected = 0;
  165. }
  166. this->selected = selected;
  167. if (Panel_eventHandlerFn(this)) {
  168. Panel_eventHandler(this, EVENT_SET_SELECTED);
  169. }
  170. }
  171. void Panel_splice(Panel* this, Vector* from) {
  172. assert (this != NULL);
  173. assert (from != NULL);
  174. Vector_splice(this->items, from);
  175. this->needsRedraw = true;
  176. }
  177. void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
  178. assert (this != NULL);
  179. int size = Vector_size(this->items);
  180. int scrollH = this->scrollH;
  181. int y = this->y;
  182. int x = this->x;
  183. int h = this->h;
  184. if (hideFunctionBar)
  185. h++;
  186. const int header_attr = focus
  187. ? CRT_colors[PANEL_HEADER_FOCUS]
  188. : CRT_colors[PANEL_HEADER_UNFOCUS];
  189. if (force_redraw) {
  190. if (Panel_printHeaderFn(this))
  191. Panel_printHeader(this);
  192. else
  193. RichString_setAttr(&this->header, header_attr);
  194. }
  195. int headerLen = RichString_sizeVal(this->header);
  196. if (headerLen > 0) {
  197. attrset(header_attr);
  198. mvhline(y, x, ' ', this->w);
  199. if (scrollH < headerLen) {
  200. RichString_printoffnVal(this->header, y, x, scrollH,
  201. MINIMUM(headerLen - scrollH, this->w));
  202. }
  203. attrset(CRT_colors[RESET_COLOR]);
  204. y++;
  205. h--;
  206. }
  207. // ensure scroll area is on screen
  208. if (this->scrollV < 0) {
  209. this->scrollV = 0;
  210. this->needsRedraw = true;
  211. } else if (this->scrollV > size - h) {
  212. this->scrollV = MAXIMUM(size - h, 0);
  213. this->needsRedraw = true;
  214. }
  215. // ensure selection is on screen
  216. if (this->selected < this->scrollV) {
  217. this->scrollV = this->selected;
  218. this->needsRedraw = true;
  219. } else if (this->selected >= this->scrollV + h) {
  220. this->scrollV = this->selected - h + 1;
  221. this->needsRedraw = true;
  222. }
  223. int first = this->scrollV;
  224. int upTo = MINIMUM(first + h, size);
  225. int selectionColor = focus
  226. ? CRT_colors[this->selectionColorId]
  227. : CRT_colors[PANEL_SELECTION_UNFOCUS];
  228. if (this->needsRedraw || force_redraw) {
  229. int line = 0;
  230. for (int i = first; line < h && i < upTo; i++) {
  231. const Object* itemObj = Vector_get(this->items, i);
  232. RichString_begin(item);
  233. Object_display(itemObj, &item);
  234. int itemLen = RichString_sizeVal(item);
  235. int amt = MINIMUM(itemLen - scrollH, this->w);
  236. if (highlightSelected && i == this->selected) {
  237. item.highlightAttr = selectionColor;
  238. }
  239. if (item.highlightAttr) {
  240. attrset(item.highlightAttr);
  241. RichString_setAttr(&item, item.highlightAttr);
  242. this->selectedLen = itemLen;
  243. }
  244. mvhline(y + line, x, ' ', this->w);
  245. if (amt > 0)
  246. RichString_printoffnVal(item, y + line, x, scrollH, amt);
  247. if (item.highlightAttr)
  248. attrset(CRT_colors[RESET_COLOR]);
  249. RichString_delete(&item);
  250. line++;
  251. }
  252. while (line < h) {
  253. mvhline(y + line, x, ' ', this->w);
  254. line++;
  255. }
  256. } else {
  257. const Object* oldObj = Vector_get(this->items, this->oldSelected);
  258. RichString_begin(old);
  259. Object_display(oldObj, &old);
  260. int oldLen = RichString_sizeVal(old);
  261. const Object* newObj = Vector_get(this->items, this->selected);
  262. RichString_begin(new);
  263. Object_display(newObj, &new);
  264. int newLen = RichString_sizeVal(new);
  265. this->selectedLen = newLen;
  266. mvhline(y + this->oldSelected - first, x + 0, ' ', this->w);
  267. if (scrollH < oldLen)
  268. RichString_printoffnVal(old, y + this->oldSelected - first, x,
  269. scrollH, MINIMUM(oldLen - scrollH, this->w));
  270. attrset(selectionColor);
  271. mvhline(y + this->selected - first, x + 0, ' ', this->w);
  272. RichString_setAttr(&new, selectionColor);
  273. if (scrollH < newLen)
  274. RichString_printoffnVal(new, y + this->selected - first, x,
  275. scrollH, MINIMUM(newLen - scrollH, this->w));
  276. attrset(CRT_colors[RESET_COLOR]);
  277. RichString_delete(&new);
  278. RichString_delete(&old);
  279. }
  280. if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
  281. if (Panel_drawFunctionBarFn(this))
  282. Panel_drawFunctionBar(this, hideFunctionBar);
  283. else if (!hideFunctionBar)
  284. FunctionBar_draw(this->currentBar);
  285. }
  286. this->oldSelected = this->selected;
  287. this->wasFocus = focus;
  288. this->needsRedraw = false;
  289. }
  290. static int Panel_headerHeight(const Panel* this) {
  291. return RichString_sizeVal(this->header) > 0 ? 1 : 0;
  292. }
  293. bool Panel_onKey(Panel* this, int key) {
  294. assert (this != NULL);
  295. const int size = Vector_size(this->items);
  296. #define PANEL_SCROLL(amount) \
  297. do { \
  298. this->selected += (amount); \
  299. this->scrollV = CLAMP(this->scrollV + (amount), 0, MAXIMUM(0, (size - this->h - Panel_headerHeight(this)))); \
  300. this->needsRedraw = true; \
  301. } while (0)
  302. switch (key) {
  303. case KEY_DOWN:
  304. case KEY_CTRL('N'):
  305. #ifdef KEY_C_DOWN
  306. case KEY_C_DOWN:
  307. #endif
  308. this->selected++;
  309. break;
  310. case KEY_UP:
  311. case KEY_CTRL('P'):
  312. #ifdef KEY_C_UP
  313. case KEY_C_UP:
  314. #endif
  315. this->selected--;
  316. break;
  317. case KEY_LEFT:
  318. case KEY_CTRL('B'):
  319. if (this->scrollH > 0) {
  320. this->scrollH -= MAXIMUM(CRT_scrollHAmount, 0);
  321. this->needsRedraw = true;
  322. }
  323. break;
  324. case KEY_RIGHT:
  325. case KEY_CTRL('F'):
  326. this->scrollH += CRT_scrollHAmount;
  327. this->needsRedraw = true;
  328. break;
  329. case KEY_PPAGE:
  330. PANEL_SCROLL(-(this->h - Panel_headerHeight(this)));
  331. break;
  332. case KEY_NPAGE:
  333. PANEL_SCROLL(+(this->h - Panel_headerHeight(this)));
  334. break;
  335. case KEY_WHEELUP:
  336. PANEL_SCROLL(-CRT_scrollWheelVAmount);
  337. break;
  338. case KEY_WHEELDOWN:
  339. PANEL_SCROLL(+CRT_scrollWheelVAmount);
  340. break;
  341. case KEY_HOME:
  342. this->selected = 0;
  343. break;
  344. case KEY_END:
  345. this->selected = size - 1;
  346. break;
  347. case KEY_CTRL('A'):
  348. case '^':
  349. this->scrollH = 0;
  350. this->needsRedraw = true;
  351. break;
  352. case KEY_CTRL('E'):
  353. case '$':
  354. this->scrollH = MAXIMUM(this->selectedLen - this->w, 0);
  355. this->needsRedraw = true;
  356. break;
  357. default:
  358. return false;
  359. }
  360. #undef PANEL_SCROLL
  361. // ensure selection within bounds
  362. if (this->selected < 0 || size == 0) {
  363. this->selected = 0;
  364. this->needsRedraw = true;
  365. } else if (this->selected >= size) {
  366. this->selected = size - 1;
  367. this->needsRedraw = true;
  368. }
  369. return true;
  370. }
  371. HandlerResult Panel_selectByTyping(Panel* this, int ch) {
  372. int size = Panel_size(this);
  373. if (ch == '#')
  374. return IGNORED;
  375. if (!this->eventHandlerState)
  376. this->eventHandlerState = xCalloc(100, sizeof(char));
  377. char* buffer = this->eventHandlerState;
  378. if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) {
  379. int len = strlen(buffer);
  380. if (!len) {
  381. if ('/' == ch) {
  382. ch = '\001';
  383. } else if ('q' == ch) {
  384. return BREAK_LOOP;
  385. }
  386. } else if (1 == len && '\001' == buffer[0]) {
  387. len--;
  388. }
  389. if (len < 99) {
  390. buffer[len] = (char) ch;
  391. buffer[len + 1] = '\0';
  392. }
  393. for (int try = 0; try < 2; try++) {
  394. len = strlen(buffer);
  395. for (int i = 0; i < size; i++) {
  396. const char* cur = ((ListItem*) Panel_get(this, i))->value;
  397. while (*cur == ' ')
  398. cur++;
  399. if (strncasecmp(cur, buffer, len) == 0) {
  400. Panel_setSelected(this, i);
  401. return HANDLED;
  402. }
  403. }
  404. // if current word did not match,
  405. // retry considering the character the start of a new word.
  406. buffer[0] = (char) ch;
  407. buffer[1] = '\0';
  408. }
  409. return HANDLED;
  410. } else if (ch != ERR) {
  411. buffer[0] = '\0';
  412. }
  413. if (ch == 13) {
  414. return BREAK_LOOP;
  415. }
  416. return IGNORED;
  417. }
  418. int Panel_getCh(Panel* this) {
  419. if (this->cursorOn) {
  420. move(this->cursorY, this->cursorX);
  421. curs_set(1);
  422. } else {
  423. curs_set(0);
  424. }
  425. #ifdef HAVE_SET_ESCDELAY
  426. set_escdelay(25);
  427. #endif
  428. return getch();
  429. }