/* This file contains functions that draw text on the screen. The major entry
* redrawrange() - called from modify.c to give hints about what parts
* of the screen need to be redrawn.
* redraw() - redraws the screen (or part of it) and positions
* the cursor where it belongs.
* idx2col() - converts a markidx() value to a logical column number.
# define NEAR (*o_nearscroll&0xff)
/* This variable contains the line number that smartdrawtext() knows best */
/* This function remembers where changes were made, so that the screen can be
* redraw in a more efficient manner.
static long redrawafter
; /* line# of first line that must be redrawn */
static long preredraw
; /* line# of last line changed, before change */
static long postredraw
; /* line# of last line changed, after change */
static int mustredraw
; /* boolean: anything forcing a screen update? */
void redrawrange(after
, pre
, post
)
long after
; /* lower bound of redrawafter */
long pre
; /* upper bound of preredraw */
long post
; /* upper bound of postredraw */
if (after
== redrawafter
)
/* multiple insertions/deletions at the same place -- combine
preredraw
-= (post
- pre
);
preredraw
+= (post
- postredraw
);
if (redrawafter
> preredraw
)
preredraw
= postredraw
= INFINITY
;
else if (postredraw
> 0L)
/* multiple changes in different places -- redraw everything
postredraw
= preredraw
= INFINITY
;
/* see if a given line uses character attribute strings */
static int hasattr(lno
, text
)
long lno
; /* the line# of the cursor */
REG
char *text
; /* the text of the line, from fetchline */
static long plno
; /* previous line number */
static long chgs
; /* previous value of changes counter */
static int panswer
;/* previous answer */
/* if charattr is off, then the answer is "no, it doesn't" */
chgs
= 0; /* <- forces us to check if charattr is later set */
/* if we already know the answer, return it... */
if (lno
== plno
&& chgs
== changes
)
/* get the line & look for "\fX" */
if (!text
[0] || !text
[1] || !text
[2])
for (scan
= text
; scan
[2] && !(scan
[0] == '\\' && scan
[1] == 'f'); scan
++)
panswer
= (scan
[2] != '\0');
/* This function checks to make sure that the correct lines are shown in
* reverse-video. This is used to handle the "v" and "V" commands.
static long vizlow
, vizhigh
; /* the starting and ending lines */
static int vizleft
, vizright
; /* starting & ending indicies */
static int vizchange
; /* boolean: must use stupid drawtext? */
/* for now, assume the worst... */
/* set newlow & newhigh according to V_from and cursor */
/* no lines should have reverse-video */
redrawrange(vizlow
, vizhigh
+ 1L, vizhigh
+ 1L);
/* figure out which lines *SHOULD* have hilites */
newlow
= markline(V_from
);
newhigh
= markline(curs
);
vizleft
= markidx(V_from
);
vizright
= markidx(curs
) + 1;
newhigh
= markline(V_from
);
vizright
= markidx(V_from
) + 1;
/* adjust for line-mode hiliting */
/* arrange for the necessary lines to be redrawn */
/* just starting to redraw */
redrawrange(newlow
, newhigh
, newhigh
);
/* Were new lines added/removed at the front? */
redrawrange(newlow
, vizlow
+ extra
, vizlow
+ extra
);
redrawrange(vizlow
, newlow
+ extra
, newlow
+ extra
);
/* Were new lines added/removed at the back? */
redrawrange(newhigh
+ 1L - extra
, vizhigh
+ 1L, vizhigh
+ 1L);
redrawrange(vizhigh
+ 1L - extra
, newhigh
, newhigh
);
/* remember which lines will contain hilighted text now */
/* This function converts a MARK to a column number. It doesn't automatically
* adjust for leftcol; that must be done by the calling function
int idx2col(curs
, text
, inputting
)
MARK curs
; /* the line# & index# of the cursor */
REG
char *text
; /* the text of the line, from fetchline */
int inputting
; /* boolean: called from input() ? */
static MARK pcursor
;/* previous cursor, for possible shortcut */
static MARK pcol
; /* column number for pcol */
static long chgs
; /* previous value of changes counter */
REG
int col
; /* used to count column numbers */
REG
int idx
; /* used to count down the index */
/* for now, assume we have to start counting at the left edge */
/* if the file hasn't changed & line number is the same & it has no
* embedded character attribute strings, can we do shortcuts?
&& !((curs
^ pcursor
) & ~(BLKSIZE
- 1))
&& !hasattr(markline(curs
), text
)
/* return the column of the char; for tabs, return its last column */
if (text
[idx
] == '\t' && !inputting
&& !*o_list
)
return pcol
+ *o_tabstop
- (pcol
% *o_tabstop
) - 1;
/* start counting from previous place */
idx
= markidx(curs
) - markidx(pcursor
);
text
+= markidx(pcursor
);
/* count over to the char after the idx position */
while (idx
> 0 && (i
= *text
)) /* yes, ASSIGNMENT! */
if (i
== '\t' && !*o_list
)
else if (i
>= '\0' && i
< ' ' || i
== '\177')
else if (i
== '\\' && text
[1] == 'f' && text
[2] && *o_charattr
)
text
+= 2; /* plus one more at bottom of loop */
/* save stuff to speed next call */
/* return the column of the char; for tabs, return its last column */
if (*text
== '\t' && !inputting
&& !*o_list
)
return col
+ *o_tabstop
- (col
% *o_tabstop
) - 1;
/* This function is similar to idx2col except that it takes care of sideways
* scrolling - for the given line, at least.
int mark2phys(m
, text
, inputting
)
MARK m
; /* a mark to convert */
char *text
; /* the line that m refers to */
int inputting
; /* boolean: caled from input() ? */
i
= idx2col(m
, text
, inputting
);
leftcol
-= *o_sidescroll
;
redrawrange(1L, INFINITY
, INFINITY
);
leftcol
+= *o_sidescroll
;
redrawrange(1L, INFINITY
, INFINITY
);
physrow
= markline(m
) - topline
;
/* This function draws a single line of text on the screen. The screen's
* cursor is assumed to be located at the leftmost column of the appropriate
static void drawtext(text
, lno
, clr
)
REG
char *text
; /* the text to draw */
long lno
; /* the number of the line to draw */
int clr
; /* boolean: do a clrtoeol? */
REG
int col
; /* column number */
REG
int tabstop
; /* *o_tabstop */
REG
int limitcol
; /* leftcol or leftcol + COLS */
int abnormal
; /* boolean: charattr != A_NORMAL? */
int rev
; /* boolean: standout mode, too? */
/* show the line number, if necessary */
sprintf(numstr
, "%6ld ", lno
);
/* if we're hiding format lines, and this is one of them, then hide it */
if (*o_hideformat
&& *text
== '.')
/* move some things into registers... */
/* skip stuff that was scrolled off left edge */
(i
= *text
) && col
< limitcol
; /* yes, ASSIGNMENT! */
if (i
== '\t' && !*o_list
)
col
= col
+ tabstop
- (col
% tabstop
);
else if (i
>= 0 && i
< ' ' || i
== '\177')
else if (i
== '\\' && text
[1] == 'f' && text
[2] && *o_charattr
)
text
+= 2; /* plus one more as part of "for" loop */
/* since this attribute might carry over, we need it */
/* Should we start hiliting at the first char of this line? */
if ((lno
> vizlow
&& lno
<= vizhigh
|| lno
== vizlow
&& vizleft
< idx
)
&& !(lno
== vizhigh
&& vizright
< idx
))
/* adjust for control char that was partially visible */
/* now for the visible characters */
limitcol
= leftcol
+ COLS
;
for (; (i
= *text
) && col
< limitcol
; text
++)
/* maybe turn hilite on/off in the middle of the line */
if (lno
== vizlow
&& vizleft
== idx
)
if (lno
== vizhigh
&& vizright
== idx
)
/* if hiliting, never emit physical tabs */
if (rev
&& i
== '\t' && !*o_list
)
i
= col
+ tabstop
- (col
% tabstop
);
} while (col
< i
&& col
< limitcol
);
if (i
== '\t' && !*o_list
)
i
= col
+ tabstop
- (col
% tabstop
);
if (!clr
&& has_PT
&& !((i
- leftcol
) & 7))
if (has_PT
&& !((i
- leftcol
) & 7))
col
+= 8; /* not exact! */
col
= i
; /* NOW it is exact */
} while (col
< i
&& col
< limitcol
);
else /* tab ending after screen? next line! */
/* needed at least when scrolling the screen right -nox */
if (clr
&& col
< limitcol
)
else if (i
>= 0 && i
< ' ' || i
== '\177')
else if (i
== '\\' && text
[1] == 'f' && text
[2] && *o_charattr
)
text
+= 2; /* plus one more as part of "for" loop */
/* get ready for the next line */
if (*o_list
&& col
< limitcol
)
/* did we hilite this whole line? If so, STOP! */
if (clr
&& col
< limitcol
)
if (!has_AM
|| col
< limitcol
)
static void nudgecursor(same
, scan
, new, lno
)
int same
; /* number of chars to be skipped over */
char *scan
; /* where the same chars end */
char *new; /* where the visible part of the line starts */
long lno
; /* line number of this line */
/* move the cursor by overwriting */
/* move the cursor by calling move() */
move((int)(lno
- topline
), col
);
/* This function draws a single line of text on the screen, possibly with
* some cursor optimization. The cursor is repositioned before drawing
* begins, so its position before doesn't really matter.
static void smartdrawtext(text
, lno
, showit
)
REG
char *text
; /* the text to draw */
long lno
; /* line number of the text */
int showit
; /* boolean: output line? (else just remember it) */
move((int)(lno
- topline
), 0);
drawtext(text
, lno
, TRUE
);
static char old
[256]; /* how the line looked last time */
char new[256]; /* how it looks now */
char *build
; /* used to put chars into new[] */
char *scan
; /* used for moving thru new[] or old[] */
char *end
; /* last non-blank changed char */
char *shift
; /* used to insert/delete chars */
int same
; /* length of a run of unchanged chars */
/* if this line has attributes, do it the dumb way instead */
move((int)(lno
- topline
), 0);
drawtext(text
, lno
, TRUE
);
/* if this line is a format line, & we're hiding format lines, then
* let the dumb drawtext() function handle it
if (*o_hideformat
&& *text
== '.')
move((int)(lno
- topline
), 0);
drawtext(text
, lno
, TRUE
);
move((int)(lno
- topline
), 0);
drawtext(text
, lno
, TRUE
);
/* skip stuff that was scrolled off left edge */
(i
= *text
) && col
< limitcol
; /* yes, ASSIGNMENT! */
if (i
== '\t' && !*o_list
)
col
= col
+ *o_tabstop
- (col
% *o_tabstop
);
else if (i
>= 0 && i
< ' ' || i
== '\177')
/* adjust for control char that was partially visible */
/* now for the visible characters */
limitcol
= leftcol
+ COLS
;
for (; (i
= *text
) && col
< limitcol
; text
++)
if (i
== '\t' && !*o_list
)
i
= col
+ *o_tabstop
- (col
% *o_tabstop
);
while (col
< i
&& col
< limitcol
)
else if (i
>= 0 && i
< ' ' || i
== '\177')
if (col
< limitcol
&& *o_list
)
/* if we're just supposed to remember this line, then remember it */
/* locate the last non-blank character */
while (end
> new && end
[-1] == ' ')
/* can we optimize the displaying of this line? */
/* nope, can't optimize - different line */
move((int)(lno
- topline
), 0);
/* show the line number, if necessary */
sprintf(numstr
, "%6ld ", lno
);
for (scan
= new, build
= old
; scan
< end
; )
if (end
< new + COLS
- (*o_number
? 8 : 0))
while (build
< old
+ COLS
)
/* skip any initial unchanged characters */
for (scan
= new, build
= old
; scan
< end
&& *scan
== *build
; scan
++, build
++)
move((int)(lno
- topline
), i
);
/* The in-between characters must be changed */
/* is this character a match? */
else /* do we want to insert? */
if (scan
< end
- 1 && scan
[1] == build
[0] && (has_IC
|| has_IM
))
nudgecursor(same
, scan
, new, lno
);
for (shift
= old
+ COLS
; --shift
> build
; )
else /* do we want to delete? */
if (build
< old
+ COLS
- 1 && scan
[0] == build
[1] && has_DC
)
nudgecursor(same
, scan
, new, lno
);
for (shift
= build
; shift
< old
+ COLS
- 1; shift
++)
else /* we must overwrite */
nudgecursor(same
, scan
, new, lno
);
end
= old
+ COLS
- (*o_number
? 8 : 0);
while (build
< end
&& *build
== ' ')
nudgecursor(same
, scan
, new, lno
);
while (build
< old
+ COLS
)
/* This function is used in visual mode for drawing the screen (or just parts
* of the screen, if that's all thats needed). It also takes care of
void redraw(curs
, inputting
)
MARK curs
; /* where to leave the screen's cursor */
int inputting
; /* boolean: being called from input() ? */
char *text
; /* a line of text to display */
static long chgs
; /* previous changes level */
static long showtop
; /* top line in window */
static long showbottom
; /* bottom line in window */
/* if curs == MARK_UNSET, then we should reset internal vars */
if (topline
< 1 || topline
> nlines
)
/* adjustments to hilited area may force extra lines to be redrawn. */
/* figure out which column the cursor will be in */
mark2phys(curs
, text
, inputting
);
/* adjust topline, if necessary, to get the cursor on the screen */
if (l
>= topline
&& l
<= botline
)
/* it is on the screen already */
/* if the file was changed but !mustredraw, then redraw line */
if (!mustredraw
&& (chgs
!= changes
|| l
< showtop
|| l
> showbottom
smartdrawtext(text
, l
, (chgs
!= changes
));
else if (l
< topline
&& l
>= topline
- NEAR
&& (has_SR
|| has_AL
))
/* near top - scroll down */
text
= fetchline(topline
);
drawtext(text
, topline
, FALSE
);
/* blank out the last line */
redrawrange(0L, INFINITY
, INFINITY
);
else if (l
> topline
&& l
<= botline
+ NEAR
)
/* near bottom -- scroll up */
topline
++; /* <-- also adjusts botline */
text
= fetchline(botline
);
drawtext(text
, botline
, FALSE
);
topline
= l
- (LINES
- 2);
redrawrange(0L, INFINITY
, INFINITY
);
/* distant line - center it & force a redraw */
topline
= l
- (LINES
- 1) / 2;
redrawrange(0L, INFINITY
, INFINITY
);
/* make sure the current line is included in the "window" */
redrawrange(l
, showtop
, showtop
);
redrawrange(showbottom
, l
, l
);
/* Now... do we really have to redraw? */
/* If redrawfter (and friends) aren't set, assume we should
if (redrawafter
== INFINITY
)
preredraw
= postredraw
= INFINITY
;
/* shrink the window, if possible */
if (showbottom
> botline
)
if (postredraw
== INFINITY
)
/* these will be set to more reasonable values later */
/* adjust smartlno to correspond with inserted/deleted lines */
if (smartlno
>= redrawafter
)
if (smartlno
< preredraw
&& postredraw
!= preredraw
) /*!!!*/
smartlno
+= (postredraw
- preredraw
);
/* should we insert some lines into the screen? */
if (preredraw
< postredraw
&& preredraw
<= botline
)
/* lines were inserted into the file */
/* decide where insertion should start */
/* insert the lines... maybe */
if (l
+ postredraw
- preredraw
> botline
|| !has_AL
|| *o_number
)
/* Whoa! a whole screen full - just redraw */
preredraw
= postredraw
= INFINITY
;
move((int)(l
- topline
), 0);
for (i
= postredraw
- preredraw
; i
> 0; i
--)
/* NOTE: the contents of those lines will be
* drawn as part of the regular redraw loop.
/* clear the last line */
/* do we want to delete some lines from the screen? */
if (preredraw
> postredraw
&& postredraw
<= botline
)
if (preredraw
> botline
|| !has_DL
|| *o_number
)
postredraw
= preredraw
= INFINITY
;
else /* we'd best delete some lines from the screen */
/* clear the last line, so it doesn't look
* ugly as it gets pulled up into the screen
move((int)(postredraw
- topline
), 0);
l
< preredraw
&& l
<= botline
;
/* draw the lines that are now newly visible
* at the bottom of the screen
i
= LINES
- 1 + (postredraw
- preredraw
);
for (l
= topline
+ i
; l
<= botline
; l
++)
/* draw the line, or ~ for non-lines */
drawtext(text
, l
, FALSE
);
/* redraw the current line */
smartdrawtext(ptext
, l
, TRUE
);
/* decide which lines must be in the "window" around the cursor */
if ((*o_window
& 0xff) + 1 == LINES
)
else if (l
< showtop
|| l
> showbottom
)
l
-= (*o_window
& 0xff) / 2;
l
+= (*o_window
& 0xff) - 1;
showtop
= showtop
- l
+ botline
;
/* decide where we should start redrawing from */
if (redrawafter
< topline
)
if (l
<= botline
&& l
< postredraw
&& (l
!= smartlno
|| botline
!= smartlno
))
/* draw the other lines */
move((int)(l
- topline
), 0);
for (; l
<= botline
&& l
< postredraw
; l
++)
/* we already drew the current line, so skip it now */
/* draw the line, or ~ for non-lines */
else if (l
< showtop
|| l
> showbottom
)
/* force total (non-partial) redraw next time if not set */
/* move the cursor to where it belongs */
move((int)(markline(curs
) - topline
), physcol
);