* Copyright (c) 1992, 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
* 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
static char sccsid
[] = "@(#)svi_refresh.c 8.1 (Berkeley) 6/9/93";
static int svi_modeline
__P((SCR
*, EXF
*));
static int svi_msgflush
__P((SCR
*));
* This is the guts of the vi curses screen code. The idea is that
* the SCR structure passed in contains the new coordinates of the
* screen. What makes this hard is that we don't know how big
* characters are, doing input can put the cursor in illegal places,
* and we're frantically trying to avoid repainting unless it's
* absolutely necessary. If you change this code, you'd better know
* what you're doing. It's subtle and quick to anger.
size_t cwtotal
, cnt
, len
, x
, y
;
* Notice that a resize is requested, and set up everything so that
* the file gets reinitialized. Done here, instead of in the vi
* loop because there may be other initialization that other screens
* need to do. The actual changing of the row/column values was done
* by calling the ex options code which put them into the environment,
* which is used by curses. Stupid, but ugly.
if (F_ISSET(sp
, S_RESIZE
)) {
F_SET(sp
, S_SSWITCH
| S_REFORMAT
| S_REDRAW
);
* If the lines themselves have changed (:set list, for example),
* fill in the map from scratch. Adjust the screen that's being
* displayed if the leftright flag is set.
if (F_ISSET(sp
, S_REFORMAT
)) {
if (svi_sm_fill(sp
, ep
, HMAP
->lno
, P_TOP
))
if (O_ISSET(sp
, O_LEFTRIGHT
) &&
(cnt
= svi_screens(sp
, ep
, LNO
, &CNO
)) != 1)
for (smp
= HMAP
; smp
<= TMAP
; ++smp
)
* Line changes can cause the top line to change as well. As
* before, if the movement is large, the screen is repainted.
* Tiny screens cannot be permitted into the "scrolling" parts of
* the smap code for two reasons. If the screen size is 1 line,
* HMAP == TMAP and the code will quickly drop core. If the screen
* size is 2, none of the divisions by 2 will work, and scrolling
* won't work. In fact, because no line change will be less than
* HALFSCREEN(sp), we always ending up "filling" the map, with a
* P_MIDDLE flag, which isn't what the user wanted. Tiny screens
* can go into the "fill" portions of the smap code, no problem.
if (svi_sm_fill(sp
, ep
, LNO
, P_TOP
))
} else if (LNO
> TMAP
->lno
)
if (svi_sm_fill(sp
, ep
, LNO
, P_BOTTOM
))
HMAP
->off
= svi_screens(sp
, ep
, LNO
, &CNO
);
* If less than half a screen away, scroll down until the
lcnt
= svi_sm_nlines(sp
, ep
, TMAP
, LNO
, HALFSCREEN(sp
));
if (lcnt
< HALFSCREEN(sp
)) {
* If less than a full screen from the bottom of the file, put
* the last line of the file on the bottom of the screen. The
* calculation is safe because we know there's at least one
* full screen of lines, otherwise couldn't have gotten here.
if (file_lline(sp
, ep
, &lastline
))
lcnt
= svi_sm_nlines(sp
, ep
, &tmp
, lastline
, sp
->t_rows
);
if (svi_sm_fill(sp
, ep
, lastline
, P_BOTTOM
))
* If more than a full screen from the last line of the file,
* put the new line in the middle of the screen.
* If less than half a screen away, scroll up until the line is
* the first line on the screen.
lcnt
= svi_sm_nlines(sp
, ep
, HMAP
, LNO
, HALFSCREEN(sp
));
if (lcnt
< HALFSCREEN(sp
)) {
if (svi_sm_1down(sp
, ep
))
* If less than half a screen from the top of the file, put the first
* line of the file at the top of the screen. Otherwise, put the line
* in the middle of the screen.
lcnt
= svi_sm_nlines(sp
, ep
, &tmp
, LNO
, HALFSCREEN(sp
));
if (lcnt
< HALFSCREEN(sp
)) {
if (svi_sm_fill(sp
, ep
, 1, P_TOP
))
middle
: if (svi_sm_fill(sp
, ep
, LNO
, P_MIDDLE
))
* At this point we know part of the line is on the screen. Since
* scrolling is done using logical lines, not physical, all of the
* line may not be on the screen. While that's not necessarily bad,
* if the part the cursor is on isn't there, we're going to lose.
* This can be tricky; if the line covers the entire screen, lno
* may be the same as both ends of the map. This isn't a problem
* for left-right scrolling, the cursor movement code handles the
* There's a real performance issue here if editing *really* long
* lines. This gets to the right spot by scrolling, and, in a
* binary, by scrolling hundreds of lines.
adjust
: if (!O_ISSET(sp
, O_LEFTRIGHT
))
cnt
= svi_screens(sp
, ep
, LNO
, &CNO
);
if (svi_sm_1down(sp
, ep
))
} else if (LNO
== TMAP
->lno
) {
cnt
= svi_screens(sp
, ep
, LNO
, &CNO
);
/* If the screen needs to be repainted, skip cursor optimization. */
if (F_ISSET(sp
, S_REDRAW
))
* Decide cursor position. If the line has changed, the cursor has
* moved over a tab, or don't know where the cursor was, reparse the
* line. Note, if we think that the cursor "hasn't moved", reparse
* the line. This is 'cause if it hasn't moved, we've almost always
* Otherwise, we've just moved over fixed-width characters, and can
* calculate the left/right scrolling and cursor movement without
* reparsing the line. Note that we don't know which (if any) of
* the characters between the old and new cursor positions changed.
* With some work, it should be possible to handle tabs quickly, at
* least in obvious situations, like moving right and encountering
* a tab, without reparsing the whole line.
* If the line we're working with has changed, reparse.
if (F_ISSET(sp
, S_CUR_INVALID
) || LNO
!= OLNO
) {
F_CLR(sp
, S_CUR_INVALID
);
* Otherwise, if nothing's changed, go fast. The one exception is
* that a single character or no characters are both column 0, and,
* if the single character required multiple screen columns, there
* may have still been movement.
* Get the current line. If this fails, we either have an empty
* file and can just repaint, or there's a real problem. This
* isn't a performance issue because there aren't any ways to get
if ((p
= file_gline(sp
, ep
, LNO
, &len
)) == NULL
) {
if (file_lline(sp
, ep
, &lastline
))
/* This is just a test. */
if (CNO
>= len
&& len
!= 0) {
msgq(sp
, M_ERR
, "Error: %s/%d: cno (%u) >= len (%u)",
tail(__FILE__
), __LINE__
, CNO
, len
);
* The basic scheme here is to look at the characters in between
* the old and new positions and decide how big they are on the
* screen, and therefore, how many screen positions to move.
* Point to the old character. The old cursor position can
* be past EOL if, for example, we just deleted the rest of
* the line. In this case, since we don't know the width of
* the characters we traversed, we have to do it slowly.
* Count up the widths of the characters. If it's a tab
* character, go do it the the slow way.
for (cwtotal
= 0; cnt
--; cwtotal
+= cname
[ch
].len
)
if ((ch
= *(u_char
*)p
--) == '\t')
* Decrement the screen cursor by the total width of the
* If we're moving left, and there's a wide character in the
* current position, go to the end of the character.
cwtotal
-= cname
[ch
].len
- 1;
* If the new column moved us out of the current screen,
* calculate a new screen.
if (O_ISSET(sp
, O_LEFTRIGHT
)) {
for (smp
= HMAP
; smp
<= TMAP
; ++smp
)
* 4b: Cursor moved right.
* Point to the first character to the right.
* Count up the widths of the characters. If it's a tab
* character, go do it the the slow way.
for (cwtotal
= 0; cnt
--; cwtotal
+= cname
[ch
].len
)
if ((ch
= *(u_char
*)p
++) == '\t')
* Increment the screen cursor by the total width of the
* If the new column moved us out of the current screen,
* calculate a new screen.
if (SCNO
>= SCREEN_COLS(sp
)) {
if (O_ISSET(sp
, O_LEFTRIGHT
)) {
for (smp
= HMAP
; smp
<= TMAP
; ++smp
)
fast
: getyx(stdscr
, y
, x
); /* Just move the cursor. */
y
-= sp
->s_off
; /* Correct for split screen. */
slow
: /* Find the current line in the map. */
for (smp
= HMAP
; smp
->lno
!= LNO
; ++smp
);
* If doing left-right scrolling, and the cursor movement has
* changed the screen being displayed, fix it.
if (!ISINFOLINE(sp
, smp
) && O_ISSET(sp
, O_LEFTRIGHT
)) {
cnt
= svi_screens(sp
, ep
, LNO
, &CNO
) % SCREEN_COLS(sp
);
for (smp
= HMAP
; smp
<= TMAP
; ++smp
)
/* Update all of the screen lines for this file line. */
for (; smp
<= TMAP
&& smp
->lno
== LNO
; ++smp
)
if (svi_line(sp
, ep
, smp
, NULL
, 0, &y
, &SCNO
))
/* Not too bad, move the cursor. */
/* Lost big, do what you have to do. */
paint
: for (smp
= HMAP
; smp
<= TMAP
; ++smp
)
if (svi_line(sp
, ep
, smp
, NULL
, 0, &y
, &SCNO
))
update
: /* Ring the bell if scheduled. */
if (F_ISSET(sp
, S_BELLSCHED
))
* If the bottom line isn't in use by vi, display any
* messages or paint the mode line.
if (!F_ISSET(&sp
->bhdr
, HDR_INUSE
))
if (sp
->msgp
!= NULL
&& !F_ISSET(sp
->msgp
, M_EMPTY
))
else if (!F_ISSET(sp
, S_UPDATE_MODE
)) {
/* Update saved information. */
/* Refresh the screen. */
if (F_ISSET(sp
, S_REFRESH
)) {
* Flush any accumulated messages.
#define MCONTMSG " [More ...]"
/* Display the messages. */
for (mp
= sp
->msgp
, p
= NULL
;
mp
!= NULL
&& !F_ISSET(mp
, M_EMPTY
); mp
= mp
->next
) {
lcont
: /* Move to the message line and clear it. */
MOVE(sp
, INFOLINE(sp
), 0);
* Turn on standout mode if requested, or, if we've split
* the screen and need a divider.
if (F_ISSET(mp
, M_INV_VIDEO
) || sp
->child
!= NULL
)
* Print up to the "more" message. Avoid the last character
* in the last line, some hardware doesn't like it.
if (svi_ncols(sp
, p
, mp
->len
, NULL
) < sp
->cols
- 1)
len
= (sp
->cols
- sizeof(MCONTMSG
)) - 1;
ADDNSTR(cname
[ch
].name
, chlen
);
/* If more, print continue message. */
mp
->next
!= NULL
&& !F_ISSET(mp
->next
, M_EMPTY
)) {
ADDNSTR(MCONTMSG
, sizeof(MCONTMSG
) - 1);
while (sp
->special
[ch
= term_key(sp
, 0)] != K_CR
&&
/* Turn off standout mode. */
if (F_ISSET(mp
, M_INV_VIDEO
) || sp
->child
!= NULL
)
#define MODESIZE (RULERSIZE + 15)
MOVE(sp
, INFOLINE(sp
), 0);
/* Display the dividing line. */
/* Display the ruler and mode. */
if (O_ISSET(sp
, O_RULER
) && sp
->cols
> RULERSIZE
+ 2) {
MOVE(sp
, INFOLINE(sp
), sp
->cols
/ 2 - RULERSIZE
/ 2);
sizeof(buf
), "%lu,%lu", sp
->lno
, sp
->cno
+ 1);
* Show the mode. Leave the last character blank, just in case
* it's a really dumb terminal with hardware scroll.
if (O_ISSET(sp
, O_SHOWMODE
) && sp
->cols
> MODESIZE
) {
MOVE(sp
, INFOLINE(sp
), sp
->cols
- 8);
s
= F_ISSET(sp
, S_INPUT
) ? " Input " : "Command ";
char buf
[DIVIDESIZE
+ 1];
dividesize
= DIVIDESIZE
> sp
->cols
? sp
->cols
: DIVIDESIZE
;
memset(buf
, ' ', dividesize
);
ADDNSTR(buf
, dividesize
);