* 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
* 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.
*/
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;
};
#include "core-pane.h"
-/* 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 <space
- * _c is the start which will be updates, and _end is the end which
- * must point to , or nul or a control char
- */
-#define foreach_attr(_attr, _val, _c, _end) \
- for (_attr = _c, _val = find_val(&_c, _end); \
- _attr; \
- _attr = _c, _val = find_val(&_c, _end))
-static const char *find_val(const char **cp safe, const char *end safe)
-{
- 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;
-}
-
-static bool amatch(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;
- 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;
-}
-
-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 void aupdate(char **cp safe, const char *v)
-{
- /* duplicate value at v and store in *cp, freeing what is there
- * first
- */
- const char *end = v;
-
- 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;
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);
(!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);
* 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")) {
aupdate(&rd->wrap_tail, v);
} else if (aprefix(a, "wrap-")) {
aappend(&wrapattr, a+5);
+ } else if (amatch(a, "word-wrap")) {
+ if (!v || *v == '1')
+ rd->word_wrap = 1;
+ else if (*v == '0')
+ rd->word_wrap = 0;
} else if (amatch(a, "hide")) {
hide = ++hide_num;
hide_depth = old_len;
/* 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:
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;
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,
int offset,
int x, int y)
{
- struct rline_data *rd = &p->data;
+ struct rline_data *rd = p->data;
char tmp;
char *str;
int len;
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.
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;
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
tmp = str[len];
str[len] = 0;
+ if (offset >= len)
+ offset = -1;
home_call(focus, "Draw:text", p, offset, NULL, str,
rd->scale, NULL, ri->attr, x, y);
str[len] = tmp;
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,
return True;
}
-static int calc_tab(int num, int margin, int scale)
+static int calc_pos(int num, int margin, int width)
{
- if (num > 0)
- return num * scale / 1000;
- if (-num > margin)
+ if (num >= 0)
+ return num * width / 10;
+ if (-num * width / 10 > margin)
return 0;
- return margin + num * scale / 1000;
+ return margin + num * width / 10;
}
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;
int left_margin;
struct xy xyscale = pane_scale(focus);
+ int curs_height;
int xdiff, ydiff;
struct call_return cr;
int x, y;
int ret = 0;
bool seen_rtab = False;
+ unsigned int offset_hide = 0;
if (!rd->content)
return ret;
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')
rd->measure_offset = offset;
rd->measure_shift_left = shift_left;
- right_margin = p->w - (rd->right_margin * rd->scale / 1000);
- left_margin = rd->left_margin * rd->scale / 1000;
-
cr = measure_str(p, "M", "");
rd->curs_width = cr.x;
+ curs_height = cr.y;
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->min_height > 10)
+ rd->line_height = rd->line_height * rd->min_height / 10;
if (rd->wrap_head) {
cr = measure_str(p, rd->wrap_head, rd->wrap_attr);
cr = measure_str(p, rd->wrap_tail ?: "\\", rd->wrap_attr);
rd->tail_length = cr.x;
+ left_margin = calc_pos(rd->left_margin, p->w, rd->curs_width);
+ /* 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];
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);
* of "\t" characters. Also handle \n and \f.
*/
x = left_margin - (shift_left > 0 ? shift_left : 0);
- y = rd->space_above * rd->scale / 1000;
- rd->width = 0;
+ y = rd->space_above * curs_height / 10;
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_tab(ri->tab, right_margin,
- rd->scale);
+ 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])
}
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;
w += ri2->width;
while (ri2 && ri2->tab == TAB_UNSET)
ri2 = ri2->next;
- margin = right_margin;
+ margin = right_margin - left_margin;
if (ri2)
- margin = left_margin + calc_tab(ri2->tab, right_margin,
- rd->scale);
+ margin = left_margin + calc_pos(ri2->tab,
+ right_margin - left_margin,
+ rd->curs_width);
if (ri->tab_align == TAB_RIGHT)
x = x + margin - x - w;
else
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)
}
}
rd->measure_height =
- (rd->space_above + rd->space_below) * rd->scale / 1000 +
+ (rd->space_above + rd->space_below) * curs_height / 10 +
ydiff + rd->line_height;
pane_resize(p, p->x, p->y, p->w, rd->measure_height);
attr_set_int(&p->attrs, "line-height", rd->line_height);
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;
* 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 &&
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;
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;
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;
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;
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);
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",
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
* 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);
}
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);
DEF_CMD(renderline_draw)
{
- struct rline_data *rd = &ci->home->data;
+ struct rline_data *rd = ci->home->data;
struct xy xy;
int offset = -1;
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)
DEF_CMD(renderline_measure)
{
- struct rline_data *rd = &ci->home->data;
+ struct rline_data *rd = ci->home->data;
int ret;
if (rd->image)
DEF_CMD(renderline_findxy)
{
- struct rline_data *rd = &ci->home->data;
+ struct rline_data *rd = ci->home->data;
const char *xyattr = NULL;
int pos;
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;
* < 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;
if (c[0] != '<')
continue;
if (c[1] == '/') {
- c[0] = ack;
- c[1] = ack;
- c[2] = etx;
- c += 2;
+ while (*c && *c != '>')
+ *c++ = ack;
+ if (!*c)
+ break;
+ *c = etx;
continue;
}
c[0] = soh;
- while (c[0] && c[1] != '>')
- c++;
- c[1] = stx;
+ 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 = 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
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);
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);
aupdate(&rd->wrap_head, NULL);
aupdate(&rd->wrap_tail, NULL);
aupdate(&rd->wrap_attr, NULL);
+ aupdate(&rd->background, NULL);
return 1;
}
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);