htop/Panel.c

447 lines
11 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, bool owner, const ObjectClass* type, 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->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->needsRedraw = true;
RichString_beginAllocated(this->header);
2015-03-23 18:26:56 +00:00
this->defaultBar = fuBar;
this->currentBar = fuBar;
this->selectionColor = CRT_colors[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_end(this->header);
2006-03-04 18:16:49 +00:00
}
void Panel_setSelectionColor(Panel* this, int color) {
this->selectionColor = color;
}
RichString* Panel_getHeader(Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
this->needsRedraw = true;
return &(this->header);
2006-03-04 18:16:49 +00:00
}
2010-02-25 01:43:18 +00:00
inline void Panel_setHeader(Panel* this, const char* header) {
RichString_write(&(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);
if (RichString_sizeVal(this->header) > 0)
2006-03-04 18:16:49 +00:00
h--;
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);
if (this->selected > 0 && this->selected >= Vector_size(this->items))
2006-03-04 18:16:49 +00:00
this->selected--;
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);
2012-03-05 11:18:27 +00:00
if (Vector_size(this->items) > 0)
return Vector_get(this->items, this->selected);
else
return NULL;
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);
2006-03-04 18:16:49 +00:00
if (this->selected > 0)
this->selected--;
}
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);
if (this->selected + 1 < Vector_size(this->items))
2006-03-04 18:16:49 +00:00
this->selected++;
}
2006-05-30 13:47:28 +00:00
int Panel_getSelectedIndex(Panel* this) {
2006-03-04 18:16:49 +00:00
assert (this != NULL);
return this->selected;
}
2009-06-02 04:51:23 +00:00
int Panel_size(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;
}
if (selected < 0)
selected = 0;
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
}
void Panel_splice(Panel *this, Vector* from) {
assert (this != NULL);
assert (from != NULL);
Vector_splice(this->items, from);
this->needsRedraw = true;
}
2006-05-30 13:47:28 +00:00
void Panel_draw(Panel* this, bool focus) {
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
int headerLen = RichString_sizeVal(this->header);
if (headerLen > 0) {
2006-03-04 18:16:49 +00:00
int attr = focus
? CRT_colors[PANEL_HEADER_FOCUS]
: CRT_colors[PANEL_HEADER_UNFOCUS];
attrset(attr);
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++;
}
// ensure scroll area is on screen
if (this->scrollV < 0) {
this->scrollV = 0;
this->needsRedraw = true;
} else if (this->scrollV >= size) {
this->scrollV = MAXIMUM(size - 1, 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
? this->selectionColor
: CRT_colors[PANEL_SELECTION_UNFOCUS];
2006-03-04 18:16:49 +00:00
if (this->needsRedraw) {
int line = 0;
for(int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i);
assert(itemObj); if(!itemObj) continue;
RichString_begin(item);
Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item);
int amt = MINIMUM(itemLen - scrollH, this->w);
2014-02-27 19:35:22 +00:00
bool selected = (i == this->selected);
if (selected) {
attrset(selectionColor);
RichString_setAttr(&item, selectionColor);
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);
2014-02-27 19:35:22 +00:00
if (selected)
attrset(CRT_colors[RESET_COLOR]);
RichString_end(item);
line++;
}
while (line < h) {
mvhline(y + line, x, ' ', this->w);
line++;
2006-03-04 18:16:49 +00:00
}
this->needsRedraw = false;
} else {
Object* oldObj = Vector_get(this->items, this->oldSelected);
assert(oldObj);
RichString_begin(old);
Object_display(oldObj, &old);
int oldLen = RichString_sizeVal(old);
Object* newObj = Vector_get(this->items, this->selected);
RichString_begin(new);
Object_display(newObj, &new);
int newLen = RichString_sizeVal(new);
this->selectedLen = newLen;
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w);
if (scrollH < oldLen)
RichString_printoffnVal(old, y+this->oldSelected - first, x,
scrollH, MINIMUM(oldLen - scrollH, this->w));
attrset(selectionColor);
mvhline(y+this->selected - first, x+0, ' ', this->w);
RichString_setAttr(&new, selectionColor);
if (scrollH < newLen)
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_end(new);
RichString_end(old);
2006-03-04 18:16:49 +00:00
}
this->oldSelected = this->selected;
move(0, 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
int size = Vector_size(this->items);
2006-03-04 18:16:49 +00:00
switch (key) {
case KEY_DOWN:
case KEY_CTRL('N'):
this->selected++;
break;
2006-03-04 18:16:49 +00:00
case KEY_UP:
case KEY_CTRL('P'):
this->selected--;
break;
2009-06-02 04:51:23 +00:00
#ifdef KEY_C_DOWN
case KEY_C_DOWN:
this->selected++;
break;
2009-06-02 04:51:23 +00:00
#endif
#ifdef KEY_C_UP
case KEY_C_UP:
this->selected--;
break;
2009-06-02 04:51:23 +00:00
#endif
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;
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;
2006-03-04 18:16:49 +00:00
case KEY_PPAGE:
2009-06-02 04:51:23 +00:00
this->selected -= (this->h - 1);
this->scrollV = MAXIMUM(0, this->scrollV - this->h + 1);
2009-06-02 04:51:23 +00:00
this->needsRedraw = true;
break;
2006-03-04 18:16:49 +00:00
case KEY_NPAGE:
2009-06-02 04:51:23 +00:00
this->selected += (this->h - 1);
this->scrollV = MAXIMUM(0, MINIMUM(Vector_size(this->items) - this->h,
this->scrollV + this->h - 1));
2009-06-02 04:51:23 +00:00
this->needsRedraw = true;
break;
case KEY_WHEELUP:
this->selected -= CRT_scrollWheelVAmount;
this->scrollV -= CRT_scrollWheelVAmount;
this->needsRedraw = true;
break;
case KEY_WHEELDOWN:
{
this->selected += CRT_scrollWheelVAmount;
this->scrollV += CRT_scrollWheelVAmount;
if (this->scrollV > Vector_size(this->items) - this->h) {
this->scrollV = Vector_size(this->items) - this->h;
}
this->needsRedraw = true;
break;
}
2006-03-04 18:16:49 +00:00
case KEY_HOME:
this->selected = 0;
break;
2006-03-04 18:16:49 +00:00
case KEY_END:
this->selected = size - 1;
break;
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;
}
// 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
}
return true;
2006-03-04 18:16:49 +00:00
}
HandlerResult Panel_selectByTyping(Panel* this, int ch) {
int size = Panel_size(this);
if (!this->eventHandlerState)
2016-02-02 14:53:02 +00:00
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
if (0 < ch && ch < 255 && isalnum((unsigned char)ch)) {
int len = strlen(buffer);
if (len < 99) {
buffer[len] = ch;
buffer[len+1] = '\0';
}
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;
}
}
// if current word did not match,
// retry considering the character the start of a new word.
buffer[0] = ch;
buffer[1] = '\0';
}
return HANDLED;
} else if (ch != ERR) {
buffer[0] = '\0';
}
if (ch == 13) {
return BREAK_LOOP;
}
return IGNORED;
}