From e7800d783fe07049150796d3f56f7c07220b58cd Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 11 Aug 2023 17:20:26 +1000 Subject: [PATCH] massive lib-renderline rewrite This now passes my tests - after adjusting some tests. lib-renderline is totally different. Parsing and processing are now distinct steps. Hopefully this will make it easier to work with in future, though it is still very rough in places, particularly with handling for \t tabs. I need to go through it several times and clean stuff up, but at least I now have a credible base to work on. Signed-off-by: NeilBrown --- DOC/Developer/06-rendering.md | 4 +- DOC/TODO.md | 4 + lib-messageline.c | 2 +- lib-renderline.c | 1825 ++++++++++++++++++--------------- tests.d/00-basic | 20 +- tests.d/01-c-mode | 2 +- tests.d/01-python | 2 +- tests.d/01-textfill | 32 +- tests.d/02-grep | 55 +- tests.d/02-presenter | 58 +- 10 files changed, 1080 insertions(+), 924 deletions(-) diff --git a/DOC/Developer/06-rendering.md b/DOC/Developer/06-rendering.md index c7fda0e7..17023d61 100644 --- a/DOC/Developer/06-rendering.md +++ b/DOC/Developer/06-rendering.md @@ -329,7 +329,7 @@ of attributes remains in effect until a balance "" or until the end of the line is reached. A less-than size can be included in the text by doubling it "<<". -Some attribute are understood by renderline, others are passed to the +Some attributes are understood by renderline, others are passed to the display to affect drawing of the text. Attributes understood by "renderline" are: @@ -412,7 +412,7 @@ errors. Properties 200-299 are for less stable results like search results of a selection. Priorities larger than 65534 are all treated identically, as are priorities less than 1. -Importantly and attribute which affects spacing, like 'tab' or 'centre' +Importantly any attribute which affects spacing, like 'tab' or 'centre' and which cannot be closed and re-opened must have priority of 0. Conversely the attribute "hide" is implicitly disabled when any other attribute has a higher priority, and so it should typically have the diff --git a/DOC/TODO.md b/DOC/TODO.md index ff63fda2..30eefd2e 100644 --- a/DOC/TODO.md +++ b/DOC/TODO.md @@ -9,8 +9,12 @@ the file. ### Triage +- [ ] \t handling in lib-renderlines got very ugly.. - [ ] find a better wat to handle "case when EOF is at the end" of a non-empty line. +- [ ] as renderline changes result if cursor is present, cursor pos + must ALWAYS be given. +- [ ] in search-replace if you repeat :A-Enter, chars get deleted - [X] input uses 0 for Efallthrough! - [X] call, not caLl in server.py - [X] lib-server received unexpected notification Close diff --git a/lib-messageline.c b/lib-messageline.c index e7beec6c..6cc0fe1a 100644 --- a/lib-messageline.c +++ b/lib-messageline.c @@ -233,7 +233,7 @@ DEF_CMD(messageline_refresh) else buf[0] = 0; /* justify 10pt (1 char) from right to avoid triggering a wrap */ - pane_str(mli->line, buf, "bold,fg:blue,right:10"); + pane_str(mli->line, buf, "bold,fg:blue,rtab"); } return 1; } diff --git a/lib-renderline.c b/lib-renderline.c index 878147b3..49936a5e 100644 --- a/lib-renderline.c +++ b/lib-renderline.c @@ -1,3 +1,7 @@ +// always remeasure? +// :xx is points: tab left_margin +// what exactly is left margin for wrapping +// wrap content with cursor should itself wrap if needed, appeared unwrapped if possible /* * Copyright Neil Brown ©2015-2023 * May be distributed under terms of GPLv2 - see file:COPYING @@ -25,30 +29,112 @@ #include #include #include +#include #define PANE_DATA_TYPE struct rline_data #include "core.h" #include "misc.h" -struct render_list { - struct render_list *next; - const char *text_orig; - const char *text safe, *attr safe; // both are allocated - bool wrap; /* set for LWS when word-wrap enabled, and when - * "wrap" attr seen. +/* There is one render_item entry + * - each string of text with all the same attributes + * - each individual TAB + * - each unknown control character + * - the \n \f or \0 which ends the line + * When word-wrap is enabled, strings of linear white-space get + * different attributes, so a different render_item entry. + * + * attributes understood at this level are: + * center or centre - equal space on either end of flushed line + * left:nn - left margin - in "points" + * right:nn - right margin + * tab:nn - move to nn from left margin or -nn from right margin + * rtab:nn - from here to next tab or eol right-aligned at nn + * ctab:nn - from here to next tab or eol centered at nn + * space-above:nn - extra space before (wrapped) line + * space-below:nn - extra space after (wrapped) line + * height:nn - override height. This effectively adds space above + * every individual line if the whole line is wrapped + * wrap - text with this attr can be hidden when used as a wrap + * point. + * wrap-margin - remember this x offset as left margin of wrapped lines + * wrap-head=xx - text is inserted at start of line when wrapped + * wrap-tail=xx - text to include at end of line when wrapped. This + * determines how far before right margin that wrapp is + * triggered. + * wrap-XXXX - attrs to apply to wrap head/tail. Anything not recognised + * has "wrap-" stripped and is used for the head and tail. + * hide - Text is hidden if cursor is not within range. + * + * "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 + * measured from the left margin or, which setting margins, from the + * left page edge. A negative value is measures from the right margin + * or right page edge. + * + */ + +/* When an entry is split for line-wrap: + * 'split_cnt' is count of splits (total lines - 1) + * ' split_list' is offsets from start where split happens + * 'x' position of wrapped portions is wrap_marign or head_length + * 'y' position of wrapped portions increases line_height for each + */ +struct render_item { + struct render_item *next; + const char *attr safe; + unsigned short *split_list; + unsigned short start, len; // in rd->line + unsigned short height, width; + signed short x,y; /* signed so x can go negative when shifted */ + signed short tab; + unsigned short wrap_x; + uint8_t split_cnt; /* wrap happens this many times at byte + * positions in split_list */ + uint8_t wrap; /* this and consecutive render_items + * with the same wrap number form an + * optional wrap point. It is only visible + * when not wrapped, or when cursor is in + * it. + */ + uint8_t hide; /* This and consecutive render_items + * with the same hide nmber form a + * hidden extent which is visible when + * the cursor is in it. */ - short x, width; - short cursorpos; - const char *xypos; /* location in text_orig where given x,y was found */ + bool wrap_margin:1; + bool hidden:1; + bool eol:1; + unsigned int tab_cols:4; /* For \t char */ + enum tab_align { + TAB_LEFT = 0, // No extra space (after tab stop) + TAB_RIGHT, // Add extra space here so there no space at + // next tab stop or margin + TAB_CENTRE, // Add extra space here, half of what TAB_RIGHT + // would insert + } tab_align:2; }; +/* A "tab" value of 0 means left margin, and negative is measured from right + * margin, so we need some other value to say "no value here" + */ +static const short TAB_UNSET = (1<<(14-2)); struct rline_data { - short prefix_len; + unsigned short prefix_bytes, prefix_pixels; short curs_width; - int scale; - int width; - const char *line; + short left_margin, right_margin; + short space_above, space_below; + unsigned short line_height, min_height; + unsigned short scale; + unsigned short width; + unsigned short ascent; + char *wrap_head, *wrap_tail, *wrap_attr; + int head_length, tail_length; + char *line safe; + bool word_wrap; + bool image; int curspos; + + struct render_item *content; }; #include "core-pane.h" @@ -58,409 +144,835 @@ enum { XYPOS, }; -static int draw_some(struct pane *p safe, struct pane *focus safe, - struct render_list **rlp safe, - int *x safe, - const char *start safe, const char **endp safe, - const char *attr safe, bool wrap, - int margin, int cursorpos, int xpos, - int scale) +/* sequentially set _attr to the an attr name, and _val to + * either the val (following ":") or NULL. + * _attr is valid up to : or , or < space and _val is valid up to , or w - margin; - struct render_list *rl; - - if (cursorpos > len) - cursorpos = -1; - if (len == 0 && cursorpos < 0) - /* Nothing to do */ - return OK; - - if (!wrap && strstr(attr, ",wrap,")) - wrap = True; - if ((*rlp == NULL || - ((*rlp)->next == NULL && (*rlp)->text_orig == NULL)) && - wrap && cursorpos < 0) - /* The text in a marker that causes a wrap is - * suppressed unless the cursor is in it. - * This will only ever be at start of line. text - * elsewhere is not active. - */ - return OK; - str = strndup(start, len); - if (*str == '\t') - /* TABs are only sent one at a time, and are rendered as space */ - *str = ' '; - if (xpos >= 0 && xpos >= *x && xpos < rmargin) { - /* reduce marking to given position, and record that - * as xypos when we hit it. - */ - rmargin = xpos; - ret = XYPOS; + const char *c = *cp; + const char *ret; + + if (!c) + return NULL; + while (c < end && *c != ':' && *c != ',') + c++; + if (c == end) { + *cp = NULL; + return NULL; } + if (*c == ',') { + while (*c == ',' && c < end) + c++; + if (c == end) { + *cp = NULL; + return NULL; + } + *cp = c; + return NULL; + } + c += 1; + ret = c; + while (c < end && *c != ',') + c++; + while (c < end && *c == ',') + c++; + if (c == end) + c = NULL; + *cp = c; + return ret; +} - rl = calloc(1, sizeof(*rl)); - cr = home_call_ret(all, focus, "Draw:text-size", p, - rmargin - *x, NULL, str, - scale, NULL, attr); - max = cr.i; - if (max == 0 && ret == XYPOS) { - /* Must have already reported XY position, don't do it again */ - rl->xypos = start; - ret = WRAP; - rmargin = p->w - margin; - cr = home_call_ret(all, focus, "Draw:text-size", p, - rmargin - *x, NULL, str, - scale, NULL, attr); - max = cr.i; +static bool amatch(const char *a safe, const char *m safe) +{ + while (*a && *a == *m) { + a += 1; + m += 1; } - if (max < len) { - /* It didn't all fit, so trim the string and - * try again. It must fit this time. - * I don't know what we expect to be different the second time.. - */ - str[max] = 0; - cr = home_call_ret(all, focus, "Draw:text-size", p, - rmargin - *x, NULL, str, - scale, NULL, attr); + if (*m) + /* Didn't match all of m */ + return False; + if (*a != ':' && *a != ',' && *a >= ' ') + /* Didn't match all of a */ + return False; + return True; +} + +static bool aprefix(const char *a safe, const char *m safe) +{ + while (*a && *a == *m) { + a += 1; + m += 1; } + if (*m) + /* Didn't match all of m */ + return False; + return True; +} - rl->text_orig = start; - rl->text = str; - rl->attr = strdup(attr); - rl->wrap = wrap; - rl->width = cr.x; - rl->x = *x; - *x += rl->width; - if (ret == XYPOS) - rl->xypos = start + strlen(str); - - if (cursorpos >= 0 && cursorpos <= len && cursorpos <= max) - rl->cursorpos = cursorpos; - else - rl->cursorpos = -1; - while (*rlp) - rlp = &(*rlp)->next; - *rlp = rl; - - if (max >= len) - return OK; - /* Didn't draw everything. */ - *endp = start + max; +static long anum(const char *v safe) +{ + char *end = NULL; + long ret = strtol(v, &end, 10); + if (end == v || !end || + (*end != ',' && *end >= ' ')) + /* Not a valid number - use zero */ + return 0; return ret; } -static char *get_last_attr(const char *attrs safe, const char *attr safe) +static void aupdate(char **cp safe, const char *v) { - const char *com = attrs + strlen(attrs); - int len = strlen(attr); + /* duplicate value at v and store in *cp, freeing what is there + * first + */ + const char *end = v; - for (; com >= attrs ; com--) { - int i = 1; - if (*com != ',' && com > attrs) - continue; - if (com == attrs) - i = 0; - if (strncmp(com+i, attr, len) != 0) - continue; - if (com[i+len] != ':') - continue; - com += i+len+1; - i = strcspn(com, ","); - return strndup(com, i); - } - return NULL; + while (end && *end != ',' && *end >= ' ') + end += 1; + + free(*cp); + if (v) + *cp = strndup(v, end-v); + else + *cp = NULL; +} + +static void aappend(struct buf *b safe, char const *a safe) +{ + const char *end = a; + while (*end >= ' ' && *end != ',') + end++; + buf_concat_len(b, a, end-a); + buf_append(b, ','); +} + +static void add_render(struct rline_data *rd safe, struct render_item **safe*rlp safe, + const char *start safe, const char *end safe, + char *attr safe, + short tab, enum tab_align align, + bool wrap_margin, + short wrap, short hide) +{ + struct render_item *rl; + struct render_item **rlend = *rlp; + + alloc(rl, pane); + rl->attr = strdup(attr); + rl->start = start - rd->line; + rl->len = end - start; + rl->tab_align = align; + rl->tab = tab; + rl->wrap = wrap; + rl->hide = hide; + rl->wrap_margin = wrap_margin; + rl->eol = !!strchr("\n\f\0", *start); + *rlend = rl; + rlend = &rl->next; + *rlp = rlend; } -static int flush_line(struct pane *p safe, struct pane *focus safe, int dodraw, - struct render_list **rlp safe, - int y, int ascent, int scale, int wrap_pos, int *wrap_margin safe, - int *wrap_prefix_sizep, - const char **xypos, const char **xyattr, const char **cursattr) +static inline bool is_ctrl(unsigned int c) { - /* Flush a render_list returning x-space used. - * If wrap_pos is > 0, stop rendering before last entry with the - * "wrap" attribute; draw the wrap-tail at wrap_pos, and insert - * wrap_head into the render_list before the next line. - * If any render_list entry reports xypos, store that in xypos with - * matching attributes in xyattr. + return c < ' ' || + (c >= 128 && c < 128 + ' '); +} + +static void parse_line(struct rline_data *rd safe) +{ + /* Parse out markup in line into a renderlist with + * global content directly in rd. */ - struct render_list *last_wrap = NULL, *end_wrap = NULL, *last_rl = NULL; - int in_wrap = 0; - int wrap_len = 0; /* length of text in final section */ - int wrap_prefix_size = 0; /* wrap margin plus prefix */ - struct render_list *rl, *tofree; - int x = 0; - char *head; - - if (!*rlp) - return 0; - for (rl = *rlp; wrap_pos && rl; rl = rl->next) { - if (rl->wrap && rl != *rlp) { - if (!in_wrap) { - last_wrap = rl; - in_wrap = 1; - wrap_len = 0; - } - wrap_len += strlen(rl->text); - end_wrap = rl->next; - } else { - if (in_wrap) - end_wrap = rl; - in_wrap = 0; - } - last_rl = rl; + struct buf attr, wrapattr; + struct render_item *rl = NULL, **rlend = &rl; + const char *line = rd->line; + bool wrap_margin = False; + int tab = TAB_UNSET, align = TAB_LEFT; + int hide = 0, hide_num = 0, hide_depth = 0; + int wrap = 0, wrap_num = 0, wrap_depth = 0; + unsigned char c; + + rd->left_margin = rd->right_margin = 0; + rd->space_above = rd->space_below = 0; + rd->min_height = 0; + aupdate(&rd->wrap_head, NULL); + aupdate(&rd->wrap_tail, NULL); + aupdate(&rd->wrap_attr, NULL); + + if (!line) { + rd->image = False; + return; + } + rd->image = strstarts(line, SOH "image:"); + if (rd->image) + return; + buf_init(&attr); + buf_init(&wrapattr); + + rl = rd->content; + rd->content = NULL; + while (rl) { + struct render_item *r = rl; + rl = r->next; + free(r->split_list); + unalloc_str_safe(r->attr, pane); + unalloc(r, pane); } - if (last_wrap) - /* A wrap was found, so finish there */ - last_rl = last_wrap; - - for (rl = *rlp; rl && rl != last_wrap; rl = rl->next) { - int cp = rl->cursorpos; - if (!*wrap_margin && strstr(rl->attr, ",wrap-margin,")) - *wrap_margin = rl->x; + do { + const char *st = line; + c = *line++; + + while (c >= ' ' && + (!rd->word_wrap || c != ' ')) + c = *line++; + + if (line - 1 > st) { + /* All text from st to line-1 has "attr' */ + add_render(rd, &rlend, st, line-1, buf_final(&attr), + tab, align, wrap_margin, wrap, hide); + align = TAB_LEFT; + tab = TAB_UNSET; + wrap_margin = False; + st = line - 1; + } + switch (c) { + case soh: { + int old_len; + const char *a, *v; + st = line; + /* Move 'line' over the attrs */ + while (*line && line[-1] != stx) + line += 1; - if (wrap_pos && - cp >= (int)strlen(rl->text) + wrap_len) - /* Don't want to place cursor at end of line before - * the wrap, only on the next line after the - * wrap. + /* A set of attrs begins and ends with ',' so that + * ",," separates sets of attrs + * An empty set will be precisely 1 ','. We strip + * "attr," as long as we can, then strip one more ',', + * which should leave either a trailing comma, or an + * empty string. */ - cp = -1; - - x = rl->x; - if (dodraw) - home_call(focus, "Draw:text", p, cp, NULL, rl->text, - scale, NULL, rl->attr, - x, y + ascent); - if (xypos && rl->xypos) { - *xypos = rl->xypos; - if (xyattr) { - /* FIXME this is a bit of a hack. - * We still 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_list *rl2; - int ax = 0; - for (rl2 = *rlp; rl2 != rl; rl2 = rl2->next) - if (strcmp(rl2->attr, rl->attr) == 0) - ax = rl2->x; - snprintf(buf, sizeof(buf), "%dx%d,", ax, y); - *xyattr = strconcat(p, buf, rl->attr); + buf_append(&attr, ','); + old_len = attr.len; + foreach_attr(a, v, st, line) { + if (amatch(a, "centre") || amatch(a, "center") || + amatch(a, "ctab")) { + if (v) + tab = anum(v); + align = TAB_CENTRE; + } else if (amatch(a, "tab") && v) { + tab = anum(v); + align = TAB_LEFT; + } else if (amatch(a, "rtab")) { + align = TAB_RIGHT; + } else if (amatch(a, "left") && v) { + rd->left_margin = anum(v); + } else if (amatch(a, "right") && v) { + rd->right_margin = anum(v); + } else if (amatch(a, "space-above") && v) { + rd->space_above = anum(v); + } else if (amatch(a, "space-below") && v) { + rd->space_below = anum(v); + } else if (amatch(a, "height") && v) { + rd->min_height = anum(v); + } else if (amatch(a, "wrap")) { + wrap = ++wrap_num; + wrap_depth = old_len; + } else if (amatch(a, "wrap-margin")) { + wrap_margin = True; + } else if (amatch(a, "wrap-head")) { + aupdate(&rd->wrap_head, v); + } else if (amatch(a, "wrap-tail")) { + aupdate(&rd->wrap_tail, v); + } else if (aprefix(a, "wrap-")) { + aappend(&wrapattr, a+5); + } else if (amatch(a, "hide")) { + hide = ++hide_num; + hide_depth = old_len; + } else + aappend(&attr, a); } + break; + } + case etx: + /* strip last set of attrs */ + while (attr.len >= 2 && + attr.b[attr.len-1] == ',' && + attr.b[attr.len-2] != ',') { + /* strip this attr */ + attr.len -= 2; + while (attr.len && attr.b[attr.len-1] != ',') + attr.len -= 1; + } + /* strip one more ',' */ + if (attr.len > 0) + attr.len -= 1; + if (attr.len <= wrap_depth) + wrap = 0; + if (attr.len <= hide_depth) + hide = 0; + break; + case ack: + /* Just ignore this */ + break; + case ' ': + /* This and following spaces are wrappable */ + st = line; + while (*line == ' ') + line += 1; + wrap = ++wrap_num; + add_render(rd, &rlend, st - 1, line, buf_final(&attr), + tab, align, wrap_margin, wrap, hide); + tab = TAB_UNSET; + align = TAB_LEFT; + wrap_margin = False; + wrap = 0; + break; + case '\0': + case '\n': + case '\f': + case '\t': + default: + /* Each tab gets an entry of its own, as does any + * stray control character. + * \f \n and even \0 do. These are needed for + * easy cursor placement. + */ + add_render(rd, &rlend, st, line, buf_final(&attr), + tab, align, wrap_margin, wrap, hide); + tab = TAB_UNSET; + align = TAB_LEFT; + wrap_margin = False; + break; } - x += rl->width; - if (cp >= 0 && cursattr) - *cursattr = strsave(p, rl->attr); + } while (c); + + rd->content = rl; + free(attr.b); + if (buf_final(&wrapattr)[0]) + rd->wrap_attr = buf_final(&wrapattr); + else { + free(wrapattr.b); + rd->wrap_attr = strdup(",fg:blue,underline,"); } - /* Draw the wrap text if it contains the cursor */ - for (; rl && rl != end_wrap; rl = rl->next) { - int cp = rl->cursorpos; - if (cp >= (int)strlen(rl->text)) - cp = -1; - - if (cp >= 0 && dodraw) - home_call(focus, "Draw:text", p, cp, NULL, rl->text, - scale, NULL, rl->attr, - rl->x, y + ascent); - x = rl->x + rl->width; - } - /* Draw the wrap-tail */ - if (wrap_pos && last_rl && dodraw) { - char *e = get_last_attr(last_rl->attr, "wrap-tail"); - if (e || !last_wrap) - home_call(focus, "Draw:text", p, -1, NULL, e ?: "\\", - scale, NULL, "underline,fg:blue", - wrap_pos, y + ascent); - free(e); +} + +static inline struct call_return do_measure(struct pane *p safe, + char *str safe, int len, + int offset, int scale, + const char *attr) +{ + struct call_return cr; + char tmp; + + if (len >= 0) { + tmp = str[len]; + str[len] = 0; } + cr = call_ret(all, "Draw:text-size", p, + offset, NULL, str, + scale, NULL, attr); + if (len >= 0) + str[len] = tmp; + return cr; +} - tofree = *rlp; - *rlp = end_wrap; - - /* Queue the wrap-head for the next flush */ - if (wrap_pos && last_rl) { - head = get_last_attr(last_rl->attr, "wrap-head"); - if (head) { - struct call_return cr = - home_call_ret(all, focus, "Draw:text-size", p, - p->w, NULL, head, - scale, NULL, last_rl->attr); - rl = calloc(1, sizeof(*rl)); - rl->text = head; - rl->attr = strdup(last_rl->attr); // FIXME underline,fg:blue ??? - rl->width = cr.x; - rl->x = *wrap_margin; - rl->cursorpos = -1; - rl->next = *rlp; - *rlp = rl; - /* 'x' is how much to shift-left remaining rl entries, - * Don't want to shift them over the wrap-head - */ - x -= cr.x; - wrap_prefix_size += cr.x; - } - x -= *wrap_margin; - wrap_prefix_size += *wrap_margin; +static inline void do_draw(struct pane *p safe, + struct pane *focus safe, + char *str safe, int len, int tab_cols, + int offset, int scale, + const char *attr, int x, int y) +{ + char tmp; + char tb[] = " "; + + if (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. + */ + if (offset == 0) + home_call(focus, "Draw:text", p, offset, NULL, "", + scale, NULL, attr, x, y); + return; } - if (wrap_prefix_sizep) - *wrap_prefix_sizep = wrap_prefix_size; - - for (rl = tofree; rl && rl != end_wrap; rl = tofree) { - tofree = rl->next; - free((void*)rl->text); - free((void*)rl->attr); - free(rl); + if (str[0] == '\t') { + /* Tab needs special handling */ + str = tb; + len = tab_cols; } + if (len >= 0) { + tmp = str[len]; + str[len] = 0; + } + home_call(focus, "Draw:text", p, offset, NULL, str, + scale, NULL, attr, x, y); + if (len >= 0) + str[len] = tmp; +} - /* Shift remaining rl to the left */ - for (rl = end_wrap; rl; rl = rl->next) - rl->x -= x; - return x; +static void add_split(struct render_item *rl safe, int split) +{ + int i = rl->split_cnt; + rl->split_cnt += 1; + rl->split_list = realloc(rl->split_list, + sizeof(rl->split_list[0]) * rl->split_cnt); + rl->split_list[i] = split; } -static void update_line_height_attr(struct pane *p safe, - struct pane *focus safe, - int *h safe, - int *a safe,int *w, char *attr safe, - char *str safe, int scale) +static int calc_tab(int num, int margin, int scale) { - struct call_return cr = home_call_ret(all, focus, "Draw:text-size", p, - -1, NULL, str, - scale, NULL, attr); - if (cr.y > *h) - *h = cr.y; - if (cr.i2 > *a) - *a = cr.i2; - if (w) - *w += cr.x; + if (num > 0) + return num * scale / 1000; + if (-num > margin) + return 0; + return margin + num * scale / 1000; } -static void strip_ctrl(char *s safe) +static bool measure_line(struct pane *p safe, struct pane *focus safe, int offset) { - while (*s) { - if (*s < ' ' || ((unsigned)*s >= 128 && (unsigned)*s < 128+' ')) - *s = 'M'; - s += 1; + /* First measure each render_item entry setting + * height, ascent, width. + * Then use that with tab information to set 'x' position for + * each unit. + * Finally identify line-break locations if needed and set 'y' + * positions + */ + struct rline_data *rd = &p->data; + struct render_item *rl, *wraprl; + int shift_left = pane_attr_get_int(focus, "shift_left", 0); + bool wrap = shift_left < 0; + int wrap_margin; + int right_margin = p->w - (rd->right_margin * rd->scale / 1000); + int xdiff, ydiff; + struct call_return cr; + int x, y; + bool eop = False; + + if (!rd->content) + return eop; + cr = do_measure(p, "M", -1, -1, rd->scale,""); + rd->curs_width = cr.x; + rd->line_height = cr.y; + rd->ascent = cr.i2; + if (rd->min_height * rd->scale / 1000 > rd->line_height) + rd->line_height = rd->min_height * rd->scale / 1000; + + if (rd->wrap_head) { + cr = do_measure(p, rd->wrap_head, -1, -1, rd->scale, + rd->wrap_attr); + rd->head_length = cr.x; } + cr = do_measure(p, rd->wrap_tail ?: "\\", -1, -1, rd->scale, + rd->wrap_attr); + rd->tail_length = cr.x; + + for (rl = rd->content; rl; rl = rl->next) { + char tmp[4] = ""; + char *txt = tmp; + int len = -1; + if (!is_ctrl(rd->line[rl->start])) { + txt = rd->line + rl->start; + len = rl->len; + } else if (rl->eol) { + /* Ensure attributes of newline add to line height. + * The width will be ignored. */ + strcpy(tmp, "M"); + if (rd->line[rl->start] == '\f') + eop = True; + } else if (rd->line[rl->start] == '\t') { + strcpy(tmp, " "); + } else { + strcpy(tmp, "^x"); + tmp[1] = '@' + (rd->line[rl->start] & 31); + } + cr = do_measure(p, txt, len, -1, rd->scale, rl->attr); + if (cr.y > rd->line_height) + rd->line_height = cr.y; + rl->height = cr.y; + if (cr.i2 > rd->ascent) + rd->ascent = cr.i2; + rl->width = rl->eol ? 0 : cr.x; + rl->hidden = False; + + if (rl->start <= offset && offset <= rl->start + rl->len) { + cr = do_measure(p, "M", -1, -1, rd->scale, rl->attr); + rd->curs_width = cr.x; + } + + rl->split_cnt = 0; + free(rl->split_list); + rl->split_list = NULL; + } + /* Set 'x' position honouring tab stops, and set length + * of "\t" characters. Also handle \n and \f. + */ + x = (rd->left_margin * rd->scale / 1000) - (shift_left > 0 ? shift_left : 0); + y = rd->space_above * rd->scale / 1000; + rd->width = 0; + for (rl = rd->content; rl; rl = rl->next) { + int w, margin; + struct render_item *rl2; + rl->y = y; + if (rl->tab != TAB_UNSET) + x = (rd->left_margin * rd->scale / 1000) + calc_tab(rl->tab, right_margin, rd->scale); + if (rl->eol) { + /* EOL */ + if (x > rd->width) + rd->width = x; + rl->x = x; + x = 0; /* Don't include shift. probably not margin */ + if (rd->line[rl->start]) + y += rd->line_height; + continue; + } + if (rl->tab_align == TAB_LEFT) { + rl->x = x; + if (rd->line[rl->start] == '\t') { + int col = x / rl->width; + int cols= 8 - (col % 8); + rl->tab_cols = cols; + rl->width *= cols; + } + x += rl->width; + continue; + } + w = rl->width; + for (rl2 = rl->next; + rl2 && rl2->tab_align == TAB_LEFT && rl2->tab == TAB_UNSET; + rl2 = rl2->next) + w += rl2->width; + while (rl2 && rl2->tab == TAB_UNSET) + rl2 = rl2->next; + margin = right_margin; + if (rl2) + margin = (rd->left_margin * rd->scale / 1000) + calc_tab(rl2->tab, right_margin, rd->scale); + if (rl->tab_align == TAB_RIGHT) { + margin -= rd->tail_length;// FIXME don't want this HACK + x = x + margin - x - w; + } else + x = x + (margin - x - w) / 2; + rl->x = x; + while (rl->next && rl->next->next && rl->next->tab_align == TAB_LEFT) { + x += rl->width; + rl = rl->next; + rl->x = x; + rl->y = y; + } + } + + /* Now we check to see if the line needs to be wrapped and + * if so, adjust some y values and reduce x. If we need to + * split an individual entry we create an array of split points. + */ + xdiff = 0; ydiff = 0; y = 0; + wraprl = NULL; + wrap_margin = rd->head_length; + for (rl = rd->content ; wrap && rl ; rl = rl->next) { + int splitpos; + char *str; + int len; + char tb[] = " "; + if (rl->wrap && (wraprl == NULL || rl->wrap != wraprl->wrap)) + wraprl = rl; + if (rl->wrap_margin) + wrap_margin = rl->x; + rl->wrap_x = wrap_margin; + rl->x += xdiff; + rl->y += ydiff; + y = rl->y; + if (rl->eol) { + xdiff = 0; + continue; + } + if (rl->x + rl->width <= right_margin - rd->tail_length) + continue; + /* This doesn't fit here */ + if (wraprl) { + /* Move wraprl to next line and hide it unless it contains cursor */ + int xd = wraprl->x - wrap_margin; + struct render_item *wraprl2, *rl2; + + /* Find last ritem in wrap region.*/ + for (wraprl2 = wraprl ; + wraprl2->next && wraprl2->next->wrap == wraprl->wrap ; + wraprl2 = wraprl2->next) + ; + if (wraprl2->next) + xd = wraprl2->next->x - wrap_margin; + if (offset >= 0 && + offset >= wraprl->start && + offset <= wraprl2->start + wraprl2->len) { + /* Cursor is here, so keep it visible. + * If we are still in the wrap region, pretend + * it didn't exist, else move first item + * after it to next line + */ + if (rl->wrap == wraprl->wrap) + goto normal_wrap; + } else { + /* Hide the wrap region */ + while (wraprl != wraprl2) { + wraprl->hidden = True; + wraprl = wraprl->next; + } + if (wraprl) + wraprl->hidden = True; + while (rl->next && rl->next->hidden) + rl = rl->next; + } + for (rl2 = wraprl2->next ; rl2 && rl2 != rl->next; rl2 = rl2->next) { + rl2->y += rd->line_height; + rl2->x -= xd; + } + xdiff -= xd; + ydiff += rd->line_height; + wraprl = NULL; + continue; + } + normal_wrap: + if (rl->x >= right_margin - rd->tail_length) { + /* This rl moves completely to next line */ + xdiff -= rl->x - wrap_margin; + rl->x = wrap_margin; + ydiff += rd->line_height; + rl->y += rd->line_height; + wraprl = NULL; + continue; + } + /* Need to split this rl into two or more pieces */ + x = rl->x; + splitpos = 0; + str = rd->line + rl->start; + len = rl->len; + if (*str == '\t') { + str = tb; + len = rl->tab_cols; + } + while (1) { + cr = do_measure(p, str + splitpos, + len - splitpos, + right_margin - rd->tail_length - x, + rd->scale, rl->attr); + if (cr.i >= len - splitpos) + /* Remainder fits now */ + break; + /* re-measure the first part */ + cr = do_measure(p, str + splitpos, + cr.i, + right_margin - rd->tail_length - x, + rd->scale, rl->attr); + + ydiff += rd->line_height; + xdiff -= cr.x; // fixme where does wrap_margin fit in there + if (splitpos == 0) + xdiff -= rl->x; + splitpos += cr.i; + x = wrap_margin; + add_split(rl, splitpos); + } + } + /* We add rd->line_height for the EOL, whether a NL is present of not */ + ydiff += rd->line_height; + pane_resize(p, p->x, p->y, p->w, + (rd->space_above + rd->space_below) * rd->scale / 1000 + + ydiff); + attr_set_int(&p->attrs, "line-height", rd->line_height); + return eop; } -static void update_line_height(struct pane *p safe, struct pane *focus safe, - int *h safe, int *a safe, - int *w safe, int *center, const char *line safe, - int scale) +static void draw_line(struct pane *p safe, struct pane *focus safe, int offset) { - /* Extract general geometry information from the line. - * Maximum height and ascent are extracted together with total - * with and h-alignment info: - * 0 - left, 1 = centered, >=2 = space-on-left, <=-2 = space from right - */ - struct buf attr; - int attr_found = 0; - const char *segstart = line; - int above = 0, below = 0; + struct rline_data *rd = &p->data; + struct render_item *rl; + char *wrap_tail = rd->wrap_tail ?: "\\"; + char *wrap_head = rd->wrap_head ?: ""; - buf_init(&attr); - buf_append(&attr, ','); - while (*line) { - char c = *line++; - const char *st = line; + home_call(focus, "Draw:clear", p); + + if (!rd->content) + return; + for (rl = rd->content ; rl; rl = rl->next) { + int split = 0; + short y = rl->y; + int cpos; - if (c != soh && c != etx && c != ack) + if (rl->hidden) continue; + if (offset < 0 || offset >= rl->start + rl->len) + cpos = -1; + else if (offset < rl->start) + cpos = 0; + else + cpos = offset - rl->start; + + do_draw(p, focus, + rd->line + rl->start, rl->split_list ? rl->split_list[0]: rl->len, + rl->split_list ? rl->split_list[0] : rl->tab_cols, + cpos, rd->scale, rl->attr, + rl->x, y + rd->ascent); + if (!rl->split_cnt && rl->next && + !rl->next->eol && rl->next->y != rl->y) { + /* we are about to wrap - draw the markers */ + if (*wrap_tail) + do_draw(p, focus, wrap_tail, -1, 0, -1, rd->scale, + rd->wrap_attr, + p->w - rd->tail_length, + y + rd->ascent); + if (*wrap_head) + do_draw(p, focus, wrap_head, -1, 0, -1, rd->scale, + rd->wrap_attr, + 0, y + rd->ascent + rd->line_height); + } - if (line - 1 > segstart) { - char *l = strndup(segstart, line - 1 - segstart); - strip_ctrl(l); - update_line_height_attr(p, focus, h, a, w, - buf_final(&attr), l, scale); - free(l); + while (split < rl->split_cnt || + (rl->next && rl->next->next && rl->next->y > y)) { + /* line wrap here */ + /* don't show head/tail for wrap-regions */ + if (*wrap_tail /*&& !rl->wrap*/) + do_draw(p, focus, wrap_tail, -1, 0, -1, rd->scale, + rd->wrap_attr, + p->w - rd->tail_length, + y + rd->ascent); + y += rd->line_height; + if (*wrap_head /*&& !rl->wrap*/) + do_draw(p, focus, wrap_head, -1, 0, -1, rd->scale, + rd->wrap_attr, + 0, y + rd->ascent); + if (rl->split_list && split < rl->split_cnt) { + int end = rl->len; + char *str = rd->line + rl->start + rl->split_list[split]; + if (rd->line[rl->start] == '\t') { + end = rl->tab_cols; + str = "\t"; + } + if (split+1 < rl->split_cnt) + end = rl->split_list[split+1]; + do_draw(p, focus, + str, + end - rl->split_list[split], + end - rl->split_list[split], + cpos - rl->split_list[split], + rd->scale, + rl->attr, rd->left_margin + rd->head_length, + y + rd->ascent); + split += 1; + } } - if (c == soh) { - char *c2; - char *b; - const char *aend; + if (offset < rl->start + rl->len) + offset = -1; + } +} - /* move 'line' over the attrs */ - while (*line && line[-1] != stx) - line += 1; - segstart = line; - - /* attrs must not contain ",," */ - aend = strstr(st, ",,"); - if (aend) - aend += 1; - if (!aend || line < aend) - aend = line; - - buf_concat_len(&attr, st, aend-st); - /* Replace trailing 'stx' with ',', and append ',' - * so ",," marks where to strip back to when we - * find etx - */ - attr.b[attr.len-1] = ','; - buf_append(&attr, ','); - b = buf_final(&attr); - if (center && strstr(b, ",center,")) - *center = 1; - if (center && (c2=strstr(b, ",left:")) != NULL) - *center = 2 + atoi(c2+6) * scale / 1000; - if (center && (c2=strstr(b, ",right:")) != NULL) - *center = -2 - atoi(c2+7) * scale / 1000; - if ((c2=strstr(b, ",space-above:")) != NULL) - above = atoi(c2+13) * scale / 1000; - if ((c2=strstr(b, ",space-below:")) != NULL) - below = atoi(c2+13) * scale / 1000; - if ((c2=strstr(b, ",tab:")) != NULL) - *w = atoi(c2+5) * scale / 1000; - attr_found = 1; - update_line_height_attr(p, focus, h, a, w, b, "", - scale); - } else if (c == etx) { - segstart = line; - /* strip back to ",," */ - if (attr.len >= 2) - attr.len -= 2; - while (attr.len > 0 && - (attr.b[attr.len] != ',' || - attr.b[attr.len+1] != ',')) - attr.len -= 1; - } else if (c == ack) { - segstart = line; +static int find_xy(struct pane *p safe, struct pane *focus safe, + short x, short y, const char **xyattr) +{ + /* Find the location in ->line that is best match for x,y. + * If x,y is on the char at that location, when store attributes + * for the char in xyattr + * We always return a location, even if no xyattr. + * We use the last render_item that is not definitely after x,y + * We do not consider the eol render_item + */ + struct call_return cr; + struct rline_data *rd = &p->data; + struct render_item *r, *rl = NULL; + + if (!rd->content) + return 0; + for (r = rd->content; r ; r = r->next) { + int split; + if (r->y <= y && r->x <= x) + rl = r; + for (split = 0; split < r->split_cnt; split++) { + if (r->y + (split + 1) * rd->line_height && + r->x <= r->wrap_x) + rl = r; } } - if (line > segstart && line[-1] == '\n') - line -= 1; - if (line > segstart || !attr_found) { - char *l = strndup(segstart, line - segstart); - strip_ctrl(l); - update_line_height_attr(p, focus, h, a, w, - buf_final(&attr), l, scale); - free(l); + if (!rl) + return 0; + if (rl->eol) + /* newline or similar. Can only be at start */ + return rl->start; + if (rl->x + rl->width > x && + rl->y + rl->height > y && + xyattr) + *xyattr = rl->attr; + if (rd->line[rl->start] == '\t') + cr.i = 0; + else + cr = do_measure(p, rd->line + rl->start, rl->len, + x - rl->x, rd->scale, rl->attr); + return rl->start + cr.i; +} + +static struct xy find_curs(struct pane *p safe, int offset, const char **cursattr) +{ + struct call_return cr; + struct xy xy = {0,0}; + int split; + int st; + struct rline_data *rd = &p->data; + struct render_item *r, *rl = NULL; + + for (r = rd->content; r; r = r->next) { + if (offset < r->start) + break; + rl = r; + } + if (!rl) { + /* This should be impossible as the eol goes past + * the largest offset. + */ + return xy; + } + if (offset < rl->start) + /* in the attrs?? */ + offset = 0; + else + offset -= rl->start; + /* offset now from rl->start */ + if (rd->line[rl->start] == '\t' && offset) + offset = rl->tab_cols; + if (cursattr) + *cursattr = rl->attr; + st = 0; + for (split = 0; split < rl->split_cnt && rl->split_list; split ++) { + if (offset < rl->split_list[split]) + break; + st = rl->split_list[split]; + } + if (rl->eol) + cr.x = offset ? rl->width : 0; + else { + char *str = rd->line + rl->start + st; + char tb[] = " "; + if (rd->line[rl->start] == '\t') { + str = tb; + if (offset) + offset = rl->tab_cols; + } + cr = do_measure(p, str, offset - st, + -1, rd->scale, rl->attr); + } + if (split) + xy.x = cr.x; /* FIXME margin?? */ + else + xy.x = rl->x + cr.x; + xy.y = rl->y + split * rd->line_height; + if (rl->next == NULL && offset > rl->len) { + /* After the newline ? Go to next line */ + xy.x = 0; + xy.y += rd->line_height; } - *h += above + below; - *a += above; - free(buf_final(&attr)); + return xy; } static void parse_map(const char *map safe, short *rowsp safe, short *colsp safe) @@ -520,7 +1032,6 @@ static int render_image(struct pane *p safe, struct pane *focus safe, while (*line && *line != stx && *line != etx) { int len = strcspn(line, "," STX ETX); - if (strstarts(line, "image:")) { fname = strndup(line+6, len-6); if (!ssize || @@ -616,477 +1127,98 @@ static int render_image(struct pane *p safe, struct pane *focus safe, return 1; } -static void set_xypos(struct render_list *rlst, - struct pane *p safe, struct pane *focus safe, int posx, - int scale) -{ - /* Find the text postition in the rlst which corresponds to - * the screen position posx, and report attribtes there too. - */ - for (; rlst && rlst->x <= posx; rlst = rlst->next) { - if (rlst->x + rlst->width < posx) - continue; - - if (rlst->x == posx) - rlst->xypos = rlst->text_orig; - else { - struct call_return cr = - home_call_ret(all, focus, "Draw:text-size", p, - posx - rlst->x, NULL, rlst->text, - scale, NULL, rlst->attr); - rlst->xypos = rlst->text_orig + cr.i; - } - } -} - -/* Render a line, with attributes and wrapping. - * The marked-up text to be processed has already been provided with - * render-line:set. It is in rd->line; - * ->num is <0, or an index into ->str where the cursor is, - * but is ignored for Refresh. which uses ->curspos for any cursor. - * and the x,y co-ords will be stored in p->cx,p->cy - * If key is "render-line:draw" or "Refresh" then send drawing commands, - * otherwise just perform measurements. - * If key is "render-line:findxy", then only measure until the position - * in x,y is reached, then return an index into ->str of where we reached. - * Store the attributes active at the time so they can be fetched later. - */ -DEF_CMD(renderline) +DEF_CMD(renderline_draw) { - struct pane *p = ci->home; - struct rline_data *rd = &p->data; - struct pane *focus = ci->focus; - const char *line = rd->line; - int dodraw = (strcmp(ci->key, "render-line:draw") == 0 || - strcmp(ci->key, "Refresh") == 0); - short posx; - short offset = strcmp(ci->key, "Refresh") == 0 ? rd->curspos : ci->num; - int x = 0; - int y = 0; - const char *line_start; - const char *start; - struct buf attr; - unsigned char ch; - int wrap_offset = 0; /*number of columns displayed in earlier lines, - * use for calculating TAB size. */ - int wrap_margin = 0; /* left margin for wrap - carried forward from - * line to line. */ - int in_tab = 0; - int shift_left = pane_attr_get_int(focus, "shift_left", 0); - bool word_wrap = pane_attr_get_int(focus, "word-wrap", 0) != 0; - bool in_lws = False; - bool seen_non_space = False; - int wrap = shift_left < 0; - char *prefix = pane_attr_get(focus, "prefix"); - int line_height = 0; - int ascent = -1; - int mwidth = -1; - int ret = OK; - int twidth = 0; - int center = 0; - int margin; - int end_of_page = 0; - struct render_list *rlst = NULL; - const char *xypos = NULL; - const char *ret_xypos = NULL; - const char *xyattr = NULL; - char *ret_xyattr = NULL; - const char *cursattr = NULL; - /* want_xypos becomes 2 when the pos is found */ - int want_xypos = strcmp(ci->key, "render-line:findxy") == 0; - struct xy xyscale = pane_scale(focus); - int scale = xyscale.x; - short cx = -1, cy = -1; - - if (!line) - return Enoarg; - /* line_start doesn't change - * start is the start of the current segment(since attr) - * is update after we call draw_some() - * line is where we are now up to. - */ - start = line_start = line; - - rd->scale = scale; - - if (dodraw) - home_call(focus, "Draw:clear", p); - - if (strstarts(line, SOH "image:")) - /* For now an must be on a line by itself. - * Maybe this can be changed later if I decide on - * something that makes sense. - */ - return render_image(p, focus, line, dodraw, scale, - offset, want_xypos, ci->x, ci->y); - - update_line_height(p, focus, &line_height, &ascent, &twidth, ¢er, - line, scale); + struct rline_data *rd = &ci->home->data; + struct xy xy; + int offset = -1; - if (line_height <= 0) - return Einval; + if (ci->num >= 0) + offset = rd->prefix_bytes + ci->num; - if (!wrap) - x -= shift_left; + if (rd->image) + render_image(ci->home, ci->focus, rd->line, True, + rd->scale, offset, False, 0, 0); else - shift_left = 0; - - mwidth = 0; - update_line_height_attr(p, focus, &line_height, &ascent, &mwidth, - "", "M", scale); - rd->curs_width = mwidth; - - if (prefix) { - const char *s = prefix + strlen(prefix); - update_line_height_attr(p, focus, &line_height, &ascent, NULL, - "bold", prefix, scale); - draw_some(p, focus, &rlst, &x, prefix, &s, ",bold,", False, - 0, -1, -1, scale); - rd->prefix_len = x + shift_left; - } else - rd->prefix_len = 0; - - if (center == 1) - x += (p->w - x - twidth) / 2; - if (center >= 2) - x += center - 2; - if (center <= -2) - x = p->w - x - twidth + (center + 2); - /* tabs are measured against this margin */ - margin = x; - - /* The attr string starts and ends with ',' and - * attrs are separated by commas. - * Groups of attrs to be popped by the next - * are separated by ",," - */ - buf_init(&attr); - buf_append(&attr, ','); - - /* If findxy was requested, ci->x and ci->y tells us - * what to look for, and we return index into line where this - * co-ordinate was reached. - * want_xypos will be set to 2 when we pass the co-ordinate - * At that time ret_xypos is set, to be used to provide return value. - * This might happen when y exceeds ypos, or we hit end-of-page. - */ - + draw_line(ci->home, ci->focus, offset); - while (*line && y < p->h && !end_of_page) { - if (mwidth <= 0) { - /* mwidth is recalculated whenever attrs change */ - struct call_return cr = home_call_ret(all, focus, - "Draw:text-size", p, - -1, NULL, "M", - scale, NULL, - buf_final(&attr)); - mwidth = cr.x; - if (mwidth <= 0) - mwidth = 1; - if (!rd->curs_width) - rd->curs_width = mwidth; - } - - if (want_xypos == 1 && - y > ci->y - line_height && - y <= ci->y) - posx = ci->x; - else - posx = -1; - - if (want_xypos == 1 && xypos) { - ret_xyattr = xyattr ? strdup(xyattr) : NULL; - ret_xypos = xypos; - want_xypos = 2; - } - - if (offset >= 0 && start - line_start <= offset) { - if (y >= 0 && (y == 0 || y + line_height <= p->h)) { - /* Don't update cursor pos while in a TAB - * as we want to leave cursor at the start. - */ - if (!in_tab) { - cy = y; - cx = x; - } - } else { - cy = cx = -1; - } - } - - if ((ret == WRAP || x >= p->w - (wrap ? mwidth : 0)) && - line[0] != soh && line[0] != ack) { - /* No room for more text */ - if (wrap && *line && *line != '\n') { - int wrap_prefix_size; - int len = flush_line(p, focus, dodraw, &rlst, - y, ascent, scale, - p->w - mwidth, &wrap_margin, - &wrap_prefix_size, - &xypos, &xyattr, &cursattr); - if (len + wrap_prefix_size <= cx && cy == y) { - cx -= len; - cy += line_height; - } - wrap_offset += len; - x -= len; - if (x < 0) - x = 0; - y += line_height; - if (want_xypos == 1 && - y >= ci->y - line_height && - y <= ci->y) - /* cursor is in the tail of rlst that - * was relocated - reassess xypos - */ - set_xypos(rlst, p, focus, ci->x, scale); - } else { - /* truncate: skip over normal text, but - * stop at newline. - */ - line += strcspn(line, "\n"); - start = line; - } - } - - ret = OK; - ch = *line; - if (line == line_start + offset) - rd->curs_width = mwidth; - if (ch >= ' ') { - bool was_in_lws = in_lws; - line += 1; - /* Only flush out if string is getting a bit long. - * i.e. if we have reached the offset we are - * measuring to, or if we could have reached the - * right margin. - * Alternately, if we are doing word-wrap and we - * have found start or end of a word. - */ - if ((*line & 0xc0) == 0x80) - /* In the middle of a UTF-8 */ - continue; - if (word_wrap) { - wint_t wch = ch; - const char *trimmed_line = line - 1; - if (ch & 0x80) { - const char *l = start + utf8_round_len(start, trimmed_line-start); - trimmed_line = l; - wch = get_utf8(&l, NULL); - } - if (!iswspace(wch)) { - seen_non_space = True; - if (in_lws) - line = trimmed_line; - in_lws = False; - } else if (!in_lws&& seen_non_space && iswspace(wch)) { - in_lws = True; - line = trimmed_line; - } - } - if (offset == (line - line_start) || - (line-start) * mwidth >= p->w - x || - (posx > x && (line - start)*mwidth > posx - x) || - was_in_lws != in_lws - ) { - ret = draw_some(p, focus, &rlst, &x, start, - &line, - buf_final(&attr), - was_in_lws, - wrap ? mwidth : 0, - offset - (start - line_start), - posx, scale); - start = line; - } - continue; - } - ret = draw_some(p, focus, &rlst, &x, start, &line, - buf_final(&attr), in_lws, - wrap ? mwidth : 0, - in_tab ?:offset - (start - line_start), - posx, scale); - start = line; - if (ret != OK || !ch) - continue; - line += 1; - switch (ch) { - case soh: { - const char *a = line; - char *tb; - const char *aend; - int ln; - - while (*line && line[-1] != stx) - line += 1; - - ln = attr.len; - - /* attrs must not contain ",," */ - aend = strstr(a, ",,"); - if (aend) - aend += 1; - if (!aend || line < aend) - aend = line; - - buf_concat_len(&attr, a, aend-a); - /* Replace trailing stx with ',', and - * append ',' so ",," marks where to - * strip back to when we find etx. - */ - attr.b[attr.len-1] = ','; - buf_append(&attr, ','); - tb = strstr(buf_final(&attr)+ln, - "tab:"); - if (tb) - x = margin + - atoi(tb+4) * scale / 1000; - if (offset == start - line_start) - offset += line-start; - start = line; - mwidth = -1; - break; - } - case etx: - /* strip back to ",," */ - if (attr.len > 0) - attr.len -= 2; - while (attr.len >=2 && - (attr.b[attr.len-1] != ',' || - attr.b[attr.len-2] != ',')) - attr.len -= 1; - if (attr.len == 1) - attr.len = 0; - if (offset == start - line_start) - offset += line-start; - start = line; - mwidth = -1; - break; - case ack: - start = line; - break; - case '\n': - xypos = line-1; - flush_line(p, focus, dodraw, &rlst, y, ascent, scale, 0, - &wrap_margin, NULL, &xypos, &xyattr, &cursattr); - y += line_height; - x = 0; - wrap_offset = 0; - start = line; - break; - case '\f': - x = 0; - start = line; - wrap_offset = 0; - end_of_page = 1; - break; - case '\t': { - int xc = (wrap_offset + x) / mwidth; - /* Note xc might be negative, so "xc % 8" won't work here */ - int w = 8 - (xc & 7); - if (seen_non_space) - in_lws = True; - ret = draw_some(p, focus, &rlst, &x, start, &line, - buf_final(&attr), in_lws, - wrap ? mwidth: 0, - offset == (start - line_start) - ? in_tab : -1, - posx, scale); - if (ret == WRAP) - ; - else if (w > 1) { - line -= 1; - in_tab = -1; // suppress extra cursors - } else - in_tab = 0; - start = line; - break; - } - default:{ - char buf[4]; - const char *b; - int l = attr.len; - buf[0] = '^'; - buf[1] = ch + '@'; - buf[2] = 0; - b = buf+2; - buf_concat(&attr, ",inverse,fg:red,"); - in_lws = False; - ret = draw_some(p, focus, &rlst, &x, buf, &b, - buf_final(&attr), in_lws, - wrap ? mwidth*2: 0, - offset - (start - line_start), - posx, scale); - attr.len = l; - start = line; - break; - } - } + if (ci->num >= 0) { + xy = find_curs(ci->home, rd->prefix_bytes + ci->num, NULL); + ci->home->cx = xy.x; + ci->home->cy = xy.y; } - if (!*line && (line > start || offset == start - line_start)) { - /* Some more to draw */ - if (want_xypos == 1 && - y > ci->y - line_height && - y <= ci->y) - posx = ci->x; - else - posx = -1; + return 1; +} - draw_some(p, focus, &rlst, &x, start, &line, - buf_final(&attr), False, - wrap ? mwidth : 0, offset - (start - line_start), - posx, scale); +DEF_CMD(renderline_refresh) +{ + struct rline_data *rd = &ci->home->data; + int offset = -1; + + if (rd->curspos >= 0) + offset = rd->prefix_bytes + rd->curspos; + if (rd->image) + render_image(ci->home, ci->focus, rd->line, True, + rd->scale, offset, False, 0, 0); + else { + measure_line(ci->home, ci->focus, offset); + draw_line(ci->home, ci->focus, offset); } + return 1; +} - flush_line(p, focus, dodraw, &rlst, y, ascent, scale, 0, - &wrap_margin, NULL, &xypos, &xyattr, &cursattr); - - if (want_xypos == 1) { - ret_xyattr = xyattr ? strdup(xyattr) : NULL; - ret_xypos = xypos ?: line; - want_xypos = 2; - } - if (offset >= 0 && line - line_start <= offset) { - if (y >= 0 && (y == 0 || y + line_height <= p->h)) { - cy = y; - cx = x; - } else { - cy = cx = -1; - } - } - if (x > 0 || y == 0) - /* No newline at the end .. but we must render as whole lines */ - y += line_height; - free(buf_final(&attr)); - if (offset >= 0) { - p->cx = cx; - p->cy = cy; - } - if (!dodraw) - /* Mustn't resize after clearing the pane, or we'll - * be out-of-sync with display manager. - */ - pane_resize(p, p->x, p->y, p->w, y); - rd->width = margin + twidth; - attr_set_int(&p->attrs, "line-height", line_height); - while (rlst) { - struct render_list *r = rlst; - rlst = r->next; - free((void*)r->text); - free((void*)r->attr); - free(r); +DEF_CMD(renderline_measure) +{ + struct rline_data *rd = &ci->home->data; + bool end_of_page; + if (rd->image) + return render_image(ci->home, ci->focus, rd->line, + False, rd->scale, ci->num, False, 0, 0); + + end_of_page = measure_line(ci->home, ci->focus, + ci->num < 0 ? -1 : rd->prefix_bytes + ci->num); + rd->prefix_pixels = 0; + if (rd->prefix_bytes) { + struct xy xy = find_curs(ci->home, rd->prefix_bytes, NULL); + rd->prefix_pixels = xy.x; } - if (want_xypos) { - int pos = 0; - if (ret_xypos) - pos = ret_xypos - line_start; - comm_call(ci->comm2, "cb", ci->focus, pos, NULL, ret_xyattr); - free(ret_xyattr); - return pos + 1; + if (ci->num >= 0) { + /* Find cursor and report x,y pos and attributes */ + const char *cursattr = NULL; + struct xy xy; + xy = find_curs(ci->home, rd->prefix_bytes + ci->num, &cursattr); + comm_call(ci->comm2, "cb", ci->focus, end_of_page, NULL, + cursattr); + ci->home->cx = xy.x; + ci->home->cy = xy.y; } - comm_call(ci->comm2, "cb", ci->focus, end_of_page, NULL, cursattr); return end_of_page ? 2 : 1; } +DEF_CMD(renderline_findxy) +{ + struct rline_data *rd = &ci->home->data; + const char *xyattr = NULL; + int pos; + + if (rd->image) + return render_image(ci->home, ci->focus, rd->line, + False, rd->scale, -1, True, + ci->x, ci->y); + + measure_line(ci->home, ci->focus, + ci->num < 0 ? -1 : rd->prefix_bytes + ci->num); + pos = find_xy(ci->home, ci->focus, ci->x, ci->y, &xyattr); + if (pos >= rd->prefix_bytes) + pos -= rd->prefix_bytes; + else { + pos = 0; + xyattr = NULL; + } + comm_call(ci->comm2, "cb", ci->focus, pos, NULL, xyattr); + return pos+1; +} + DEF_CMD(renderline_get) { struct rline_data *rd = &ci->home->data; @@ -1096,7 +1228,7 @@ DEF_CMD(renderline_get) if (!ci->str) return Enoarg; if (strcmp(ci->str, "prefix_len") == 0) - snprintf(buf, sizeof(buf), "%d", rd->prefix_len); + snprintf(buf, sizeof(buf), "%d", rd->prefix_pixels); else if (strcmp(ci->str, "curs_width") == 0) snprintf(buf, sizeof(buf), "%d", rd->curs_width); else if (strcmp(ci->str, "width") == 0) @@ -1146,16 +1278,30 @@ DEF_CMD(renderline_set) struct rline_data *rd = &ci->home->data; const char *old = rd->line; struct xy xyscale = pane_scale(ci->focus); + char *prefix = pane_attr_get(ci->focus, "prefix"); + bool word_wrap = pane_attr_get_int(ci->focus, "word-wrap", 0) != 0; + + if (!ci->str) + return -Enoarg; + if (prefix) + prefix = cvt(strconcat(ci->home, "", prefix, "")); - if (ci->str) - rd->line = cvt(strdup(ci->str)); + if (prefix) + rd->line = strconcat(NULL, prefix, ci->str); else - rd->line = NULL; + rd->line = strdup(ci->str); + rd->prefix_bytes = strlen(prefix?:""); + cvt(rd->line + rd->prefix_bytes); + rd->curspos = ci->num; - if (strcmp(rd->line ?:"", old ?:"") != 0 || - (old && xyscale.x != rd->scale)) { + if (strcmp(rd->line, old) != 0 || + (old && xyscale.x != rd->scale) || + (old && word_wrap != rd->word_wrap)) { pane_damaged(ci->home, DAMAGED_REFRESH); pane_damaged(ci->home->parent, DAMAGED_REFRESH); + rd->scale = xyscale.x; + rd->word_wrap = word_wrap; + parse_line(rd); } free((void*)old); ci->home->damaged &= ~DAMAGED_VIEW; @@ -1176,13 +1322,14 @@ DEF_LOOKUP_CMD(renderline_handle, rl_map); DEF_CMD(renderline_attach) { struct pane *p; + struct rline_data *rd; if (!rl_map) { rl_map = key_alloc(); - key_add(rl_map, "render-line:draw", &renderline); - key_add(rl_map, "Refresh", &renderline); - key_add(rl_map, "render-line:measure", &renderline); - key_add(rl_map, "render-line:findxy", &renderline); + key_add(rl_map, "render-line:draw", &renderline_draw); + key_add(rl_map, "Refresh", &renderline_refresh); + key_add(rl_map, "render-line:measure", &renderline_measure); + key_add(rl_map, "render-line:findxy", &renderline_findxy); key_add(rl_map, "get-attr", &renderline_get); key_add(rl_map, "render-line:set", &renderline_set); key_add(rl_map, "Close", &renderline_close); @@ -1192,6 +1339,8 @@ DEF_CMD(renderline_attach) p = pane_register(ci->focus, ci->num, &renderline_handle.c); if (!p) return Efail; + rd = &p->data; + rd->line = strdup(""); return comm_call(ci->comm2, "cb", p); } diff --git a/tests.d/00-basic b/tests.d/00-basic index 02f837dd..a51baf52 100644 --- a/tests.d/00-basic +++ b/tests.d/00-basic @@ -496,23 +496,23 @@ Display 80,30 D93F2373A7DF927C9EB3D262820B6AE7 39,12 Key ":C-X" Display 80,30 1DDA3EB8BD545775122754FBB14890C3 39,12 Key "-3" -Display 80,30 CFA5451193DFC1577BCD5A44D2352FD5 1,22 +Display 80,30 DC439162B4C21AFC981D664054D81258 1,22 Key ":C-X" -Display 80,30 CC99B3DB56A53019FA6FCAC63030980D 1,22 +Display 80,30 4FB72326B29B11ACCF8789CFC4F6C0AA 1,22 Key "-2" -Display 80,30 D81F2C938FD0EAAE10B26D4019BAD9EA 1,6 +Display 80,30 A33BAC6C988997D1AA06D54C1840FEA8 1,6 Key ":C-X" -Display 80,30 28AE72129103B1C0F7CD5792338B7877 1,6 +Display 80,30 B704A177DA5EB1C12A89E4BD1A733A5A 1,6 Key "-3" -Display 80,30 527B0C87BB47DF04505DE2168AB66B56 5,12 +Display 80,30 5323D915F466BFBFD8C0E10B001258D0 5,11 Key ":C-X" -Display 80,30 4E062FC31924BF613C116E9BDF558400 5,12 +Display 80,30 DA07D5AA2EA00CCC8DDB1BB912CDDF25 5,11 Key "-1" -Display 80,30 492579A17EB3AB136ED171A0B1F95294 1,5 +Display 80,30 B239318A0DD9E1855A5A26A3F691A514 1,5 Key "-1" -Display 80,30 AAC8757F1F89839D84651ABC5CE859FF 1,5 +Display 80,30 7CE6F50089626122AA324DC3405B81D1 1,5 Key ":C-X" -Display 80,30 8A2FB14B047A45D03C0C6ADACF2BC3A1 1,5 +Display 80,30 3F3E99E22F900FE80219C96345C56493 1,5 Key "-0" Display 80,30 A0D3F582FB0BC89B3E583EE79401D7E9 39,6 Key ":C-P" @@ -603,4 +603,4 @@ Key ":C-X" Display 80,30 84F4B35BF26F9D5EEBB67D572ADD0BBF 1,0 Key ":C-C" Display 80,30 D2E6DB1DE1E62B191C28D1B2CFF7F1C0 1,0 -Close 1579 +Close 1581 diff --git a/tests.d/01-c-mode b/tests.d/01-c-mode index a0e55dec..0100d268 100644 --- a/tests.d/01-c-mode +++ b/tests.d/01-c-mode @@ -449,4 +449,4 @@ Display 80,30 9668618AF877EA4CD97E4F8D2C729215 1,22 Key ":Up" Display 80,30 F6BF3A75AC3B026DCFAC496EECDD4E0A 1,21 Key ":Tab" -Close 825 +Close 823 diff --git a/tests.d/01-python b/tests.d/01-python index c76e16be..36fe2b04 100644 --- a/tests.d/01-python +++ b/tests.d/01-python @@ -498,4 +498,4 @@ Key ":C-X" Display 80,30 662292DCAF96B09B7F39A91DB348091A 61,16 Key ":C-C" Display 80,30 03A8C2118CA808DFC0F0AB2107FC9EF3 61,16 -Close 871 +Close 865 diff --git a/tests.d/01-textfill b/tests.d/01-textfill index 8a5c03e9..b746af68 100644 --- a/tests.d/01-textfill +++ b/tests.d/01-textfill @@ -40,7 +40,7 @@ Display 80,30 0BF8A3329820C0BABD0071D9106FA5F4 67,10 Key ":C-D" Display 80,30 0EB974B845899B07E316B72DA8A9A7C2 67,10 Key "- " -Display 80,30 C162ED6AF8CC017A4BCAD2F53A1F9275 68,10 +Display 80,30 DBDB0AC22FBE69127B90DC3E8B3224AB 68,10 Key ":A-q" Display 80,30 FA67B2D87113A78A2A78DD4874AAC023 1,11 Key ":C-N" @@ -52,27 +52,29 @@ Display 80,30 B578FB4C292BB3C399992EC2E0965A09 1,14 Key ":Backspace" Display 80,30 FD4E06BFE6CDE70D7C46DB0220B4CF76 69,13 Key "- " -Display 80,30 196C3E7351561901F28CBDC7DF45687E 1,14 +Display 80,30 F657142367737316722769DDBC97BEB0 70,13 +Key ":C-A" +Display 80,30 F657142367737316722769DDBC97BEB0 1,13 Key ":C-N" -Display 80,30 B055B23428B4AC5B0357DABB297F4339 1,15 +Display 80,30 96149E154B823B49A51C15A22EDCCEC8 1,15 Key ":C-D" -Display 80,30 80582183AA9037B111A6B0AB98556D9A 1,15 +Display 80,30 7FF29BCAC1BBF972450C3488D3C4DE73 1,15 Key "- " -Display 80,30 18A39A95F40CFF85560046443D3B3BC6 2,15 +Display 80,30 08BBC7698A6990166976179B93435151 2,15 Key ":C-N" -Display 80,30 3B31660B88F9BFFB5EA7734EE1A19AD6 2,16 +Display 80,30 66C7F58F5470C970980BE9B5D1A14BB4 2,16 Key ":C-F" -Display 80,30 3B31660B88F9BFFB5EA7734EE1A19AD6 3,16 +Display 80,30 66C7F58F5470C970980BE9B5D1A14BB4 3,16 Key ":C-P" -Display 80,30 18A39A95F40CFF85560046443D3B3BC6 3,15 +Display 80,30 08BBC7698A6990166976179B93435151 3,15 Key ":C-P" -Display 80,30 A2C9862B0385D55D7C13DBE87BB0E625 3,13 +Display 80,30 CA6DFF48877AFFCE8CB75FF4E90628D6 3,13 Key ":C-E" -Display 80,30 A2C9862B0385D55D7C13DBE87BB0E625 70,14 +Display 80,30 CA6DFF48877AFFCE8CB75FF4E90628D6 60,14 Key ":C-D" -Display 80,30 CB3DFEC69DAE0BE3BCC6EC7B296CB7CD 70,14 +Display 80,30 B0E6737A65ABFA40D32EE44661CB799E 60,14 Key "- " -Display 80,30 D06FD759C811DEE78E89CAD2016C6C0E 71,14 +Display 80,30 60E419782F256F529FF80895C8895D65 71,14 Key ":A-q" Display 80,30 40B143D919E0708D3C48DA445B146889 1,15 Key ":A-3" @@ -568,8 +570,8 @@ Display 80,30 0583241404FDD3ABC6D829C0BC4F765D 12,13 Key ":C-X" Display 80,30 05546B93D9066F6349E248340D0926C3 12,13 Key ":C-C" -Display 80,30 8AFD6B53262C6D39D2C93373D2AB6995 21,9 +Display 80,30 B99FFCEA765A616C065971C8C8263640 21,8 Key "-n" -Display 80,30 8AFD6B53262C6D39D2C93373D2AB6995 21,10 +Display 80,30 B99FFCEA765A616C065971C8C8263640 21,9 Key "-n" -Close 1020 +Close 1022 diff --git a/tests.d/02-grep b/tests.d/02-grep index 196bea7f..55b90d5d 100644 --- a/tests.d/02-grep +++ b/tests.d/02-grep @@ -160,6 +160,7 @@ Display 80,30 D4C9EF28C1BC7C6B7B65CCDCAF516479 53,13 Key "-c" Display 80,30 319AF12A183FDD7D5B4FC0C6E218D1CA 54,13 Key ":Enter" +Display 80,30 D24CF94877A55DB6B922A31361C6AA7A 11,10 Display 80,30 C1BCF5F3004964015667E6B86738CFD9 11,10 Key ":C-N" Display 80,30 C1BCF5F3004964015667E6B86738CFD9 11,11 @@ -174,61 +175,61 @@ Display 80,30 C1BCF5F3004964015667E6B86738CFD9 11,15 Key ":C-N" Display 80,30 C1BCF5F3004964015667E6B86738CFD9 11,16 Key "-f" -Display 80,30 EB33B46161DDC5A9F8426ABCD7978EA8 11,12 +Display 80,30 0662203642222B15195944D33E42B0CB 11,12 Key ":C-V" -Display 80,30 84FB907CBEEC587399521E805F3120F7 11,7 +Display 80,30 7542B2673CB816613E5AFA3EFF7E8680 11,7 Key ":C-V" -Display 80,30 19E8D6850F361BF69FD15E391E7B3024 11,7 +Display 80,30 EF9D9BE6379CA61FFF5700271DCB6BB4 11,7 Key ":C-V" -Display 80,30 A2AC85E39B28DB2C07C6370AA0C1057B 11,7 +Display 80,30 61721B5272A51EDF6B45450D660374EB 11,7 Key ":A-B" Display 80,30 AECF9530714EDA1396E5EE297A285515 1,1 Key ":C-X" Display 80,30 ED0B24C0BE5F7005C9F3E8287F13E4B1 1,1 Key "-`" -Display 80,30 579489B82E46EC53B78647FF7CD9AC82 1,6 +Display 80,30 8557455273011DFCCAB6707018F20080 1,6 Key "-`" -Display 80,30 92D21D194A19E86D71EB2328034DB6E9 1,6 +Display 80,30 9554D9EB1BFDBAB109AD1EB8A52410F0 1,6 Key "-`" -Display 80,30 965951B80460C159F854AF8C8CA5D369 1,6 +Display 80,30 87AE6B35ED6379123760062801456E69 1,6 Key ":C-X" -Display 80,30 24184DE29BD90EFD76F70D68A5DC8887 1,6 +Display 80,30 1B93FE4A0F3F6D0DA9A9A7767239F356 1,6 Key "-o" -Display 80,30 BE1746D532C5988615F69BA70F7328BC 1,23 +Display 80,30 010E4E8994BB534A4F27DD543E023E74 1,23 Key ":A->" -Display 80,30 A64F6639801C693DA91DAF5F7C843492 1,26 +Display 80,30 9A15E3055B548090C98D2000DAFF99D7 1,26 Key ":C-P" -Display 80,30 4DD346949A2DFB5B2A7289AAE644A1FD 1,25 +Display 80,30 6E9EE5792E5C449FED0928C36D76CEEF 1,25 Key ":C-P" -Display 80,30 AD6DE94C2BE4E57B01EB96CAAA1B79EC 1,24 +Display 80,30 139545FE550D60719A018A39D8CA8D6B 1,24 Key ":C-P" -Display 80,30 C928A00DFB3153F96D5F7B139335F5CE 1,22 +Display 80,30 C6295F2236324A003C13D099545E1A6E 1,22 Key ":C-P" -Display 80,30 87C8F092EB42AA7DAB85C5BC60B179D5 1,20 +Display 80,30 0511BEC973B20E70100A487F75C49940 1,20 Key ":C-P" -Display 80,30 AAFA2480AB96B8539C2ACCA95B2F389A 1,18 +Display 80,30 19AAD8F2704882304093F1C1C66B64D8 1,18 Key ":Enter" -Display 80,30 ED6F4ACF7E4F1D380D05787A22FF0E71 1,6 +Display 80,30 042FDC43469CEB44CEA2FE0BBEF312FC 1,6 Key ":C-X" -Display 80,30 26956957B7F94DAC1AF0042BC3BF502C 1,6 +Display 80,30 3B1975D517BE71FA5D8846D4B8ACFD00 1,6 Key "-`" -Display 80,30 C83C39892998D168F9EC73F0506171F2 1,6 +Display 80,30 1D0ECC4EEEB7CA4E0A2B6C80CE787EE9 1,7 Key "-`" -Display 80,30 1DE4B8F55F152356A04BA6C05CECFF5E 1,6 +Display 80,30 F5AA3022E69EE65627109416A4153F8C 1,6 Key "-`" -Display 80,30 51CE6EF89B87FD0F4811F700F3C7BAA8 1,6 +Display 80,30 0038D3EA200706CBE2B8F7E35E0247D6 1,7 Key ":C-X" -Display 80,30 48663F3E5BEDD33BB8256A5E5ABEC4EC 1,6 +Display 80,30 DD0B850460093327B7F2F877E46EA378 1,7 Key "-`" -Display 80,30 EE46801C68531B915055392CCFC82DB4 1,8 +Display 80,30 9BFCD6FB7DDEEB9FF736EEE48A983DF2 1,6 Key "-`" -Display 80,30 26C59F5A375274F0F241A27884852B9B 1,8 +Display 80,30 5BC5B368B3E25A73BE8F8D595D61AEE6 1,6 Key "-`" -Display 80,30 ED4241080236CCCE97E22BBF878A7775 1,9 +Display 80,30 833C0953C1A07AB120C791119694A956 1,6 Key ":A-0" -Display 80,30 94DF2463FE2438B36DD9E2F3514ABCBD 1,9 +Display 80,30 D2C4EC51CA2F4603BF5077E49BB9E062 1,6 Key ":C-X" -Display 80,30 B5607D0A0B2B4B309938AE2D541D343C 1,9 +Display 80,30 F7F97F692331C536CBE72D9B591B3029 1,6 Key "-`" Display 80,30 DF026DBC4E8B692B626AB5D9364E8DD1 1,6 Key "-`" @@ -241,4 +242,4 @@ Key ":C-X" Display 80,30 1B93FE4A0F3F6D0DA9A9A7767239F356 1,6 Key ":C-C" Display 80,30 010E4E8994BB534A4F27DD543E023E74 1,6 -Close 916 +Close 905 diff --git a/tests.d/02-presenter b/tests.d/02-presenter index 165a5593..3dba9ec1 100644 --- a/tests.d/02-presenter +++ b/tests.d/02-presenter @@ -138,37 +138,37 @@ Display 80,30 044180CC92EE2BE0BC334923A47B60A1 41,25 Key ":C-V" Display 80,30 3A947DFE99802DE31303D714138CCB9A 41,19 Key ":C-V" -Display 80,30 D209BFA807C99C27DD22EACFA230EB61 41,25 +Display 80,30 E25D4D4E23BF97097A937C262CA37C92 41,25 Key ":C-V" -Display 80,30 8EA6354A53E833E2FC0B0A9719522879 41,22 +Display 80,30 C8DF287E4215AC855DB1986B893467D2 41,22 Key ":C-V" -Display 80,30 8D4FD726FC8A4BBF7FAD0397B76D05F9 41,20 +Display 80,30 570BD2760B91172210B4A212FF60A817 41,20 Key ":C-V" -Display 80,30 BC2D091735C45F3E2FB45E6808629FBC 41,27 +Display 80,30 B95115ED6E8B91CDA95E97E8D417FEBC 41,27 Key ":A-v" -Display 80,30 ED5FA2B98E6E3BE322454142293FE9A1 41,19 +Display 80,30 CD29064456214CBA8FF7B5EDFB090B7A 41,19 Key ":A-v" -Display 80,30 80997D662EDCAACA56C9AF69C20F48CB 41,4 +Display 80,30 37E4118A48D5B33BC6D839B3FA02EE3C 41,4 Key ":A-v" -Display 80,30 D5FB680529E4DE67D70CB02C3B0440AB 41,2 +Display 80,30 3C209A154B1D4DF8F227BCA63FFE1A17 41,2 Key ":A-v" -Display 80,30 3A0112A98E12D76C49F9B459ED4FBAE9 41,5 +Display 80,30 2730B761FB074BE13E2AB6A29FB59F8C 41,5 Key ":C-X" -Display 80,30 3651901A240E3BB920270281A82A009D 41,5 +Display 80,30 C77A763803918CD43C14DE9BBC75FE7B 41,5 Key "-o" -Display 80,30 3A0112A98E12D76C49F9B459ED4FBAE9 41,5 +Display 80,30 2730B761FB074BE13E2AB6A29FB59F8C 41,5 Key "- " -Display 80,30 39CF8614D9BFDA2386C9D2E86EEE0BB7 41,5 +Display 80,30 8178E5D779901F5CD74DA4382634C76D 41,5 Key "- " -Display 80,30 798269E4D2AAF8CC2996BF61B7206388 41,5 +Display 80,30 2F2695E7555CCCAD3FD201F7B47EE68C 41,5 Key ":Backspace" -Display 80,30 7D8B4D07E2709C799767155BB5F189FF 41,5 +Display 80,30 3CF9C8944EEF67AB8A585156CA877980 41,5 Key ":Backspace" -Display 80,30 A086D32E3AB9E2399BFD97012DB16C64 41,5 +Display 80,30 AC9441B9C4E2E4AB7845A277EE77BD69 41,5 Key ":C-X" -Display 80,30 AFFB64D02C7136F9F3728A2AE35F82B9 41,5 +Display 80,30 086A5FA98E85A5FF79CDEB45477C300A 41,5 Key "-o" -Display 80,30 A086D32E3AB9E2399BFD97012DB16C64 41,5 +Display 80,30 AC9441B9C4E2E4AB7845A277EE77BD69 41,5 Key ":A-<" Display 80,30 9F3C9AC6499FD19BB195983AC6CA2B4F 41,0 Key ":C-V" @@ -183,26 +183,26 @@ Display 80,30 CA1B8E3C4CD1729DB7C76DF3F69AD69B 71,0 Key "-m" Display 80,30 8885ADEACA6C80F1ADBE221161451DED 72,0 Key "-p" -Display 80,30 87FECE28B683D9DAADF995B0B19012F4 73,0 -Display 80,30 BDFCEFA26188F9B16767B9437CE776A3 73,0 +Display 80,30 4D53B2E73D94031B1446A0F74DAB6F8E 73,0 +Display 80,30 0A1C54425BE91943C442E9973785337C 73,0 Key ":Enter" -Display 80,30 0C3ED5BC773523DDCF0E465D3C04D4FB 52,13 +Display 80,30 59DC26C12D614861D6AB2A9169890D56 52,13 Key ":C-L" -Display 80,30 676A348D70F6D824892DE9D2B7CC07AC 52,13 +Display 80,30 8FF83F4CFCF9314EC7A21DBD1ED3AD5A 52,13 Key ":C-S" -Display 80,30 FB733176BEC6F026B5BCBA70218C09B8 70,0 +Display 80,30 6ADB8740688FCFE5BA145697BB23EC6A 70,0 Key ":C-S" -Display 80,30 28D3AE8B50C3255C3D39519F8BEF2865 73,0 -Display 80,30 633316645A29F653C59FE6BD61A1C7B6 73,0 +Display 80,30 B97EA79A82B7E6FCA844BCE54ED6850B 73,0 +Display 80,30 27C5C62506AFD47B4306B361470C1638 73,0 Key ":C-S" -Display 80,30 9DD2BDBFDFD3A8FD0789E20DDCC7EF74 72,0 -Display 80,30 E9F80B778A15CB6C731BD8EE487A9E93 72,0 +Display 80,30 E08225DC9AE0CEC1F3FCBD3E969FC76A 72,0 +Display 80,30 DEB9EF790701DF8B3E7BE60B08269EBC 72,0 Key ":Enter" -Display 80,30 4F5BC341507676A911121A5DBF673CDF 52,21 +Display 80,30 1B3FA479059E9F35935CFBDAA9397570 52,21 Key ":C-V" -Display 80,30 159350C231134A892A43CE63F288E9E8 41,25 +Display 80,30 2241E4BEE6A092DF19A64DA83947F19F 41,25 Key ":C-V" -Display 80,30 A7E3B6AABA871694F10193A4598CDD75 41,20 +Display 80,30 64165CE4FA4212934904A1FCE2ED32E3 41,20 Key ":A->" Display 80,30 053694614D5402063B088DFE74620098 41,25 Key ":C-L" @@ -221,4 +221,4 @@ Key ":C-X" Display 80,30 136AE3FD2089F71E23116BE983A85809 41,13 Key ":C-C" Display 80,30 748340AC4DCFE9D3B620E7E1B59586F0 41,13 -Close 1440 +Close 1420 -- 2.39.5