forked from Lephenixnoir/hex-editor
heditor: navigation by cursor rather than row
This commit is contained in:
parent
d69a2947ed
commit
7a0978071e
150
src/heditor.c
150
src/heditor.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue