heditor: navigation by cursor rather than row

This commit is contained in:
Lephenixnoir 2022-06-20 01:02:52 +01:00
parent d69a2947ed
commit 7a0978071e
Signed by untrusted user: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
3 changed files with 98 additions and 63 deletions

View File

@ -26,6 +26,7 @@ heditor *heditor_create(jwidget *parent)
e->source = NULL;
e->scroll = 0;
e->cursor = 0;
e->visible_lines = 0;
e->bytes_per_line = 8;
e->line_spacing = 3;
@ -34,25 +35,32 @@ heditor *heditor_create(jwidget *parent)
return e;
}
static int compute_max_scroll(heditor *e)
static void compute_maxima(heditor *e, int *max_scroll, int *max_cursor)
{
if(!e || !e->source)
return 0;
return;
int bpl = e->bytes_per_line;
int rows = (source_size(e->source) + bpl - 1) / bpl;
return max(rows - e->visible_lines, 0) * bpl;
int size = source_size(e->source);
int rows = (size + bpl - 1) / bpl;
if(max_scroll)
*max_scroll = max(rows - e->visible_lines, 0) * bpl;
if(max_cursor)
*max_cursor = size - 1;
}
/* Adjust positioning to make sure we stay on legal values */
static void shake(heditor *e)
{
heditor_scroll_to(e, e->scroll);
heditor_move_to(e, e->cursor);
}
bool heditor_scroll_to(heditor *e, int target_scroll)
bool heditor_move_to(heditor *e, int target_cursor)
{
bool changed = false;
int max_scroll=0, max_cursor=0;
compute_maxima(e, &max_scroll, &max_cursor);
/* Recompute the number of visible lines */
@ -64,11 +72,32 @@ bool heditor_scroll_to(heditor *e, int target_scroll)
changed = true;
e->visible_lines = visible_lines;
/* Clamp the scroll to allowed limits */
/* Clamp the cursor to allowed limits */
int max_scroll = compute_max_scroll(e);
int adjusted_scroll = target_scroll - (target_scroll % e->bytes_per_line);
int new_scroll = max(0, min(adjusted_scroll, max_scroll));
target_cursor = max(0, min(target_cursor, max_cursor));
if(target_cursor != e->cursor)
changed = true;
e->cursor = target_cursor;
/* Determine the scroll offset for the target. If the target falls within
the center of the current view (ie. everything but the first and last
lines), do nothing. Otherwise, scroll to make it the first/last line. */
int bpl = e->bytes_per_line;
int target_row = target_cursor - (target_cursor % bpl);
int target_scroll =
// Minimum scroll where target is in the center
max(target_row - (visible_lines - 2) * bpl,
// Default value
min(e->scroll,
// Maximum scroll where target is in the center
target_row - bpl));
/* Clamp the newly-decided scroll to allowed limits */
int new_scroll = target_scroll - (target_scroll % bpl);
new_scroll = max(0, min(new_scroll, max_scroll));
if(new_scroll != e->scroll)
changed = true;
@ -76,7 +105,7 @@ bool heditor_scroll_to(heditor *e, int target_scroll)
/* Keep the source's front buffer aligned with the view */
int view_size = e->visible_lines * e->bytes_per_line;
int view_size = e->visible_lines * bpl;
view_size = min(view_size, max(source_size(e->source) - e->scroll, 0));
if(source_ensure_loaded(e->source, e->scroll, view_size))
changed = true;
@ -92,7 +121,7 @@ void heditor_set_source(heditor *e, source_t *source)
{
e->source = source;
/* Force a change event */
if(!heditor_scroll_to(e, 0)) {
if(!heditor_move_to(e, 0)) {
e->widget.update = true;
jwidget_emit(e, (jevent){ .type = HEDITOR_CHANGED });
}
@ -139,60 +168,57 @@ static void heditor_poly_layout(void *e0)
shake(e);
}
/* Generate hexdump strings from raw data */
static int hexdump(source_t *s, off_t offset, char *header, char *bytes,
char *ascii, int n)
{
if(!s) return 1;
sprintf(header, "%08X:", offset);
int bytes_i = 0;
for(int k = 0; k < n; k++) {
off_t o = offset + k;
if(o >= s->buf_offset && o < (int)(s->buf_offset+s->buf->data_size)) {
int c = *(uint8_t *)(s->buf->mem + o - s->buf_offset);
ascii[k] = (c >= 0x20 && c < 0x7f) ? c : '.';
bytes_i += sprintf(bytes + bytes_i, "%02X%s", c,
(k & 1) ? " " : "");
}
else {
ascii[k] = 0x7f;
bytes_i += sprintf(bytes + bytes_i, "--%s", (k & 1) ? " " : "");
}
}
ascii[n] = 0;
return 0;
}
static void heditor_poly_render(void *e0, int x, int y)
{
heditor *e = e0;
char header[12], bytes[48], ascii[24];
source_t *s = e->source;
char byte[16];
int offset = e->scroll;
int line_height = e->font->line_height + e->line_spacing;
if(!e->source) {
if(!s) {
dtext(x, y, C_BLACK, "No source opened");
return;
}
int offset = e->scroll;
int line_height = e->font->line_height + e->line_spacing;
int front_buffer_max = s->buf_offset + s->buf->data_size;
int n = e->bytes_per_line;
for(int i = 0; i < e->visible_lines; i++) {
int rc = hexdump(e->source, offset, header, bytes, ascii,
e->bytes_per_line);
int line_y = y + line_height * i;
dprint(x, line_y, C_BLACK, "%08X:", offset);
dtext(x, y + line_height*i, C_BLACK, header);
dtext(x+85, y + line_height*i, rc ? C_RED : C_BLACK, bytes);
int bytes_x = x + 85;
int ascii_x = x + 250;
for(int k = 7; k >= 0; k--) {
ascii[k+1] = 0;
dtext(x + 250 + 9*k, y + 12*i, C_BLACK, ascii+k);
for(int k = 0; k < n; k++) {
off_t o = offset + k;
strcpy(byte, "--");
if(o >= s->buf_offset && o < front_buffer_max) {
int c = *(uint8_t *)(s->buf->mem + o - s->buf_offset);
sprintf(byte, "%02X", c);
dprint(ascii_x, line_y, C_BLACK, "%c",
(c >= 0x20 && c < 0x7f) ? c : '.');
}
int text_w;
dsize(byte, NULL, &text_w, NULL);
int fg = C_BLACK;
if(e->cursor == o) {
drect(bytes_x - 1, line_y - 1, bytes_x + text_w,
line_y + e->font->line_height, C_BLACK);
fg = C_WHITE;
}
dtext(bytes_x, line_y, fg, byte);
bytes_x += text_w + ((k & 1) ? 7 : 2);
ascii_x += 9;
}
offset += e->bytes_per_line;
offset += n;
}
}
@ -204,25 +230,33 @@ static bool heditor_poly_event(void *e0, jevent ev)
if(ev.type != JSCENE_KEY || ev.key.type == KEYEV_UP)
return false;
int max_scroll = compute_max_scroll(e);
int key = ev.key.key;
int move_speed = (ev.key.alpha ? e->visible_lines : 1);
if(key == KEY_UP && ev.key.shift) {
heditor_scroll_to(e, 0);
heditor_move_to(e, 0);
return true;
}
else if(key == KEY_UP) {
heditor_scroll_to(e, e->scroll - move_speed * e->bytes_per_line);
heditor_move_to(e, e->cursor - move_speed * e->bytes_per_line);
return true;
}
if(key == KEY_DOWN && ev.key.shift) {
heditor_scroll_to(e, max_scroll);
heditor_move_to(e, source_size(e->source) - 1);
return true;
}
else if(key == KEY_DOWN) {
heditor_scroll_to(e, e->scroll + move_speed * e->bytes_per_line);
heditor_move_to(e, e->cursor + move_speed * e->bytes_per_line);
return true;
}
if(key == KEY_LEFT) {
heditor_move_to(e, e->cursor - 1);
return true;
}
if(key == KEY_RIGHT) {
heditor_move_to(e, e->cursor + 1);
return true;
}

View File

@ -21,6 +21,8 @@ typedef struct {
source_t *source;
/* Current scroll offset, in bytes */
int scroll;
/* Current cursor position in source, in bytes */
int cursor;
/* Number of visible lines */
int8_t visible_lines;
/* Number of bytes printed per line */
@ -42,8 +44,8 @@ heditor *heditor_create(jwidget *parent);
/* Switch the source. The old source is not freed by the editor. */
void heditor_set_source(heditor *e, source_t *source);
/* Scroll to the requested offset. */
bool heditor_scroll_to(heditor *e, int offset);
/* Scroll to the requested cursor position. */
bool heditor_move_to(heditor *e, int offset);
/* Trivial properties */
void heditor_set_font(heditor *e, font_t const *font);

View File

@ -101,16 +101,15 @@ void hex_view(void)
}
if(e.type == JINPUT_VALIDATED) {
/* Parse string into hexa */
uint32_t target = 0;
char const *str = jinput_value(input);
uint32_t target = 0;
for(int k = 0; k < str[k]; k++)
{
target <<= 4;
if(str[k] <= '9') target += (str[k] - '0');
else target += ((str[k]|0x20) - 'a' + 10);
}
heditor_scroll_to(mem, target);
heditor_move_to(mem, target);
}
if(e.type == JINPUT_VALIDATED || e.type == JINPUT_CANCELED) {
jwidget_set_visible(input, false);