mirror of https://github.com/tildeclub/ex-vi.git
1163 lines
28 KiB
C
1163 lines
28 KiB
C
/*
|
|
* This code contains changes by
|
|
* Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
|
|
*
|
|
* Conditions 1, 2, and 4 and the no-warranty notice below apply
|
|
* to these changes.
|
|
*
|
|
*
|
|
* Copyright (c) 1980, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*
|
|
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* Redistributions of source code and documentation must retain the
|
|
* above copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed or owned by Caldera
|
|
* International, Inc.
|
|
* Neither the name of Caldera International, Inc. nor the names of
|
|
* other contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
|
|
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef lint
|
|
#ifdef DOSCCS
|
|
static char sccsid[] = "@(#)ex_vadj.c 1.11 (gritter) 3/4/05";
|
|
#endif
|
|
#endif
|
|
|
|
/* from ex_vadj.c 7.9 (Berkeley) 6/7/85 */
|
|
|
|
#include "ex.h"
|
|
#include "ex_tty.h"
|
|
#include "ex_vis.h"
|
|
|
|
/*
|
|
* Routines to deal with management of logical versus physical
|
|
* display, opening and redisplaying lines on the screen, and
|
|
* use of intelligent terminal operations. Routines to deal with
|
|
* screen cleanup after a change.
|
|
*/
|
|
|
|
/*
|
|
* Display a new line at physical line p, returning
|
|
* the depth of the newly displayed line. We may decide
|
|
* to expand the window on an intelligent terminal if it is
|
|
* less than a full screen by deleting a line above the top of the
|
|
* window before doing an insert line to keep all the good text
|
|
* on the screen in which case the line may actually end up
|
|
* somewhere other than line p.
|
|
*/
|
|
void
|
|
vopen(line *tp, int p)
|
|
{
|
|
register int cnt;
|
|
register struct vlinfo *vp, *vpc;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace != NULL)
|
|
tfixnl(), fprintf(trace, "vopen(%d, %d)\n", lineno(tp), p);
|
|
#endif
|
|
if (state != VISUAL) {
|
|
if (vcnt)
|
|
if (hold & HOLDROL)
|
|
vup1();
|
|
else
|
|
vclean();
|
|
|
|
/*
|
|
* Forget all that we once knew.
|
|
*/
|
|
vcnt = vcline = 0;
|
|
p = WBOT; LASTLINE = WBOT + 1;
|
|
state = bastate;
|
|
WTOP = basWTOP;
|
|
WLINES = basWLINES;
|
|
}
|
|
vpc = &vlinfo[vcline];
|
|
for (vp = &vlinfo[vcnt]; vp >= vpc; vp--)
|
|
vlcopy(vp[1], vp[0]);
|
|
vcnt++;
|
|
if (Pline == numbline)
|
|
/*
|
|
* Dirtying all the lines is rather inefficient
|
|
* internally, but number mode is used rarely
|
|
* and so its not worth optimizing.
|
|
*/
|
|
vdirty(vcline+1, WECHO);
|
|
getline(*tp);
|
|
|
|
/*
|
|
* If we are opening at the top of the window, can try a window
|
|
* expansion at the top.
|
|
*/
|
|
if (state == VISUAL && vcline == 0 && vcnt > 1 && p > ZERO) {
|
|
cnt = p + vdepth() - LINE(1);
|
|
if (cnt > 0) {
|
|
p -= cnt;
|
|
if (p < ZERO)
|
|
p = ZERO;
|
|
WTOP = p;
|
|
WLINES = WBOT - WTOP + 1;
|
|
}
|
|
}
|
|
vpc->vliny = p, vpc->vdepth = 0, vpc->vflags = 0;
|
|
cnt = vreopen(p, lineno(tp), vcline);
|
|
if (vcline + 1 == vcnt)
|
|
LINE(vcnt) = LINE(vcline) + cnt;
|
|
}
|
|
|
|
/*
|
|
* Redisplay logical line l at physical line p with line number lineno.
|
|
*/
|
|
int
|
|
vreopen(int p, int lineno, int l)
|
|
{
|
|
register int d;
|
|
register struct vlinfo *vp = &vlinfo[l];
|
|
|
|
if (p < 0)
|
|
error("Line too long to fit on screen");
|
|
d = vp->vdepth;
|
|
if (d == 0 || (vp->vflags & VDIRT))
|
|
vp->vdepth = d = vdepth();
|
|
vp->vliny = p, vp->vflags &= ~VDIRT;
|
|
|
|
/*
|
|
* Try to win by making the screen larger rather than inserting
|
|
* a line and driving text off the bottom.
|
|
*/
|
|
p = vglitchup(l, 0);
|
|
|
|
/*
|
|
* BUG: Should consider using CE here to clear to end of line.
|
|
* As it stands we always strike over the current text.
|
|
* Since often the current text is the same as what
|
|
* we are overstriking with, it tends not to show.
|
|
* On the other hand if it is different and we end up
|
|
* spacing out a lot of text, we could have won with
|
|
* a CE. This is probably worthwhile at low speed
|
|
* only however, since clearly computation will be
|
|
* necessary to determine which way to go.
|
|
*/
|
|
vigoto(p, 0);
|
|
pline(lineno);
|
|
|
|
/*
|
|
* When we are typing part of a line for hardcopy open, don't
|
|
* want to type the '$' marking an end of line if in list mode.
|
|
*/
|
|
if (hold & HOLDDOL)
|
|
return (d);
|
|
if (Putchar == listchar)
|
|
putchar('$');
|
|
|
|
/*
|
|
* Optimization of cursor motion may prevent screen rollup if the
|
|
* line has blanks/tabs at the end unless we force the cursor to appear
|
|
* on the last line segment.
|
|
*/
|
|
if (vp->vliny + d - 1 > WBOT)
|
|
vcsync();
|
|
|
|
/*
|
|
* Switch into hardcopy open mode if we are in one line (adm3)
|
|
* open mode and this line is now too long. If in hardcopy
|
|
* open mode, then call sethard to move onto the next line
|
|
* with appropriate positioning.
|
|
*/
|
|
if (state == ONEOPEN) {
|
|
WCOLS = OCOLUMNS;
|
|
if (vdepth() > 1) {
|
|
WCOLS = TUBECOLS;
|
|
sethard();
|
|
} else
|
|
WCOLS = TUBECOLS;
|
|
} else if (state == HARDOPEN)
|
|
sethard();
|
|
|
|
/*
|
|
* Unless we filled (completely) the last line we typed on,
|
|
* we have to clear to the end of the line
|
|
* in case stuff is left from before.
|
|
*/
|
|
if (vp->vliny + d > destline) {
|
|
if (IN && destcol == WCOLS)
|
|
vigoto(vp->vliny + d - 1, 0);
|
|
vclreol();
|
|
}
|
|
return (d);
|
|
}
|
|
|
|
/*
|
|
* Real work for winning growing of window at top
|
|
* when inserting in the middle of a partially full
|
|
* screen on an intelligent terminal. We have as argument
|
|
* the logical line number to be inserted after, and the offset
|
|
* from that line where the insert will go.
|
|
* We look at the picture of depths and positions, and if we can
|
|
* delete some (blank) lines from the top of the screen so that
|
|
* later inserts will not push stuff off the bottom.
|
|
*/
|
|
int
|
|
vglitchup(int l, int o)
|
|
{
|
|
register struct vlinfo *vp = &vlinfo[l];
|
|
register int need;
|
|
register int p = vp->vliny;
|
|
short oldhold = 0, oldheldech = 0;
|
|
bool glitched = 0;
|
|
|
|
if (l < vcnt - 1) {
|
|
need = p + vp->vdepth - (vp+1)->vliny;
|
|
if (need > 0) {
|
|
if (state == VISUAL && WTOP - ZERO >= need && AL && DL) {
|
|
glitched++;
|
|
WTOP -= need;
|
|
WLINES = WBOT - WTOP + 1;
|
|
p -= need;
|
|
if (p + o == WTOP) {
|
|
vp->vliny = WTOP;
|
|
return (WTOP + o);
|
|
}
|
|
vdellin(WTOP, need, -1);
|
|
oldheldech = heldech;
|
|
oldhold = hold;
|
|
hold |= HOLDECH;
|
|
}
|
|
vinslin((vp+1)->vliny, need, l);
|
|
if (glitched) {
|
|
hold = oldhold;
|
|
heldech = oldheldech;
|
|
}
|
|
}
|
|
} else
|
|
vp[1].vliny = vp[0].vliny + vp->vdepth;
|
|
return (p + o);
|
|
}
|
|
|
|
/*
|
|
* Insert cnt blank lines before line p,
|
|
* logically and (if supported) physically.
|
|
*/
|
|
void
|
|
vinslin(register int p, register int cnt, int l)
|
|
{
|
|
register int i;
|
|
bool could = 1;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vinslin(%d, %d, %d)\n", p, cnt, l);
|
|
#endif
|
|
if (p + cnt > WBOT && CD) {
|
|
/*
|
|
* Really quick -- clear to end of screen.
|
|
*/
|
|
cnt = WECHO + 1 - p;
|
|
vgoto(p, 0), vputp(CD, cnt);
|
|
vclrech(1);
|
|
vadjAL(p, cnt);
|
|
} else if (SR && p == WTOP && costSR < costAL) {
|
|
/*
|
|
* Use reverse scroll mode of the terminal, at
|
|
* the top of the window. Reverse linefeed works
|
|
* too, since we only use it from line WTOP.
|
|
*/
|
|
for (i = cnt; i > 0; i--) {
|
|
vgoto(p, 0), vputp(SR, 0);
|
|
if (i > 1 && (hold & HOLDAT) == 0)
|
|
putchar('@');
|
|
/*
|
|
* If we are at the top of the screen, and the
|
|
* terminal retains display above, then we
|
|
* should try to clear to end of line.
|
|
* Have to use CE since we don't remember what is
|
|
* actually on the line.
|
|
*/
|
|
if (CE && (DA || p != 0))
|
|
vputp(CE, 1);
|
|
}
|
|
vadjAL(p, cnt);
|
|
} else if (AL) {
|
|
/*
|
|
* Use insert line.
|
|
*/
|
|
vgoto(p, 0);
|
|
if (AL_PARM && (cnt>1 || *AL==0)) {
|
|
/* insert cnt lines. Should do @'s too. */
|
|
vputp(tgoto(AL_PARM, p, cnt), WECHO+1-p);
|
|
}
|
|
else if (xCS && *AL==0) {
|
|
/* vt100 change scrolling region to fake AL */
|
|
vputp(SC, 1);
|
|
vputp(tgoto(xCS, TLINES-1,p), 1);
|
|
vputp(RC, 1); /* xCS homes stupid cursor */
|
|
for (i=cnt; i>0; i--)
|
|
vputp(SR, 1); /* should do @'s */
|
|
vputp(tgoto(xCS, TLINES-1,0), 1);
|
|
vputp(RC, 1); /* Once again put it back */
|
|
}
|
|
else {
|
|
vputp(AL, WECHO + 1 - p);
|
|
for (i = cnt - 1; i > 0; i--) {
|
|
vgoto(outline+1, 0);
|
|
vputp(AL, WECHO + 1 - outline);
|
|
if ((hold & HOLDAT) == 0)
|
|
putchar('@');
|
|
}
|
|
}
|
|
vadjAL(p, cnt);
|
|
} else
|
|
could = 0;
|
|
vopenup(cnt, could, l);
|
|
}
|
|
|
|
/*
|
|
* Logically open up after line l, cnt of them.
|
|
* We need to know if it was done ``physically'' since in this
|
|
* case we accept what the hardware gives us. If we have to do
|
|
* it ourselves (brute force) we will squish out @ lines in the process
|
|
* if this will save us work.
|
|
*/
|
|
void
|
|
vopenup(int cnt, int could, int l)
|
|
{
|
|
register struct vlinfo *vc = &vlinfo[l + 1];
|
|
register struct vlinfo *ve = &vlinfo[vcnt];
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vopenup(%d, %d, %d)\n", cnt, could, l);
|
|
#endif
|
|
if (could)
|
|
/*
|
|
* This will push @ lines down the screen,
|
|
* just as the hardware did. Since the default
|
|
* for intelligent terminals is to never have @
|
|
* lines on the screen, this should never happen,
|
|
* and the code makes no special effort to be nice in this
|
|
* case, e.g. squishing out the @ lines by delete lines
|
|
* before doing append lines.
|
|
*/
|
|
for (; vc <= ve; vc++)
|
|
vc->vliny += cnt;
|
|
else {
|
|
/*
|
|
* Will have to clean up brute force eventually,
|
|
* so push the line data around as little as possible.
|
|
*/
|
|
vc->vliny += cnt, vc->vflags |= VDIRT;
|
|
while (vc < ve) {
|
|
register int i = vc->vliny + vc->vdepth;
|
|
|
|
vc++;
|
|
if (i <= vc->vliny)
|
|
break;
|
|
vc->vliny = i, vc->vflags |= VDIRT;
|
|
}
|
|
}
|
|
vscrap();
|
|
}
|
|
|
|
/*
|
|
* Adjust data structure internally to account for insertion of
|
|
* blank lines on the screen.
|
|
*/
|
|
void
|
|
vadjAL(int p, int cnt)
|
|
{
|
|
cell *tlines[TUBELINES];
|
|
register int from, to;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vadjal(%d, %d)\n", p, cnt);
|
|
#endif
|
|
copy(tlines, vtube, sizeof vtube); /*SASSIGN*/
|
|
for (from = p, to = p + cnt; to <= WECHO; from++, to++)
|
|
vtube[to] = tlines[from];
|
|
for (to = p; from <= WECHO; from++, to++) {
|
|
vtube[to] = tlines[from];
|
|
vclrcell(vtube[to], WCOLS);
|
|
}
|
|
/*
|
|
* Have to clear the echo area since its contents aren't
|
|
* necessarily consistent with the rest of the display.
|
|
*/
|
|
vclrech(0);
|
|
}
|
|
|
|
/*
|
|
* Roll the screen up logically and physically
|
|
* so that line dl is the bottom line on the screen.
|
|
*/
|
|
void
|
|
vrollup(int dl)
|
|
{
|
|
register int cnt;
|
|
register int dc = destcol;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vrollup(%d)\n", dl);
|
|
#endif
|
|
cnt = dl - (splitw ? WECHO : WBOT);
|
|
if (splitw && (state == VISUAL || state == CRTOPEN))
|
|
holdupd = 1;
|
|
vmoveitup(cnt, 1);
|
|
vscroll(cnt);
|
|
destline = dl - cnt, destcol = dc;
|
|
}
|
|
|
|
void
|
|
vup1(void)
|
|
{
|
|
|
|
vrollup(WBOT + 1);
|
|
}
|
|
|
|
/*
|
|
* Scroll the screen up cnt lines physically.
|
|
* If doclr is true, do a clear eol if the terminal
|
|
* has standout (to prevent it from scrolling up)
|
|
*/
|
|
void
|
|
vmoveitup(register int cnt, int doclr)
|
|
{
|
|
|
|
if (cnt == 0)
|
|
return;
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vmoveitup(%d)\n", cnt);
|
|
#endif
|
|
if (doclr && (SO || SE))
|
|
vclrech(0);
|
|
if (SF) {
|
|
destline = WECHO;
|
|
destcol = (NONL ? 0 : outcol % WCOLS);
|
|
fgoto();
|
|
while (cnt > 0)
|
|
vputp(SF, 0), cnt--;
|
|
return;
|
|
}
|
|
destline = WECHO + cnt;
|
|
destcol = (NONL ? 0 : outcol % WCOLS);
|
|
fgoto();
|
|
if (state == ONEOPEN || state == HARDOPEN) {
|
|
outline = destline = 0;
|
|
vclrcell(vtube[0], WCOLS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scroll the screen up cnt lines logically.
|
|
*/
|
|
void
|
|
vscroll(register int cnt)
|
|
{
|
|
register int from, to;
|
|
cell *tlines[TUBELINES];
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
fprintf(trace, "vscroll(%d)\n", cnt);
|
|
#endif
|
|
if (cnt < 0 || cnt > TUBELINES)
|
|
error(catgets(catd, 1, 219, "Internal error: vscroll"));
|
|
if (cnt == 0)
|
|
return;
|
|
copy(tlines, vtube, sizeof vtube);
|
|
for (to = ZERO, from = ZERO + cnt; to <= WECHO - cnt; to++, from++)
|
|
vtube[to] = tlines[from];
|
|
for (from = ZERO; to <= WECHO; to++, from++) {
|
|
vtube[to] = tlines[from];
|
|
vclrcell(vtube[to], WCOLS);
|
|
}
|
|
for (from = 0; from <= vcnt; from++)
|
|
LINE(from) -= cnt;
|
|
}
|
|
|
|
/*
|
|
* Discard logical lines due to physical wandering off the screen.
|
|
*/
|
|
void
|
|
vscrap(void)
|
|
{
|
|
register int i, j;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vscrap\n"), tvliny();
|
|
#endif
|
|
if (splitw)
|
|
return;
|
|
if (vcnt && WBOT != WECHO && LINE(0) < WTOP && LINE(0) >= ZERO) {
|
|
WTOP = LINE(0);
|
|
WLINES = WBOT - WTOP + 1;
|
|
}
|
|
for (j = 0; j < vcnt; j++)
|
|
if (LINE(j) >= WTOP) {
|
|
if (j == 0)
|
|
break;
|
|
/*
|
|
* Discard the first j physical lines off the top.
|
|
*/
|
|
vcnt -= j, vcline -= j;
|
|
for (i = 0; i <= vcnt; i++)
|
|
vlcopy(vlinfo[i], vlinfo[i + j]);
|
|
break;
|
|
}
|
|
/*
|
|
* Discard lines off the bottom.
|
|
*/
|
|
if (vcnt) {
|
|
for (j = 0; j <= vcnt; j++)
|
|
if (LINE(j) > WBOT || LINE(j) + DEPTH(j) - 1 > WBOT) {
|
|
vcnt = j;
|
|
break;
|
|
}
|
|
LASTLINE = LINE(vcnt-1) + DEPTH(vcnt-1);
|
|
}
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tvliny();
|
|
#endif
|
|
/*
|
|
* May have no lines!
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Repaint the screen, with cursor at curs, aftern an arbitrary change.
|
|
* Handle notification on large changes.
|
|
*/
|
|
void
|
|
vrepaint(char *curs)
|
|
{
|
|
|
|
wdot = NOLINE;
|
|
/*
|
|
* In open want to notify first.
|
|
*/
|
|
noteit(0);
|
|
vscrap();
|
|
|
|
/*
|
|
* Deal with a totally useless display.
|
|
*/
|
|
if (vcnt == 0 || vcline < 0 || vcline > vcnt || holdupd && state != VISUAL) {
|
|
register line *odol = dol;
|
|
|
|
vcnt = 0;
|
|
if (holdupd)
|
|
if (state == VISUAL)
|
|
ignore(peekkey());
|
|
else
|
|
vup1();
|
|
holdupd = 0;
|
|
if (odol == zero)
|
|
fixzero();
|
|
vcontext(dot, '.');
|
|
noteit(1);
|
|
if (noteit(1) == 0 && odol == zero) {
|
|
CATCH
|
|
error(catgets(catd, 1, 220,
|
|
"No lines in buffer"));
|
|
ENDCATCH
|
|
linebuf[0] = 0;
|
|
splitw = 0;
|
|
}
|
|
vnline(curs);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Have some useful displayed text; refresh it.
|
|
*/
|
|
getDOT();
|
|
|
|
/*
|
|
* This is for boundary conditions in open mode.
|
|
*/
|
|
if (FLAGS(0) & VDIRT)
|
|
vsync(WTOP);
|
|
|
|
/*
|
|
* If the current line is after the last displayed line
|
|
* or the bottom of the screen, then special effort is needed
|
|
* to get it on the screen. We first try a redraw at the
|
|
* last line on the screen, hoping it will fill in where @
|
|
* lines are now. If this doesn't work, then roll it onto
|
|
* the screen.
|
|
*/
|
|
if (vcline >= vcnt || LINE(vcline) > WBOT) {
|
|
short oldhold = hold;
|
|
hold |= HOLDAT, vredraw(LASTLINE), hold = oldhold;
|
|
if (vcline >= vcnt) {
|
|
register int i = vcline - vcnt + 1;
|
|
|
|
dot -= i;
|
|
vcline -= i;
|
|
vroll(i);
|
|
} else
|
|
vsyncCL();
|
|
} else
|
|
vsync(vcline > 0 ? LINE(vcline - 1) : WTOP);
|
|
|
|
/*
|
|
* Notification on large change for visual
|
|
* has to be done last or we may lose
|
|
* the echo area with redisplay.
|
|
*/
|
|
noteit(1);
|
|
|
|
/*
|
|
* Finally. Move the cursor onto the current line.
|
|
*/
|
|
vnline(curs);
|
|
}
|
|
|
|
/*
|
|
* Fully cleanup the screen, leaving no @ lines except at end when
|
|
* line after last won't completely fit. The routine vsync is
|
|
* more conservative and much less work on dumb terminals.
|
|
*/
|
|
void
|
|
vredraw(register int p)
|
|
{
|
|
register int l;
|
|
register line *tp;
|
|
char temp[LBSIZE];
|
|
bool anydl = 0;
|
|
short oldhold = hold;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vredraw(%d)\n", p), tvliny();
|
|
#endif
|
|
if (holdupd) {
|
|
holdupd = 3;
|
|
return;
|
|
}
|
|
if (state == HARDOPEN || splitw)
|
|
return;
|
|
if (p < 0 /* || p > WECHO */)
|
|
error(catgets(catd, 1, 221, "Internal error: vredraw"));
|
|
|
|
/*
|
|
* Trim the ragged edges (lines which are off the screen but
|
|
* not yet logically discarded), save the current line, and
|
|
* search for first logical line affected by the redraw.
|
|
*/
|
|
vscrap();
|
|
CP(temp, linebuf);
|
|
l = 0;
|
|
tp = dot - vcline;
|
|
if (vcnt == 0)
|
|
LINE(0) = WTOP;
|
|
while (l < vcnt && LINE(l) < p)
|
|
l++, tp++;
|
|
|
|
/*
|
|
* We hold off echo area clearing during the redraw in deference
|
|
* to a final clear of the echo area at the end if appropriate.
|
|
*/
|
|
heldech = 0;
|
|
hold |= HOLDECH;
|
|
for (; l < vcnt && Peekkey != ATTN; l++) {
|
|
if (l == vcline)
|
|
strcLIN(temp);
|
|
else
|
|
getline(*tp);
|
|
|
|
/*
|
|
* Delete junk between displayed lines.
|
|
*/
|
|
if (LINE(l) != LINE(l + 1) && LINE(l) != p) {
|
|
if (anydl == 0 && DB && CD) {
|
|
hold = oldhold;
|
|
vclrech(0);
|
|
anydl = 1;
|
|
hold |= HOLDECH;
|
|
heldech = 0;
|
|
}
|
|
vdellin(p, LINE(l) - p, l);
|
|
}
|
|
|
|
/*
|
|
* If line image is not know to be up to date, then
|
|
* redisplay it; else just skip onward.
|
|
*/
|
|
LINE(l) = p;
|
|
if (FLAGS(l) & VDIRT) {
|
|
DEPTH(l) = vdepth();
|
|
if (l != vcline && p + DEPTH(l) - 1 > WBOT) {
|
|
vscrap();
|
|
break;
|
|
}
|
|
FLAGS(l) &= ~VDIRT;
|
|
vreopen(p, lineno(tp), l);
|
|
p = LINE(l) + DEPTH(l);
|
|
} else
|
|
p += DEPTH(l);
|
|
tp++;
|
|
}
|
|
|
|
/*
|
|
* That takes care of lines which were already partially displayed.
|
|
* Now try to fill the rest of the screen with text.
|
|
*/
|
|
if (state == VISUAL && p <= WBOT) {
|
|
int ovcline = vcline;
|
|
|
|
vcline = l;
|
|
for (; tp <= dol && Peekkey != ATTN; tp++) {
|
|
getline(*tp);
|
|
if (p + vdepth() - 1 > WBOT)
|
|
break;
|
|
vopen(tp, p);
|
|
p += DEPTH(vcline);
|
|
vcline++;
|
|
}
|
|
vcline = ovcline;
|
|
}
|
|
|
|
/*
|
|
* Thats all the text we can get on.
|
|
* Now rest of lines (if any) get either a ~ if they
|
|
* are past end of file, or an @ if the next line won't fit.
|
|
*/
|
|
for (; p <= WBOT && Peekkey != ATTN; p++)
|
|
vclrlin(p, tp);
|
|
strcLIN(temp);
|
|
hold = oldhold;
|
|
if (heldech)
|
|
vclrech(0);
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tvliny();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Do the real work in deleting cnt lines starting at line p from
|
|
* the display. First affected line is line l.
|
|
*/
|
|
void
|
|
vdellin(int p, int cnt, int l)
|
|
{
|
|
register int i;
|
|
|
|
if (cnt == 0)
|
|
return;
|
|
if (DL == NOSTR || cnt < 0) {
|
|
/*
|
|
* Can't do it; just remember that line l is munged.
|
|
*/
|
|
FLAGS(l) |= VDIRT;
|
|
return;
|
|
}
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vdellin(%d, %d, %d)\n", p, cnt, l);
|
|
#endif
|
|
/*
|
|
* Send the deletes to the screen and then adjust logical
|
|
* and physical internal data structures.
|
|
*/
|
|
vgoto(p, 0);
|
|
if (DL_PARM && (cnt>1 || *DL==0)) {
|
|
vputp(tgoto(DL_PARM, p, cnt), WECHO-p);
|
|
}
|
|
else if (xCS && *DL==0) {
|
|
/* vt100: fake DL by changing scrolling region */
|
|
vputp(SC, 1); /* Save since xCS homes stupid cursor */
|
|
vputp(tgoto(xCS, TLINES-1, p), 1);
|
|
vputp(tgoto(CM, 0, TLINES-1), 1);/* Go to lower left corner */
|
|
for (i=0; i<cnt; i++) /* .. and scroll cnt times */
|
|
putch('\n'); /* should check NL too */
|
|
vputp(tgoto(xCS, TLINES-1, 0), 1);/* restore scrolling region */
|
|
vputp(RC, 1); /* put cursor back */
|
|
}
|
|
else {
|
|
for (i = 0; i < cnt; i++)
|
|
vputp(DL, WECHO - p);
|
|
}
|
|
vadjDL(p, cnt);
|
|
vcloseup(l, cnt);
|
|
}
|
|
/*
|
|
* Adjust internal physical screen image to account for deleted lines.
|
|
*/
|
|
void
|
|
vadjDL(int p, int cnt)
|
|
{
|
|
cell *tlines[TUBELINES];
|
|
register int from, to;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vadjDL(%d, %d)\n", p, cnt);
|
|
#endif
|
|
/*
|
|
* Would like to use structured assignment but early
|
|
* v7 compiler (released with phototypesetter for v6)
|
|
* can't hack it.
|
|
*/
|
|
copy(tlines, vtube, sizeof vtube); /*SASSIGN*/
|
|
for (from = p + cnt, to = p; from <= WECHO; from++, to++)
|
|
vtube[to] = tlines[from];
|
|
for (from = p; to <= WECHO; from++, to++) {
|
|
vtube[to] = tlines[from];
|
|
vclrcell(vtube[to], WCOLS);
|
|
}
|
|
}
|
|
/*
|
|
* Sync the screen, like redraw but more lazy and willing to leave
|
|
* @ lines on the screen. VsyncCL syncs starting at the current line.
|
|
* In any case, if the redraw option is set then all syncs map to redraws
|
|
* as if vsync didn't exist.
|
|
*/
|
|
void
|
|
vsyncCL(void)
|
|
{
|
|
|
|
vsync(LINE(vcline));
|
|
}
|
|
|
|
void
|
|
vsync(register int p)
|
|
{
|
|
|
|
if (value(REDRAW))
|
|
vredraw(p);
|
|
else
|
|
vsync1(p);
|
|
}
|
|
|
|
/*
|
|
* The guts of a sync. Similar to redraw but
|
|
* just less ambitous.
|
|
*/
|
|
void
|
|
vsync1(register int p)
|
|
{
|
|
register int l;
|
|
char temp[LBSIZE];
|
|
register struct vlinfo *vp = &vlinfo[0];
|
|
short oldhold = hold;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vsync1(%d)\n", p), tvliny();
|
|
#endif
|
|
if (holdupd) {
|
|
if (holdupd < 3)
|
|
holdupd = 2;
|
|
return;
|
|
}
|
|
if (state == HARDOPEN || splitw)
|
|
return;
|
|
vscrap();
|
|
CP(temp, linebuf);
|
|
if (vcnt == 0)
|
|
LINE(0) = WTOP;
|
|
l = 0;
|
|
while (l < vcnt && vp->vliny < p)
|
|
l++, vp++;
|
|
heldech = 0;
|
|
hold |= HOLDECH;
|
|
while (p <= WBOT && Peekkey != ATTN) {
|
|
/*
|
|
* Want to put a line here if not in visual and first line
|
|
* or if there are lies left and this line starts before
|
|
* the current line, or if this line is piled under the
|
|
* next line (vreplace does this and we undo it).
|
|
*/
|
|
if (l == 0 && state != VISUAL ||
|
|
(l < vcnt && (vp->vliny <= p || vp[0].vliny == vp[1].vliny))) {
|
|
if (l == 0 || vp->vliny < p || (vp->vflags & VDIRT)) {
|
|
if (l == vcline)
|
|
strcLIN(temp);
|
|
else
|
|
getline(dot[l - vcline]);
|
|
/*
|
|
* Be careful that a long line doesn't cause the
|
|
* screen to shoot up.
|
|
*/
|
|
if (l != vcline && (vp->vflags & VDIRT)) {
|
|
vp->vdepth = vdepth();
|
|
vp->vflags &= ~VDIRT;
|
|
if (p + vp->vdepth - 1 > WBOT)
|
|
break;
|
|
}
|
|
vreopen(p, lineDOT() + (l - vcline), l);
|
|
}
|
|
p = vp->vliny + vp->vdepth;
|
|
vp++;
|
|
l++;
|
|
} else
|
|
/*
|
|
* A physical line between logical lines,
|
|
* so we settle for an @ at the beginning.
|
|
*/
|
|
vclrlin(p, dot + (l - vcline)), p++;
|
|
}
|
|
strcLIN(temp);
|
|
hold = oldhold;
|
|
if (heldech)
|
|
vclrech(0);
|
|
}
|
|
|
|
/*
|
|
* Subtract (logically) cnt physical lines from the
|
|
* displayed position of lines starting with line l.
|
|
*/
|
|
void
|
|
vcloseup(int l, register int cnt)
|
|
{
|
|
register int i;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tfixnl(), fprintf(trace, "vcloseup(%d, %d)\n", l, cnt);
|
|
#endif
|
|
for (i = l + 1; i <= vcnt; i++)
|
|
LINE(i) -= cnt;
|
|
}
|
|
|
|
/*
|
|
* Workhorse for rearranging line descriptors on changes.
|
|
* The idea here is that, starting with line l, cnt lines
|
|
* have been replaced with newcnt lines. All of these may
|
|
* be ridiculous, i.e. l may be -1000, cnt 50 and newcnt 0,
|
|
* since we may be called from an undo after the screen has
|
|
* moved a lot. Thus we have to be careful.
|
|
*
|
|
* Many boundary conditions here.
|
|
*/
|
|
void
|
|
vreplace(int l, int cnt, int newcnt)
|
|
{
|
|
register int from, to, i;
|
|
bool savenote = 0;
|
|
|
|
#ifdef ADEBUG
|
|
if (trace) {
|
|
tfixnl(), fprintf(trace, "vreplace(%d, %d, %d)\n", l, cnt, newcnt);
|
|
tvliny();
|
|
}
|
|
#endif
|
|
if (l >= vcnt)
|
|
return;
|
|
if (l < 0) {
|
|
if (l + cnt < 0) {
|
|
/*
|
|
* Nothing on the screen is relevant.
|
|
* Settle for redrawing from scratch (later).
|
|
*/
|
|
vcnt = 0;
|
|
return;
|
|
}
|
|
/*
|
|
* Normalize l to top of screen; the add is
|
|
* really a subtract from cnt since l is negative.
|
|
*/
|
|
cnt += l;
|
|
l = 0;
|
|
|
|
/*
|
|
* Unseen lines were affect so notify (later).
|
|
*/
|
|
savenote++;
|
|
}
|
|
|
|
/*
|
|
* These shouldn't happen
|
|
* but would cause great havoc.
|
|
*/
|
|
if (cnt < 0)
|
|
cnt = 0;
|
|
if (newcnt < 0)
|
|
newcnt = 0;
|
|
|
|
/*
|
|
* Surely worthy of note if more than report
|
|
* lines were changed.
|
|
*/
|
|
if (cnt > value(REPORT) || newcnt > value(REPORT))
|
|
savenote++;
|
|
|
|
/*
|
|
* Same number of lines affeted as on screen, and we
|
|
* can insert and delete lines. Thus we just type
|
|
* over them, since otherwise we will push them
|
|
* slowly off the screen, a clear lose.
|
|
*/
|
|
if (cnt == newcnt || vcnt - l == newcnt && AL && DL) {
|
|
if (cnt > 1 && l + cnt > vcnt)
|
|
savenote++;
|
|
vdirty(l, newcnt);
|
|
} else {
|
|
/*
|
|
* Lines are going away, squish them out.
|
|
*/
|
|
if (cnt > 0) {
|
|
/*
|
|
* If non-displayed lines went away,
|
|
* always notify.
|
|
*/
|
|
if (cnt > 1 && l + cnt > vcnt)
|
|
savenote++;
|
|
if (l + cnt >= vcnt)
|
|
cnt = vcnt - l;
|
|
else
|
|
for (from = l + cnt, to = l; from <= vcnt; to++, from++)
|
|
vlcopy(vlinfo[to], vlinfo[from]);
|
|
vcnt -= cnt;
|
|
}
|
|
/*
|
|
* Open up space for new lines appearing.
|
|
* All new lines are piled in the same place,
|
|
* and will be unpiled by vredraw/vsync, which
|
|
* inserts lines in front as it unpiles.
|
|
*/
|
|
if (newcnt > 0) {
|
|
/*
|
|
* Newlines are appearing which may not show,
|
|
* so notify (this is only approximately correct
|
|
* when long lines are present).
|
|
*/
|
|
if (newcnt > 1 && l + newcnt > vcnt + 1)
|
|
savenote++;
|
|
|
|
/*
|
|
* If there will be more lines than fit, then
|
|
* just throw way the rest of the stuff on the screen.
|
|
*/
|
|
if (l + newcnt > WBOT && AL && DL) {
|
|
vcnt = l;
|
|
goto skip;
|
|
}
|
|
from = vcnt, to = vcnt + newcnt;
|
|
i = TUBELINES - to;
|
|
if (i < 0)
|
|
from += i, to += i;
|
|
vcnt = to;
|
|
for (; from >= l; from--, to--)
|
|
vlcopy(vlinfo[to], vlinfo[from]);
|
|
for (from = to + 1, to = l; to < l + newcnt && to <= WBOT + 1; to++) {
|
|
LINE(to) = LINE(from);
|
|
DEPTH(to) = 0;
|
|
FLAGS(to) = VDIRT;
|
|
}
|
|
}
|
|
}
|
|
skip:
|
|
if (Pline == numbline && cnt != newcnt)
|
|
/*
|
|
* When lines positions are shifted, the numbers
|
|
* will be wrong.
|
|
*/
|
|
vdirty(l, WECHO);
|
|
if (!savenote)
|
|
notecnt = 0;
|
|
#ifdef ADEBUG
|
|
if (trace)
|
|
tvliny();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Start harcopy open.
|
|
* Print an image of the line to the left of the cursor
|
|
* under the full print of the line and position the cursor.
|
|
* If we are in a scroll ^D within hardcopy open then all this
|
|
* is suppressed.
|
|
*/
|
|
void
|
|
sethard(void)
|
|
{
|
|
|
|
if (state == VISUAL)
|
|
return;
|
|
rubble = 0;
|
|
state = HARDOPEN;
|
|
if (hold & HOLDROL)
|
|
return;
|
|
vup1();
|
|
LINE(0) = WBOT;
|
|
if (Pline == numbline)
|
|
vgoto(WBOT, 0), printf("%6d ", lineDOT());
|
|
}
|
|
|
|
/*
|
|
* Mark the lines starting at base for i lines
|
|
* as dirty so that they will be checked for correct
|
|
* display at next sync/redraw.
|
|
*/
|
|
void
|
|
vdirty(register int base, register int i)
|
|
{
|
|
register int l;
|
|
|
|
for (l = base; l < vcnt; l++) {
|
|
if (--i < 0)
|
|
return;
|
|
FLAGS(l) |= VDIRT;
|
|
}
|
|
}
|