1307 lines
25 KiB
C
1307 lines
25 KiB
C
/* redraw.c */
|
|
|
|
/* Author:
|
|
* Steve Kirkendall
|
|
* 14407 SW Teal Blvd. #C
|
|
* Beaverton, OR 97005
|
|
* kirkenda@cs.pdx.edu
|
|
*/
|
|
|
|
|
|
/* This file contains functions that draw text on the screen. The major entry
|
|
* points are:
|
|
* 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.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "vi.h"
|
|
#ifdef CRUNCH
|
|
# define NEAR LINES
|
|
#else
|
|
# define NEAR (*o_nearscroll&0xff)
|
|
#endif
|
|
|
|
/* This variable contains the line number that smartdrawtext() knows best */
|
|
static long smartlno;
|
|
|
|
/* 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
|
|
* them
|
|
*/
|
|
preredraw -= (post - pre);
|
|
if (postredraw < post)
|
|
{
|
|
preredraw += (post - postredraw);
|
|
postredraw = post;
|
|
}
|
|
if (redrawafter > preredraw)
|
|
{
|
|
redrawafter = preredraw;
|
|
}
|
|
if (redrawafter < 1L)
|
|
{
|
|
redrawafter = 0L;
|
|
preredraw = postredraw = INFINITY;
|
|
}
|
|
}
|
|
else if (postredraw > 0L)
|
|
{
|
|
/* multiple changes in different places -- redraw everything
|
|
* after "after".
|
|
*/
|
|
postredraw = preredraw = INFINITY;
|
|
if (after < redrawafter)
|
|
redrawafter = after;
|
|
}
|
|
else
|
|
{
|
|
/* first change */
|
|
redrawafter = after;
|
|
preredraw = pre;
|
|
postredraw = post;
|
|
}
|
|
mustredraw = TRUE;
|
|
}
|
|
|
|
|
|
#ifndef NO_CHARATTR
|
|
/* 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 */
|
|
char *scan;
|
|
|
|
/* if charattr is off, then the answer is "no, it doesn't" */
|
|
if (!*o_charattr)
|
|
{
|
|
chgs = 0; /* <- forces us to check if charattr is later set */
|
|
return FALSE;
|
|
}
|
|
|
|
/* if we already know the answer, return it... */
|
|
if (lno == plno && chgs == changes)
|
|
{
|
|
return panswer;
|
|
}
|
|
|
|
/* get the line & look for "\fX" */
|
|
if (!text[0] || !text[1] || !text[2])
|
|
{
|
|
panswer = FALSE;
|
|
}
|
|
else
|
|
{
|
|
for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++)
|
|
{
|
|
}
|
|
panswer = (scan[2] != '\0');
|
|
}
|
|
|
|
/* save the results */
|
|
plno = lno;
|
|
chgs = changes;
|
|
|
|
/* return the results */
|
|
return panswer;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef NO_VISIBLE
|
|
/* 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? */
|
|
static void setviz(curs)
|
|
MARK curs;
|
|
{
|
|
long newlow, newhigh;
|
|
long extra = 0L;
|
|
|
|
/* for now, assume the worst... */
|
|
vizchange = TRUE;
|
|
|
|
/* set newlow & newhigh according to V_from and cursor */
|
|
if (!V_from)
|
|
{
|
|
/* no lines should have reverse-video */
|
|
if (vizlow)
|
|
{
|
|
redrawrange(vizlow, vizhigh + 1L, vizhigh + 1L);
|
|
vizlow = vizhigh = 0L;
|
|
}
|
|
else
|
|
{
|
|
vizchange = FALSE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* figure out which lines *SHOULD* have hilites */
|
|
if (V_from < curs)
|
|
{
|
|
newlow = markline(V_from);
|
|
newhigh = markline(curs);
|
|
vizleft = markidx(V_from);
|
|
vizright = markidx(curs) + 1;
|
|
}
|
|
else
|
|
{
|
|
newlow = markline(curs);
|
|
newhigh = markline(V_from);
|
|
vizleft = markidx(curs);
|
|
vizright = markidx(V_from) + 1;
|
|
}
|
|
|
|
/* adjust for line-mode hiliting */
|
|
if (V_linemd)
|
|
{
|
|
vizleft = 0;
|
|
vizright = BLKSIZE - 1;
|
|
}
|
|
else
|
|
{
|
|
extra = 1L;
|
|
}
|
|
|
|
/* arrange for the necessary lines to be redrawn */
|
|
if (vizlow == 0L)
|
|
{
|
|
/* just starting to redraw */
|
|
redrawrange(newlow, newhigh, newhigh);
|
|
}
|
|
else
|
|
{
|
|
/* Were new lines added/removed at the front? */
|
|
if (newlow != vizlow)
|
|
{
|
|
if (newlow < vizlow)
|
|
redrawrange(newlow, vizlow + extra, vizlow + extra);
|
|
else
|
|
redrawrange(vizlow, newlow + extra, newlow + extra);
|
|
}
|
|
|
|
/* Were new lines added/removed at the back? */
|
|
if (newhigh != vizhigh)
|
|
{
|
|
if (newhigh < vizhigh)
|
|
redrawrange(newhigh + 1L - extra, vizhigh + 1L, vizhigh + 1L);
|
|
else
|
|
redrawrange(vizhigh + 1L - extra, newhigh, newhigh);
|
|
}
|
|
}
|
|
|
|
/* remember which lines will contain hilighted text now */
|
|
vizlow = newlow;
|
|
vizhigh = newhigh;
|
|
}
|
|
#endif /* !NO_VISIBLE */
|
|
|
|
|
|
/* 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 */
|
|
REG int i;
|
|
|
|
/* for now, assume we have to start counting at the left edge */
|
|
col = 0;
|
|
idx = markidx(curs);
|
|
|
|
/* if the file hasn't changed & line number is the same & it has no
|
|
* embedded character attribute strings, can we do shortcuts?
|
|
*/
|
|
if (chgs == changes
|
|
&& !((curs ^ pcursor) & ~(BLKSIZE - 1))
|
|
#ifndef NO_CHARATTR
|
|
&& !hasattr(markline(curs), text)
|
|
#endif
|
|
)
|
|
{
|
|
/* no movement? */
|
|
if (curs == pcursor)
|
|
{
|
|
/* 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;
|
|
}
|
|
else
|
|
{
|
|
return pcol;
|
|
}
|
|
}
|
|
|
|
/* movement to right? */
|
|
if (curs > pcursor)
|
|
{
|
|
/* start counting from previous place */
|
|
col = pcol;
|
|
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)
|
|
{
|
|
col += *o_tabstop;
|
|
col -= col % *o_tabstop;
|
|
}
|
|
else if (i >= '\0' && i < ' ' || i == '\177')
|
|
{
|
|
col += 2;
|
|
}
|
|
#ifndef NO_CHARATTR
|
|
else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
|
|
{
|
|
text += 2; /* plus one more at bottom of loop */
|
|
idx -= 2;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
col++;
|
|
}
|
|
text++;
|
|
idx--;
|
|
}
|
|
|
|
/* save stuff to speed next call */
|
|
pcursor = curs;
|
|
pcol = col;
|
|
chgs = changes;
|
|
|
|
/* 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;
|
|
}
|
|
else
|
|
{
|
|
return col;
|
|
}
|
|
}
|
|
|
|
|
|
/* 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() ? */
|
|
{
|
|
int i;
|
|
|
|
i = idx2col(m, text, inputting);
|
|
while (i < leftcol)
|
|
{
|
|
leftcol -= *o_sidescroll;
|
|
mustredraw = TRUE;
|
|
redrawrange(1L, INFINITY, INFINITY);
|
|
}
|
|
while (i > rightcol)
|
|
{
|
|
leftcol += *o_sidescroll;
|
|
mustredraw = TRUE;
|
|
redrawrange(1L, INFINITY, INFINITY);
|
|
}
|
|
physrow = markline(m) - topline;
|
|
physcol = i - leftcol;
|
|
if (*o_number)
|
|
physcol += 8;
|
|
|
|
return physcol;
|
|
}
|
|
|
|
/* 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
|
|
* row.
|
|
*/
|
|
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 i;
|
|
REG int tabstop; /* *o_tabstop */
|
|
REG int limitcol; /* leftcol or leftcol + COLS */
|
|
int abnormal; /* boolean: charattr != A_NORMAL? */
|
|
#ifndef NO_VISIBLE
|
|
int rev; /* boolean: standout mode, too? */
|
|
int idx = 0;
|
|
#endif
|
|
char numstr[9];
|
|
|
|
/* show the line number, if necessary */
|
|
if (*o_number)
|
|
{
|
|
sprintf(numstr, "%6ld ", lno);
|
|
qaddstr(numstr);
|
|
}
|
|
|
|
#ifndef NO_SENTENCE
|
|
/* if we're hiding format lines, and this is one of them, then hide it */
|
|
if (*o_hideformat && *text == '.')
|
|
{
|
|
clrtoeol();
|
|
#if OSK
|
|
qaddch('\l');
|
|
#else
|
|
qaddch('\n');
|
|
#endif
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* move some things into registers... */
|
|
limitcol = leftcol;
|
|
tabstop = *o_tabstop;
|
|
abnormal = FALSE;
|
|
|
|
#ifndef CRUNCH
|
|
if (clr)
|
|
clrtoeol();
|
|
#endif
|
|
|
|
/* skip stuff that was scrolled off left edge */
|
|
for (col = 0;
|
|
(i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
|
|
text++)
|
|
{
|
|
#ifndef NO_VISIBLE
|
|
idx++;
|
|
#endif
|
|
if (i == '\t' && !*o_list)
|
|
{
|
|
col = col + tabstop - (col % tabstop);
|
|
}
|
|
else if (i >= 0 && i < ' ' || i == '\177')
|
|
{
|
|
col += 2;
|
|
}
|
|
#ifndef NO_CHARATTR
|
|
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 */
|
|
switch (*text)
|
|
{
|
|
case 'R':
|
|
case 'P':
|
|
attrset(A_NORMAL);
|
|
abnormal = FALSE;
|
|
break;
|
|
|
|
case 'B':
|
|
attrset(A_BOLD);
|
|
abnormal = TRUE;
|
|
break;
|
|
|
|
case 'U':
|
|
attrset(A_UNDERLINE);
|
|
abnormal = TRUE;
|
|
break;
|
|
|
|
case 'I':
|
|
attrset(A_ALTCHARSET);
|
|
abnormal = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
col++;
|
|
}
|
|
}
|
|
|
|
#ifndef NO_VISIBLE
|
|
/* Should we start hiliting at the first char of this line? */
|
|
if ((lno > vizlow && lno <= vizhigh
|
|
|| lno == vizlow && vizleft < idx)
|
|
&& !(lno == vizhigh && vizright < idx))
|
|
{
|
|
do_VISIBLE();
|
|
rev = TRUE;
|
|
}
|
|
#endif
|
|
|
|
/* adjust for control char that was partially visible */
|
|
while (col > limitcol)
|
|
{
|
|
qaddch(' ');
|
|
limitcol++;
|
|
}
|
|
|
|
/* now for the visible characters */
|
|
limitcol = leftcol + COLS;
|
|
if (*o_number)
|
|
limitcol -= 8;
|
|
for (; (i = *text) && col < limitcol; text++)
|
|
{
|
|
#ifndef NO_VISIBLE
|
|
/* maybe turn hilite on/off in the middle of the line */
|
|
if (lno == vizlow && vizleft == idx)
|
|
{
|
|
do_VISIBLE();
|
|
rev = TRUE;
|
|
}
|
|
if (lno == vizhigh && vizright == idx)
|
|
{
|
|
do_SE();
|
|
rev = FALSE;
|
|
}
|
|
idx++;
|
|
|
|
/* if hiliting, never emit physical tabs */
|
|
if (rev && i == '\t' && !*o_list)
|
|
{
|
|
i = col + tabstop - (col % tabstop);
|
|
do
|
|
{
|
|
qaddch(' ');
|
|
col++;
|
|
} while (col < i && col < limitcol);
|
|
}
|
|
else
|
|
#endif /* !NO_VISIBLE */
|
|
if (i == '\t' && !*o_list)
|
|
{
|
|
i = col + tabstop - (col % tabstop);
|
|
if (i < limitcol)
|
|
{
|
|
#ifdef CRUNCH
|
|
if (!clr && has_PT && !((i - leftcol) & 7))
|
|
#else
|
|
if (has_PT && !((i - leftcol) & 7))
|
|
#endif
|
|
{
|
|
do
|
|
{
|
|
qaddch('\t');
|
|
col += 8; /* not exact! */
|
|
} while (col < i);
|
|
col = i; /* NOW it is exact */
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
qaddch(' ');
|
|
col++;
|
|
} while (col < i && col < limitcol);
|
|
}
|
|
}
|
|
else /* tab ending after screen? next line! */
|
|
{
|
|
#ifdef CRUNCH
|
|
/* needed at least when scrolling the screen right -nox */
|
|
if (clr && col < limitcol)
|
|
clrtoeol();
|
|
#endif
|
|
col = limitcol;
|
|
if (has_AM)
|
|
{
|
|
addch('\n'); /* GB */
|
|
}
|
|
}
|
|
}
|
|
else if (i >= 0 && i < ' ' || i == '\177')
|
|
{
|
|
col += 2;
|
|
qaddch('^');
|
|
if (col <= limitcol)
|
|
{
|
|
qaddch(i ^ '@');
|
|
}
|
|
}
|
|
#ifndef NO_CHARATTR
|
|
else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
|
|
{
|
|
text += 2; /* plus one more as part of "for" loop */
|
|
switch (*text)
|
|
{
|
|
case 'R':
|
|
case 'P':
|
|
attrset(A_NORMAL);
|
|
abnormal = FALSE;
|
|
break;
|
|
|
|
case 'B':
|
|
attrset(A_BOLD);
|
|
abnormal = TRUE;
|
|
break;
|
|
|
|
case 'U':
|
|
attrset(A_UNDERLINE);
|
|
abnormal = TRUE;
|
|
break;
|
|
|
|
case 'I':
|
|
attrset(A_ALTCHARSET);
|
|
abnormal = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
col++;
|
|
qaddch(i);
|
|
}
|
|
}
|
|
|
|
/* get ready for the next line */
|
|
#ifndef NO_CHARATTR
|
|
if (abnormal)
|
|
{
|
|
attrset(A_NORMAL);
|
|
}
|
|
#endif
|
|
if (*o_list && col < limitcol)
|
|
{
|
|
qaddch('$');
|
|
col++;
|
|
}
|
|
|
|
#ifndef NO_VISIBLE
|
|
/* did we hilite this whole line? If so, STOP! */
|
|
if (rev)
|
|
{
|
|
do_SE();
|
|
}
|
|
#endif
|
|
|
|
#ifdef CRUNCH
|
|
if (clr && col < limitcol)
|
|
{
|
|
clrtoeol();
|
|
}
|
|
#endif
|
|
if (!has_AM || col < limitcol)
|
|
{
|
|
addch('\n');
|
|
}
|
|
|
|
wqrefresh();
|
|
}
|
|
|
|
|
|
#ifndef CRUNCH
|
|
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 */
|
|
{
|
|
int col;
|
|
|
|
if (same > 0)
|
|
{
|
|
if (same < 5)
|
|
{
|
|
/* move the cursor by overwriting */
|
|
while (same > 0)
|
|
{
|
|
qaddch(scan[-same]);
|
|
same--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* move the cursor by calling move() */
|
|
col = (int)(scan - new);
|
|
if (*o_number)
|
|
col += 8;
|
|
move((int)(lno - topline), col);
|
|
}
|
|
}
|
|
}
|
|
#endif /* not CRUNCH */
|
|
|
|
/* 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) */
|
|
{
|
|
#ifdef CRUNCH
|
|
move((int)(lno - topline), 0);
|
|
if (showit)
|
|
{
|
|
drawtext(text, lno, TRUE);
|
|
}
|
|
#else /* not CRUNCH */
|
|
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 */
|
|
int limitcol;
|
|
int col;
|
|
int i;
|
|
char numstr[9];
|
|
|
|
# ifndef NO_CHARATTR
|
|
/* if this line has attributes, do it the dumb way instead */
|
|
if (hasattr(lno, text))
|
|
{
|
|
move((int)(lno - topline), 0);
|
|
drawtext(text, lno, TRUE);
|
|
return;
|
|
}
|
|
# endif
|
|
# ifndef NO_SENTENCE
|
|
/* 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);
|
|
return;
|
|
}
|
|
# endif
|
|
# ifndef NO_VISIBLE
|
|
if (vizchange)
|
|
{
|
|
move((int)(lno - topline), 0);
|
|
drawtext(text, lno, TRUE);
|
|
smartlno = 0L;
|
|
return;
|
|
}
|
|
# endif
|
|
|
|
/* skip stuff that was scrolled off left edge */
|
|
limitcol = leftcol;
|
|
for (col = 0;
|
|
(i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
|
|
text++)
|
|
{
|
|
if (i == '\t' && !*o_list)
|
|
{
|
|
col = col + *o_tabstop - (col % *o_tabstop);
|
|
}
|
|
else if (i >= 0 && i < ' ' || i == '\177')
|
|
{
|
|
col += 2;
|
|
}
|
|
else
|
|
{
|
|
col++;
|
|
}
|
|
}
|
|
|
|
/* adjust for control char that was partially visible */
|
|
build = new;
|
|
while (col > limitcol)
|
|
{
|
|
*build++ = ' ';
|
|
limitcol++;
|
|
}
|
|
|
|
/* now for the visible characters */
|
|
limitcol = leftcol + COLS;
|
|
if (*o_number)
|
|
limitcol -= 8;
|
|
for (; (i = *text) && col < limitcol; text++)
|
|
{
|
|
if (i == '\t' && !*o_list)
|
|
{
|
|
i = col + *o_tabstop - (col % *o_tabstop);
|
|
while (col < i && col < limitcol)
|
|
{
|
|
*build++ = ' ';
|
|
col++;
|
|
}
|
|
}
|
|
else if (i >= 0 && i < ' ' || i == '\177')
|
|
{
|
|
col += 2;
|
|
*build++ = '^';
|
|
if (col <= limitcol)
|
|
{
|
|
*build++ = (i ^ '@');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
col++;
|
|
*build++ = i;
|
|
}
|
|
}
|
|
if (col < limitcol && *o_list)
|
|
{
|
|
*build++ = '$';
|
|
col++;
|
|
}
|
|
end = build;
|
|
while (col < limitcol)
|
|
{
|
|
*build++ = ' ';
|
|
col++;
|
|
}
|
|
|
|
/* if we're just supposed to remember this line, then remember it */
|
|
if (!showit)
|
|
{
|
|
smartlno = lno;
|
|
strncpy(old, new, COLS);
|
|
return;
|
|
}
|
|
|
|
/* locate the last non-blank character */
|
|
while (end > new && end[-1] == ' ')
|
|
{
|
|
end--;
|
|
}
|
|
|
|
/* can we optimize the displaying of this line? */
|
|
if (lno != smartlno)
|
|
{
|
|
/* nope, can't optimize - different line */
|
|
move((int)(lno - topline), 0);
|
|
|
|
/* show the line number, if necessary */
|
|
if (*o_number)
|
|
{
|
|
sprintf(numstr, "%6ld ", lno);
|
|
qaddstr(numstr);
|
|
}
|
|
|
|
/* show the new line */
|
|
for (scan = new, build = old; scan < end; )
|
|
{
|
|
qaddch(*scan);
|
|
*build++ = *scan++;
|
|
}
|
|
if (end < new + COLS - (*o_number ? 8 : 0))
|
|
{
|
|
clrtoeol();
|
|
while (build < old + COLS)
|
|
{
|
|
*build++ = ' ';
|
|
}
|
|
}
|
|
smartlno = lno;
|
|
return;
|
|
}
|
|
|
|
/* skip any initial unchanged characters */
|
|
for (scan = new, build = old; scan < end && *scan == *build; scan++, build++)
|
|
{
|
|
}
|
|
i = (scan - new);
|
|
if (*o_number)
|
|
i += 8;
|
|
move((int)(lno - topline), i);
|
|
|
|
/* The in-between characters must be changed */
|
|
same = 0;
|
|
while (scan < end)
|
|
{
|
|
/* is this character a match? */
|
|
if (scan[0] == build[0])
|
|
{
|
|
same++;
|
|
}
|
|
else /* do we want to insert? */
|
|
if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM))
|
|
{
|
|
nudgecursor(same, scan, new, lno);
|
|
same = 0;
|
|
|
|
insch(*scan);
|
|
for (shift = old + COLS; --shift > build; )
|
|
{
|
|
shift[0] = shift[-1];
|
|
}
|
|
*build = *scan;
|
|
}
|
|
else /* do we want to delete? */
|
|
if (build < old + COLS - 1 && scan[0] == build[1] && has_DC)
|
|
{
|
|
nudgecursor(same, scan, new, lno);
|
|
same = 0;
|
|
|
|
delch();
|
|
same++;
|
|
for (shift = build; shift < old + COLS - 1; shift++)
|
|
{
|
|
shift[0] = shift[1];
|
|
}
|
|
if (*o_number)
|
|
shift -= 8;
|
|
*shift = ' ';
|
|
}
|
|
else /* we must overwrite */
|
|
{
|
|
nudgecursor(same, scan, new, lno);
|
|
same = 0;
|
|
|
|
addch(*scan);
|
|
*build = *scan;
|
|
}
|
|
|
|
build++;
|
|
scan++;
|
|
}
|
|
|
|
/* maybe clear to EOL */
|
|
end = old + COLS - (*o_number ? 8 : 0);
|
|
while (build < end && *build == ' ')
|
|
{
|
|
build++;
|
|
}
|
|
if (build < end)
|
|
{
|
|
nudgecursor(same, scan, new, lno);
|
|
same = 0;
|
|
|
|
clrtoeol();
|
|
while (build < old + COLS)
|
|
{
|
|
*build++ = ' ';
|
|
}
|
|
}
|
|
#endif /* not CRUNCH */
|
|
}
|
|
|
|
|
|
/* 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
|
|
* scrolling.
|
|
*/
|
|
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 */
|
|
long l;
|
|
int i;
|
|
#ifndef CRUNCH
|
|
static long showtop; /* top line in window */
|
|
static long showbottom; /* bottom line in window */
|
|
#endif
|
|
|
|
/* if curs == MARK_UNSET, then we should reset internal vars */
|
|
if (curs == MARK_UNSET)
|
|
{
|
|
if (topline < 1 || topline > nlines)
|
|
{
|
|
topline = 1L;
|
|
}
|
|
else
|
|
{
|
|
move(LINES - 1, 0);
|
|
clrtoeol();
|
|
}
|
|
leftcol = 0;
|
|
mustredraw = TRUE;
|
|
redrawafter = INFINITY;
|
|
preredraw = 0L;
|
|
postredraw = 0L;
|
|
chgs = 0;
|
|
smartlno = 0L;
|
|
#ifndef NO_VISIBLE
|
|
vizlow = vizhigh = 0L;
|
|
vizchange = FALSE;
|
|
#endif
|
|
#ifndef CRUNCH
|
|
showtop = 0;
|
|
showbottom = INFINITY;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifndef NO_VISIBLE
|
|
/* adjustments to hilited area may force extra lines to be redrawn. */
|
|
setviz(curs);
|
|
#endif
|
|
|
|
/* figure out which column the cursor will be in */
|
|
l = markline(curs);
|
|
text = fetchline(l);
|
|
mark2phys(curs, text, inputting);
|
|
|
|
#ifndef NO_COLOR
|
|
fixcolor();
|
|
#endif
|
|
|
|
/* 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
|
|
#ifndef NO_VISIBLE
|
|
|| V_from
|
|
#endif
|
|
#ifndef CRUNCH
|
|
|| l < showtop || l > showbottom
|
|
#endif
|
|
))
|
|
{
|
|
smartdrawtext(text, l, (chgs != changes));
|
|
}
|
|
}
|
|
else if (l < topline && l >= topline - NEAR && (has_SR || has_AL))
|
|
{
|
|
/* near top - scroll down */
|
|
if (!mustredraw)
|
|
{
|
|
move(0,0);
|
|
while (l < topline)
|
|
{
|
|
topline--;
|
|
if (has_SR)
|
|
{
|
|
do_SR();
|
|
}
|
|
else
|
|
{
|
|
insertln();
|
|
}
|
|
text = fetchline(topline);
|
|
drawtext(text, topline, FALSE);
|
|
do_UP();
|
|
}
|
|
|
|
/* blank out the last line */
|
|
move(LINES - 1, 0);
|
|
clrtoeol();
|
|
}
|
|
else
|
|
{
|
|
topline = l;
|
|
redrawrange(0L, INFINITY, INFINITY);
|
|
}
|
|
}
|
|
else if (l > topline && l <= botline + NEAR)
|
|
{
|
|
/* near bottom -- scroll up */
|
|
if (!mustredraw)
|
|
{
|
|
move(LINES - 1,0);
|
|
clrtoeol();
|
|
while (l > botline)
|
|
{
|
|
topline++; /* <-- also adjusts botline */
|
|
text = fetchline(botline);
|
|
drawtext(text, botline, FALSE);
|
|
}
|
|
#ifndef CRUNCH
|
|
showbottom = l;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
topline = l - (LINES - 2);
|
|
redrawrange(0L, INFINITY, INFINITY);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* distant line - center it & force a redraw */
|
|
topline = l - (LINES - 1) / 2;
|
|
if (topline < 1)
|
|
{
|
|
topline = 1;
|
|
}
|
|
redrawrange(0L, INFINITY, INFINITY);
|
|
smartlno = 0L;
|
|
changes++;
|
|
}
|
|
|
|
#ifndef CRUNCH
|
|
/* make sure the current line is included in the "window" */
|
|
if (l < showtop)
|
|
{
|
|
redrawrange(l, showtop, showtop);
|
|
showtop = l;
|
|
}
|
|
if (l > showbottom)
|
|
{
|
|
redrawrange(showbottom, l, l);
|
|
showbottom = l;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Now... do we really have to redraw? */
|
|
if (mustredraw)
|
|
{
|
|
/* If redrawfter (and friends) aren't set, assume we should
|
|
* redraw everything.
|
|
*/
|
|
if (redrawafter == INFINITY)
|
|
{
|
|
redrawafter = 0L;
|
|
preredraw = postredraw = INFINITY;
|
|
}
|
|
|
|
#ifndef CRUNCH
|
|
/* shrink the window, if possible */
|
|
if (showtop < topline)
|
|
{
|
|
showtop = topline;
|
|
}
|
|
if (showbottom > botline)
|
|
{
|
|
showbottom = botline;
|
|
}
|
|
if (postredraw == INFINITY)
|
|
{
|
|
/* these will be set to more reasonable values later */
|
|
showtop = INFINITY;
|
|
showbottom = 0L;
|
|
}
|
|
#endif
|
|
|
|
/* adjust smartlno to correspond with inserted/deleted lines */
|
|
if (smartlno >= redrawafter)
|
|
{
|
|
if (smartlno < preredraw && postredraw != preredraw) /*!!!*/
|
|
{
|
|
smartlno = 0L;
|
|
}
|
|
else
|
|
{
|
|
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 */
|
|
if (preredraw < topline)
|
|
{
|
|
l = topline;
|
|
}
|
|
else
|
|
{
|
|
l = preredraw;
|
|
}
|
|
|
|
/* insert the lines... maybe */
|
|
if (l + postredraw - preredraw > botline || !has_AL || *o_number)
|
|
{
|
|
/* Whoa! a whole screen full - just redraw */
|
|
preredraw = postredraw = INFINITY;
|
|
}
|
|
else
|
|
{
|
|
/* really insert 'em */
|
|
move((int)(l - topline), 0);
|
|
for (i = postredraw - preredraw; i > 0; i--)
|
|
{
|
|
insertln();
|
|
}
|
|
|
|
/* NOTE: the contents of those lines will be
|
|
* drawn as part of the regular redraw loop.
|
|
*/
|
|
|
|
/* clear the last line */
|
|
move(LINES - 1, 0);
|
|
clrtoeol();
|
|
}
|
|
}
|
|
|
|
/* 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(LINES - 1, 0);
|
|
clrtoeol();
|
|
|
|
/* delete the lines */
|
|
move((int)(postredraw - topline), 0);
|
|
for (l = postredraw;
|
|
l < preredraw && l <= botline;
|
|
l++)
|
|
{
|
|
deleteln();
|
|
}
|
|
|
|
/* draw the lines that are now newly visible
|
|
* at the bottom of the screen
|
|
*/
|
|
i = LINES - 1 + (postredraw - preredraw);
|
|
move(i, 0);
|
|
for (l = topline + i; l <= botline; l++)
|
|
{
|
|
/* clear this line */
|
|
clrtoeol();
|
|
|
|
/* draw the line, or ~ for non-lines */
|
|
if (l <= nlines)
|
|
{
|
|
text = fetchline(l);
|
|
drawtext(text, l, FALSE);
|
|
}
|
|
else
|
|
{
|
|
addstr("~\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* redraw the current line */
|
|
l = markline(curs);
|
|
pfetch(l);
|
|
smartdrawtext(ptext, l, TRUE);
|
|
|
|
#ifndef CRUNCH
|
|
/* decide which lines must be in the "window" around the cursor */
|
|
l = markline(curs);
|
|
if ((*o_window & 0xff) + 1 == LINES)
|
|
{
|
|
showtop = 1;
|
|
showbottom = INFINITY;
|
|
}
|
|
else if (l < showtop || l > showbottom)
|
|
{
|
|
l -= (*o_window & 0xff) / 2;
|
|
if (l < topline)
|
|
{
|
|
l = topline;
|
|
}
|
|
if (l < showtop)
|
|
{
|
|
showtop = l;
|
|
}
|
|
l += (*o_window & 0xff) - 1;
|
|
if (l > botline)
|
|
{
|
|
showtop = showtop - l + botline;
|
|
l = botline;
|
|
}
|
|
if (l > showbottom)
|
|
{
|
|
showbottom = l;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* decide where we should start redrawing from */
|
|
if (redrawafter < topline)
|
|
{
|
|
l = topline;
|
|
}
|
|
else
|
|
{
|
|
l = redrawafter;
|
|
}
|
|
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 */
|
|
if (l == smartlno)
|
|
{
|
|
#if OSK
|
|
qaddch('\l');
|
|
#else
|
|
qaddch('\n');
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
/* draw the line, or ~ for non-lines */
|
|
if (l > nlines)
|
|
{
|
|
qaddch('~');
|
|
clrtoeol();
|
|
addch('\n');
|
|
}
|
|
#ifndef CRUNCH
|
|
else if (l < showtop || l > showbottom)
|
|
{
|
|
qaddch('@');
|
|
clrtoeol();
|
|
addch('\n');
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
text = fetchline(l);
|
|
drawtext(text, l, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
mustredraw = FALSE;
|
|
}
|
|
|
|
/* force total (non-partial) redraw next time if not set */
|
|
redrawafter = INFINITY;
|
|
preredraw = 0L;
|
|
postredraw = 0L;
|
|
|
|
/* move the cursor to where it belongs */
|
|
move((int)(markline(curs) - topline), physcol);
|
|
wqrefresh();
|
|
|
|
chgs = changes;
|
|
}
|