home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1990,1991,1992 Chris and John Downey */
- #ifndef lint
- static char *sccsid = "@(#)screen.c 2.3 (Chris & John Downey) 9/4/92";
- #endif
-
- /***
-
- * program name:
- xvi
- * function:
- PD version of UNIX "vi" editor, with extensions.
- * module name:
- screen.c
- * module function:
- Screen handling functions.
- * history:
- STEVIE - ST Editor for VI Enthusiasts, Version 3.10
- Originally by Tim Thompson (twitch!tjt)
- Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
- Heavily modified by Chris & John Downey
- Amiga specific optimizations added by Dan Schmelzer
-
- ***/
-
- #include "xvi.h"
-
- /*
- * Size of command buffer - we won't allow anything more
- * to be typed when we get to this limit.
- */
- #define CMDSZ 80
-
- /*
- * The following is used to optimise screen updating; we
- * keep a record of the real screen state and compare it
- * with the new version before actually doing any updating.
- *
- * The l_line part is guaranteed to be always null-terminated.
- */
- typedef struct line_struct {
- char *l_line; /* storage for characters in line */
- int l_used; /* number of bytes actually used */
- unsigned int l_flags; /* information bits */
- } Sline;
-
- /*
- * Bit definitions for l_flags.
- */
- #define L_TEXT 0x01 /* is an ordinary text line */
- #define L_MARKER 0x02 /* is a marker line ('@' or '~') */
- #define L_DIRTY 0x04 /* has been modified */
- #define L_MESSAGE 0x08 /* is a message line */
- #define L_COMMAND 0x10 /* is a command line */
- #define L_READONLY 0x20 /* message line for readonly buffer */
-
- #define L_STATUS (L_MESSAGE | L_COMMAND) /* is a status line */
-
- static Sline *new_screen; /* screen being updated */
- static Sline *real_screen; /* state of real screen */
-
- /*
- * Status line glitch handling.
- *
- * Some terminals leave a space when changing colour. The number of spaces
- * left is returned by the v_colour_cost() method within the VirtScr, and
- * stored in the colour_cost variable herein - this is not perfect, it should
- * really be in the Xviwin structure, but what the hell.
- *
- * "st_spare_cols" is the number of columns which are not used at the
- * end of the status line; this is to prevent wrapping on this line,
- * as this can do strange things to some terminals.
- */
-
- static int colour_cost = 0;
- static int st_spare_cols = 1;
-
- static int line_to_new P((Xviwin *, Line *, int, long));
- static void file_to_new P((Xviwin *));
- static void new_to_screen P((VirtScr *, int, int));
- static void do_sline P((Xviwin *));
- static void clrline P((int));
-
- /*
- * This routine must be called to set up the screen memory -
- * if it is not, we will probably get a core dump.
- *
- * Note that, at the moment, it must be called with a whole-screen
- * window, i.e. the first window, and only that window, so that the
- * nrows and ncols fields represent the whole screen.
- */
- /*ARGSUSED*/
- void
- init_screen(win)
- Xviwin *win;
- {
- static char *real_area, *new_area;
- register int count;
- VirtScr *vs;
- vs = win->w_vs;
- colour_cost = VScolour_cost(vs);
- st_spare_cols = 1 + (colour_cost * 2);
-
- /*
- * If we're changing the size of the screen, free the old stuff.
- */
- if (real_screen != NULL) {
- free((char *) real_screen);
- free((char *) new_screen);
- free(real_area);
- free(new_area);
- }
-
- /*
- * Allocate space for the lines, and for the structure holding
- * information about each line. Notice that we allocate an
- * extra byte at the end of each line for null termination.
- */
- real_screen = (Sline *) malloc((unsigned) VSrows(vs) * sizeof(Sline));
- new_screen = (Sline *) malloc((unsigned) VSrows(vs) * sizeof(Sline));
- real_area = malloc((unsigned) VSrows(vs) * (VScols(vs) + 1));
- new_area = malloc((unsigned) VSrows(vs) * (VScols(vs) + 1));
-
- if (real_screen == NULL || new_screen == NULL ||
- real_area == NULL || new_area == NULL) {
- /* What to do now? */
-
- sys_exit(0);
- }
-
- /*
- * Now assign all the rows ...
- */
- for (count = 0; count < VSrows(vs); count++) {
- register Sline *rp, *np;
- register int offset;
-
- rp = &real_screen[count];
- np = &new_screen[count];
-
- offset = count * (VScols(vs) + 1);
-
- rp->l_line = real_area + offset;
- np->l_line = new_area + offset;
- rp->l_line[0] = np->l_line[0] = '\0';
- rp->l_used = np->l_used = 0;
- rp->l_flags = np->l_flags = 0;
- }
- }
-
- /*
- * Set the L_DIRTY bit for a given line in both real_screen &
- * new_screen if the stored representations are in fact different:
- * otherwise clear it.
- */
- static void
- mark_dirty(row)
- int row;
- {
- Sline *rp;
- Sline *np;
- int used;
-
- rp = &real_screen[row];
- np = &new_screen[row];
- if (
-
- (rp->l_flags & ~L_DIRTY) != (np->l_flags & ~L_DIRTY)
- ||
- (used = rp->l_used) != np->l_used
- ||
- strncmp(rp->l_line, np->l_line, used) != 0
- ) {
- /*
- * The lines are different.
- */
- np->l_flags |= L_DIRTY;
- rp->l_flags |= L_DIRTY;
- } else {
- rp->l_flags = (np->l_flags &= ~L_DIRTY);
- }
- }
-
- /*
- * Transfer the specified window line into the "new" screen array, at
- * the given row. Returns the number of screen lines taken up by the
- * logical buffer line lp, or 0 if the line would not fit; this happens
- * with longlines at the end of the screen. In this case, the lines
- * which could not be displayed will have been marked with an '@'.
- */
- static int
- line_to_new(window, lp, start_row, line)
- Xviwin *window;
- Line *lp;
- int start_row;
- long line;
- {
- register unsigned c; /* next character from file */
- register Sline *curr_line; /* output line - used for efficiency */
- register char *ltext; /* pointer to text of line */
- register int curr_index; /* current index in line */
- bool_t eoln; /* true when line is done */
- char extra[MAX_TABSTOP];
- /* Stack for extra characters. */
- int nextra = 0; /* index into stack */
- int srow, scol; /* current screen row and column */
- int vcol; /* virtual column */
-
- ltext = lp->l_text;
- srow = start_row;
- scol = vcol = 0;
- curr_line = &new_screen[srow];
- curr_index = 0;
- eoln = FALSE;
-
- if (Pb(P_number)) {
- static Flexbuf ftmp;
-
- flexclear(&ftmp);
- (void) lformat(&ftmp, NUM_FMT, line);
- (void) strcpy(curr_line->l_line, flexgetstr(&ftmp));
- scol += NUM_SIZE;
- }
-
- while (!eoln) {
- /*
- * Get the next character to put on the screen.
- */
-
- /*
- * "extra" is a stack containing any extra characters
- * we have to put on the screen - this is for chars
- * which have a multi-character representation, and
- * for the $ at end-of-line in list mode.
- */
-
- if (nextra > 0) {
- c = extra[--nextra];
- } else {
- unsigned n;
-
- c = (unsigned char) (ltext[curr_index++]);
-
- /*
- * Deal with situations where it is not
- * appropriate just to copy characters
- * straight onto the screen.
- */
- if (c == '\0') {
-
- if (Pb(P_list)) {
- /*
- * Have to show a '$' sign in list mode.
- */
- extra[nextra++] = '\0';
- c = '$';
- }
-
- } else {
- char *p;
-
- n = vischar((int) c, &p, vcol);
- /*
- * This is a bit paranoid assuming
- * that Pn(P_tabstop) can never be
- * greater than sizeof (extra), but
- * so what.
- */
- if (nextra + n > sizeof extra)
- n = (sizeof extra - nextra);
- /*
- * Stack the extra characters so that
- * they appear in the right order.
- */
- while (n > 1) {
- extra[nextra++] = p[--n];
- }
- c = p[0];
- }
- }
-
- if (c == '\0') {
- /*
- * End of line. Terminate it and finish.
- */
- eoln = TRUE;
- curr_line->l_flags = L_TEXT;
- curr_line->l_used = scol;
- curr_line->l_line[scol] = '\0';
- mark_dirty(srow);
- break;
- } else {
- /*
- * Sline folding.
- */
- if (scol >= window->w_ncols) {
- curr_line->l_flags = L_TEXT;
- curr_line->l_used = scol;
- curr_line->l_line[scol] = '\0';
- mark_dirty(srow);
- srow += 1;
- scol = 0;
- curr_line = &new_screen[srow];
- }
-
- if (srow >= window->w_cmdline) {
- for (srow = start_row; srow < window->w_cmdline; srow++) {
- curr_line = &new_screen[srow];
-
- curr_line->l_flags = L_MARKER;
- curr_line->l_used = 1;
- curr_line->l_line[0] = '@';
- curr_line->l_line[1] = '\0';
- mark_dirty(srow);
- }
- return(0);
- }
-
- /*
- * Store the character in new_screen.
- */
- curr_line->l_line[scol++] = c;
- vcol++;
- }
- }
-
- return((srow - start_row) + 1);
- }
-
- /*
- * file_to_new()
- *
- * Based on the current value of topline, transfer a screenful
- * of stuff from file to new_screen, and update botline.
- */
- static void
- file_to_new(win)
- register Xviwin *win;
- {
- register int row;
- register Line *line;
- register Buffer *buffer;
- long lnum;
-
- buffer = win->w_buffer;
- row = win->w_winpos;
- line = win->w_topline;
- lnum = lineno(buffer, line);
-
- while (row < win->w_cmdline && line != buffer->b_lastline) {
- int nlines;
-
- nlines = line_to_new(win, line, row, lnum);
- if (nlines == 0) {
- /*
- * Make it look like we have updated
- * all the screen lines, since they
- * have '@' signs on them.
- */
- row = win->w_cmdline;
- break;
- } else {
- row += nlines;
- line = line->l_next;
- lnum++;
- }
- }
-
- win->w_botline = line;
-
- /*
- * If there are any lines remaining, fill them in
- * with '~' characters.
- */
- for ( ; row < win->w_cmdline; row++) {
- register Sline *curr_line;
-
- curr_line = &new_screen[row];
-
- curr_line->l_flags = L_MARKER;
- curr_line->l_used = 1;
- curr_line->l_line[0] = '~';
- curr_line->l_line[1] = '\0';
- mark_dirty(row);
- }
- }
-
- /*
- * new_to_screen
- *
- * Transfer the contents of new_screen to the screen,
- * starting at "start_row", for "nlines" lines,
- * using real_screen to avoid unnecessary output.
- */
- static void
- new_to_screen(vs, start_row, nlines)
- VirtScr *vs;
- int start_row;
- int nlines;
- {
- int row; /* current row */
- int end_row; /* row after last one to be updated */
- int columns;
-
- columns = VScols(vs);
-
- if (!(echo & e_CHARUPDATE)) {
- return;
- }
-
- end_row = start_row + nlines;
-
- VSset_colour(vs, Pn(P_colour));
-
- for (row = start_row; row < end_row; row++) {
- register int ncol; /* current column in new_screen */
- register Sline *new,
- *real; /* pointers to current lines */
- register unsigned nflags;
- unsigned rflags; /* flags for current lines */
- register char *ntextp,
- *rtextp;
- /* pointers to line text */
- register int nc; /* current character in new_screen */
- int n_used,
- r_used;
-
- nflags = (new = &new_screen[row])->l_flags;
- rflags = (real = &real_screen[row])->l_flags;
-
- /*
- * If the real and new screens are both "clean",
- * don't bother.
- */
- if (!((nflags & L_DIRTY) || (rflags & L_DIRTY))) {
- continue;
- }
-
- ntextp = new->l_line;
- rtextp = real->l_line;
-
- n_used = new->l_used;
- r_used = real->l_used;
-
- if ((nflags & L_MESSAGE) ||
- (rflags & L_STATUS) != (nflags & L_STATUS)) {
- /*
- * If it's a message line, or its status (text line,
- * command line or message line) has changed, and either
- * the real line or the new line is "dirty", better update
- * the whole thing; if any colour changes are required,
- * the effects of cursor movements may not be predictable
- * on some terminals.
- */
- VSgoto(vs, row, 0);
- if (nflags & L_STATUS) {
- VSset_colour(vs, (nflags & L_READONLY) ? Pn(P_roscolour) :
- Pn(P_statuscolour));
- }
- if ((nc = ntextp[0]) != '\0') {
- #ifdef OPTIMIZE_AMIGA
- outchar(nc);
- #else
- VSputc(vs, row, 0, nc);
- #endif
- }
- /*
- * For command lines, only the first character should be
- * highlighted.
- */
- if (nflags & L_COMMAND) {
- VSset_colour(vs, Pn(P_colour));
- }
- if (nc != '\0') {
- #ifdef OPTIMIZE_AMIGA
- outstr(&ntextp[1]);
- #else
- VSwrite(vs, row, 1, &ntextp[1]);
- #endif
- }
-
- /*
- * Clear the rest of the line, if
- * there is any left to be cleared.
- */
- if (n_used < columns) {
- #ifdef OPTIMIZE_AMIGA
- erase_line();
- #else
- VSclear_line(vs, row, n_used);
- #endif
- }
-
- /*
- * Change back to text colour if we have to.
- */
- if ((nflags & L_MESSAGE) != 0) {
- VSset_colour(vs, Pn(P_colour));
- }
-
- (void) strncpy(rtextp, ntextp, (int) (columns - st_spare_cols));
- } else {
- #ifdef OPTIMIZE_AMIGA /*______________________________________________*/
- /*
- * First check for identical lines since it will occur
- * under certain circumstances (with the * dirty bit set).
- * We skip updating the line if this is the case.
- * Do only character by character checking with the new line
- * and the line on screen until the first mismatch is found.
- * Then just write the rest of the line. On the Amiga
- * tty_goto() causes about 9 characters to be sent to the
- * Amiga console device on the average for each character
- * put out using the original algorithm. It's just faster
- * to send a whole line at a time.
- * Note that we don't use scol here since for the Amiga
- * there are no hidden characters for color, etc; so we use
- * ncol instead. DAS.
- */
- if (strcmp(ntextp, rtextp) != 0) {
- ncol = 0;
- if ((nflags & L_COMMAND) != 0) {
- /*
- * A command line should have the first character
- * - and only the first character - highlighted.
- */
- VSgoto(vs, row, 0);
- VSset_colour(vs, Pn(P_statuscolour));
- outchar(ntextp[0]);
- VSset_colour(vs, Pn(P_colour));
- ncol++;
- } else {
- /*
- * Skip over any initial matching characters.
- */
- while (ntextp[ncol] == rtextp[ncol])
- ncol++;
- VSgoto(vs, row, ncol);
- }
- (void) strcpy(&rtextp[ncol], &ntextp[ncol]);
-
- /*
- * Write the line to the display.
- */
- if (ncol < columns)
- outstr(&ntextp[ncol]);
- /*
- * Check the old line to see if we still have stuff on
- * it. If so we must clear it from the display.
- * We use erase_line() instead of VSclear_line()
- * because we are already at the correct place and
- * VSclear_line() will call tty_goto() unnecessarily.
- */
- if (r_used > n_used)
- erase_line();
- }
- #else /*______________________________________________________________*/
- /*
- * Look at each character in the line, comparing
- * the new version with the one on the screen.
- * If they differ, put it out.
- *
- * There is some optimisation here to avoid large
- * use of tty_goto.
- */
- register int scol;
- /* current column on physical screen */
- register int last_ncol;
- /* last column to be updated */
-
- for (ncol = scol = last_ncol = 0;
- ncol < n_used && ncol < r_used;
- (ncol++, scol++)) {
- if ((nc = ntextp[ncol]) != rtextp[ncol]) {
- /*
- * They are different. Get to the right
- * place before putting out the char.
- */
- if (ncol != 0) {
- VSadvise(vs, row, last_ncol + 1,
- ncol - last_ncol - 1,
- ntextp + last_ncol + 1);
- } else {
- VSgoto(vs, row, scol);
- /*
- * A command line should have the first character
- * - and only the first character - highlighted.
- */
- if (ncol == 0 && (nflags & L_STATUS) != 0) {
- VSset_colour(vs, (nflags & L_READONLY) ?
- Pn(P_roscolour) : Pn(P_statuscolour));
- }
- }
-
- VSputc(vs, row, ncol, nc);
-
- if (ncol == 0 && (nflags & L_COMMAND) != 0) {
- VSset_colour(vs, Pn(P_colour));
- }
- last_ncol = ncol;
- rtextp[ncol] = nc;
- }
- if (ncol == 0 && (nflags & L_COMMAND) != 0) {
- scol += (colour_cost * 2);
- }
- }
-
- if (n_used > r_used) {
- /*
- * We have got to the end of the previous
- * screen line; if there is anything left,
- * we should just display it.
- */
- (void) strcpy(&rtextp[ncol], &ntextp[ncol]);
- if (ncol == 0 && (nflags & L_COMMAND) != 0) {
- /*
- * A command line should have the first character
- * - and only the first character - highlighted.
- */
- VSgoto(vs, row, 0);
- VSset_colour(vs, Pn(P_statuscolour));
- VSputc(vs, row, 0, ntextp[0]);
- VSset_colour(vs, Pn(P_colour));
- ncol = 1;
- } else {
- /*
- * Skip over initial whitespace.
- */
- while (ntextp[ncol] == ' ') {
- ncol++;
- scol++;
- }
- }
- if (ncol < columns)
- VSwrite(vs, row, scol, &ntextp[ncol]);
- } else if (r_used > n_used) {
- /*
- * We have got to the end of the new screen
- * line, but the old one still has stuff on
- * it. We must therefore clear it.
- */
- VSclear_line(vs, row, scol);
- }
- #endif /*_____________________________________________________________*/
- }
-
- real->l_line[n_used] = '\0';
- real->l_used = n_used;
-
- /*
- * The real screen line is a message or command line if the
- * newly-updated one was. Otherwise, it isn't.
- *
- * Both the new and real screens may now be considered
- * "clean".
- */
- real->l_flags = (
- /*
- * Turn these flags off first ...
- */
- (rflags & ~(L_STATUS | L_DIRTY))
- /*
- * ... then set whatever L_STATUS flags are
- * set in new_screen.
- */
- | (nflags & L_STATUS)
- );
- new->l_flags &= ~L_DIRTY;
- }
- }
-
- /*
- * Update the status line of the given window, and cause the status
- * line to be written out. Note that we call new_to_screen() to cause
- * the output to be generated; since there will be no other changes,
- * only the status line will be changed on the screen.
- */
- void
- update_sline(win)
- Xviwin *win;
- {
- do_sline(win);
- new_to_screen(win->w_vs, (int) win->w_cmdline, 1);
- VSflush(win->w_vs);
- }
-
- /*
- * Update the status line of the given window,
- * from the one in win->w_statusline.
- */
- static void
- do_sline(win)
- Xviwin *win;
- {
- register char *from;
- register char *to;
- register char *end;
- Sline *slp;
-
- from = flexgetstr(&win->w_statusline);
- slp = &new_screen[win->w_cmdline];
- to = slp->l_line;
- end = to + win->w_ncols - st_spare_cols;
-
- while (*from != '\0' && to < end) {
- *to++ = *from++;
- }
-
- /*
- * Fill with spaces, and null-terminate.
- */
- while (to < end) {
- *to++ = ' ';
- }
- *to = '\0';
-
- slp->l_used = win->w_ncols - st_spare_cols;
- slp->l_flags = L_MESSAGE;
- if (is_readonly(win->w_buffer)) {
- slp->l_flags |= L_READONLY;
- }
- mark_dirty(win->w_cmdline);
- }
-
- void
- update_cline(win)
- Xviwin *win;
- {
- Sline *clp;
- unsigned width, maxwidth;
-
- clp = &new_screen[win->w_cmdline];
-
- maxwidth = win->w_ncols - st_spare_cols;
- if ((width = flexlen(&win->w_statusline)) > maxwidth) {
- width = maxwidth;
- }
- (void) strncpy(clp->l_line, flexgetstr(&win->w_statusline),
- (int) width);
- clp->l_used = width;
- clp->l_line[width] = '\0';
- clp->l_flags = (L_COMMAND | L_DIRTY);
- /*
- * We don't bother calling mark_dirty() here: it isn't worth
- * it because the line's contents have almost certainly
- * changed.
- */
- new_to_screen(win->w_vs, (int) win->w_cmdline, 1);
- VSflush(win->w_vs);
- }
-
- /*
- * 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.
- */
- void
- updateline(window)
- Xviwin *window;
- {
- Line *currline;
- int nlines;
- int curs_row;
-
- currline = window->w_cursor->p_line;
-
- /*
- * Find out which screen line the cursor line starts on.
- * This is not necessarily the same as window->w_row,
- * because longlines are different.
- */
- if (plines(window, currline) > 1) {
- curs_row = (int) cntplines(window, window->w_topline, currline);
- } else {
- curs_row = window->w_row;
- }
-
- nlines = line_to_new(window, currline,
- (int) (curs_row + window->w_winpos),
- (long) lineno(window->w_buffer, currline));
-
- if (nlines != window->w_c_line_size) {
- update_buffer(window->w_buffer);
- } else {
- new_to_screen(window->w_vs,
- (int) (curs_row + window->w_winpos), nlines);
- VSflush(window->w_vs);
- }
- }
-
- /*
- * Completely update the representation of the given window.
- */
- void
- update_window(window)
- Xviwin *window;
- {
- if (window->w_nrows > 1) {
- file_to_new(window);
- new_to_screen(window->w_vs,
- (int) window->w_winpos, (int) window->w_nrows);
- VSflush(window->w_vs);
- }
- }
-
- /*
- * Update all windows.
- */
- void
- update_all()
- {
- Xviwin *w = curwin;
-
- do {
- if (w->w_nrows > 1) {
- file_to_new(w);
- }
- if (w->w_nrows > 0) {
- do_sline(w);
- }
- } while ((w = next_window(w)) != curwin);
-
- new_to_screen(w->w_vs, 0, (int) VSrows(w->w_vs));
- VSflush(w->w_vs);
- }
-
- /*
- * Totally redraw the screen.
- */
- void
- redraw_screen()
- {
- if (curwin != NULL) {
- clear(curwin);
- update_all();
- }
- }
-
- void
- clear(win)
- Xviwin *win;
- {
- register int row;
- int nrows;
-
- nrows = VSrows(win->w_vs);
-
- VSset_colour(win->w_vs, Pn(P_colour));
- VSclear_all(win->w_vs);
-
- /*
- * Clear the real screen lines, and mark them as modified.
- */
- for (row = 0; row < nrows; row++) {
- clrline(row);
- }
- }
-
- /*
- * 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(win, row, nlines) - insert 'nlines' lines at 'row'
- */
- void
- s_ins(win, row, nlines)
- Xviwin *win;
- register int row;
- int nlines;
- {
- register int from, to;
- int count;
- VirtScr *vs;
-
- if (!(echo & e_SCROLL))
- return;
-
- #ifdef OPTIMIZE_AMIGA
- /* On Amiga multiple line scrolling is slow, limit it to 2 lines. */
- if (nlines > 2)
- return;
- #endif
-
- /*
- * There's no point in scrolling more lines than there are
- * (below row) in the window, or in scrolling 0 lines.
- */
- if (nlines == 0 || nlines + row >= win->w_nrows - 1)
- return;
-
- /*
- * The row specified is relative to the top of the window;
- * add the appropriate offset to make it into a screen row.
- */
- row += win->w_winpos;
-
- /*
- * Note that we avoid the use of 1-line scroll regions; these
- * only ever occur at the bottom of a window, and it is better
- * just to leave the line to be updated in the best way by
- * update{line,screen}.
- */
- if (nlines == 1 && row + 1 == win->w_cmdline) {
- return;
- }
-
- vs = win->w_vs;
-
- if (vs->v_scroll != NULL) {
- if (!VSscroll(vs, row, (int) win->w_cmdline - 1, -nlines)) {
- /*
- * Can't scroll what we were asked to - try scrolling
- * the whole window including the status line.
- */
- VSclear_line(vs, (int) win->w_cmdline, 0);
- clrline(win->w_cmdline);
- if (!VSscroll(vs, row, (int) win->w_cmdline, -nlines)) {
- /*
- * Failed.
- */
- return;
- }
- }
- } else {
- return;
- }
-
- /*
- * Update the stored screen image so it matches what has
- * happened on the screen.
- */
-
- /*
- * Move section of text down to the bottom.
- *
- * We do this by rearranging the pointers within the Slines,
- * rather than copying the characters.
- */
- for (to = win->w_cmdline - 1, from = to - nlines; from >= row;
- --from, --to) {
- register char *temp;
-
- temp = real_screen[to].l_line;
- real_screen[to].l_line = real_screen[from].l_line;
- real_screen[from].l_line = temp;
- real_screen[to].l_used = real_screen[from].l_used;
- }
-
- /*
- * Clear the newly inserted lines.
- */
- for (count = row; count < row + nlines; count++) {
- clrline(count);
- }
- }
-
- /*
- * s_del(win, row, nlines) - delete 'nlines' lines starting at 'row'.
- */
- void
- s_del(win, row, nlines)
- register Xviwin *win;
- int row;
- int nlines;
- {
- register int from, to;
- int count;
- VirtScr *vs;
-
- if (!(echo & e_SCROLL))
- return;
-
- #ifdef OPTIMIZE_AMIGA
- if (nlines > 1)
- return; /* On Amiga multiple line scrolling is slow. */
- #endif
-
- /*
- * There's no point in scrolling more lines than there are
- * (below row) in the window, or in scrolling 0 lines.
- */
- if (nlines == 0 || nlines + row >= win->w_nrows - 1)
- return;
-
- /*
- * The row specified is relative to the top of the window;
- * add the appropriate offset to make it into a screen row.
- */
- row += win->w_winpos;
-
- /*
- * We avoid the use of 1-line scroll regions, since they don't
- * work with many terminals, especially if we are using
- * (termcap) DO to scroll the region.
- */
- if (nlines == 1 && row + 1 == win->w_cmdline) {
- return;
- }
-
- vs = win->w_vs;
-
- if (vs->v_scroll != NULL) {
- if (!VSscroll(vs, row, (int) win->w_cmdline - 1, nlines)) {
- /*
- * Can't scroll what we were asked to - try scrolling
- * the whole window including the status line.
- */
- VSclear_line(vs, (int) win->w_cmdline, 0);
- clrline(win->w_cmdline);
- if (!VSscroll(vs, row, (int) win->w_cmdline, nlines)) {
- /*
- * Failed.
- */
- return;
- }
- }
- } else {
- return;
- }
-
- /*
- * Update the stored screen image so it matches what has
- * happened on the screen.
- */
-
- /*
- * Move section of text up from the bottom.
- *
- * We do this by rearranging the pointers within the Slines,
- * rather than copying the characters.
- */
- for (to = row, from = to + nlines;
- from < win->w_cmdline;
- from++, to++) {
- register char *temp;
-
- temp = real_screen[to].l_line;
- real_screen[to].l_line = real_screen[from].l_line;
- real_screen[from].l_line = temp;
- real_screen[to].l_used = real_screen[from].l_used;
- }
-
- /*
- * Clear the deleted lines.
- */
- for (count = win->w_cmdline - nlines; count < win->w_cmdline; count++) {
- clrline(count);
- }
- }
-
- /*
- * Insert a character at the cursor position, updating the screen as
- * necessary. Note that this routine doesn't have to do anything, as
- * the screen will eventually be correctly updated anyway; it's just
- * here for speed of screen updating.
- */
- void
- s_inschar(window, newchar)
- Xviwin *window;
- int newchar;
- {
- register char *curp;
- register char *cp;
- register char *sp;
- Sline *rp;
- Posn *pp;
- VirtScr *vs; /* the VirtScr for this window */
- char *newstr; /* printable string for newchar */
- register unsigned nchars; /* number of chars in newstr */
- unsigned currow;
- unsigned curcol;
- unsigned columns;
-
- vs = window->w_vs;
- if (vs->v_insert == NULL)
- return;
-
- if (!(echo & e_CHARUPDATE))
- return;
-
- pp = window->w_cursor;
-
- /*
- * If we are at (or near) the end of the line, it's not worth
- * the bother. Define near as 0 or 1 characters to be moved.
- */
- cp = pp->p_line->l_text + pp->p_index;
- if (*cp == '\0' || *(cp+1) == '\0')
- return;
-
- curcol = window->w_col;
-
- /*
- * If the cursor is on a longline, and not on the last actual
- * screen line of that longline, we can't do it.
- */
- if (window->w_c_line_size > 1 && curcol != window->w_virtcol)
- return;
-
- nchars = vischar(newchar, &newstr, curcol);
-
- /*
- * And don't bother if we are (or will be) at the last screen column.
- */
- columns = window->w_ncols;
- if (curcol + nchars >= columns)
- return;
-
- /*
- * Also, trying to push tabs rightwards doesn't work very
- * well. It's usually better not to use the insert character
- * sequence because in most cases we'll only have to update
- * the line as far as the tab anyway.
- */
- if ((!Pb(P_list) && Pb(P_tabs)) && strchr(cp, '\t') != NULL) {
- return;
- }
-
- /*
- * Okay, we can do it.
- */
- currow = window->w_row;
-
- VSinsert(vs, window->w_winpos + currow, curcol, newstr);
-
- /*
- * Update real_screen.
- */
- rp = &real_screen[window->w_winpos + currow];
- curp = &rp->l_line[curcol];
- if ((rp->l_used += nchars) > columns)
- rp->l_used = columns;
- cp = &rp->l_line[rp->l_used - 1];
- cp[1] = '\0';
- if (cp - curp >= nchars)
- {
- sp = cp - nchars;
- for (;;) {
- *cp-- = *sp;
- if (sp-- <= curp)
- break;
- }
- }
-
- /*
- * This is the string we've just inserted.
- */
- sp = newstr;
- while (nchars-- > 0) {
- *curp++ = *sp++;
- }
- }
-
- void
- wind_goto(win)
- Xviwin *win;
- {
- VirtScr *vs;
-
- if (echo & e_CHARUPDATE) {
- vs = win->w_vs;
- VSgoto(vs, (int) win->w_winpos + win->w_row, win->w_col);
- VSflush(vs);
- }
- }
-
- static char inbuf[CMDSZ]; /* command input buffer */
- static unsigned int inpos = 0; /* posn of next input char */
- static unsigned char colposn[CMDSZ]; /* holds n chars per char */
-
- /*
- * cmd_init(window, firstch)
- *
- * Initialise command line input.
- */
- void
- cmd_init(win, firstch)
- Xviwin *win;
- int firstch;
- {
- if (inpos > 0) {
- show_error(win, "Internal error: re-entered command line input mode");
- return;
- }
-
- State = CMDLINE;
-
- flexclear(&win->w_statusline);
- (void) flexaddch(&win->w_statusline, firstch);
- inbuf[0] = firstch;
- inpos = 1;
- update_cline(win);
- colposn[0] = 0;
- }
-
- /*
- * cmd_input(window, character)
- *
- * Deal with command line input. Takes an input character and returns
- * one of cmd_CANCEL (meaning they typed ESC or deleted past the
- * prompt character), cmd_COMPLETE (indicating that the command has
- * been completely input), or cmd_INCOMPLETE (indicating that command
- * line is still the right mode to be in).
- *
- * Once cmd_COMPLETE has been returned, it is possible to call
- * get_cmd(win) to obtain the command line.
- */
- Cmd_State
- cmd_input(win, ch)
- Xviwin *win;
- int ch;
- {
- static bool_t literal_next = FALSE;
-
- if (!literal_next) {
- switch (ch) {
- case CTRL('V'):
- literal_next = TRUE;
- return(cmd_INCOMPLETE);
-
- case '\n': /* end of line */
- case '\r':
- inbuf[inpos] = '\0'; /* terminate input line */
- inpos = 0;
- State = NORMAL; /* return state to normal */
- do_sline(win); /* line is now a message line */
- return(cmd_COMPLETE); /* and indicate we are done */
-
- case '\b': /* backspace or delete */
- case DEL:
- {
- unsigned len;
-
- inbuf[--inpos] = '\0';
- len = colposn[inpos - 1] + 1;
- while (flexlen(&win->w_statusline) > len)
- (void) flexrmchar(&win->w_statusline);
- update_cline(win);
- if (inpos == 0) {
- /*
- * Deleted past first char;
- * go back to normal mode.
- */
- State = NORMAL;
- return(cmd_CANCEL);
- }
- return(cmd_INCOMPLETE);
- }
-
- case '\033':
- #ifdef AMIGA
- /* This seems to what comment above really meant for ESC! */
- inpos = 0;
- inbuf[0] = '\0';
- flexclear(&win->w_statusline);
- (void) flexaddch(&win->w_statusline, inbuf[0]);
- update_cline(win);
- State = NORMAL;
- return(cmd_CANCEL);
- #endif
- case EOF:
- case CTRL('U'): /* line kill */
- inpos = 1;
- inbuf[1] = '\0';
- flexclear(&win->w_statusline);
- (void) flexaddch(&win->w_statusline, inbuf[0]);
- update_cline(win);
- return(cmd_INCOMPLETE);
-
- default:
- break;
- }
- }
-
- literal_next = FALSE;
-
- if (inpos >= sizeof(inbuf) - 1) {
- /*
- * Must not overflow buffer.
- */
- beep(win);
- } else {
- unsigned curposn;
- unsigned w;
- char *p;
-
- curposn = colposn[inpos - 1];
- w = vischar(ch, &p, (int) curposn);
- if (curposn + w >= win->w_ncols - 1) {
- beep(win);
- } else {
- colposn[inpos] = curposn + w;
- inbuf[inpos++] = ch;
- (void) lformat(&win->w_statusline, "%s", p);
- update_cline(win);
- }
- }
-
- return(cmd_INCOMPLETE);
- }
-
- /*ARGSUSED*/
- char *
- get_cmd(win)
- Xviwin *win;
- {
- return(inbuf);
- }
-
- /*ARGSUSED*/
- void
- gotocmd(win, clr)
- Xviwin *win;
- bool_t clr;
- {
- VirtScr *vs;
-
- vs = win->w_vs;
- if (clr) {
- VSclear_line(vs, (int) win->w_cmdline, 0);
- }
- VSgoto(vs, (int) win->w_cmdline, 0);
- VSflush(vs);
- }
-
- /*
- * Display a prompt on the bottom line of the screen.
- */
- void
- prompt(message)
- char *message;
- {
- VirtScr *vs;
- int row;
-
- vs = curwin->w_vs;
-
- row = VSrows(vs) - 1;
- VSgoto(vs, row, 0);
- VSset_colour(vs, Pn(P_statuscolour));
- VSwrite(vs, row, 0, message);
- VSset_colour(vs, Pn(P_colour));
- VSgoto(vs, row, strlen(message));
- VSflush(vs);
- }
-
- /*
- * Sound the alert.
- */
- void
- beep(window)
- register Xviwin *window;
- {
- VSbeep(window->w_vs);
- }
-
- static char *(*disp_func) P((void));
- static int disp_colwidth;
- static int disp_maxcol;
- static bool_t disp_listmode;
-
- /*
- * Start off "display" mode. The "func" argument is a function pointer
- * which will be called to obtain each subsequent string to display.
- * The function returns NULL when no more lines are available.
- */
- void
- disp_init(win, func, colwidth, listmode)
- Xviwin *win;
- char *(*func) P((void));
- int colwidth;
- bool_t listmode;
- {
- State = DISPLAY;
- disp_func = func;
- if (colwidth > win->w_ncols)
- colwidth = win->w_ncols;
- disp_colwidth = colwidth;
- disp_maxcol = (win->w_ncols / colwidth) * colwidth;
- disp_listmode = listmode;
- (void) disp_screen(win);
- }
-
- /*
- * Display text in glass-teletype mode, in approximately the style of
- * the more(1) program.
- *
- * If the return value from (*disp_func)() is NULL, it means we've got
- * to the end of the text to be displayed, so we wait for another key
- * before redisplaying our editing screen.
- */
- bool_t
- disp_screen(win)
- Xviwin *win;
- {
- int row; /* current screen row */
- int col; /* current screen column */
- static bool_t finished = FALSE;
- VirtScr *vs;
-
- vs = win->w_vs;
-
- if (finished || kbdintr) {
- /*
- * Clear the screen, and then ensure that the window
- * on the current buffer is in the right place and
- * updated; finally update the whole screen.
- */
- clear(win);
- move_window_to_cursor(win);
- update_all();
- State = NORMAL;
- finished = FALSE;
- if (kbdintr) {
- imessage = TRUE;
- }
- return(TRUE);
- }
-
- VSclear_all(vs);
-
- for (col = 0; col < disp_maxcol; col += disp_colwidth) {
- for (row = 0; row < VSrows(vs) - 1; row++) {
- static char *line;
- int width;
-
- if (line == NULL && (line = (*disp_func)()) == NULL) {
- /*
- * We've got to the end.
- */
- prompt("[Hit return to continue] ");
- finished = TRUE;
- return(FALSE);
- }
-
- for (width = 0; *line != '\0'; line++) {
- char *p;
- unsigned w;
-
- w = vischar(*line, &p, disp_listmode ? -1 : width);
-
- if ((width += w) <= disp_colwidth) {
- VSwrite(vs, row, col + width - w, p);
- } else {
- /*
- * The line is too long, so we
- * have to wrap around to the
- * next screen line.
- */
- break;
- }
- }
-
- if (*line == '\0') {
- if (disp_listmode) {
- /*
- * In list mode, we have to
- * display a '$' to show the
- * end of a line.
- */
- if (width < disp_colwidth) {
- VSputc(vs, row, col + width, '$');
- } else {
- /*
- * We have to wrap it
- * to the next screen
- * line.
- */
- continue;
- }
- }
- /*
- * If we're not in list mode, or we
- * were able to display the '$', we've
- * finished with this line.
- */
- line = NULL;
- }
- }
- }
-
- prompt("[More] ");
-
- return(FALSE);
- }
-
- /*
- * Clear the given line, marking it as dirty.
- */
- static void
- clrline(line)
- int line;
- {
- real_screen[line].l_used = 0;
- real_screen[line].l_line[0] = '\0';
- mark_dirty(line);
- }
-