/*
- * Copyright Neil Brown <neil@brown.name> 2015
+ * Copyright Neil Brown ©2015-2023 <neil@brown.name>
* May be distributed under terms of GPLv2 - see file:COPYING
*
* hexedit renderer
#include <string.h>
#include <stdio.h>
+#define PANE_DATA_TYPE struct he_data
#include "core.h"
+#include "misc.h"
struct he_data {
- struct mark *top, *bot;
- int ignore_point;
- struct command type;
- int typenum;
struct pane *pane;
+ bool bytes;
};
+#include "core-pane.h"
static struct map *he_map;
-static void render_hex_attach(struct pane *parent, struct point **ptp);
+static struct pane *do_render_hex_attach(struct pane *parent safe);
-static int put_str(struct pane *p, char *buf, char *attrs, int x, int y)
-{
- int len = 0;
- while (buf[len]) {
- pane_text(p, buf[len], attrs, x, y);
- x += 1;
- len += 1;
- }
- return len;
-}
-
-static struct mark *render(struct point **ptp, struct pane *p)
-{
- struct he_data *he = p->data;
- struct doc *d = (*ptp)->doc;
- int x = 0, y = 0;
- struct mark *m;
- int c;
- struct cmd_info ci2 = {0};
-
- pane_clear(p, NULL);
-
- ci2.key = "CountLines";
- ci2.pointp = ptp;
- ci2.mark = he->top;
- key_lookup(d->ed->commands, &ci2);
-
- c = attr_find_int(*mark_attr(he->top), "chars");
-
- m = mark_dup(he->top, 0);
-
- p->cx = -1;
- p->cy = -1;
-
- for (y = 0; y < p->h; y++) {
- int xcol = 0;
- int ccol = 10+16*3+2+1;
- char buf[20];
-
- sprintf(buf, "%08x: ", c);
- xcol += put_str(p, buf, NULL, xcol, y);
- for (x = 0; x < 16; x++) {
- wint_t ch;
- if (mark_same(d, m, mark_of_point(*ptp))) {
- p->cx = xcol;
- p->cy = y;
- }
- ch = mark_next(d, m);
- if (ch == WEOF)
- break;
- sprintf(buf, "%02x ", ch & 0xff);
- xcol += put_str(p, buf, NULL, xcol, y);
- if (x == 7)
- xcol += 1;
-
- if (ch < ' ')
- ch = '?';
- pane_text(p, ch, NULL, ccol, y);
- ccol += 1;
- if (x == 7)
- ccol += 1;
- }
- c += x;
- if (x < 16)
- break;
- }
- if (mark_ordered(mark_of_point(*ptp), he->top) &&
- !mark_same(d, mark_of_point(*ptp), he->top))
- p->cx = p->cy = -1;
- return m;
-}
-
-static struct mark *find_top(struct point **ptp, struct pane *p,
- struct mark *top, struct mark *bot)
-{
- /* top and bot might be NULL, else they record what is currently
- * visible.
- * We find the location of point, top, bot and then choose a new
- * top.
- * top must be a multiple of 16, must keep point on the pane,
- * and should leave old values as unchanged as possible.
- */
- struct mark *m;
- int ppos, tpos, bpos, pos, point_pos;
- struct he_data *he = p->data;
- struct doc *d = (*ptp)->doc;
- struct cmd_info ci2 = {0};
-
- ci2.key = "CountLines";
- ci2.pointp = ptp;
- ci2.mark = top;
- key_lookup(d->ed->commands, &ci2);
- point_pos = attr_find_int(*mark_attr(mark_of_point(*ptp)), "chars");
- tpos = bpos = ppos = point_pos;
- if (top) {
- tpos = attr_find_int(*mark_attr(top), "chars");
- }
- if (bot) {
- ci2.mark = bot;
- key_lookup(d->ed->commands, &ci2);
- bpos = attr_find_int(*mark_attr(bot), "chars");
- }
- ppos -= ppos % 16;
- tpos -= tpos % 16;
- bpos -= bpos % 16;
- if (tpos <= ppos && tpos + p->h * 16 > ppos) {
- /* point is within displayed region - no change */
- pos = tpos;
- } else if (ppos < tpos && tpos - ppos < (p->h/2) * 16) {
- /* point is less than half a pane before current display,
- * just scroll twice the gap */
- pos = ppos - (tpos - ppos);
- if (pos < 0)
- pos = 0;
- } else if (ppos > tpos + p->h*16 && ppos - (tpos + p->h*16) < (p->h/2) * 16) {
- /* point is less than half a pane below display, so scroll
- * twice the gap */
- pos = ppos + (ppos - (tpos + p->h*16)) - p->h*16;
- } else {
- /* to far - just re-center */
- if (ppos < p->h/2 * 16)
- pos = 0;
- else
- pos = ppos - p->h/2 * 16;
- }
- m = mark_at_point(*ptp, he->typenum);
-
- while (pos < point_pos) {
- mark_prev(d, m);
- point_pos -= 1;
- }
- return m;
-}
-
-static int hex_refresh(struct cmd_info *ci)
-{
- struct pane *p = ci->home;
- struct he_data *he = p->data;
- struct mark *end = NULL, *top;
- struct doc *d;
-
- if (!ci->pointp)
- return 0;
- d = (*ci->pointp)->doc;
- pane_check_size(p);
-
- if (he->top) {
- struct cmd_info ci2 = {0};
- int tpos;
- ci2.key = "CountLines";
- ci2.pointp = ci->pointp;
- ci2.mark = he->top;
- key_lookup(d->ed->commands, &ci2);
- tpos = attr_find_int(*mark_attr(he->top), "chars");
- if (tpos % 16 != 0) {
- top = find_top(ci->pointp, p, he->top, end);
- mark_free(he->top);
- he->top = top;
- }
- }
-
- if (he->top) {
- end = render(ci->pointp, p);
- if (he->ignore_point || p->cx >= 0)
- goto found;
- }
- top = find_top(ci->pointp, p, he->top, end);
- mark_free(he->top);
- mark_free(end);
- he->top = top;
- end = render(ci->pointp, p);
-found:
- mark_free(he->bot);
- he->bot = end;
- return 0;
-}
-
-static int do_render_hex_handle(struct command *c, struct cmd_info *ci)
-{
- struct pane *p = ci->home;
- struct he_data *he = p->data;
- struct doc *d;
- int ret;
-
- ret = key_lookup(he_map, ci);
- if (ret)
- return ret;
-
- if (strcmp(ci->key, "Close") == 0) {
- struct pane *p = he->pane;
-
- d = (*ci->pointp)->doc;
- mark_free(he->top);
- mark_free(he->bot);
- he->pane = NULL;
- doc_del_view(d, &he->type);
- p->data = NULL;
- p->handle = NULL;
- free(he);
- return 1;
- }
- if (strcmp(ci->key, "Clone") == 0) {
- struct pane *parent = ci->focus;
- struct pane *c;
-
- render_hex_attach(parent, NULL);
- c = pane_child(p);
- if (c)
- return pane_clone(c, parent->focus);
- return 1;
- }
- if (strcmp(ci->key, "Refresh") == 0)
- return hex_refresh(ci);
- return 0;
-}
-DEF_CMD(render_hex_handle, do_render_hex_handle);
-
-static int render_hex_notify(struct command *c, struct cmd_info *ci)
-{
- struct he_data *he = container_of(c, struct he_data, type);
-
- if (strcmp(ci->key, "Replace") == 0) {
- if (he->bot == NULL || ci->mark == NULL ||
- mark_ordered(ci->mark, he->bot))
- /* A change that was not after the bot, so offsets
- * probably changed, so redraw
- */
- pane_damaged(he->pane, DAMAGED_CONTENT);
- return 0;
- }
- if (strcmp(ci->key, "Release") == 0) {
- if (he->pane)
- pane_close(he->pane);
- return 1;
- }
- return 0;
-}
+DEF_LOOKUP_CMD(render_hex_handle, he_map);
-static int render_hex_move(struct command *c, struct cmd_info *ci)
+DEF_CMD_CLOSED(render_hex_close)
{
struct pane *p = ci->home;
- int rpt = RPT_NUM(ci);
struct he_data *he = p->data;
- struct point *pt = *ci->pointp;
-
- if (!he->top)
- return 0;
- if (strcmp(ci->key, "Move-View-Large") == 0)
- rpt *= p->h - 2;
- rpt *= 16;
- he->ignore_point = 1;
- while (rpt < 0 && mark_prev(pt->doc, he->top) != WEOF)
- rpt += 1;
- while (rpt > 0 && mark_next(pt->doc, he->top) != WEOF)
- rpt -= 1;
- pane_damaged(p, DAMAGED_CONTENT);
+ he->pane = NULL;
return 1;
}
-DEF_CMD(comm_move, render_hex_move);
-static int render_hex_follow_point(struct command *c, struct cmd_info *ci)
+DEF_CMD(render_hex_clone)
{
- struct pane *p = ci->home;
- struct he_data *he = p->data;
+ struct pane *parent = ci->focus;
- if (he->ignore_point) {
- pane_damaged(p, DAMAGED_CONTENT);
- he->ignore_point = 0;
- }
- return 0;
-}
-DEF_CMD(comm_follow, render_hex_follow_point);
-
-static int render_hex_set_cursor(struct command *c, struct cmd_info *ci)
-{
- struct pane *p = ci->home;
- struct point *pt = *ci->pointp;
- struct he_data *he = p->data;
- struct mark *m;
- int n, x;
-
- if (!he->top)
- return 0;
-
- if (ci->x < 10)
- x = 0;
- else if (ci->x < 10 + 8*3)
- x = (ci->x - 10) / 3;
- else if (ci->x < 10 + 1 + 16*3)
- x = (ci->x - 11) / 3;
- else if (ci->x < 10 + 1 + 2 + 16*3 + 8)
- x = ci->x - (10+1+2+16*3);
- else if (ci->x < 10 + 1 + 2 + 16*3 + 8 + 1 + 8)
- x = ci->x - (10+1+2+16*3 + 1);
- else
- x = 15;
- n = ci->y * 16 + x;
- m = mark_dup(he->top, 1);
- while (n > 0 && mark_next(pt->doc, m) != WEOF)
- n -= 1;
- point_to_mark(pt, m);
- mark_free(m);
- pane_focus(p);
+ do_render_hex_attach(parent);
+ pane_clone_children(ci->home, parent->focus);
return 1;
}
-DEF_CMD(comm_cursor, render_hex_set_cursor);
-static int render_hex_move_line(struct command *c, struct cmd_info *ci)
+DEF_CMD(render_hex_notify_replace)
{
- /* MV_CHAR 16 times repeat count */
- struct cmd_info ci2 = {0};
-
- ci2 = *ci;
- ci2.key = "Move-Char";
- ci2.numeric = RPT_NUM(ci) * 16;
- return key_handle_focus(&ci2);
+ /* If change happens only after the view port, we don't
+ * need damage.
+ * If before, we might need to update addresses.
+ * However we cannot currently access the view port, so
+ * always signal damage. FIXME.
+ */
+ call("view:changed", pane_focus(ci->home));
+ return 1;
}
-DEF_CMD(comm_line, render_hex_move_line);
-static int render_hex_eol(struct command *c, struct cmd_info *ci)
+DEF_CMD(render_hex_eol)
{
- struct point *pt = *ci->pointp;
wint_t ch = 1;
int rpt = RPT_NUM(ci);
+ bool one_more = ci->num2 > 0;
int pos;
- struct he_data *he = ci->home->data;
- pos = attr_find_int(*mark_attr(ci->mark), "chars");
+ if (!ci->mark)
+ return Enoarg;
+ call("CountLines", ci->focus, 0, ci->mark);
+
+ pos = attr_find_int(*mark_attr(ci->mark), "char") - 1;
while (rpt > 0 && ch != WEOF) {
while ((pos & 15) != 15 &&
- (ch = mark_next(pt->doc, ci->mark)) != WEOF)
+ (ch = doc_next(ci->focus, ci->mark)) != WEOF)
pos += 1;
rpt -= 1;
- if (rpt) {
- ch = mark_next(pt->doc, ci->mark);
+ if (rpt || one_more) {
+ ch = doc_next(ci->focus, ci->mark);
pos += 1;
}
}
while (rpt < 0 && ch != WEOF) {
while ((pos & 15) != 0 &&
- (ch = mark_prev(pt->doc, ci->mark)) != WEOF)
+ (ch = doc_prev(ci->focus, ci->mark)) != WEOF)
pos -= 1;
rpt += 1;
- if (rpt) {
- ch = mark_prev(pt->doc, ci->mark);
+ if (rpt || one_more) {
+ ch = doc_prev(ci->focus, ci->mark);
pos -= 1;
}
}
- he->ignore_point = 0;
return 1;
}
-DEF_CMD(comm_eol, render_hex_eol);
+
+DEF_CMD(render_line)
+{
+ struct buf ret;
+ struct mark *m = NULL;
+ struct mark *pm = ci->mark2;
+ int pos;
+ int pm_offset = -1;
+ int i;
+ char buf[30];
+ int rv;
+
+ if (!ci->mark)
+ return Enoarg;
+
+ call("CountLines", ci->focus, 0, ci->mark);
+ pos = attr_find_int(*mark_attr(ci->mark), "char") - 1;
+
+ buf_init(&ret);
+ if (doc_following(ci->focus, ci->mark) == WEOF)
+ return Efail;
+ snprintf(buf, sizeof(buf), "<bold>%08x:</> ", pos);
+ buf_concat(&ret, buf);
+ m = mark_dup_view(ci->mark);
+ for (i = 0; i < 16; i++) {
+ wint_t ch;
+ struct mark *m2 = ci->mark;
+
+ if (pm && mark_same(m2, pm) && pm_offset < 0)
+ pm_offset = ret.len;
+ if (ci->num >= 0 && ci->num <= ret.len)
+ goto done;
+
+ ch = doc_next(ci->focus, m2);
+ if (ch == WEOF)
+ strcpy(buf, " ");
+ else
+ sprintf(buf, "%02x ", ch & 0xff);
+ buf_concat(&ret, buf);
+ if (i == 7)
+ buf_append(&ret, ' ');
+ }
+
+ buf_concat(&ret, " <fg:red>");
+ for (i = 0; i < 16; i++) {
+ wint_t ch;
+
+ ch = doc_next(ci->focus, m);
+ if (ch == WEOF)
+ ch = ' ';
+ if (ch < ' ')
+ ch = '?';
+ buf_append(&ret, ch);
+ if (ch == '<')
+ /* '<<' to quote the '<' */
+ buf_append(&ret, ch);
+ buf_append(&ret, ' ');
+ if (i == 7)
+ buf_append(&ret, ' ');
+ }
+ buf_concat(&ret, "</>\n");
+done:
+ if (m)
+ mark_free(m);
+ rv = comm_call(ci->comm2, "callback:render", ci->focus, pm_offset, NULL,
+ buf_final(&ret));
+ free(ret.b);
+ return rv ?: 1;
+}
+
+DEF_CMD(render_line_prev)
+{
+ /* If ->num is 0, round down to multiple of 16.
+ * if it is 1, subtract a further 16.
+ */
+ int to, from;
+
+ if (!ci->mark)
+ return Enoarg;
+ call("CountLines", ci->focus, 0, ci->mark);
+
+ from = attr_find_int(*mark_attr(ci->mark), "char") - 1;
+ to = from & ~0xF;
+ if (ci->num) {
+ if (to >= 16)
+ to -= 16;
+ else
+ return Efail;
+ }
+ while (to < from) {
+ doc_prev(ci->focus, ci->mark);
+ from -= 1;
+ }
+ return 1;
+}
+
+DEF_CMD(hex_char)
+{
+ struct he_data *he = ci->home->data;
+
+ if (he->bytes)
+ return home_call(ci->home->parent, "doc:byte", ci->focus,
+ ci->num, ci->mark, ci->str,
+ ci->num2, ci->mark2, ci->str2);
+ else
+ return home_call(ci->home->parent, "doc:char", ci->focus,
+ ci->num, ci->mark, ci->str,
+ ci->num2, ci->mark2, ci->str2);
+}
static void render_hex_register_map(void)
{
he_map = key_alloc();
- key_add_range(he_map, "Move-", "Move-\377", &comm_follow);
- key_add(he_map, "Move-View-Small", &comm_move);
- key_add(he_map, "Move-View-Large", &comm_move);
- key_add(he_map, "Move-CursorXY", &comm_cursor);
- key_add(he_map, "Click-1", &comm_cursor);
- key_add(he_map, "Press-1", &comm_cursor);
- key_add(he_map, "Move-Line", &comm_line);
+ key_add(he_map, "doc:EOL", &render_hex_eol);
+ key_add(he_map, "doc:char", &hex_char);
+
+ key_add(he_map, "doc:render-line-prev", &render_line_prev);
+ key_add(he_map, "doc:render-line", &render_line);
- key_add(he_map, "Move-EOL", &comm_eol);
- key_add(he_map, "Replace", &comm_follow);
+ key_add(he_map, "Close", &render_hex_close);
+ key_add(he_map, "Clone", &render_hex_clone);
+ key_add(he_map, "doc:replaced", &render_hex_notify_replace);
}
-static void render_hex_attach(struct pane *parent, struct point **ptp)
+static struct pane *do_render_hex_attach(struct pane *parent safe)
{
- struct he_data *he = malloc(sizeof(*he));
+ struct he_data *he;
struct pane *p;
-
- if (!ptp)
- ptp = pane_point(parent);
- if (!ptp)
- return;
-
- he->top = NULL;
- he->bot = NULL;
- he->ignore_point = 0;
- he->type.func = render_hex_notify;
- he->typenum = doc_add_view((*ptp)->doc, &he->type);
- p = pane_register(parent, 0, &render_hex_handle, he, NULL);
- he->pane = p;
+ char *charset = pane_attr_get(parent, "doc:charset");
if (!he_map)
render_hex_register_map();
+
+ p = pane_register(parent, 0, &render_hex_handle.c);
+ if (!p)
+ return NULL;
+ he = p->data;
+ call("doc:request:doc:replaced", p);
+ attr_set_str(&p->attrs, "render-wrap", "no");
+ attr_set_str(&p->attrs, "heading", "<bold> 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0 1 2 3 4 5 6 7 8 9 a b c d e f</>");
+ he->pane = p;
+ he->bytes = (charset && strcmp(charset, "8bit") != 0);
+ return call_ret(pane, "attach-render-lines", p);
}
-static int do_render_hex_attach(struct command *c, struct cmd_info *ci)
+
+DEF_CMD(render_hex_attach)
{
- render_hex_attach(ci->focus, ci->pointp);
- return 1;
+ struct pane *p = do_render_hex_attach(ci->focus);
+
+ if (!p)
+ return Efail;
+ return comm_call(ci->comm2, "callback:attach", p);
+}
+
+DEF_CMD(hex_appeared)
+{
+ char *t = pane_attr_get(ci->focus, "doc-type");
+ if (t && strcmp(t, "text") == 0)
+ attr_set_str(&ci->focus->attrs, "render-cmd-H", "hex");
+ return Efallthrough;
}
-DEF_CMD(comm_attach, do_render_hex_attach);
-void edlib_init(struct editor *ed)
+void edlib_init(struct pane *ed safe)
{
- key_add(ed->commands, "render-hex-attach", &comm_attach);
+ call_comm("global-set-command", ed, &render_hex_attach, 0, NULL, "attach-render-hex");
+ call_comm("global-set-command", ed, &hex_appeared, 0, NULL, "doc:appeared-hex");
}