ScreensPanel.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. htop - ScreensPanel.c
  3. (C) 2004-2011 Hisham H. Muhammad
  4. (C) 2020-2023 htop dev team
  5. Released under the GNU GPLv2+, see the COPYING file
  6. in the source distribution for its full text.
  7. */
  8. #include "config.h" // IWYU pragma: keep
  9. #include "ScreensPanel.h"
  10. #include <assert.h>
  11. #include <ctype.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "AvailableColumnsPanel.h"
  15. #include "CRT.h"
  16. #include "FunctionBar.h"
  17. #include "Hashtable.h"
  18. #include "ProvideCurses.h"
  19. #include "Settings.h"
  20. #include "XUtils.h"
  21. static void ScreenListItem_delete(Object* cast) {
  22. ScreenListItem* this = (ScreenListItem*)cast;
  23. if (this->ss) {
  24. ScreenSettings_delete(this->ss);
  25. }
  26. ListItem_delete(cast);
  27. }
  28. ObjectClass ScreenListItem_class = {
  29. .extends = Class(ListItem),
  30. .display = ListItem_display,
  31. .delete = ScreenListItem_delete,
  32. .compare = ListItem_compare
  33. };
  34. ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) {
  35. ScreenListItem* this = AllocThis(ScreenListItem);
  36. ListItem_init((ListItem*)this, value, 0);
  37. this->ss = ss;
  38. return this;
  39. }
  40. static const char* const ScreensFunctions[] = {" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
  41. static const char* const DynamicFunctions[] = {" ", "Rename", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
  42. static void ScreensPanel_delete(Object* object) {
  43. Panel* super = (Panel*) object;
  44. /* do not delete screen settings still in use */
  45. int n = Panel_size(super);
  46. for (int i = 0; i < n; i++) {
  47. ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
  48. item->ss = NULL;
  49. }
  50. Panel_delete(object);
  51. }
  52. static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
  53. ScreensPanel* const this = (ScreensPanel*) super;
  54. if (ch >= 32 && ch < 127 && ch != '=') {
  55. if (this->cursor < SCREEN_NAME_LEN - 1) {
  56. this->buffer[this->cursor] = (char)ch;
  57. this->cursor++;
  58. super->selectedLen = strlen(this->buffer);
  59. Panel_setCursorToSelection(super);
  60. }
  61. return HANDLED;
  62. }
  63. switch (ch) {
  64. case 127:
  65. case KEY_BACKSPACE:
  66. if (this->cursor > 0) {
  67. this->cursor--;
  68. this->buffer[this->cursor] = '\0';
  69. super->selectedLen = strlen(this->buffer);
  70. Panel_setCursorToSelection(super);
  71. }
  72. break;
  73. case '\n':
  74. case '\r':
  75. case KEY_ENTER: {
  76. ListItem* item = (ListItem*) Panel_getSelected(super);
  77. if (!item)
  78. break;
  79. assert(item == this->renamingItem);
  80. free(this->saved);
  81. item->value = xStrdup(this->buffer);
  82. this->renamingItem = NULL;
  83. super->cursorOn = false;
  84. Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
  85. ScreensPanel_update(super);
  86. break;
  87. }
  88. case 27: { // Esc
  89. ListItem* item = (ListItem*) Panel_getSelected(super);
  90. if (!item)
  91. break;
  92. assert(item == this->renamingItem);
  93. item->value = this->saved;
  94. this->renamingItem = NULL;
  95. super->cursorOn = false;
  96. Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
  97. break;
  98. }
  99. }
  100. return HANDLED;
  101. }
  102. static void startRenaming(Panel* super) {
  103. ScreensPanel* const this = (ScreensPanel*) super;
  104. ListItem* item = (ListItem*) Panel_getSelected(super);
  105. if (item == NULL)
  106. return;
  107. this->renamingItem = item;
  108. super->cursorOn = true;
  109. char* name = item->value;
  110. this->saved = name;
  111. strncpy(this->buffer, name, SCREEN_NAME_LEN);
  112. this->buffer[SCREEN_NAME_LEN] = '\0';
  113. this->cursor = strlen(this->buffer);
  114. item->value = this->buffer;
  115. Panel_setSelectionColor(super, PANEL_EDIT);
  116. super->selectedLen = strlen(this->buffer);
  117. Panel_setCursorToSelection(super);
  118. }
  119. static void rebuildSettingsArray(Panel* super, int selected) {
  120. ScreensPanel* const this = (ScreensPanel*) super;
  121. int n = Panel_size(super);
  122. free(this->settings->screens);
  123. this->settings->screens = xMallocArray(n + 1, sizeof(ScreenSettings*));
  124. this->settings->screens[n] = NULL;
  125. for (int i = 0; i < n; i++) {
  126. ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
  127. this->settings->screens[i] = item->ss;
  128. }
  129. this->settings->nScreens = n;
  130. /* ensure selection is in valid range */
  131. if (selected > n - 1)
  132. selected = n - 1;
  133. else if (selected < 0)
  134. selected = 0;
  135. this->settings->ssIndex = selected;
  136. this->settings->ss = this->settings->screens[selected];
  137. }
  138. static void addNewScreen(Panel* super) {
  139. ScreensPanel* const this = (ScreensPanel*) super;
  140. const char* name = "New";
  141. ScreenSettings* ss = Settings_newScreen(this->settings, &(const ScreenDefaults) { .name = name, .columns = "PID Command", .sortKey = "PID" });
  142. ScreenListItem* item = ScreenListItem_new(name, ss);
  143. int idx = Panel_getSelectedIndex(super);
  144. Panel_insert(super, idx + 1, (Object*) item);
  145. Panel_setSelected(super, idx + 1);
  146. }
  147. static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) {
  148. ScreensPanel* const this = (ScreensPanel*) super;
  149. int selected = Panel_getSelectedIndex(super);
  150. ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super);
  151. bool shouldRebuildArray = false;
  152. HandlerResult result = IGNORED;
  153. switch (ch) {
  154. case '\n':
  155. case '\r':
  156. case KEY_ENTER:
  157. case KEY_MOUSE:
  158. case KEY_RECLICK: {
  159. this->moving = !this->moving;
  160. Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
  161. ListItem* item = (ListItem*) Panel_getSelected(super);
  162. if (item)
  163. item->moving = this->moving;
  164. result = HANDLED;
  165. break;
  166. }
  167. case EVENT_SET_SELECTED:
  168. result = HANDLED;
  169. break;
  170. case KEY_NPAGE:
  171. case KEY_PPAGE:
  172. case KEY_HOME:
  173. case KEY_END:
  174. Panel_onKey(super, ch);
  175. break;
  176. case KEY_F(2):
  177. case KEY_CTRL('R'):
  178. startRenaming(super);
  179. result = HANDLED;
  180. break;
  181. case KEY_F(5):
  182. case KEY_CTRL('N'):
  183. if (this->settings->dynamicScreens)
  184. break;
  185. addNewScreen(super);
  186. startRenaming(super);
  187. shouldRebuildArray = true;
  188. result = HANDLED;
  189. break;
  190. case KEY_UP:
  191. if (!this->moving) {
  192. Panel_onKey(super, ch);
  193. break;
  194. }
  195. /* FALLTHRU */
  196. case KEY_F(7):
  197. case '[':
  198. case '-':
  199. Panel_moveSelectedUp(super);
  200. shouldRebuildArray = true;
  201. result = HANDLED;
  202. break;
  203. case KEY_DOWN:
  204. if (!this->moving) {
  205. Panel_onKey(super, ch);
  206. break;
  207. }
  208. /* FALLTHRU */
  209. case KEY_F(8):
  210. case ']':
  211. case '+':
  212. Panel_moveSelectedDown(super);
  213. shouldRebuildArray = true;
  214. result = HANDLED;
  215. break;
  216. case KEY_F(9):
  217. //case KEY_DC:
  218. if (Panel_size(super) > 1)
  219. Panel_remove(super, selected);
  220. shouldRebuildArray = true;
  221. result = HANDLED;
  222. break;
  223. default:
  224. if (ch < 255 && isalpha(ch))
  225. result = Panel_selectByTyping(super, ch);
  226. if (result == BREAK_LOOP)
  227. result = IGNORED;
  228. break;
  229. }
  230. ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super);
  231. if (newFocus && oldFocus != newFocus) {
  232. Hashtable* dynamicColumns = this->settings->dynamicColumns;
  233. ColumnsPanel_fill(this->columns, newFocus->ss, dynamicColumns);
  234. AvailableColumnsPanel_fill(this->availableColumns, newFocus->ss->dynamic, dynamicColumns);
  235. result = HANDLED;
  236. }
  237. if (shouldRebuildArray)
  238. rebuildSettingsArray(super, selected);
  239. if (result == HANDLED)
  240. ScreensPanel_update(super);
  241. return result;
  242. }
  243. static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) {
  244. ScreensPanel* const this = (ScreensPanel*) super;
  245. if (this->renamingItem) {
  246. return ScreensPanel_eventHandlerRenaming(super, ch);
  247. } else {
  248. return ScreensPanel_eventHandlerNormal(super, ch);
  249. }
  250. }
  251. PanelClass ScreensPanel_class = {
  252. .super = {
  253. .extends = Class(Panel),
  254. .delete = ScreensPanel_delete
  255. },
  256. .eventHandler = ScreensPanel_eventHandler
  257. };
  258. ScreensPanel* ScreensPanel_new(Settings* settings) {
  259. ScreensPanel* this = AllocThis(ScreensPanel);
  260. Panel* super = (Panel*) this;
  261. Hashtable* columns = settings->dynamicColumns;
  262. FunctionBar* fuBar = FunctionBar_new(settings->dynamicScreens ? DynamicFunctions : ScreensFunctions, NULL, NULL);
  263. Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
  264. this->settings = settings;
  265. this->columns = ColumnsPanel_new(settings->screens[0], columns, &(settings->changed));
  266. this->availableColumns = AvailableColumnsPanel_new((Panel*) this->columns, columns);
  267. this->moving = false;
  268. this->renamingItem = NULL;
  269. super->cursorOn = false;
  270. this->cursor = 0;
  271. Panel_setHeader(super, "Screens");
  272. for (unsigned int i = 0; i < settings->nScreens; i++) {
  273. ScreenSettings* ss = settings->screens[i];
  274. char* name = ss->heading;
  275. Panel_add(super, (Object*) ScreenListItem_new(name, ss));
  276. }
  277. return this;
  278. }
  279. void ScreensPanel_update(Panel* super) {
  280. ScreensPanel* this = (ScreensPanel*) super;
  281. int size = Panel_size(super);
  282. this->settings->changed = true;
  283. this->settings->lastUpdate++;
  284. this->settings->screens = xReallocArray(this->settings->screens, size + 1, sizeof(ScreenSettings*));
  285. for (int i = 0; i < size; i++) {
  286. ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
  287. ScreenSettings* ss = item->ss;
  288. free_and_xStrdup(&ss->heading, ((ListItem*) item)->value);
  289. this->settings->screens[i] = ss;
  290. }
  291. this->settings->screens[size] = NULL;
  292. }