/* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" /* * The following variable is set (in cursupdate) 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 updateline() and redrawline() * 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 * updateNextscreen() and updateRealscreen() 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 */ /* * updateline() - like updateNextscreen() but only for cursor line * * This determines whether or not we need to call updateNextscreen() to examine the * entire screen for changes. This occurs if the size of the cursor line * (in rows) hasn't changed. */ void updateline() { int row, col; register char *screenp; LPTR memp; register char *nextrow; char extra[16]; int nextra = 0; char c; int n; bool_t eof; MustRedrawLine = TRUE; #ifndef AMIGA if (MustRedrawScreen) { msg("STEVIE internal error: updateline called"); sleep(5); } #endif screenp = Nextscreen + (Cline_row * Columns); memp = *Curschar; memp.index = 0; eof = FALSE; col = 0; row = Cline_row; while (!eof) { /* 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 = 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 % 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++ = c; col++; } if ((row - Cline_row) == Cline_size) updateNextscreen(); } /* * redrawline * * Like updateRealscreen() but only for the cursor line. */ void redrawline() { register char *np = Nextscreen + (Cline_row * Columns); register char *rp = Realscreen + (Cline_row * Columns); register char *endline; int row, col; int gorow = -1, gocol = -1; if (RedrawingDisabled) return; if (!MustRedrawLine && !MustRedrawScreen) return; if (MustRedrawScreen) { msg("STEVIE internal error: redrawline called"); sleep(5); } endline = np + (Cline_size * Columns); row = Cline_row; col = 0; outstr(T_CI); /* disable cursor */ 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++; } } outstr(T_CV); /* enable cursor again */ MustRedrawScreen = FALSE; } void screenclear() { char *rp, *np; char *end; outstr(T_ED); /* clear the display */ rp = Realscreen; end = Realscreen + Rows * Columns; np = Nextscreen; /* blank out the stored screens */ while (rp != end) *rp++ = *np++ = ' '; } void cursupdate() { LPTR *p; char c; int incr, nlines; int i; int didincr; 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); updateNextscreen(); } 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); } updateNextscreen(); } Cursrow = Curscol = Cursvcol = 0; for (p = Topchar; p->linep != Curschar->linep; p = nextline(p)) Cursrow += plines(p); Cline_row = Cursrow; Cline_size = plines(p); 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)) incr = P(P_TS) - (Curscol % P(P_TS)); else incr = chars[c].ch_size; Curscol += incr; Cursvcol += incr; if (Curscol >= Columns) { Curscol -= Columns; Cursrow++; didincr = TRUE; } else didincr = FALSE; } if (didincr) Cursrow--; if (c == TAB && State == NORMAL && !P(P_LS)) { Curscol--; Cursvcol--; } else { Curscol -= incr; Cursvcol -= incr; } 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; { char *s, *d; /* src & dest for block copy */ char *e; /* end point for copy */ int i; if ((T_IL[0] == NUL) || RedrawingDisabled || nlines <= 0) return; /* * It "looks" better if we do all the inserts at once */ outstr(T_SC); /* save position */ if (T_IL_B[0] == NUL) { for (i = 0; i < nlines; i++) { windgoto(row, 0); outstr(T_IL); } } else { windgoto(row, 0); outstr(T_IL); if (nlines >= 10) outchar((char) (nlines / 10 + '0')); outchar((char) (nlines % 10 + '0')); outstr(T_IL_B); } windgoto(Rows - 1, 0); /* delete any garbage that may have */ outstr(T_EL); /* been shifted to the bottom line */ outstr(T_RC); /* restore the cursor position */ /* * 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; { char *s, *d, *e; int i; if ((T_DL[0] == NUL) || RedrawingDisabled || nlines <= 0) return; outstr(T_SC); /* save position */ windgoto(Rows - 1, 0); /* delete any garbage that */ outstr(T_EL); /* was on the status line */ /* delete the lines */ if (T_DL_B[0] == NUL) { for (i = 0; i < nlines; i++) { windgoto(row, 0); outstr(T_DL); /* delete a line */ } } else { windgoto(row, 0); outstr(T_DL); if (nlines >= 10) outchar((char) (nlines / 10 + '0')); outchar((char) (nlines % 10 + '0')); outstr(T_DL_B); } outstr(T_RC); /* restore position */ /* * 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++ = ' '; }