]> git.neil.brown.name Git - edlib.git/blobdiff - lib-renderline.c
TODO: clean out done items.
[edlib.git] / lib-renderline.c
index 62ebe1133fb6610ba31ca547b558e1b1fdc56be9..e5d8169aaa951529a5b7c839ac2d17df2eeff07e 100644 (file)
@@ -63,7 +63,6 @@
  *                       recognised has "wrap-" stripped and is used for the
  *                       head and tail. Default is fg:blue,underline
  *  hide               - Text is hidden if cursor is not within range.
- *                       NOT YET IMPLEMENTED
  *
  * "nn" is measured in "points" which is 1/10 the nominal width of chars
  * in the default font size, which is called "10".  A positive value is
@@ -96,7 +95,7 @@ struct render_item {
                                 * it.
                                 */
        uint8_t         hide;   /* This and consecutive render_items
-                                * with the same hide nmber form a
+                                * with the same hide number form a
                                 * hidden extent which is visible when
                                 * the cursor is in it.
                                 */
@@ -129,10 +128,12 @@ struct rline_data {
        char            *wrap_head, *wrap_tail, *wrap_attr;
        int             head_length, tail_length;
        char            *line safe;
+       char            *background;
        bool            word_wrap;
        bool            image;
        int             curspos;
 
+       struct xy       image_size;
        /* These used to check is measuring is needed, or to record
         * results of last measurement */
        unsigned short measure_width, measure_height;
@@ -206,8 +207,10 @@ static void parse_line(struct rline_data *rd safe)
                return;
        }
        rd->image = strstarts(line, SOH "image:");
-       if (rd->image)
+       if (rd->image) {
+               rd->image_size.x = 0;
                return;
+       }
        buf_init(&attr);
        buf_init(&wrapattr);
 
@@ -230,7 +233,7 @@ static void parse_line(struct rline_data *rd safe)
                       (!rd->word_wrap || c != ' '))
                        c = *line++;
 
-               if (line - 1 > st) {
+               if (line - 1 > st || tab != TAB_UNSET) {
                        /* All text from st to line-1 has "attr' */
                        add_render(rd, &riend, st, line-1, buf_final(&attr),
                                   &tab, &align, &wrap_margin, wrap, hide);
@@ -252,8 +255,8 @@ static void parse_line(struct rline_data *rd safe)
                         * which should leave either a trailing comma, or an
                         * empty string.
                         */
-                       buf_append(&attr, ',');
                        old_len = attr.len;
+                       buf_append(&attr, ',');
                        foreach_attr(a, v, st, line) {
                                if (amatch(a, "centre") || amatch(a, "center") ||
                                    amatch(a, "ctab")) {
@@ -312,9 +315,9 @@ static void parse_line(struct rline_data *rd safe)
                        /* strip one more ',' */
                        if (attr.len > 0)
                                attr.len -= 1;
-                       if (attr.len <= wrap_depth)
+                       if (wrap && attr.len <= wrap_depth)
                                wrap = 0;
-                       if (attr.len <= hide_depth)
+                       if (hide && attr.len <= hide_depth)
                                hide = 0;
                        break;
                case ack:
@@ -361,13 +364,13 @@ static inline struct call_return do_measure(struct pane *p safe,
                                            int splitpos, int len,
                                            int maxwidth)
 {
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
        struct call_return cr;
        char tb[] = "        ";
        char *str = rd->line + ri->start + splitpos;
        char tmp;
 
-       if (rd->line[ri->start] == '\t') {
+       if (ri->len && rd->line[ri->start] == '\t') {
                str = tb;
                if (len < 0)
                        len = ri->tab_cols - splitpos;
@@ -400,7 +403,7 @@ static inline struct call_return measure_str(struct pane *p safe,
                                             char *str safe,
                                             const char *attr)
 {
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
 
        return call_ret(all, "Draw:text-size", p,
                        -1, NULL, str,
@@ -413,7 +416,7 @@ static inline void do_draw(struct pane *p safe,
                           int offset,
                           int x, int y)
 {
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
        char tmp;
        char *str;
        int len;
@@ -423,7 +426,7 @@ static inline void do_draw(struct pane *p safe,
        len = ri->len;
 
        y += rd->ascent;
-       if (strchr("\f\n\0", str[0])) {
+       if (ri->len && strchr("\f\n\0", str[0])) {
                /* end marker - len extends past end of string,
                 * but mustn't write there.  Only need to draw if
                 * cursor is here.
@@ -433,7 +436,7 @@ static inline void do_draw(struct pane *p safe,
                                  rd->scale, NULL, ri->attr, x, y);
                return;
        }
-       if (str[0] == '\t') {
+       if (ri->len && str[0] == '\t') {
                len = ri->tab_cols;
                if (split)
                        offset = -1;
@@ -445,7 +448,7 @@ static inline void do_draw(struct pane *p safe,
                        len -= ri->split_list[split-1];
        }
 
-       if (str[0] == '\t')
+       if (ri->len && str[0] == '\t')
                /* Tab need a list of spaces */
                str = tb;
        else
@@ -469,7 +472,7 @@ static inline void draw_wrap(struct pane *p safe,
                             char *str safe,
                             int x, int y)
 {
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
 
        home_call(focus, "Draw:text", p,
                  -1, NULL, str,
@@ -512,9 +515,9 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
         * 3 if both.
         * 0 if neither
         */
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
        struct render_item *ri, *wraprl;
-       int shift_left = pane_attr_get_int(focus, "shift_left", 0);
+       int shift_left = pane_attr_get_int(focus, "render-wrap", -1);
        bool wrap = shift_left < 0;
        int wrap_margin;
        int right_margin;
@@ -526,6 +529,7 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
        int x, y;
        int ret = 0;
        bool seen_rtab = False;
+       unsigned int offset_hide = 0;
 
        if (!rd->content)
                return ret;
@@ -534,6 +538,8 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
            offset == rd->measure_offset) {
                /* No change */
                for (ri = rd->content ; ri ; ri = ri->next) {
+                       if (ri->hidden)
+                               continue;
                        if (ri->eol && rd->line[ri->start] == '\n')
                                ret |= 1;
                        if (ri->eol && rd->line[ri->start] == '\f')
@@ -566,8 +572,22 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
        /* 0 means right edge for right_margin, and left edge for all others */
        right_margin = p->w - calc_pos(-rd->right_margin, p->w, rd->curs_width);
 
+       if (offset >= 0)
+               for (ri = rd->content ; ri ; ri = ri->next)
+                       if (offset < ri->start + ri->len) {
+                               offset_hide = ri->hide;
+                               break;
+                       }
+
+       rd->width = 0;
        for (ri = rd->content; ri; ri = ri->next) {
-               if ((unsigned char)rd->line[ri->start] >= ' ') {
+               ri->hidden = (ri->hide && ri->hide != offset_hide);
+               if (ri->hidden) {
+                       ri->width = 0;
+                       continue;
+               }
+               if (ri->len == 0 ||
+                   (unsigned char)rd->line[ri->start] >= ' ') {
                        cr = do_measure(p, ri, 0, -1, -1);
                } else {
                        char tmp[4];
@@ -594,7 +614,7 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
                if (cr.i2 > rd->ascent)
                        rd->ascent = cr.i2;
                ri->width = ri->eol ? 0 : cr.x;
-               ri->hidden = False;
+               rd->width += ri->width;
 
                if (ri->start <= offset && offset <= ri->start + ri->len) {
                        cr = measure_str(p, "M", ri->attr);
@@ -610,19 +630,20 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
         */
        x = left_margin - (shift_left > 0 ? shift_left : 0);
        y = rd->space_above * curs_height / 10;
-       rd->width = 0;
        for (ri = rd->content; ri; ri = ri->next) {
                int w, margin;
                struct render_item *ri2;
                ri->y = y;
+               if (ri->hidden) {
+                       ri->x = x;
+                       continue;
+               }
                if (ri->tab != TAB_UNSET)
                        x =  left_margin + calc_pos(ri->tab,
                                                    right_margin - left_margin,
                                                    rd->curs_width);
                if (ri->eol) {
                        /* EOL */
-                       if (x > rd->width)
-                               rd->width = x;
                        ri->x = x;
                        x = 0; /* Don't include shift. probably not margin */
                        if (rd->line[ri->start])
@@ -631,7 +652,7 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
                }
                if (ri->tab_align == TAB_LEFT) {
                        ri->x = x;
-                       if (rd->line[ri->start] == '\t') {
+                       if (ri->len && rd->line[ri->start] == '\t') {
                                int col = x / ri->width;
                                int cols= 8 - (col % 8);
                                ri->tab_cols = cols;
@@ -676,6 +697,8 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
        wrap_margin = left_margin + rd->head_length;
        for (ri = rd->content ; wrap && ri ; ri = ri->next) {
                int splitpos;
+               if (ri->hidden)
+                       continue;
                if (ri->wrap && (wraprl == NULL || ri->wrap != wraprl->wrap))
                        wraprl = ri;
                if (ri->wrap_margin)
@@ -802,12 +825,12 @@ static int measure_line(struct pane *p safe, struct pane *focus safe, int offset
 
 static void draw_line(struct pane *p safe, struct pane *focus safe, int offset)
 {
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
        struct render_item *ri;
        char *wrap_tail = rd->wrap_tail ?: "\\";
        char *wrap_head = rd->wrap_head ?: "";
 
-       home_call(focus, "Draw:clear", p);
+       home_call(focus, "Draw:clear", p, 0, NULL, rd->background);
 
        if (!rd->content)
                return;
@@ -862,16 +885,19 @@ static int find_xy(struct pane *p safe, struct pane *focus safe,
         * We do not consider the eol render_item
         */
        struct call_return cr;
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
        struct render_item *r, *ri = NULL;
        int splitpos = 0;
        int start = 0;
 
        for (r = rd->content; r ; r = r->next) {
                int split;
+               if (r->hidden)
+                       continue;
                if (r->y <= y && r->x <= x) {
                        ri = r;
                        start = r->start;
+                       splitpos = 0;
                }
                for (split = 0; split < r->split_cnt; split++) {
                        if (r->y + (split + 1) * rd->line_height <= y &&
@@ -889,9 +915,23 @@ static int find_xy(struct pane *p safe, struct pane *focus safe,
                return start;
        cr = do_measure(p, ri, splitpos, -1, x - ri->x);
        if ((splitpos ? ri->wrap_x : ri->x ) + cr.x > x &&
-           ri->y + rd->line_height * splitpos > y &&
-           xyattr)
-               *xyattr = ri->attr;
+           ri->y + rd->line_height * (1 + splitpos) > y &&
+           xyattr) {
+               /* This is a bit of a hack.
+                * We stick the x,y co-ords of the start
+                * of the current attr in front of the
+                * attrs so render-lines can provide a
+                * good location for a menu
+                */
+               char buf[100];
+               struct render_item *ri2;
+               int ax = ri->x;
+               for (ri2 = rd->content; ri2 != ri; ri2 = ri2->next)
+                       if (strcmp(ri2->attr, ri->attr) == 0)
+                               ax = ri2->x;
+               snprintf(buf, sizeof(buf), "%dx%d,", ax, y);
+               *xyattr = strconcat(p, buf, ri->attr);
+       }
        if (cr.s)
                return cr.s - rd->line;
        return start + cr.i;
@@ -903,10 +943,12 @@ static struct xy find_curs(struct pane *p safe, int offset, const char **cursatt
        struct xy xy = {0,0};
        int split;
        int st;
-       struct rline_data *rd = &p->data;
+       struct rline_data *rd = p->data;
        struct render_item *r, *ri = NULL;
 
        for (r = rd->content; r; r = r->next) {
+               if (r->hidden)
+                       continue;
                if (offset < r->start)
                        break;
                ri = r;
@@ -923,7 +965,7 @@ static struct xy find_curs(struct pane *p safe, int offset, const char **cursatt
        else
                offset -= ri->start;
        /* offset now from ri->start */
-       if (rd->line[ri->start] == '\t' && offset)
+       if (ri->len && rd->line[ri->start] == '\t' && offset)
                offset = ri->tab_cols;
        if (cursattr)
                *cursattr = ri->attr;
@@ -991,6 +1033,7 @@ static int render_image(struct pane *p safe, struct pane *focus safe,
                        int dodraw,
                        int offset, int want_xypos, short x, short y)
 {
+       struct rline_data *rd = p->data;
        char *fname = NULL;
        const char *orig_line = line;
        short width, height;
@@ -999,8 +1042,10 @@ static int render_image(struct pane *p safe, struct pane *focus safe,
        int ioffset;
        struct xy xyscale = pane_scale(focus);
        int scale = xyscale.x;
-       char *ssize = attr_find(p->attrs, "cached-size");
        struct xy size= {-1, -1};
+       char mode[30] = "";
+       const char *a, *v;
+       const char *end;
 
        if (dodraw)
                home_call(focus, "Draw:clear", p);
@@ -1011,12 +1056,11 @@ static int render_image(struct pane *p safe, struct pane *focus safe,
        while (*line == soh)
                line += 1;
 
-       while (*line && *line != stx && *line != etx) {
-               int len = strcspn(line, "," STX ETX);
-               if (strstarts(line, "image:")) {
-                       fname = strndup(line+6, len-6);
-                       if (!ssize ||
-                           sscanf(ssize, "%hdx%hd", &size.x, &size.y) != 2) {
+       end = line + strcspn(line, STX);
+       foreach_attr(a, v, line, end) {
+               if (amatch(a, "image") && v) {
+                       aupdate(&fname, v);
+                       if (rd->image_size.x <= 0) {
                                struct call_return cr =
                                        home_call_ret(all, focus,
                                                      "Draw:image-size",
@@ -1024,26 +1068,22 @@ static int render_image(struct pane *p safe, struct pane *focus safe,
                                if (cr.x > 0 && cr.y > 0) {
                                        size.x = cr.x;
                                        size.y = cr.y;
-                                       asprintf(&ssize, "%hdx%hd",
-                                                cr.x, cr.y);
-                                       attr_set_str(&p->attrs,
-                                                    "cached-size", ssize);
+                                       rd->image_size = size;
                                }
-                       }
-               } else if (strstarts(line, "width:")) {
-                       width = atoi(line + 6);
-                       width = width * scale / 1000;
-               } else if (strstarts(line, "height:")) {
-                       height = atoi(line + 7);
-                       height = height * scale / 1000;
-               } else if (strstarts(line, "noupscale") &&
+                       } else
+                               size = rd->image_size;
+               } else if (amatch(a, "width") && v) {
+                       width = anum(v) * scale / 1000;
+               } else if (amatch(a, "height") && v) {
+                       height = anum(v) * scale / 1000;
+               } else if (amatch(a, "noupscale") &&
                           fname && size.x > 0) {
                        if (size.x < p->parent->w)
                                width = size.x;
                        if (size.y < p->parent->h)
                                height = size.y;
                } else if ((offset >= 0 || want_xypos) &&
-                          strstarts(line, "map:")) {
+                          amatch(a, "map") && v) {
                        /*
                         * A map is map:LxxxLxxxLxxxLxxx or similar
                         * Where each "Lxxx" recognised by a CAP followed
@@ -1055,11 +1095,12 @@ static int render_image(struct pane *p safe, struct pane *focus safe,
                         * If offset is in the map, then set ->cx,->cy to
                         * the appropriate location.
                         */
-                       map_offset = line+4 - orig_line;
-                       parse_map(line+4, &rows, &cols);
+                       map_offset = v - orig_line;
+                       parse_map(v, &rows, &cols);
+                       if (rows > 0 && cols > 0)
+                               snprintf(mode, sizeof(mode),
+                                        ":%dx%d", cols, rows);
                }
-               line += len;
-               line += strspn(line, ",");
        }
        pane_resize(p, (p->parent->w - width)/2, p->y, width, height);
 
@@ -1092,8 +1133,8 @@ static int render_image(struct pane *p safe, struct pane *focus safe,
        }
 
        if (fname && dodraw)
-               home_call(focus, "Draw:image", p, 5, NULL, fname,
-                         0, NULL, NULL, cols, rows);
+               home_call(focus, "Draw:image", p, 0, NULL, fname,
+                         0, NULL, mode);
 
        free(fname);
 
@@ -1110,7 +1151,7 @@ static int render_image(struct pane *p safe, struct pane *focus safe,
 
 DEF_CMD(renderline_draw)
 {
-       struct rline_data *rd = &ci->home->data;
+       struct rline_data *rd = ci->home->data;
        struct xy xy;
        int offset = -1;
 
@@ -1133,7 +1174,7 @@ DEF_CMD(renderline_draw)
 
 DEF_CMD(renderline_refresh)
 {
-       struct rline_data *rd = &ci->home->data;
+       struct rline_data *rd = ci->home->data;
        int offset = -1;
 
        if (rd->curspos >= 0)
@@ -1150,7 +1191,7 @@ DEF_CMD(renderline_refresh)
 
 DEF_CMD(renderline_measure)
 {
-       struct rline_data *rd = &ci->home->data;
+       struct rline_data *rd = ci->home->data;
        int ret;
 
        if (rd->image)
@@ -1179,7 +1220,7 @@ DEF_CMD(renderline_measure)
 
 DEF_CMD(renderline_findxy)
 {
-       struct rline_data *rd = &ci->home->data;
+       struct rline_data *rd = ci->home->data;
        const char *xyattr = NULL;
        int pos;
 
@@ -1203,7 +1244,7 @@ DEF_CMD(renderline_findxy)
 
 DEF_CMD(renderline_get)
 {
-       struct rline_data *rd = &ci->home->data;
+       struct rline_data *rd = ci->home->data;
        char buf[20];
        const char *val = buf;
 
@@ -1229,7 +1270,7 @@ static char *cvt(char *str safe)
         *    < stuff > to soh stuff stx
         *    </> to ack ack etx
         */
-       char *c;
+       char *c, *c1;
        for (c = str; *c; c += 1) {
                if (c[0] == soh || c[0] == ack)
                        break;
@@ -1249,27 +1290,37 @@ static char *cvt(char *str safe)
                        continue;
                }
                c[0] = soh;
-               while (c[0] && c[1] != '>')
-                       c++;
-               if (!c[0])
+               c += 1;
+               c1 = c;
+               while (*c && *c != '>') {
+                       if (*c == '\\' &&
+                           (c[1] == '\\' || c[1] == '>'))
+                               c++;
+                       *c1++ = *c++;
+               }
+               while (c1 < c)
+                       *c1++ = ack;
+               if (!*c)
                        break;
-               c[1] = stx;
+               *c = stx;
        }
        return str;
 }
 
 DEF_CMD(renderline_set)
 {
-       struct rline_data *rd = &ci->home->data;
+       struct rline_data *rd = ci->home->data;
        const char *old = rd->line;
        char *prefix = pane_attr_get(ci->focus, "prefix");
        bool word_wrap = pane_attr_get_int(ci->focus, "word-wrap", 0) != 0;
+       bool bg_changed = False;
 
        if (!ci->str)
                return -Enoarg;
        if (prefix)
-               prefix = cvt(strconcat(ci->home, "<bold>", prefix, "</>"));
-
+               prefix = strconcat(ci->home, ACK SOH "bold" STX,
+                                  prefix, // No mark in prefix!
+                                  ETX);
        if (prefix)
                rd->line = strconcat(NULL, prefix, ci->str);
        else
@@ -1277,8 +1328,22 @@ DEF_CMD(renderline_set)
        rd->prefix_bytes = strlen(prefix?:"");
        cvt(rd->line + rd->prefix_bytes);
 
+       if (ci->str2 && !rd->background) {
+               rd->background = strdup(ci->str2);
+               bg_changed = True;
+       } else if (!ci->str2 && rd->background) {
+               free(rd->background);
+               rd->background = NULL;
+               bg_changed = True;
+       } else if (ci->str2 && rd->background &&
+                  strcmp(ci->str2, rd->background) != 0) {
+               free(rd->background);
+               rd->background = strdup(ci->str2);
+               bg_changed = True;
+       }
+
        rd->curspos = ci->num;
-       if (strcmp(rd->line, old) != 0 ||
+       if (strcmp(rd->line, old) != 0 || bg_changed ||
            (old && word_wrap != rd->word_wrap)) {
                pane_damaged(ci->home, DAMAGED_REFRESH);
                pane_damaged(ci->home->parent, DAMAGED_REFRESH);
@@ -1290,9 +1355,9 @@ DEF_CMD(renderline_set)
        return 1;
 }
 
-DEF_CMD(renderline_close)
+DEF_CMD_CLOSED(renderline_close)
 {
-       struct rline_data *rd = &ci->home->data;
+       struct rline_data *rd = ci->home->data;
        struct render_item *ri = rd->content;
 
        free((void*)rd->line);
@@ -1306,6 +1371,7 @@ DEF_CMD(renderline_close)
        aupdate(&rd->wrap_head, NULL);
        aupdate(&rd->wrap_tail, NULL);
        aupdate(&rd->wrap_attr, NULL);
+       aupdate(&rd->background, NULL);
        return 1;
 }
 
@@ -1326,13 +1392,12 @@ DEF_CMD(renderline_attach)
                key_add(rl_map, "get-attr", &renderline_get);
                key_add(rl_map, "render-line:set", &renderline_set);
                key_add(rl_map, "Close", &renderline_close);
-               key_add(rl_map, "Free", &edlib_do_free);
        }
 
        p = pane_register(ci->focus, ci->num, &renderline_handle.c);
        if (!p)
                return Efail;
-       rd = &p->data;
+       rd = p->data;
        rd->line = strdup(ETX); // Imposible string
 
        return comm_call(ci->comm2, "cb", p);