2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * Rendering for any document which presents as a sequence of lines.
7 * The underlying document, or an intervening filter, must return lines of
8 * text in response to the "doc:render-line" command.
9 * This takes a mark and moves it to the end of the rendered line
10 * so that another call will produce another line.
11 * "doc:render-line" must always return a full line including '\n'
12 * unless the result would be bigger than the 'max' passed in ->num or
13 * ->num < 0. In these cases it can stop before a '\n'. In each case,
14 * the mark is moved to the end of the region that was rendered;
15 * This allows a mark to be found for a given character position.
16 * If mark2 is given, the offset in the rendering when mark2 is reached
17 * is reported as ->num in the callback.
18 * For the standard 'render the whole line' functionality, ->num should
21 * The document or filter must also provide "doc:render-line-prev" which
22 * moves mark to a start-of-line. If num is 0, then don't skip over any
23 * newlines. If it is '1', then skip one newline.
25 * The returned line can contain attribute markings as <attr,attr>. </>
26 * is used to pop most recent attributes. << is used to include a
27 * literal '<'. Lines generally contain UTF-8. Control character '\n'
28 * is end of line and '\t' tabs 1-8 spaces. '\f' marks end of page -
29 * nothing after this will be displayed.
31 * Other control characters should be rendered as
32 * e.g. <fg:red>^X</> - in particular, nul must not appear in the line.
34 * We store all start-of-line the marks found while rendering a pane in
35 * a 'view' on the document. The line returned for a given mark is
36 * attached to extra space allocated for that mark. When a change
37 * notification is received for a mark we discard that string. So the
38 * string associated with a mark is certainly the string that would be
39 * rendered after that mark (though it may be truncated). The set of
40 * marks in a view should always identify exactly the set of lines to be
41 * displayed. Each mark should be at a start-of-line except possibly
42 * for the first and last. The first may be internal to a long line,
43 * but the line rendering attached will always continue to the
44 * end-of-line. We record the number of display lines in that first
46 * The last mark may also be mid-line, and it must never have an
48 * In the worst case of there being no newlines in the document, there
49 * will be precisely two marks: one contains a partial line and one that
50 * marks the end of that line. When point moves outside that range a
51 * new start will be chosen before point using "doc:render-line-prev"
52 * and the old start is discarded.
54 * To render the pane we:
55 * 1/ call 'render-line-prev' on a mark at the point and look for that mark
57 * 2/ If the mark matches and has a string, we have a starting point,
58 * else we call "doc:render-line" and store the result, thus
59 * producing a starting point. We determine how many display lines
60 * are needed to display this text-line and set 'y' accordingly.
61 * At this point we have two marks: start and end, with known text of known
63 * 3/ Then we move outwards, back from the first mark and forward from
64 * the last mark. If we find a mark already in the view in the
65 * desired direction with text attached it is correct and we use
66 * that. Otherwise we find start (when going backwards) and render a
67 * new line. Any old mark that is in the range is discarded.
68 * 4/ When we have a full set of marks and the full height of the pane,
69 * we discard marks outside the range and start rendering from the
70 * top. ARG how is cursor drawn.
72 * If we already have correct marks on one side and not the other, we prefer
73 * to advance on that first side to maximize the amount of text that was common
74 * with the previous rendering of the page.
76 * Sometimes we need to render without a point. In this case we start
77 * at the first mark in the view and move forward. If we can we do this
78 * anyway, and only try the slow way if the target point wasn't found.
81 #define MARK_DATA_PTR struct pane
82 #define _GNU_SOURCE /* for asprintf */
83 #define PANE_DATA_TYPE struct rl_data
86 #include <stdio.h> /* snprintf */
89 * All functions involved in sending Draw and size requests
90 * to the display are given two panes: p and focus.
91 * 'p' is the pane where the drawing happens. 'focus' is the
92 * leaf on the current stack.
93 * These are different when the drawing is segmented into regions
94 * of the target pane, with light-weight panes being used to avoid
95 * having to refresh the whole target pane when the only change is
97 * The calls to the display are home_calls with 'focus' as the home
98 * pane, and 'p' as the focus. The x,y co-ords are, as always,
99 * relative to the focus pane 'p'.
103 int top_sol; /* true when first mark is at a start-of-line */
105 int skip_height; /* Skip display-lines for first "line" */
106 int skip_line_height; /* height of lines in skip_height */
107 int tail_height; /* display lines at eop not display */
108 int cursor_line; /* line that contains the cursor starts
110 short target_x, target_y;
111 short i_moved; /* I moved cursor, so don't clear
117 short shift_left_last_refresh;
120 int repositioned; /* send "render:reposition" when we know
121 * full position again.
123 short lines; /* lines drawn before we hit eof */
124 short cols; /* columns used for longest line */
125 short margin; /* distance from top/bottom required for cursor */
126 bool background_drawn;
127 bool background_uniform;
129 /* If cursor not visible, we add this pane in bottom-right and place
132 struct pane *cursor_pane;
134 #include "core-pane.h"
136 static void vmark_clear(struct mark *m safe)
139 pane_close(m->mdata);
144 static void vmark_free(struct mark *m safe)
150 static void vmark_set(struct pane *p safe, struct pane *focus safe,
151 struct mark *m safe, char *line safe)
154 m->mdata = call_ret(pane, "attach-renderline", p, -1);
156 pane_call(m->mdata, "render-line:set", focus, -1, NULL, line);
159 static void vmark_invalidate(struct mark *m safe)
162 pane_damaged(m->mdata, DAMAGED_VIEW);
165 static bool vmark_is_valid(struct mark *m safe)
167 return mark_valid(m) && m->mdata && !(m->mdata->damaged & DAMAGED_VIEW);
170 /* Returns 'true' at end-of-page */
171 static int _measure_line(struct pane *p safe, struct pane *focus safe,
172 struct mark *mk safe, short cursor_offset,
175 struct rl_data *rl = p->data;
176 struct pane *hp = mk->mdata;
177 struct call_return cr;
179 if (!mark_valid(mk) || !hp)
181 pane_resize(hp, hp->x, hp->y, p->w, p->h);
182 if (!rl->shift_locked) {
183 int sl = pane_attr_get_int(focus, "render-wrap", -2);
184 if (sl != rl->shift_left) {
186 asprintf(&sla, "%d auto", rl->shift_left);
187 attr_set_str(&focus->attrs, "render-wrap", sla);
191 cr = pane_call_ret(all, hp, "render-line:measure",
192 focus, cursor_offset);
195 /* end-of-page flag */
198 #define measure_line(...) VFUNC(measure_line, __VA_ARGS__)
199 static inline int measure_line3(struct pane *p safe, struct pane *focus safe,
200 struct mark *mk safe)
202 return _measure_line(p, focus, mk, -1, NULL);
204 static inline int measure_line4(struct pane *p safe, struct pane *focus safe,
205 struct mark *mk safe, short cursor_offset)
207 return _measure_line(p, focus, mk, cursor_offset, NULL);
209 static inline int measure_line5(struct pane *p safe, struct pane *focus safe,
210 struct mark *mk safe, short cursor_offset,
213 return _measure_line(p, focus, mk, cursor_offset, cursor_attr);
216 /* Returns offset of posx,posy */
217 static int find_xy_line(struct pane *p safe, struct pane *focus safe,
218 struct mark *mk safe, short posx, short posy,
221 struct pane *hp = mk->mdata;
222 struct call_return cr;
226 cr = pane_call_ret(all, hp,
227 "render-line:findxy",
231 posx - hp->x, posy - hp->y);
235 return cr.ret > 0 ? (cr.ret - 1) : -1;
238 static void draw_line(struct pane *p safe, struct pane *focus safe,
239 struct mark *mk safe, short offset, bool refresh_all)
241 struct pane *hp = mk->mdata;
244 (refresh_all || hp->damaged & DAMAGED_REFRESH)) {
245 hp->damaged &= ~DAMAGED_REFRESH;
246 pane_call(hp, "render-line:draw", focus, offset);
250 static struct mark *call_render_line_prev(struct pane *p safe,
257 if (m->viewnum < 0) {
261 ret = call("doc:render-line-prev", p, n, m);
263 /* if n>0 we can fail because start-of-file was found before
264 * any newline. In that case ret == Efail, and we return NULL.
267 *found = (ret == Efail);
272 /* current line is start-of-file */
277 m2 = vmark_matching(m);
285 static void call_render_line(struct pane *home safe, struct pane *p safe,
286 struct mark *start safe, struct mark **end)
291 if (vmark_is_valid(start))
294 m = mark_dup_view(start);
295 if (doc_following(p, m) == WEOF) {
296 /* We only create a subpane for EOF when it is at start
297 * of line, else it is included in the preceding line.
299 call("doc:render-line-prev", p, 0, m);
300 if (!mark_same(m, start)) {
307 s = call_ret(strsave, "doc:render-line", p, -1, m);
309 if (!mark_valid(start)) {
314 vmark_set(home, p, start, s);
316 m2 = vmark_matching(m);
321 /*FIXME shouldn't be needed */
324 /* Any mark between start and m2 must be discarded,
326 while ((m = vmark_next(start)) != NULL &&
328 if (end && m == *end)
332 /* Any mark at same location as m2 must go too. */
333 while ((m = vmark_next(m2)) != NULL &&
335 if (end && m == *end)
339 /* Any mark at same location as start must go too. */
340 while ((m = vmark_prev(start)) != NULL &&
341 mark_same(m, start)) {
351 static struct mark *call_render_line_offset(struct pane *p safe,
352 struct mark *start safe, int offset)
356 m = mark_dup_view(start);
357 if (call_comm("doc:render-line", p, &no_save, offset, m) <= 0) {
372 static int call_render_line_to_point(struct pane *p safe, struct mark *pm safe,
373 struct mark *start safe)
376 struct mark *m = mark_dup_view(start);
378 len = call_comm("doc:render-line", p, &get_offset, -1, m, NULL, 0, pm);
386 /* Choose a new set of lines to display, and mark each one with a line marker.
387 * We start at pm and move both backwards and forwards one line at a time.
388 * We stop moving in one of the directions when
389 * - we hit start/end of file
390 * - when the edge in the *other* direction enters the previously visible
391 * area (if there was one). This increases stability of display when
392 * we move off a line or 2.
393 * - when we reach the given line count (vline). A positive count restricts
394 * backward movement, a negative restricts forwards movement.
397 static bool step_back(struct pane *p safe, struct pane *focus safe,
398 struct mark **startp safe, struct mark **endp,
399 short *y_pre safe, short *line_height_pre safe)
401 /* step backwards moving start */
402 struct rl_data *rl = p->data;
404 bool found_start = False;
405 struct mark *start = *startp;
409 m = call_render_line_prev(focus, mark_dup_view(start),
412 /* no text before 'start' */
417 call_render_line(p, focus, start, endp);
418 measure_line(p, focus, start);
419 h = start->mdata ? start->mdata->h : 0;
423 attr_find_int(start->mdata->attrs,
432 static bool step_fore(struct pane *p safe, struct pane *focus safe,
433 struct mark **startp safe, struct mark **endp safe,
434 short *y_post safe, short *line_height_post safe)
436 struct mark *end = *endp;
440 call_render_line(p, focus, end, startp);
441 measure_line(p, focus, end);
443 *y_post = end->mdata->h;
444 if (*y_post > 0 && end->mdata)
446 attr_find_int(end->mdata->attrs,
448 if (!end->mdata || !end->mdata->h)
451 end = vmark_next(end);
453 if (p->h >= *line_height_post *2)
461 static int consume_space(struct pane *p safe, int y,
462 short *y_prep safe, short *y_postp safe,
463 short *lines_above safe, short *lines_below safe,
464 int found_start, int found_end,
465 int line_height_pre, int line_height_post,
469 int y_post = *y_postp;
471 if (y_pre > 0 && y_post > 0 && !found_start && !found_end) {
472 int consume = (y_post < y_pre
473 ? y_post : y_pre) * 2;
475 if (consume > p->h - y)
477 if (line_at_a_time && consume > 2*line_height_pre &&
479 consume = 2*line_height_pre;
480 if (line_at_a_time && consume > 2*line_height_post &&
481 line_height_post > 0)
482 consume = 2*line_height_post;
483 if (y_pre > y_post) {
484 above = consume - (consume/2);
487 below = consume - (consume/2);
492 *lines_above += above / (line_height_pre?:1);
494 *lines_below += below / (line_height_post?:1);
495 /* We have just consumed all of one of
496 * lines_{above,below} so they are no longer
500 if (found_end && y_pre && !found_start) {
501 int consume = p->h - y;
504 if (line_at_a_time && consume > line_height_pre &&
506 consume = line_height_pre;
509 *lines_above += consume / (line_height_pre?:1);
511 if (found_start && y_post && !found_end) {
512 int consume = p->h - y;
513 if (consume > y_post)
515 if (line_at_a_time && consume > line_height_post &&
516 line_height_post > 0)
517 consume = line_height_post;
520 *lines_below += consume / (line_height_post?:1);
528 * Choose new start/end to be displayed in the given pane.
529 * 'pm' must be displayed, and if vline is not NO_NUMERIC,
530 * pm should be displayed on that line of the display, where
531 * negative numbers count from the bottom of the page.
532 * Otherwise pm should be at least rl->margin from top and bottom,
533 * but in no case should start-of-file be *after* top of display.
534 * If there is an existing display, move the display as little as
535 * possible while complying with the above.
537 * We start at 'pm' and move both forward and backward one line at a
538 * time measuring each line and assessing space used.
539 * - If the space above pm reaches positive vline, that will be top.
540 * - If the space below reaches negative vline, that will likely be bottom
541 * - If pm was before old top and we reach the old top going down,
542 * and if space measured before pm has reached ->margin, we stop
544 * - If pm was after old bottom and we reach the old bottom going up
545 * and if space measured after pm has reached ->margin, we stop
548 * If we decide to stop moving in both directions, but have not
549 * reached EOF or full height of display, keep moving downwards.
551 * "start" is a mark at the start of the first line we currently
552 * intend to display, and y_pre is the number of pixel from the top
553 * of the display of that line, to the top pixel that will be displayed.
554 * We only move 'start' backward when y_pre is zero, and initially y_pre
555 * is the full height of that line.
557 * Similarly "end" is the start of the last line we currently intend
558 * to display, and y_post is the number of pixel from the bottom of that display
559 * up to the point we currently intend to display. We only move "end" forward
560 * when y_post is zero, and when we do we set y_post to the full height of the
563 * Until we decide on the start or end (found_start, found_end), we
564 * repeatedly add equal parts of y_pre and y_post into the total to
565 * be display - consume_space() does this. The space removed from y_pre
566 * and y_post is added to 'y' - the total height.
567 * It is also included into lines_above and lines_below which count text lines,
568 * rather than pixels, using line_height_pre and line_height_post as scale
569 * factors. These are used to determine when vline or rl->margin requirements
572 static void find_lines(struct mark *pm safe, struct pane *p safe,
573 struct pane *focus safe,
576 struct rl_data *rl = p->data;
577 /* orig_top/bot bound what is currently displayed and
578 * are used to determine if the display has been repositioned.
579 * orig_bot is *after* the last displayed line. Its ->mdata
582 struct mark *orig_top, *orig_bot;
583 /* top and bot are used to enhance stability. They are NULL
584 * if vline is given, else they match orig_top/bot.
586 struct mark *top, *bot;
588 /* Current estimate of new display. From y_pre pixels down
589 * from the top of line at 'start', to y_post pixels up
590 * from the end of the line before 'end' there are 'y'
591 * pixel lines that we have committed to display.
593 struct mark *start, *end; // current estimate for new display
594 short y_pre = 0, y_post = 0;
596 /* Number of text-lines in the committed region above or below
597 * the baseline of the line containing pm. These lines might not
598 * all be the same height. line_height_pre/post are the heights of
599 * start and end-1 so changes in y_pre/y_post can be merged into these
602 short lines_above = 0, lines_below = 0; /* distance from start/end
605 short line_height_pre = 1, line_height_post = 1;
607 short offset; // pos of pm while measureing the line holding the cursor.
608 /* We set found_start we we don't want to consider anything above the
609 * top that we currently intend to display. Once it is set,
610 * 'start', y_pre, lines_above are all frozen.
611 * Similarly once found_end is set we freeze end, y_pos, lines_below,
612 * but we mught unfreeze those if there is room for more text at end of
614 * found_start is set:
615 * - when y_pre is zero and start is at top of file
616 * - when lines_above reaches positive vline
617 * - when intended display has grown down into the previous
618 * display. This means we have added enough lines above and
619 * don't want to scroll the display more than we need.
620 * - When we hit unexpected errors moving backwards
622 * - when we hit end-of-file
623 * - when lines_below reached -vline
624 * - when the top of the intended display overlaps the
627 bool found_start = False, found_end = False;
629 orig_top = vmark_first(focus, rl->typenum, p);
630 orig_bot = vmark_last(focus, rl->typenum, p);
631 /* Protect top/bot from being freed by call_render_line */
633 orig_top = mark_dup(orig_top);
635 orig_bot = mark_dup(orig_bot);
637 start = vmark_new(focus, rl->typenum, p);
640 /* FIXME why is this here. We set ->repositioned at the end
641 * if the marks move. Maybe we need to check if y_pre moves too.
643 rl->repositioned = 1;
644 mark_to_mark(start, pm);
645 start = call_render_line_prev(focus, start, 0, &rl->top_sol);
649 /* Render the cursor line, and find where the cursor is. */
650 offset = call_render_line_to_point(focus, pm, start);
651 call_render_line(p, focus, start, NULL);
652 end = vmark_next(start);
653 /* Note: 'end' might be NULL if 'start' is end-of-file, otherwise
654 * call_render_line() will have created 'end' if it didn't exist.
657 if (!rl->shift_locked)
661 struct pane *hp = start->mdata;
664 found_end = measure_line(p, focus, start, offset) & 2;
666 curs_width = pane_attr_get_int(
667 start->mdata, "curs_width", 1);
670 // FIXME this loops indefinitely if cursor after
671 // right-justified text.
672 while (!rl->do_wrap && !rl->shift_locked &&
673 hp->cx + curs_width >= p->w) {
674 int shift = 8 * curs_width;
677 rl->shift_left += shift;
678 measure_line(p, focus, start, offset);
682 /* ->cy is top of cursor, we want to measure from bottom */
683 line_height_pre = attr_find_int(start->mdata->attrs, "line-height");
684 if (line_height_pre < 1)
686 /* We now have a better estimate than '1' */
687 line_height_post = line_height_pre;
688 y_pre = start->mdata->cy + line_height_pre;
689 y_post = start->mdata->h - y_pre;
691 /* Should never happen */
696 /* When cursor at EOF, leave 10% height of display
697 * blank at bottom to make this more obvious - unless
698 * the display is so small that might push the last line partly
699 * off display at the top.
701 if (p->h > line_height_pre * 2)
704 /* Small display, no space at EOF */
710 if (rl->header && rl->header->mdata)
711 y = rl->header->mdata->h;
713 /* We have start/end of the focus line. When rendered this,
714 * plus header and eof-footer would use y_pre + y + y_post
718 if (vline != NO_NUMERIC) {
719 /* ignore current position - top/bot irrelevant */
727 while ((!found_start || !found_end) && y < p->h) {
728 if (vline != NO_NUMERIC) {
729 /* As lines_above/below measure from the baseline
730 * of the cursor line, and as we want to see the top
731 * of he cursor line as well, these two cases are
734 if (!found_start && vline > 0 &&
735 lines_above >= vline)
737 if (!found_end && vline < 0 &&
738 lines_below >= -vline-1)
741 if (!found_start && y_pre <= 0)
742 found_start = step_back(p, focus, &start, &end,
743 &y_pre, &line_height_pre);
745 if (found_end && y_post && bot &&
746 mark_ordered_or_same(start, bot))
747 /* Extra vertical space gets inserted after EOF when
748 * there is a long jump to get there, but if we hit 'bot'
749 * soon when searching back, we discard any unused space.
753 if (!found_end && bot &&
754 (!end || mark_ordered_or_same(bot, end)) &&
755 lines_below >= rl->margin)
756 if (mark_ordered_not_same(start, bot) ||
757 /* Overlap original from below, so prefer to
758 * maximize that overlap.
760 (mark_same(start, bot) &&
761 y_pre - rl->skip_height >= y_post))
762 /* No overlap in marks yet, but over-lap in
763 * space, so same result as above.
767 if (!found_end && y_post <= 0)
769 found_end = step_fore(p, focus, &start, &end,
770 &y_post, &line_height_post);
772 /* This test has "> rl->margin" while found_end test has
773 * ">= rl->margin" due to the asymmetry of measuring from the
774 * baseline, not the centreling, of the target text.
776 if (!found_start && top && end &&
777 mark_ordered_or_same(start, top) &&
778 lines_above > rl->margin)
779 if (mark_ordered_not_same(top, end) ||
780 (mark_same(top, end) &&
781 y_post - rl->tail_height >= y_pre))
784 y = consume_space(p, y, &y_pre, &y_post,
785 &lines_above, &lines_below,
786 found_start, found_end,
787 line_height_pre, line_height_post,
788 vline && vline != NO_NUMERIC);
790 /* We might need to continue downwards even after found_end
791 * if there is more space.
793 found_end = end == NULL;
794 while (!found_end && y < p->h) {
796 found_end = step_fore(p, focus, &start, &end,
797 &y_post, &line_height_post);
798 y = consume_space(p, y, &y_pre, &y_post,
799 &lines_above, &lines_below,
800 found_start, found_end,
801 line_height_pre, line_height_post,
802 vline && vline != NO_NUMERIC);
805 if (start->mdata && start->mdata->h <= y_pre) {
807 m = vmark_next(start);
814 rl->skip_height = y_pre;
815 rl->skip_line_height = line_height_pre;
816 rl->tail_height = y_post;
817 /* Now discard any marks outside start-end */
818 if (end && end->seq < start->seq)
819 /* something confused, make sure we don't try to use 'end' after
823 while ((m = vmark_prev(start)) != NULL)
827 while ((m = vmark_next(end)) != NULL)
835 if (rl->header && rl->header->mdata) {
836 y = rl->header->mdata->h;
837 rl->cols = pane_attr_get_int(rl->header->mdata, "width", 0);
839 y -= rl->skip_height;
840 for (m = vmark_first(focus, rl->typenum, p);
841 m && m->mdata ; m = vmark_next(m)) {
842 struct pane *hp = m->mdata;
844 if (pane_resize(hp, hp->x, y, hp->w, hp->h) &&
845 !rl->background_uniform)
846 pane_damaged(hp, DAMAGED_REFRESH);
848 cols = pane_attr_get_int(hp, "width", 0);
853 pane_damaged(p, DAMAGED_REFRESH);
854 m = vmark_first(focus, rl->typenum, p);
855 if (!m || !orig_top || !mark_same(m, orig_top))
856 rl->repositioned = 1;
857 m = vmark_last(focus, rl->typenum, p);
858 if (!m || !orig_bot || !mark_same(m, orig_bot))
859 rl->repositioned = 1;
866 DEF_CMD(cursor_handle)
871 static int render(struct mark *pm, struct pane *p safe,
872 struct pane *focus safe)
874 struct rl_data *rl = p->data;
877 struct xy scale = pane_scale(focus);
879 bool hide_cursor = False;
880 bool cursor_drawn = False;
881 bool refresh_all = rl->shift_left != rl->shift_left_last_refresh;
883 rl->shift_left_last_refresh = rl->shift_left;
884 s = pane_attr_get(focus, "hide-cursor");
885 if (s && strcmp(s, "yes") == 0)
889 m = vmark_first(focus, rl->typenum, p);
890 if (!rl->background_drawn) {
892 rl->background_uniform = True;
894 s = pane_attr_get(focus, "background");
895 if (s && strstarts(s, "call:")) {
896 home_call(focus, "Draw:clear", p, 0, NULL, "");
897 home_call(focus, s+5, p, 0, m);
899 rl->background_uniform = False;
900 } else if (rl->background_drawn)
903 home_call(focus, "Draw:clear", p, 0, NULL, "");
904 else if (strstarts(s, "color:")) {
908 home_call(focus, "Draw:clear", p, 0, NULL, a);
910 } else if (strstarts(s, "image:")) {
911 home_call(focus, "Draw:clear", p);
912 home_call(focus, "Draw:image", p, 16, NULL, s+6);
913 rl->background_uniform = False;
915 home_call(focus, "Draw:clear", p, 0, NULL, "");
916 rl->background_drawn = True;
918 if (rl->header && vmark_is_valid(rl->header)) {
919 struct pane *hp = rl->header->mdata;
920 draw_line(p, focus, rl->header, -1, refresh_all);
922 rl->cols = pane_attr_get_int(hp, "width", 0);
924 y -= rl->skip_height;
929 while (m && m->mdata) {
931 if (!hide_cursor && p->cx <= 0 && pm &&
932 mark_ordered_or_same(m, pm) &&
933 (!(m2 && doc_following(focus, m2) != WEOF) ||
934 mark_ordered_not_same(pm, m2))) {
936 struct pane *hp = m->mdata;
937 short len = call_render_line_to_point(focus, pm,
939 draw_line(p, focus, m, len, True);
940 rl->cursor_line = hp->y + hp->cy;
941 curs = pane_mapxy(hp, p, hp->cx, hp->cy, False);
942 if (hp->cx < 0 || hp->cx >= hp->w) {
951 draw_line(p, focus, m, -1, refresh_all);
954 int cols = pane_attr_get_int(m->mdata, "width", 0);
957 y = m->mdata->y + m->mdata->h;
961 if (m && !m->mdata && vmark_next(m))
962 LOG("render-lines: break in vmark sequence");
963 if (!cursor_drawn && !hide_cursor) {
964 /* Place cursor in bottom right */
965 struct pane *cp = rl->cursor_pane;
970 /* Note: this pane will container rl_data
973 cp = pane_register(p, -1, &cursor_handle);
974 rl->cursor_pane = cp;
979 m2 = vmark_last(focus, rl->typenum, p);
981 while (m2 && mwidth <= 0) {
983 mwidth = pane_attr_get_int(
984 m2->mdata, "curs_width", -1);
985 lineheight = pane_attr_get_int(
986 m2->mdata, "line-height", -1);
1001 home_call(focus, "Draw:clear", cp);
1002 home_call(focus, "Draw:text", cp, 0, NULL, " ",
1006 } else if (rl->cursor_pane) {
1007 pane_close(rl->cursor_pane);
1008 rl->cursor_pane = NULL;
1013 DEF_CMD(render_lines_point_moving)
1015 struct pane *p = ci->home;
1016 struct rl_data *rl = p->data;
1017 struct mark *pt = call_ret(mark, "doc:point", ci->home);
1020 if (!pt || ci->mark != pt)
1022 /* Stop igoring point, because it is probably relevant now */
1023 rl->ignore_point = 0;
1025 /* Someone else moved the point, so reset target column */
1027 m = vmark_at_or_before(ci->focus, pt, rl->typenum, p);
1029 /* End marker is no use, want to refresh last line */
1031 if (m && m->mdata) {
1032 pane_damaged(m->mdata, DAMAGED_REFRESH);
1033 pane_damaged(m->mdata->parent, DAMAGED_REFRESH);
1038 static int revalidate_start(struct rl_data *rl safe,
1039 struct pane *p safe, struct pane *focus safe,
1040 struct mark *start safe, struct mark *pm,
1044 bool on_screen = False;
1045 struct mark *m, *m2;
1046 bool found_end = False;
1050 /* This loop is fragile and sometimes spins. So ensure we
1051 * never loop more than 100 times.
1053 if (pm && !rl->do_wrap && !rl->shift_locked && shifts++ < 100) {
1056 /* Need to check if side-shift is needed on cursor line */
1058 call("doc:render-line-prev", focus, 0, m2);
1060 m = vmark_at_or_before(focus, m2, rl->typenum, p);
1063 if (m && refresh_all)
1064 vmark_invalidate(m);
1065 if (m && m->mdata && !vmark_is_valid(m)) {
1066 pane_damaged(p, DAMAGED_REFRESH);
1067 call("doc:render-line-prev", focus, 0, m);
1068 call_render_line(p, focus, m, &start);
1070 if (m && m->mdata) {
1071 struct pane *hp = m->mdata;
1073 int offset = call_render_line_to_point(focus,
1075 measure_line(p, focus, m, offset);
1076 prefix_len = pane_attr_get_int(
1077 m->mdata, "prefix_len", -1);
1078 curs_width = pane_attr_get_int(
1079 m->mdata, "curs_width", 1);
1081 while (hp->cx + curs_width > p->w && shifts++ < 100) {
1082 int shift = 8 * curs_width;
1085 rl->shift_left += shift;
1086 measure_line(p, focus, m, offset);
1089 /* We shift right is cursor is off the left end, or if
1090 * doing so wouldn't hide anything on the right end
1092 cols = pane_attr_get_int(hp, "width", 0);
1093 while ((hp->cx < prefix_len
1094 || (cols-rl->shift_left) + curs_width * 8 + curs_width < p->w) &&
1095 rl->shift_left > 0 &&
1097 hp->cx + curs_width * 8 < p->w) {
1098 int shift = 8 * curs_width;
1099 if (shift > rl->shift_left)
1100 shift = rl->shift_left;
1101 rl->shift_left -= shift;
1102 measure_line(p, focus, m, offset);
1103 cols = pane_attr_get_int(hp, "width", 0);
1110 struct pane *hp = rl->header->mdata;
1112 measure_line(p, focus, rl->header);
1114 pane_resize(hp, hp->x, y, hp->w, hp->h);
1119 y -= rl->skip_height;
1120 start_of_file = doc_prior(focus, start) == WEOF;
1121 for (m = start; m && !found_end && y < p->h; m = vmark_next(m)) {
1126 vmark_invalidate(m);
1127 call_render_line(p, focus, m, NULL);
1129 /* The "found & 1" handles case when EOF is at the end
1130 * of a non-empty line.
1132 if (pm && m2 && mark_same(pm, m2))
1133 /* Cursor at end shouldn't affect appearance */
1134 found = measure_line(p, focus, m);
1135 if (pm && m2 && mark_ordered_or_same(m, pm) &&
1136 (mark_ordered_not_same(pm, m2) ||
1137 (mark_same(pm, m2) && !(found & 1))))
1138 /* Cursor is on this line */
1139 offset = call_render_line_to_point(focus,
1141 if (pm && mark_same(m, pm))
1142 /* Probably EOF - cursor is here */
1144 found = measure_line(p, focus, m, offset);
1147 if (!mark_valid(m) || !hp)
1150 found_end = found & 2;
1153 pane_damaged(p, DAMAGED_REFRESH);
1154 hp->damaged &= ~DAMAGED_SIZE;
1155 pane_resize(hp, hp->x, y, hp->w, hp->h);
1156 if (hp->damaged & DAMAGED_SIZE && !rl->background_uniform)
1157 pane_damaged(hp, DAMAGED_REFRESH);
1161 /* Cursor is on this line */
1162 int lh = attr_find_int(hp->attrs,
1164 int cy = y - hp->h + hp->cy;
1167 if (m == start && rl->skip_height > 0) {
1168 /* Point might be in this line, but off top
1171 if (hp->cy >= rl->skip_height + rl->margin)
1172 /* Cursor is visible on this line
1173 * and after margin from top.
1176 else if (start_of_file && rl->skip_height == 0)
1177 /* Cannot make more margin space */
1179 } else if (y >= p->h) {
1180 /* point might be in this line, but off end
1184 y - hp->h + hp->cy <= p->h - lh - rl->margin) {
1185 /* Cursor is on screen */
1188 } else if (rl->margin == 0)
1190 else if (cy >= rl->margin &&
1191 cy <= p->h - rl->margin - lh)
1192 /* Cursor at least margin from edge */
1197 rl->tail_height = p->h - y;
1199 rl->tail_height = 0;
1200 if (mark_valid(m)) {
1202 while (mark_valid(m2 = vmark_next(m)) && m2) {
1203 /* end of view has clearly changed */
1204 rl->repositioned = 1;
1208 if (!pm || on_screen) {
1209 if (rl->repositioned) {
1210 rl->repositioned = 0;
1211 call("render:reposition", focus,
1212 rl->lines, vmark_first(focus,
1215 rl->cols, vmark_last(focus,
1225 DEF_CMD(render_lines_revise)
1227 struct pane *p = ci->home;
1228 struct pane *focus = ci->focus;
1229 struct rl_data *rl = p->data;
1230 struct mark *pm = NULL;
1231 struct mark *m1, *m2;
1232 bool refresh_all = False;
1238 a = pane_attr_get(focus, "render-wrap");
1239 wrap = (!a || strcmp(a, "yes") == 0);
1240 if (rl->do_wrap != wrap) {
1246 rl->shift_locked = True;
1248 /* avoid any ambiguity */
1249 attr_set_str(&focus->attrs, "render-wrap", "yes");
1253 shift = strtol(a, &end, 10);
1254 if (end == a || (end && *end && *end != ' '))
1260 if (a && shift >= 0) {
1261 if (rl->shift_left != shift)
1264 rl->shift_left = shift;
1265 rl->shift_locked = strstr(a, "auto") == NULL;
1267 /* unrecognised - no wrap, not locked */
1268 if (rl->shift_locked)
1271 rl->shift_locked = 0;
1272 attr_set_str(&focus->attrs, "render-wrap", "0 auto");
1276 for (v = vmark_first(focus, rl->typenum, p);
1277 (v && v->mdata) ; v = vmark_next(v))
1278 pane_damaged(v->mdata, DAMAGED_REFRESH);
1281 rl->margin = pane_attr_get_int(focus, "render-vmargin", 0);
1282 if (rl->margin >= p->h/2)
1283 rl->margin = p->h/2;
1285 hdr = pane_attr_get(focus, "heading");
1291 rl->header = mark_new(focus);
1293 vmark_set(p, focus, rl->header, hdr);
1294 measure_line(p, focus, rl->header);
1296 } else if (rl->header) {
1297 vmark_free(rl->header);
1301 if (!rl->ignore_point)
1302 pm = call_ret(mark, "doc:point", focus);
1303 m1 = vmark_first(focus, rl->typenum, p);
1304 m2 = vmark_last(focus, rl->typenum, p);
1306 if (m1 && !vmark_is_valid(m1))
1307 /* newline before might have been deleted, better check */
1308 call("doc:render-line-prev", focus, 0, m1);
1309 // FIXME double check that we invalidate line before any change...
1312 (!pm || (mark_ordered_or_same(m1,pm)))) {
1313 /* We maybe be able to keep m1 as start, if things work out.
1314 * So check all sub-panes are still valid and properly
1317 if (revalidate_start(rl, p, focus, m1, pm, refresh_all))
1320 /* Need to find a new top-of-display */
1322 pm = call_ret(mark, "doc:point", focus);
1324 /* Don't know what to do here... */
1326 find_lines(pm, p, focus, NO_NUMERIC);
1327 rl->repositioned = 0;
1328 call("render:reposition", focus,
1329 rl->lines, vmark_first(focus, rl->typenum, p), NULL,
1330 rl->cols, vmark_last(focus, rl->typenum, p), NULL,
1335 DEF_CMD(render_lines_refresh)
1337 struct pane *p = ci->home;
1338 struct pane *focus = ci->focus;
1339 struct rl_data *rl = p->data;
1340 struct mark *m, *pm = NULL;
1341 int cols = rl->cols;
1342 int lines = rl->lines;
1344 //pane_damaged(p, DAMAGED_VIEW);
1346 pm = call_ret(mark, "doc:point", focus);
1348 m = vmark_first(focus, rl->typenum, p);
1353 rl->lines = render(pm, p, focus);
1354 if (rl->lines != lines || rl->cols != cols)
1355 call("render:reposition", focus, rl->lines, NULL, NULL, rl->cols);
1360 DEF_CMD_CLOSED(render_lines_close)
1362 struct rl_data *rl = ci->home->data;
1365 vmark_free(rl->header);
1371 DEF_CMD_CLOSED(render_lines_close_mark)
1373 struct mark *m = ci->mark;
1380 DEF_CMD(render_lines_abort)
1382 struct pane *p = ci->home;
1383 struct rl_data *rl = p->data;
1385 rl->ignore_point = 0;
1388 pane_damaged(p, DAMAGED_VIEW);
1390 /* Allow other handlers to complete the Abort */
1391 return Efallthrough;
1394 DEF_CMD(render_lines_move_view)
1397 * Find a new 'top' for the displayed region so that render()
1398 * will draw from there.
1399 * When moving backwards we move back a line and render it.
1400 * When moving forwards we render and then step forward
1401 * At each point we count the number of display lines that result.
1402 * When we choose a new start, we delete all earlier marks.
1403 * We also delete marks before current top when moving forward
1404 * where there are more than a page full.
1406 struct pane *p = ci->home;
1407 struct pane *focus = ci->focus;
1408 int rpt = RPT_NUM(ci);
1409 struct rl_data *rl = p->data;
1410 struct mark *top, *old_top;
1412 top = vmark_first(focus, rl->typenum, p);
1414 return Efallthrough;
1416 old_top = mark_dup(top);
1420 rl->ignore_point = 1;
1422 if (rl->skip_line_height <= 0)
1423 rl->skip_line_height = 1;
1426 /* Need to add new lines at the top and remove
1432 struct mark *prevtop = top;
1434 if (rl->skip_height) {
1435 rl->skip_height -= rl->skip_line_height;
1436 if (rl->skip_height < rl->skip_line_height/2)
1437 rl->skip_height = 0;
1438 rpt += rl->skip_line_height;
1444 m = mark_dup_view(top);
1445 top = call_render_line_prev(focus, m,
1447 if (!top && doc_prior(focus, prevtop) != WEOF) {
1448 /* Double check - maybe a soft top-of-file - Ctrl-L*/
1449 m = mark_dup(prevtop);
1451 top = call_render_line_prev(focus, m,
1457 while (m && m->seq < prevtop->seq &&
1458 !mark_same(m, prevtop)) {
1459 call_render_line(p, focus, m, NULL);
1460 if (m->mdata == NULL) {
1464 measure_line(p, focus, m);
1468 /* FIXME remove extra lines, maybe add */
1469 rl->skip_height = y;
1472 /* Need to remove lines from top */
1473 call_render_line(p, focus, top, NULL);
1474 measure_line(p, focus, top);
1475 while (top && top->mdata && rpt > 0) {
1479 if (rpt < y - rl->skip_height) {
1480 rl->skip_height += rpt;
1483 rpt -= y - rl->skip_height;
1484 rl->skip_height = 0;
1485 top = vmark_next(top);
1488 call_render_line(p, focus, top, NULL);
1489 measure_line(p, focus, top);
1491 if (top && top->mdata) {
1492 /* We didn't fall off the end, so it is OK to remove
1493 * everything before 'top'
1496 while ((old = vmark_first(focus, rl->typenum, p)) != NULL &&
1501 rl->repositioned = 1;
1502 pane_damaged(ci->home, DAMAGED_VIEW);
1503 top = vmark_first(focus, rl->typenum, p);
1504 if (top && mark_same(top, old_top)) {
1512 static char *get_action_tag(const char *tag safe, const char *a)
1514 int taglen = strlen(tag);
1521 t = strstr(a, ",action-");
1525 } while (!(strncmp(t+8, tag, taglen) == 0 &&
1526 t[8+taglen] == ':'));
1528 t += 8 + taglen + 1;
1530 return strndup(t, c?c-t: (int)strlen(t));
1533 DEF_CMD(render_lines_set_cursor)
1535 /* ->str gives a context specific action to perform
1536 * If the attributes at the location include
1537 * action-$str then the value of that attribute
1538 * is send as a command
1540 struct pane *p = ci->home;
1541 struct pane *focus = ci->focus;
1542 struct rl_data *rl = p->data;
1543 const char *action = ci->str;
1544 const char *xyattr = NULL;
1546 struct mark *m2 = NULL;
1550 cih = pane_mapxy(ci->focus, ci->home,
1551 ci->x == INT_MAX ? p->cx : ci->x,
1552 ci->y == INT_MAX ? p->cy : ci->y,
1555 m = vmark_first(p, rl->typenum, p);
1557 while (m && m->mdata && m->mdata->y + m->mdata->h <= cih.y &&
1562 /* There is nothing rendered? */
1565 /* might be able to find a position in the line */
1566 if (cih.y < m->mdata->y) {
1567 /* action only permitted in precise match */
1569 cih.y = m->mdata->y;
1571 xypos = find_xy_line(p, focus, m, cih.x, cih.y, &xyattr);
1573 (m2 = call_render_line_offset(focus, m, xypos)) != NULL) {
1575 wint_t c = doc_following(focus, m2);
1576 if (c == WEOF || is_eol(c))
1577 /* after last char on line - no action. */
1579 if (action && xyattr &&
1580 (tag = get_action_tag(action, xyattr)) != NULL) {
1582 /* This is a hack to get the
1583 * start of these attrs so menu
1584 * can be placed correctly.
1585 * Only works for menus below
1588 if (!strstr(xyattr, ",menu-at-mouse,") &&
1589 sscanf(xyattr, "%dx%d,", &x, &y) == 2) {
1591 cih.y = m->mdata->y + y +
1592 attr_find_int(m->mdata->attrs,
1596 call(tag, focus, 0, m2, xyattr,
1604 mark_to_mark(ci->mark, m);
1606 call("Move-to", focus, 0, m);
1612 DEF_CMD(render_lines_action)
1614 /* If there is an action-$str: at '->mark', send the command
1617 struct mark *m = ci->mark;
1618 struct pane *p = ci->home;
1619 struct rl_data *rl = p->data;
1620 struct pane *focus = ci->focus;
1623 char *attr = NULL, *tag;
1627 v = vmark_first(p, rl->typenum, p);
1629 while (v && v->mdata && (n = vmark_next(v)) &&
1630 mark_ordered_or_same(n, m))
1633 if (!v || !v->mdata || !mark_ordered_or_same(v, m))
1634 return Efallthrough;
1635 offset = call_render_line_to_point(focus, m, v);
1636 measure_line(p, focus, v, offset, &attr);
1638 return Efallthrough;
1639 tag = get_action_tag(ci->str, attr);
1641 return Efallthrough;
1642 call(tag, focus, 0, m, attr);
1646 DEF_CMD(render_lines_move_pos)
1648 struct pane *p = ci->home;
1649 struct pane *focus = ci->focus;
1650 struct rl_data *rl = p->data;
1651 struct mark *pm = ci->mark;
1652 struct mark *top, *bot;
1656 rl->ignore_point = 1;
1657 top = vmark_first(focus, rl->typenum, p);
1658 bot = vmark_last(focus, rl->typenum, p);
1659 if (top && rl->skip_height)
1660 /* top line not fully displayed, being in that line is
1662 top = vmark_next(top);
1664 /* last line might not be fully displayed, so don't assume */
1665 bot = vmark_prev(bot);
1667 !mark_ordered_or_same(top, pm) ||
1668 !mark_ordered_not_same(pm, bot))
1669 /* pos not displayed */
1670 find_lines(pm, p, focus, NO_NUMERIC);
1671 pane_damaged(p, DAMAGED_REFRESH);
1675 DEF_CMD(render_lines_view_line)
1677 struct pane *p = ci->home;
1678 struct pane *focus = ci->focus;
1679 struct rl_data *rl = p->data;
1680 struct mark *pm = ci->mark;
1685 if (line == NO_NUMERIC)
1688 rl->ignore_point = 1;
1689 find_lines(pm, p, focus, line);
1690 pane_damaged(p, DAMAGED_REFRESH);
1694 DEF_CMD(render_lines_move_line)
1696 /* FIXME should be able to select between display lines
1697 * and content lines - different when a line wraps.
1698 * For now just content lines.
1699 * target_x and target_y are the target location in a line
1700 * relative to the start of line.
1701 * We use doc:EOL to find a suitable start of line, then
1702 * render that line and find the last location not after x,y
1704 struct pane *p = ci->home;
1705 struct pane *focus = ci->focus;
1706 struct rl_data *rl = p->data;
1709 struct mark *m = ci->mark;
1710 struct mark *start, *m2;
1713 m = call_ret(mark, "doc:point", focus);
1717 if (rl->target_x < 0) {
1718 rl->target_x = p->cx;
1719 rl->target_y = p->cy - rl->cursor_line;
1721 if (rl->target_x < 0)
1722 /* maybe not displayed yet */
1723 rl->target_x = rl->target_y = 0;
1727 if (call("doc:EOL", ci->focus, num, m, NULL, 1) <= 0) {
1731 if (RPT_NUM(ci) < 0) {
1732 /* at end of target line, move to start */
1733 if (call("doc:EOL", ci->focus, -1, m) <= 0) {
1739 /* We are at the start of the target line. We might
1740 * like to find the target_x column, but if anything
1741 * goes wrong it isn't a failure.
1742 * Need to ensure there is a vmark here. call_render_line_prev()
1743 * wil only move the mark if it is in a multi-line rendering,
1744 * such as an image which acts as though it is multiple lines.
1745 * It will check if there is already a mark at the target location.
1746 * It will free the mark passed in unless it returns it.
1748 start = vmark_new(focus, rl->typenum, p);
1751 mark_to_mark(start, m);
1752 start = call_render_line_prev(focus, start, 0, NULL);
1756 pane_damaged(p, DAMAGED_VIEW);
1759 if (vmark_first(focus, rl->typenum, p) == start &&
1760 !vmark_is_valid(start))
1761 /* New first mark, so view will have changed */
1762 rl->repositioned = 1;
1764 if (rl->target_x == 0 && rl->target_y == 0)
1765 /* No need to move to target column - already there.
1766 * This simplifies life for render-complete which is
1767 * always at col 0, and messes with markup a bit.
1771 /* FIXME only do this if point is active/volatile, or
1772 * if start->mdata is NULL
1774 vmark_invalidate(start);
1775 call_render_line(p, focus, start, NULL);
1779 xypos = find_xy_line(p, focus, start, rl->target_x,
1780 rl->target_y + start->mdata->y, NULL);
1784 /* xypos is the distance from start-of-line to the target */
1786 m2 = call_render_line_offset(focus, start, xypos);
1790 if (!mark_same(start, m)) {
1791 /* This is a multi-line render and we aren't on
1792 * the first line. We might need a larger 'y'.
1793 * For now, ensure that we move in the right
1795 * FIXME this loses target_x and can move up
1796 * too far. How to fix??
1798 if (num > 0 && mark_ordered_not_same(m2, m))
1799 mark_to_mark(m2, m);
1800 if (num < 0 && mark_ordered_not_same(m, m2))
1801 mark_to_mark(m2, m);
1803 mark_to_mark(m, m2);
1812 DEF_CMD(render_lines_notify_replace)
1814 struct pane *p = ci->home;
1815 struct rl_data *rl = p->data;
1816 struct mark *start = ci->mark;
1817 struct mark *end = ci->mark2;
1820 if (strcmp(ci->key, "doc:replaced") == 0) {
1821 struct mark *pt = call_ret(mark, "doc:point", ci->home);
1823 /* If anyone changes the doc, reset the target. This might
1824 * be too harsh, but I mainly want target tracking for
1825 * close-in-time movement, so it probably doesn't matter.
1829 /* If the replacement happened at 'point', then stop
1830 * ignoring it, and handle the fact that point moved.
1832 if (ci->mark2 == pt)
1833 pane_call(p, "mark:moving", ci->focus, 0, pt);
1836 if (strcmp(ci->key, "view:changed") == 0)
1837 /* Cursor possibly moved, so need to refresh */
1838 pane_damaged(ci->home, DAMAGED_REFRESH);
1840 if (!start && !end) {
1841 /* No marks given - assume everything changed */
1843 for (m = vmark_first(p, rl->typenum, p);
1846 vmark_invalidate(m);
1848 pane_damaged(p, DAMAGED_VIEW);
1849 return Efallthrough;
1852 if (start && end && start->seq > end->seq) {
1857 if (strcmp(ci->key, "doc:replaced") == 0) {
1858 first = vmark_first(ci->home, rl->typenum, p);
1859 if (first && start && end && mark_same(first, end))
1860 /* Insert just before visible region */
1861 mark_to_mark(first, start);
1865 start = vmark_at_or_before(ci->home, start, rl->typenum, p);
1867 start = vmark_first(ci->home, rl->typenum, p);
1869 start = vmark_at_or_before(ci->home, end, rl->typenum, p);
1871 /* change is before visible region */
1872 return Efallthrough;
1873 /* FIXME check 'start' is at least 'num' before end */
1876 end = vmark_at_or_before(ci->home, end, rl->typenum, p);
1878 end = vmark_last(ci->home, rl->typenum, p);
1879 } else if (start) { /* smatch needs to know start in not NULL */
1880 end = vmark_at_or_before(ci->home, start, rl->typenum, p);
1882 end = vmark_first(ci->home, rl->typenum, p);
1884 return Efallthrough;
1885 if (vmark_next(end))
1886 end = vmark_next(end);
1887 /* FIXME check that 'end' is at least 'num' after start */
1891 /* Change outside visible region */
1892 return Efallthrough;
1894 while (end && mark_ordered_or_same(start, end)) {
1895 vmark_invalidate(end);
1896 end = vmark_prev(end);
1898 /* Must be sure to invalidate the line *before* the change */
1900 vmark_invalidate(end);
1902 pane_damaged(p, DAMAGED_VIEW);
1904 return Efallthrough;
1907 DEF_CMD(render_lines_clip)
1909 struct rl_data *rl = ci->home->data;
1911 marks_clip(ci->home, ci->mark, ci->mark2, rl->typenum, ci->home,
1914 mark_clip(rl->header, ci->mark, ci->mark2, !!ci->num);
1915 return Efallthrough;
1918 DEF_CMD(render_lines_attach);
1919 DEF_CMD(render_lines_clone)
1921 struct pane *parent = ci->focus;
1923 render_lines_attach.func(ci);
1924 pane_clone_children(ci->home, parent->focus);
1928 DEF_CMD(render_lines_resize)
1930 struct pane *p = ci->home;
1931 struct rl_data *rl = p->data;
1934 for (m = vmark_first(p, rl->typenum, p);
1936 m = vmark_next(m)) {
1937 vmark_invalidate(m);
1938 pane_damaged(m->mdata, DAMAGED_REFRESH);
1940 rl->background_drawn = False;
1941 pane_damaged(p, DAMAGED_VIEW | DAMAGED_REFRESH);
1943 /* Allow propagation to children */
1947 DEF_CMD(render_send_reposition)
1949 /* Some (probably new) pane wants to know the extend of the
1950 * view, so resent render:resposition.
1952 struct pane *p = ci->home;
1953 struct rl_data *rl = p->data;
1955 rl->repositioned = 1;
1956 return Efallthrough;
1959 static struct map *rl_map;
1961 DEF_LOOKUP_CMD(render_lines_handle, rl_map);
1963 static void render_lines_register_map(void)
1965 rl_map = key_alloc();
1967 key_add(rl_map, "Move-View", &render_lines_move_view);
1968 key_add(rl_map, "Move-View-Pos", &render_lines_move_pos);
1969 key_add(rl_map, "Move-View-Line", &render_lines_view_line);
1970 key_add(rl_map, "Move-CursorXY", &render_lines_set_cursor);
1971 key_add(rl_map, "Move-Line", &render_lines_move_line);
1973 /* Make it easy to stop ignoring point */
1974 key_add(rl_map, "Abort", &render_lines_abort);
1976 key_add(rl_map, "Action", &render_lines_action);
1978 key_add(rl_map, "Close", &render_lines_close);
1979 key_add(rl_map, "Close:mark", &render_lines_close_mark);
1980 key_add(rl_map, "Clone", &render_lines_clone);
1981 key_add(rl_map, "Refresh", &render_lines_refresh);
1982 key_add(rl_map, "Refresh:view", &render_lines_revise);
1983 key_add(rl_map, "Refresh:size", &render_lines_resize);
1984 key_add(rl_map, "Notify:clip", &render_lines_clip);
1985 key_add(rl_map, "mark:moving", &render_lines_point_moving);
1987 key_add(rl_map, "doc:replaced", &render_lines_notify_replace);
1988 key_add(rl_map, "doc:replaced-attr", &render_lines_notify_replace);
1989 /* view:changed is sent to a tile when the display might need
1990 * to change, even though the doc may not have*/
1991 key_add(rl_map, "view:changed", &render_lines_notify_replace);
1992 key_add(rl_map, "render:request:reposition", &render_send_reposition);
1995 REDEF_CMD(render_lines_attach)
2001 render_lines_register_map();
2004 if (strcmp(ci->key, "attach-render-text") == 0) {
2005 p = call_ret(pane, "attach-markup", p);
2009 p = pane_register(p, 0, &render_lines_handle.c);
2016 rl->typenum = home_call(ci->focus, "doc:add-view", p) - 1;
2017 call("doc:request:doc:replaced", p);
2018 call("doc:request:doc:replaced-attr", p);
2019 call("doc:request:mark:moving", p);
2021 return comm_call(ci->comm2, "callback:attach", p);
2024 void edlib_init(struct pane *ed safe)
2026 call_comm("global-set-command", ed, &render_lines_attach, 0, NULL,
2027 "attach-render-lines");
2028 call_comm("global-set-command", ed, &render_lines_attach, 0, NULL,
2029 "attach-render-text");