htop/Panel.c

508 lines
13 KiB
C
Raw Normal View History

2006-03-04 18:16:49 +00:00
/*
2006-05-30 13:47:28 +00:00
htop - Panel.c
2011-05-26 16:35:07 +00:00
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
2006-03-04 18:16:49 +00:00
in the source distribution for its full text.
*/
2006-05-30 13:47:28 +00:00
#include "Panel.h"
2011-12-26 21:35:57 +00:00
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
2006-03-04 18:16:49 +00:00
#include "CRT.h"
#include "ListItem.h"
#include "Macros.h"
#include "ProvideCurses.h"
#include "RichString.h"
2020-10-14 18:21:09 +00:00
#include "XUtils.h"
2006-03-04 18:16:49 +00:00
2011-12-26 21:35:57 +00:00
2020-10-05 11:19:50 +00:00
const PanelClass Panel_class = {
.super = {
.extends = Class(Object),
.delete = Panel_delete
},
2015-03-25 02:12:43 +00:00
.eventHandler = Panel_selectByTyping,
};
Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
2006-05-30 13:47:28 +00:00
Panel* this;
2016-02-02 14:53:02 +00:00
this = xMalloc(sizeof(Panel));
Object_setClass(this, Class(Panel));
2015-03-23 18:26:56 +00:00
Panel_init(this, x, y, w, h, type, owner, fuBar);
2006-03-04 18:16:49 +00:00
return this;
}
2006-05-30 13:47:28 +00:00
void Panel_delete(Object* cast) {
Panel* this = (Panel*)cast;
Panel_done(this);
2006-03-04 18:16:49 +00:00
free(this);
}
void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
2006-03-04 18:16:49 +00:00
this->x = x;
this->y = y;
this->w = w;
this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE);
2006-03-04 18:16:49 +00:00
this->scrollV = 0;
this->scrollH = 0;
this->selected = 0;
this->oldSelected = 0;
this->selectedLen = 0;
2006-03-04 18:16:49 +00:00
this->needsRedraw = true;
this->cursorOn = false;
this->wasFocus = false;
RichString_beginAllocated(this->header);
2015-03-23 18:26:56 +00:00
this->defaultBar = fuBar;
this->currentBar = fuBar;
this->selectionColorId = PANEL_SELECTION_FOCUS;
2006-03-04 18:16:49 +00:00
}
2006-05-30 13:47:28 +00:00
void Panel_done(Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
free(this->eventHandlerState);
Vector_delete(this->items);
2015-03-23 18:26:56 +00:00
FunctionBar_delete(this->defaultBar);
RichString_delete(&this->header);
2006-03-04 18:16:49 +00:00
}
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
this->selectionColorId = colorId;
}
2010-02-25 01:43:18 +00:00
inline void Panel_setHeader(Panel* this, const char* header) {
RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
this->needsRedraw = true;
2006-03-04 18:16:49 +00:00
}
2006-05-30 13:47:28 +00:00
void Panel_move(Panel* this, int x, int y) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
this->x = x;
this->y = y;
this->needsRedraw = true;
}
2006-05-30 13:47:28 +00:00
void Panel_resize(Panel* this, int w, int h) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
this->w = w;
this->h = h;
this->needsRedraw = true;
}
2006-05-30 13:47:28 +00:00
void Panel_prune(Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
Vector_prune(this->items);
2006-03-04 18:16:49 +00:00
this->scrollV = 0;
this->selected = 0;
this->oldSelected = 0;
this->needsRedraw = true;
}
2006-05-30 13:47:28 +00:00
void Panel_add(Panel* this, Object* o) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
Vector_add(this->items, o);
2006-03-04 18:16:49 +00:00
this->needsRedraw = true;
}
2006-05-30 13:47:28 +00:00
void Panel_insert(Panel* this, int i, Object* o) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
Vector_insert(this->items, i, o);
2006-03-04 18:16:49 +00:00
this->needsRedraw = true;
}
2006-05-30 13:47:28 +00:00
void Panel_set(Panel* this, int i, Object* o) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
Vector_set(this->items, i, o);
2006-03-04 18:16:49 +00:00
}
2006-05-30 13:47:28 +00:00
Object* Panel_get(Panel* this, int i) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
return Vector_get(this->items, i);
2006-03-04 18:16:49 +00:00
}
2006-05-30 13:47:28 +00:00
Object* Panel_remove(Panel* this, int i) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
this->needsRedraw = true;
Object* removed = Vector_remove(this->items, i);
2020-11-01 00:09:51 +00:00
if (this->selected > 0 && this->selected >= Vector_size(this->items)) {
2006-03-04 18:16:49 +00:00
this->selected--;
2020-11-01 00:09:51 +00:00
}
2006-03-04 18:16:49 +00:00
return removed;
}
2006-05-30 13:47:28 +00:00
Object* Panel_getSelected(Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
2020-11-01 00:09:51 +00:00
if (Vector_size(this->items) > 0) {
2012-03-05 11:18:27 +00:00
return Vector_get(this->items, this->selected);
2020-11-01 00:09:51 +00:00
} else {
2012-03-05 11:18:27 +00:00
return NULL;
2020-11-01 00:09:51 +00:00
}
2006-03-04 18:16:49 +00:00
}
2006-05-30 13:47:28 +00:00
void Panel_moveSelectedUp(Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
Vector_moveUp(this->items, this->selected);
2020-11-01 00:09:51 +00:00
if (this->selected > 0) {
2006-03-04 18:16:49 +00:00
this->selected--;
2020-11-01 00:09:51 +00:00
}
2006-03-04 18:16:49 +00:00
}
2006-05-30 13:47:28 +00:00
void Panel_moveSelectedDown(Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
Vector_moveDown(this->items, this->selected);
2020-11-01 00:09:51 +00:00
if (this->selected + 1 < Vector_size(this->items)) {
2006-03-04 18:16:49 +00:00
this->selected++;
2020-11-01 00:09:51 +00:00
}
2006-03-04 18:16:49 +00:00
}
int Panel_getSelectedIndex(const Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
return this->selected;
}
int Panel_size(const Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
return Vector_size(this->items);
2006-03-04 18:16:49 +00:00
}
2006-05-30 13:47:28 +00:00
void Panel_setSelected(Panel* this, int selected) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
int size = Vector_size(this->items);
if (selected >= size) {
2015-11-02 15:33:22 +00:00
selected = size - 1;
}
2020-11-01 00:09:51 +00:00
if (selected < 0) {
selected = 0;
2020-11-01 00:09:51 +00:00
}
2006-03-04 18:16:49 +00:00
this->selected = selected;
if (Panel_eventHandlerFn(this)) {
2015-03-25 02:12:43 +00:00
Panel_eventHandler(this, EVENT_SET_SELECTED);
}
2006-03-04 18:16:49 +00:00
}
2020-10-31 22:28:02 +00:00
void Panel_splice(Panel* this, Vector* from) {
assert (this != NULL);
assert (from != NULL);
Vector_splice(this->items, from);
this->needsRedraw = true;
}
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
int size = Vector_size(this->items);
2006-03-04 18:16:49 +00:00
int scrollH = this->scrollH;
int y = this->y;
int x = this->x;
int h = this->h;
2006-03-04 18:16:49 +00:00
if (hideFunctionBar)
h++;
const int header_attr = focus
? CRT_colors[PANEL_HEADER_FOCUS]
: CRT_colors[PANEL_HEADER_UNFOCUS];
if (force_redraw) {
if (Panel_printHeaderFn(this))
Panel_printHeader(this);
else
RichString_setAttr(&this->header, header_attr);
}
int headerLen = RichString_sizeVal(this->header);
if (headerLen > 0) {
attrset(header_attr);
2006-03-04 18:16:49 +00:00
mvhline(y, x, ' ', this->w);
if (scrollH < headerLen) {
RichString_printoffnVal(this->header, y, x, scrollH,
MINIMUM(headerLen - scrollH, this->w));
2006-03-04 18:16:49 +00:00
}
attrset(CRT_colors[RESET_COLOR]);
y++;
h--;
2006-03-04 18:16:49 +00:00
}
// ensure scroll area is on screen
if (this->scrollV < 0) {
this->scrollV = 0;
this->needsRedraw = true;
} else if (this->scrollV > size - h) {
this->scrollV = MAXIMUM(size - h, 0);
this->needsRedraw = true;
}
// ensure selection is on screen
if (this->selected < this->scrollV) {
this->scrollV = this->selected;
this->needsRedraw = true;
} else if (this->selected >= this->scrollV + h) {
this->scrollV = this->selected - h + 1;
this->needsRedraw = true;
}
int first = this->scrollV;
int upTo = MINIMUM(first + h, size);
int selectionColor = focus
? CRT_colors[this->selectionColorId]
2020-10-31 21:14:27 +00:00
: CRT_colors[PANEL_SELECTION_UNFOCUS];
2006-03-04 18:16:49 +00:00
if (this->needsRedraw || force_redraw) {
int line = 0;
2020-10-31 19:55:36 +00:00
for (int i = first; line < h && i < upTo; i++) {
const Object* itemObj = Vector_get(this->items, i);
RichString_begin(item);
Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item);
int amt = MINIMUM(itemLen - scrollH, this->w);
if (highlightSelected && i == this->selected) {
2020-10-31 01:56:16 +00:00
item.highlightAttr = selectionColor;
}
if (item.highlightAttr) {
attrset(item.highlightAttr);
RichString_setAttr(&item, item.highlightAttr);
this->selectedLen = itemLen;
2006-03-04 18:16:49 +00:00
}
mvhline(y + line, x, ' ', this->w);
2014-02-27 19:35:22 +00:00
if (amt > 0)
RichString_printoffnVal(item, y + line, x, scrollH, amt);
2020-10-31 01:56:16 +00:00
if (item.highlightAttr)
2014-02-27 19:35:22 +00:00
attrset(CRT_colors[RESET_COLOR]);
RichString_delete(&item);
line++;
}
while (line < h) {
mvhline(y + line, x, ' ', this->w);
line++;
2006-03-04 18:16:49 +00:00
}
} else {
const Object* oldObj = Vector_get(this->items, this->oldSelected);
RichString_begin(old);
Object_display(oldObj, &old);
int oldLen = RichString_sizeVal(old);
const Object* newObj = Vector_get(this->items, this->selected);
RichString_begin(new);
Object_display(newObj, &new);
int newLen = RichString_sizeVal(new);
this->selectedLen = newLen;
2020-10-31 22:28:02 +00:00
mvhline(y + this->oldSelected - first, x + 0, ' ', this->w);
if (scrollH < oldLen)
2020-10-31 22:28:02 +00:00
RichString_printoffnVal(old, y + this->oldSelected - first, x,
scrollH, MINIMUM(oldLen - scrollH, this->w));
attrset(selectionColor);
2020-10-31 22:28:02 +00:00
mvhline(y + this->selected - first, x + 0, ' ', this->w);
RichString_setAttr(&new, selectionColor);
if (scrollH < newLen)
2020-10-31 22:28:02 +00:00
RichString_printoffnVal(new, y + this->selected - first, x,
scrollH, MINIMUM(newLen - scrollH, this->w));
2006-03-04 18:16:49 +00:00
attrset(CRT_colors[RESET_COLOR]);
RichString_delete(&new);
RichString_delete(&old);
2006-03-04 18:16:49 +00:00
}
if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
if (Panel_drawFunctionBarFn(this))
Panel_drawFunctionBar(this, hideFunctionBar);
else if (!hideFunctionBar)
FunctionBar_draw(this->currentBar);
}
2006-03-04 18:16:49 +00:00
this->oldSelected = this->selected;
this->wasFocus = focus;
this->needsRedraw = false;
2006-03-04 18:16:49 +00:00
}
static int Panel_headerHeight(const Panel* this) {
return RichString_sizeVal(this->header) > 0 ? 1 : 0;
}
2009-06-02 04:51:23 +00:00
bool Panel_onKey(Panel* this, int key) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
2019-10-31 16:39:12 +00:00
const int size = Vector_size(this->items);
2020-11-21 16:03:17 +00:00
#define PANEL_SCROLL(amount) \
do { \
this->selected += (amount); \
this->scrollV = CLAMP(this->scrollV + (amount), 0, MAXIMUM(0, (size - this->h - Panel_headerHeight(this)))); \
this->needsRedraw = true; \
} while (0)
2020-11-21 16:03:17 +00:00
2006-03-04 18:16:49 +00:00
switch (key) {
case KEY_DOWN:
case KEY_CTRL('N'):
2009-06-02 04:51:23 +00:00
#ifdef KEY_C_DOWN
case KEY_C_DOWN:
2020-11-21 16:00:58 +00:00
#endif
this->selected++;
break;
2020-11-21 16:00:58 +00:00
case KEY_UP:
case KEY_CTRL('P'):
2009-06-02 04:51:23 +00:00
#ifdef KEY_C_UP
case KEY_C_UP:
2020-11-21 16:00:58 +00:00
#endif
this->selected--;
break;
2020-11-21 16:00:58 +00:00
2006-03-04 18:16:49 +00:00
case KEY_LEFT:
case KEY_CTRL('B'):
2006-03-04 18:16:49 +00:00
if (this->scrollH > 0) {
this->scrollH -= MAXIMUM(CRT_scrollHAmount, 0);
2006-03-04 18:16:49 +00:00
this->needsRedraw = true;
}
break;
2020-11-21 16:04:25 +00:00
2006-03-04 18:16:49 +00:00
case KEY_RIGHT:
case KEY_CTRL('F'):
2014-02-27 19:35:22 +00:00
this->scrollH += CRT_scrollHAmount;
2006-03-04 18:16:49 +00:00
this->needsRedraw = true;
break;
2020-11-21 16:04:25 +00:00
2006-03-04 18:16:49 +00:00
case KEY_PPAGE:
PANEL_SCROLL(-(this->h - Panel_headerHeight(this)));
break;
2020-11-21 16:04:25 +00:00
2006-03-04 18:16:49 +00:00
case KEY_NPAGE:
PANEL_SCROLL(+(this->h - Panel_headerHeight(this)));
break;
2020-11-21 16:04:25 +00:00
case KEY_WHEELUP:
PANEL_SCROLL(-CRT_scrollWheelVAmount);
break;
2020-11-21 16:04:25 +00:00
case KEY_WHEELDOWN:
PANEL_SCROLL(+CRT_scrollWheelVAmount);
break;
2020-11-21 16:04:25 +00:00
2006-03-04 18:16:49 +00:00
case KEY_HOME:
this->selected = 0;
break;
2020-11-21 16:04:25 +00:00
2006-03-04 18:16:49 +00:00
case KEY_END:
this->selected = size - 1;
break;
2020-11-21 16:04:25 +00:00
case KEY_CTRL('A'):
case '^':
this->scrollH = 0;
this->needsRedraw = true;
break;
case KEY_CTRL('E'):
case '$':
this->scrollH = MAXIMUM(this->selectedLen - this->w, 0);
this->needsRedraw = true;
break;
default:
return false;
}
#undef PANEL_SCROLL
2020-11-21 16:03:17 +00:00
// ensure selection within bounds
if (this->selected < 0 || size == 0) {
this->selected = 0;
this->needsRedraw = true;
2019-10-31 16:39:12 +00:00
} else if (this->selected >= size) {
this->selected = size - 1;
this->needsRedraw = true;
2006-03-04 18:16:49 +00:00
}
2020-11-21 16:04:25 +00:00
return true;
2006-03-04 18:16:49 +00:00
}
HandlerResult Panel_selectByTyping(Panel* this, int ch) {
int size = Panel_size(this);
2020-11-21 16:04:25 +00:00
if (!this->eventHandlerState)
2016-02-02 14:53:02 +00:00
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
2020-11-21 17:02:39 +00:00
if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) {
int len = strlen(buffer);
2020-11-21 16:04:05 +00:00
if (!len) {
if ('/' == ch) {
ch = '\001';
} else if ('q' == ch) {
return BREAK_LOOP;
}
} else if (1 == len && '\001' == buffer[0]) {
len--;
}
if (len < 99) {
buffer[len] = (char) ch;
2021-07-14 17:18:27 +00:00
buffer[len + 1] = '\0';
}
2020-11-21 16:04:05 +00:00
for (int try = 0; try < 2; try++) {
len = strlen(buffer);
for (int i = 0; i < size; i++) {
const char* cur = ((ListItem*) Panel_get(this, i))->value;
while (*cur == ' ') cur++;
if (strncasecmp(cur, buffer, len) == 0) {
Panel_setSelected(this, i);
return HANDLED;
}
}
2020-11-21 16:04:25 +00:00
// if current word did not match,
// retry considering the character the start of a new word.
buffer[0] = (char) ch;
buffer[1] = '\0';
}
2020-11-21 16:04:25 +00:00
return HANDLED;
} else if (ch != ERR) {
buffer[0] = '\0';
}
2020-11-21 16:04:25 +00:00
if (ch == 13) {
return BREAK_LOOP;
}
2020-11-21 16:04:25 +00:00
return IGNORED;
}
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
#ifdef HAVE_SET_ESCDELAY
set_escdelay(25);
#endif
return getch();
}