]> git.neil.brown.name Git - edlib.git/blobdiff - render-hex.c
TODO: clean out done items.
[edlib.git] / render-hex.c
index 26c75bfdad0ee5e2ee5f15951f705727f9567bb4..28bde9e7a178ae0296ad02df2a27e85b183ec5da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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");
 }