|
|
/*
* * Routines to manipulate the screen representations. */
#include "stevie.h"
/*
* This gets set if we ignored an update request while input was pending. * We check this when the input is drained to see if the screen should be * updated. */ bool_t need_redraw = FALSE;
/*
* The following variable is set (in filetonext) to the number of physical * lines taken by the line the cursor is on. We use this to avoid extra * calls to plines(). The optimized routines lfiletonext() and lnexttoscreen() * make sure that the size of the cursor line hasn't changed. If so, lines * below the cursor will move up or down and we need to call the routines * filetonext() and nexttoscreen() to examine the entire screen. */ static int Cline_size; /* size (in rows) of the cursor line */ static int Cline_row; /* starting row of the cursor line */
static char *mkline(); /* calculate line string for "number" mode */
/*
* filetonext() * * Based on the current value of Topchar, transfer a screenfull of * stuff from Filemem to Nextscreen, and update Botchar. */
static void filetonext() { register int row, col; register char *screenp = Nextscreen; LNPTR memp; LNPTR save; /* save pos. in case line won't fit */ register char *endscreen; register char *nextrow; char extra[16]; int nextra = 0; register int c; int n; bool_t done; /* if TRUE, we hit the end of the file */ bool_t didline; /* if TRUE, we finished the last line */ int srow; /* starting row of the current line */ int lno; /* number of the line we're doing */ int coff; /* column offset */
coff = P(P_NU) ? 8 : 0;
save = memp = *Topchar;
if (P(P_NU)) lno = cntllines(Filemem, Topchar);
/*
* The number of rows shown is Rows-1. * The last line is the status/command line. */ endscreen = &screenp[(Rows-1)*Columns];
done = didline = FALSE; srow = row = col = 0; /*
* We go one past the end of the screen so we can find out if the * last line fit on the screen or not. */ while ( screenp <= endscreen && !done) {
if (P(P_NU) && col == 0 && memp.index == 0) { strcpy(extra, mkline(lno++)); nextra = 8; }
/* Get the next character to put on the screen. */
/* The 'extra' array contains the extra stuff that is */ /* inserted to represent special characters (tabs, and */ /* other non-printable stuff. The order in the 'extra' */ /* array is reversed. */
if ( nextra > 0 ) c = extra[--nextra]; else { c = (unsigned)(0xff & gchar(&memp)); if (inc(&memp) == -1) done = 1; /* when getting a character from the file, we */ /* may have to turn it into something else on */ /* the way to putting it into 'Nextscreen'. */ if ( c == TAB && !P(P_LS) ) { strcpy(extra," "); /* tab amount depends on current column */ nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS)); c = ' '; } else if ( c == NUL && P(P_LS) ) { extra[0] = NUL; nextra = 1; c = '$'; } else if ((n = chars[c].ch_size) > 1 ) { char *p; nextra = 0; p = chars[c].ch_str; /* copy 'ch-str'ing into 'extra' in reverse */ while ( n > 1 ) extra[nextra++] = p[--n]; c = p[0]; } }
if (screenp == endscreen) { /*
* We're one past the end of the screen. If the * current character is null, then we really did * finish, so set didline = TRUE. In either case, * break out because we're done. */ dec(&memp); if (memp.index != 0 && c == NUL) { didline = TRUE; inc(&memp); } break; }
if ( c == NUL ) { srow = ++row; /*
* Save this position in case the next line won't * fit on the screen completely. */ save = memp; /* get pointer to start of next row */ nextrow = &Nextscreen[row*Columns]; /* blank out the rest of this row */ while ( screenp != nextrow ) *screenp++ = ' '; col = 0; continue; } if ( col >= Columns ) { row++; col = 0; } /* store the character in Nextscreen */ *screenp++ = (char)c; col++; } /*
* If we didn't hit the end of the file, and we didn't finish * the last line we were working on, then the line didn't fit. */ if (!done && !didline) { /*
* Clear the rest of the screen and mark the unused lines. */ screenp = &Nextscreen[srow * Columns]; while (screenp < endscreen) *screenp++ = ' '; for (; srow < (Rows-1) ;srow++) Nextscreen[srow * Columns] = '@'; *Botchar = save; return; } /* make sure the rest of the screen is blank */ while ( screenp < endscreen ) *screenp++ = ' '; /* put '~'s on rows that aren't part of the file. */ if ( col != 0 ) row++; while ( row < Rows ) { Nextscreen[row*Columns] = '~'; row++; } if (done) /* we hit the end of the file */ *Botchar = *Fileend; else *Botchar = memp; /* FIX - prev? */ }
/*
* nexttoscreen * * Transfer the contents of Nextscreen to the screen, using Realscreen * to avoid unnecessary output. */ static void nexttoscreen() { register char *np = Nextscreen; register char *rp = Realscreen; register char *endscreen; register int row = 0, col = 0; int gorow = -1, gocol = -1;
if (anyinput()) { need_redraw = TRUE; return; }
endscreen = &np[(Rows-1)*Columns];
InvisibleCursor();
for ( ; np < endscreen ; np++,rp++ ) { /* If desired screen (contents of Nextscreen) does not */ /* match what's really there, put it there. */ if ( *np != *rp ) { /* if we are positioned at the right place, */ /* we don't have to use windgoto(). */ if (gocol != col || gorow != row) { /*
* If we're just off by one, don't send * an entire esc. seq. (this happens a lot!) */ if (gorow == row && gocol+1 == col) { outchar(*(np-1)); gocol++; } else windgoto(gorow=row,gocol=col); } outchar(*rp = *np); gocol++; } if ( ++col >= Columns ) { col = 0; row++; } } VisibleCursor(); }
/*
* lfiletonext() - like filetonext() but only for cursor line * * Returns true if the size of the cursor line (in rows) hasn't changed. * This determines whether or not we need to call filetonext() to examine * the entire screen for changes. */ static bool_t lfiletonext() { register int row, col; register char *screenp; LNPTR memp; register char *nextrow; char extra[16]; int nextra = 0; register int c; int n; bool_t eof; int lno; /* number of the line we're doing */ int coff; /* column offset */
coff = P(P_NU) ? 8 : 0;
/*
* This should be done more efficiently. */ if (P(P_NU)) lno = cntllines(Filemem, Curschar);
screenp = Nextscreen + (Cline_row * Columns);
memp = *Curschar; memp.index = 0;
eof = FALSE; col = 0; row = Cline_row;
while (!eof) {
if (P(P_NU) && col == 0 && memp.index == 0) { strcpy(extra, mkline(lno)); nextra = 8; }
/* Get the next character to put on the screen. */
/* The 'extra' array contains the extra stuff that is */ /* inserted to represent special characters (tabs, and */ /* other non-printable stuff. The order in the 'extra' */ /* array is reversed. */
if ( nextra > 0 ) c = extra[--nextra]; else { c = (unsigned)(0xff & gchar(&memp)); if (inc(&memp) == -1) eof = TRUE; /* when getting a character from the file, we */ /* may have to turn it into something else on */ /* the way to putting it into 'Nextscreen'. */ if ( c == TAB && !P(P_LS) ) { strcpy(extra," "); /* tab amount depends on current column */ nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS)); c = ' '; } else if ( c == NUL && P(P_LS) ) { extra[0] = NUL; nextra = 1; c = '$'; } else if (c != NUL && (n=chars[c].ch_size) > 1 ) { char *p; nextra = 0; p = chars[c].ch_str; /* copy 'ch-str'ing into 'extra' in reverse */ while ( n > 1 ) extra[nextra++] = p[--n]; c = p[0]; } }
if ( c == NUL ) { row++; /* get pointer to start of next row */ nextrow = &Nextscreen[row*Columns]; /* blank out the rest of this row */ while ( screenp != nextrow ) *screenp++ = ' '; col = 0; break; }
if ( col >= Columns ) { row++; col = 0; } /* store the character in Nextscreen */ *screenp++ = (char)c; col++; } return ((row - Cline_row) == Cline_size); }
/*
* lnexttoscreen * * Like nexttoscreen() but only for the cursor line. */ static void lnexttoscreen() { register char *np = Nextscreen + (Cline_row * Columns); register char *rp = Realscreen + (Cline_row * Columns); register char *endline; register int row, col; int gorow = -1, gocol = -1;
if (anyinput()) { need_redraw = TRUE; return; }
endline = np + (Cline_size * Columns);
row = Cline_row; col = 0;
InvisibleCursor();
for ( ; np < endline ; np++,rp++ ) { /* If desired screen (contents of Nextscreen) does not */ /* match what's really there, put it there. */ if ( *np != *rp ) { /* if we are positioned at the right place, */ /* we don't have to use windgoto(). */ if (gocol != col || gorow != row) { /*
* If we're just off by one, don't send * an entire esc. seq. (this happens a lot!) */ if (gorow == row && gocol+1 == col) { outchar(*(np-1)); gocol++; } else windgoto(gorow=row,gocol=col); } outchar(*rp = *np); gocol++; } if ( ++col >= Columns ) { col = 0; row++; } } VisibleCursor(); }
static char * mkline(n) register int n; { static char lbuf[9]; register int i = 2;
strcpy(lbuf, " ");
lbuf[i++] = (char)((n % 10) + '0'); n /= 10; if (n != 0) { lbuf[i++] = (char)((n % 10) + '0'); n /= 10; } if (n != 0) { lbuf[i++] = (char)((n % 10) + '0'); n /= 10; } if (n != 0) { lbuf[i++] = (char)((n % 10) + '0'); n /= 10; } if (n != 0) { lbuf[i++] = (char)((n % 10) + '0'); n /= 10; } return lbuf; }
/*
* updateline() - update the line the cursor is on * * Updateline() is called after changes that only affect the line that * the cursor is on. This improves performance tremendously for normal * insert mode operation. The only thing we have to watch for is when * the cursor line grows or shrinks around a row boundary. This means * we have to repaint other parts of the screen appropriately. If * lfiletonext() returns FALSE, the size of the cursor line (in rows) * has changed and we have to call updatescreen() to do a complete job. */ void updateline() { if (!lfiletonext()) updatescreen(); /* bag it, do the whole screen */ else lnexttoscreen(); }
void updatescreen() { extern bool_t interactive;
if (interactive) { filetonext(); nexttoscreen(); } }
/*
* prt_line() - print the given line */ void prt_line(s) char *s; { register int si = 0; register int c; register int col = 0;
char extra[16]; int nextra = 0; int n;
for (;;) {
if ( nextra > 0 ) c = extra[--nextra]; else { c = s[si++]; if ( c == TAB && !P(P_LS) ) { strcpy(extra, " "); /* tab amount depends on current column */ nextra = (P(P_TS) - 1) - col%P(P_TS); c = ' '; } else if ( c == NUL && P(P_LS) ) { extra[0] = NUL; nextra = 1; c = '$'; } else if ( c != NUL && (n=chars[c].ch_size) > 1 ) { char *p;
nextra = 0; p = chars[c].ch_str; /* copy 'ch-str'ing into 'extra' in reverse */ while ( n > 1 ) extra[nextra++] = p[--n]; c = p[0]; } }
if ( c == NUL ) break;
outchar(c); col++; } }
void screenclear() { register char *rp, *np; register char *end;
ClearDisplay();
rp = Realscreen; end = Realscreen + Rows * Columns; np = Nextscreen;
/* blank out the stored screens */ while (rp != end) *rp++ = *np++ = ' '; }
void cursupdate() { register LNPTR *p; register int icnt, c, nlines; register int i; int didinc;
if (bufempty()) { /* special case - file is empty */ *Topchar = *Filemem; *Curschar = *Filemem; } else if ( LINEOF(Curschar) < LINEOF(Topchar) ) { nlines = cntllines(Curschar,Topchar); /* if the cursor is above the top of */ /* the screen, put it at the top of the screen.. */ *Topchar = *Curschar; Topchar->index = 0; /* ... and, if we weren't very close to begin with, */ /* we scroll so that the line is close to the middle. */ if ( nlines > Rows/3 ) { for (i=0, p = Topchar; i < Rows/3 ;i++, *Topchar = *p) if ((p = prevline(p)) == NULL) break; } else s_ins(0, nlines-1); updatescreen(); } else if (LINEOF(Curschar) >= LINEOF(Botchar)) { nlines = cntllines(Botchar,Curschar); /* If the cursor is off the bottom of the screen, */ /* put it at the top of the screen.. */ /* ... and back up */ if ( nlines > Rows/3 ) { p = Curschar; for (i=0; i < (2*Rows)/3 ;i++) if ((p = prevline(p)) == NULL) break; *Topchar = *p; } else { scrollup(nlines+1); } updatescreen(); }
Cursrow = Curscol = Cursvcol = 0; for ( p=Topchar; p->linep != Curschar->linep ;p = nextline(p) ) Cursrow += plines(p);
Cline_row = Cursrow; Cline_size = plines(p);
if (P(P_NU)) Curscol = 8;
for (i=0; i <= Curschar->index ;i++) { c = Curschar->linep->s[i]; /* A tab gets expanded, depending on the current column */ if ( c == TAB && !P(P_LS) ) icnt = P(P_TS) - (Cursvcol % P(P_TS)); else icnt = chars[(unsigned)(c & 0xff)].ch_size; Curscol += icnt; Cursvcol += icnt; if ( Curscol >= Columns ) { Curscol -= Columns; Cursrow++; didinc = TRUE; } else didinc = FALSE; } if (didinc) Cursrow--;
if (c == TAB && State == NORMAL && !P(P_LS)) { Curscol--; Cursvcol--; } else { Curscol -= icnt; Cursvcol -= icnt; } if (Curscol < 0) Curscol += Columns;
if (set_want_col) { Curswant = Cursvcol; set_want_col = FALSE; } }
/*
* The rest of the routines in this file perform screen manipulations. * The given operation is performed physically on the screen. The * corresponding change is also made to the internal screen image. * In this way, the editor anticipates the effect of editing changes * on the appearance of the screen. That way, when we call screenupdate * a complete redraw isn't usually necessary. Another advantage is that * we can keep adding code to anticipate screen changes, and in the * meantime, everything still works. */
/*
* s_ins(row, nlines) - insert 'nlines' lines at 'row' */ void s_ins(row, nlines) int row; int nlines; { register char *s, *d; /* src & dest for block copy */ register char *e; /* end point for copy */
SaveCursor();
// clip to screen
if(row <= Rows-2-nlines) { Scroll(row,0,Rows-2-nlines,Columns-1,row+nlines,0); EraseNLinesAtRow(nlines,row); } else { // just erase to end of screen
EraseNLinesAtRow(Rows-2-row+1,row); }
windgoto(Rows-1, 0); /* delete any garbage that may have */ EraseLine(); RestoreCursor();
/*
* Now do a block move to update the internal screen image */ d = Realscreen + (Columns * (Rows - 1)) - 1; s = d - (Columns * nlines); e = Realscreen + (Columns * row);
while (s >= e) *d-- = *s--;
/*
* Clear the inserted lines */ s = Realscreen + (row * Columns); e = s + (nlines * Columns); while (s < e) *s++ = ' '; }
/*
* s_del(row, nlines) - delete 'nlines' lines at 'row' */ void s_del(row, nlines) int row; int nlines; { register char *s, *d, *e;
SaveCursor(); windgoto(Rows-1,0); EraseLine(); // erase status line
windgoto(row,0);
if(row + nlines >= Rows - 1) { // more than a screenfull?
EraseNLinesAtRow(Rows-row-1,row); } else { Scroll(row+nlines,0,Rows-2,Columns-1,row,0); EraseNLinesAtRow(nlines,Rows-nlines-1); } RestoreCursor();
/*
* do a block move to update the internal image */ d = Realscreen + (row * Columns); s = d + (nlines * Columns); e = Realscreen + ((Rows - 1) * Columns);
while (s < e) *d++ = *s++;
while (d < e) /* clear the lines at the bottom */ *d++ = ' '; }
|