home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
x
/
xvisrc.zoo
/
screen.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-28
|
33KB
|
1,420 lines
/* 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
***/
#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') {
VSputc(vs, row, 0, nc);
}
/*
* For command lines, only the first character should be
* highlighted.
*/
if (nflags & L_COMMAND) {
VSset_colour(vs, Pn(P_colour));
}
if (nc != '\0') {
VSwrite(vs, row, 1, &ntextp[1]);
}
/*
* Clear the rest of the line, if
* there is any left to be cleared.
*/
if (n_used < columns) {
VSclear_line(vs, row, n_used);
}
/*
* 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 {
/*
* 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);
}
}
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;
/*
* 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;
/*
* 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':
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);
}