* 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_smap.c 8.1 (Berkeley) 6/9/93";
* Make a change to the screen.
svi_change(sp
, ep
, lno
, op
)
/* Appending is the same as inserting, if the line is incremented. */
/* Ignore the change if the line is after the map. */
* If the line is before the map, and it's a decrement, decrement
* the map. If it's an increment, increment the map. Otherwise,
for (p
= HMAP
; p
<= TMAP
; ++p
)
else if (op
== LINE_INSERT
)
for (p
= HMAP
; p
<= TMAP
; ++p
)
/* Invalidate the cursor, if it's on this line. */
F_SET(sp
, S_CUR_INVALID
);
getyx(stdscr
, oldy
, oldx
);
if (svi_sm_delete(sp
, ep
, lno
))
if (svi_sm_insert(sp
, ep
, lno
))
if (svi_sm_reset(sp
, ep
, lno
))
* Fill in the screen map, placing the specified line at the
svi_sm_fill(sp
, ep
, lno
, pos
)
/* See if less than half a screen from the top. */
if (svi_sm_nlines(sp
, ep
,
&tmp
, lno
, HALFSCREEN(sp
)) <= HALFSCREEN(sp
)) {
/* See if less than half a screen from the bottom. */
if (file_lline(sp
, ep
, &tmp
.lno
))
tmp
.off
= svi_screens(sp
, ep
, tmp
.lno
, NULL
);
if (svi_sm_nlines(sp
, ep
,
&tmp
, lno
, HALFSCREEN(sp
)) <= HALFSCREEN(sp
)) {
/* If we fail, just punt. */
top
: for (p
= HMAP
, p
->lno
= lno
, p
->off
= 1; p
< TMAP
; ++p
)
if (svi_sm_next(sp
, ep
, p
, p
+ 1))
/* If we fail, guess that the file is too small. */
middle
: p
= HMAP
+ (TMAP
- HMAP
) / 2;
for (p
->lno
= lno
, p
->off
= 1; p
> HMAP
; --p
)
if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
/* If we fail, just punt. */
p
= HMAP
+ (TMAP
- HMAP
) / 2;
if (svi_sm_next(sp
, ep
, p
, p
+ 1))
/* If we fail, guess that the file is too small. */
TMAP
->off
= svi_screens(sp
, ep
, lno
, NULL
);
bottom
: for (p
= TMAP
; p
> HMAP
; --p
)
if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
* Try and put *something* on the screen. If this fails,
* we have a serious hard error.
for (p
= HMAP
; p
< TMAP
; ++p
)
if (svi_sm_next(sp
, ep
, p
, p
+ 1))
* For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
* screen only contains one line, or, if the line is the entire screen, this
* gets fairly exciting. Skip the fun and simply return if there's only one
* line in the screen, or just call fill. Fill may not be entirely accurate,
* i.e. we may be painting the screen with something not even close to the
* cursor, but it's not like we're into serious performance issues here, and
* the refresh routine will fix it for us.
if (cnt_orig >= sp->t_rows) { \
if (file_gline(sp, ep, lno, NULL) == NULL) \
if (file_lline(sp, ep, &lno)) \
return (svi_sm_fill(sp, ep, lno, P_TOP)); \
* Delete a line out of the SMAP.
svi_sm_delete(sp
, ep
, lno
)
* Find the line in the map, and count the number of screen lines
* which display any part of the deleted line.
for (p
= HMAP
; p
->lno
!= lno
; ++p
);
for (cnt_orig
= 1, t
= p
+ 1;
t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
);
/* Delete that many lines from the screen. */
if (svi_deleteln(sp
, cnt_orig
))
/* Shift the screen map up. */
memmove(p
, p
+ cnt_orig
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
/* Decrement the line numbers for the rest of the map. */
for (t
= TMAP
- cnt_orig
; p
<= t
; ++p
)
/* Display the new lines. */
for (p
= TMAP
- cnt_orig
;;) {
if (p
< TMAP
&& svi_sm_next(sp
, ep
, p
, p
+ 1))
if (svi_line(sp
, ep
, ++p
, NULL
, 0, NULL
, NULL
))
* Insert a line into the SMAP.
svi_sm_insert(sp
, ep
, lno
)
* Find the line in the map, find out how many screen lines
* needed to display the line.
for (p
= HMAP
; p
->lno
!= lno
; ++p
);
cnt_orig
= svi_screens(sp
, ep
, lno
, NULL
);
* The lines left in the screen override the number of screen
* lines in the inserted line.
/* Push down that many lines. */
if (svi_insertln(sp
, cnt_orig
))
/* Shift the screen map down. */
memmove(p
+ cnt_orig
, p
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
/* Increment the line numbers for the rest of the map. */
for (t
= p
+ cnt_orig
; t
<= TMAP
; ++t
)
/* Fill in the SMAP for the new lines, and display. */
for (cnt
= 1, t
= p
; cnt
<= cnt_orig
; ++t
, ++cnt
) {
if (svi_line(sp
, ep
, t
, NULL
, 0, NULL
, NULL
))
* Reset a line in the SMAP.
svi_sm_reset(sp
, ep
, lno
)
size_t cnt_orig
, cnt_new
, cnt
, diff
;
* See if the number of on-screen rows taken up by the old display
* for the line is the same as the number needed for the new one.
* If so, repaint, otherwise do it the hard way.
for (p
= HMAP
; p
->lno
!= lno
; ++p
);
for (cnt_orig
= 0, t
= p
;
t
->lno
== lno
&& t
<= TMAP
; ++cnt_orig
, ++t
);
cnt_new
= svi_screens(sp
, ep
, lno
, NULL
);
if (cnt_orig
== cnt_new
) {
if (svi_line(sp
, ep
, p
, NULL
, 0, NULL
, NULL
))
if (cnt_orig
< cnt_new
) {
/* Get the difference. */
diff
= cnt_new
- cnt_orig
;
* The lines left in the screen override the number of screen
* lines in the inserted line.
/* Push down the extra lines. */
if (svi_insertln(sp
, diff
))
/* Shift the screen map down. */
memmove(p
+ diff
, p
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
/* Fill in the SMAP for the replaced line, and display. */
for (cnt
= 1, t
= p
; cnt_new
-- && t
<= TMAP
; ++t
, ++cnt
) {
if (svi_line(sp
, ep
, t
, NULL
, 0, NULL
, NULL
))
/* Get the difference. */
diff
= cnt_orig
- cnt_new
;
/* Delete that many lines from the screen. */
if (svi_deleteln(sp
, diff
))
/* Shift the screen map up. */
memmove(p
, p
+ diff
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
/* Fill in the SMAP for the replaced line, and display. */
for (cnt
= 1, t
= p
; cnt_new
--; ++t
, ++cnt
) {
if (svi_line(sp
, ep
, t
, NULL
, 0, NULL
, NULL
))
/* Display the new lines at the bottom of the screen. */
for (t
= TMAP
- diff
;;) {
if (t
< TMAP
&& svi_sm_next(sp
, ep
, t
, t
+ 1))
if (svi_line(sp
, ep
, ++t
, NULL
, 0, NULL
, NULL
))
* Scroll the SMAP up count logical lines.
svi_sm_up(sp
, ep
, rp
, count
, cursor_move
)
/* Set the default return position. */
* There are two forms of this command, one where the cursor follows
* the line, and one where it doesn't. In the latter, we try and keep
* the cursor at the same position on the screen, but, if the screen
* is small enough and the line length large enough, the cursor can
* end up in very strange places. Probably not worth fixing.
* Find the line in the SMAP.
"Line %lu not on the screen.", sp
->lno
);
if (file_lline(sp
, ep
, &last
))
for (svmap
= *p
, scrolled
= 0;; scrolled
= 1) {
/* Decide what would show up on the screen. */
if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
/* If the line doesn't exist, we're done. */
/* Scroll up one logical line. */
if (!cursor_move
&& p
> HMAP
)
* If didn't move enough lines, it's an error if we're at the
* EOF, else move there. Otherwise, try and place the cursor
* roughly where it was before.
if (!scrolled
|| count
) {
* If the line itself moved, invalidate the cursor, because
* the comparison with the old line/new line won't be right
F_SET(sp
, S_CUR_INVALID
);
/* It's an error if we didn't scroll enough. */
if (!scrolled
|| count
) {
/* If the cursor moved off the screen, move it to the top. */
* On a logical movement, we try and keep the cursor as close as
* possible to the last position, but also set it up so that the
* next "real" movement will return the cursor to the closest position
* to the last real movement.
if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
* Scroll the SMAP up one.
* Delete the top line of the screen. Shift the screen map up.
* Display a new line at the bottom of the screen.
/* One-line screens can fail. */
if (svi_sm_next(sp
, ep
, TMAP
, TMAP
))
memmove(HMAP
, HMAP
+ 1, sp
->rows
* sizeof(SMAP
));
if (svi_sm_next(sp
, ep
, TMAP
- 1, TMAP
))
if (svi_line(sp
, ep
, TMAP
, NULL
, 0, NULL
, NULL
))
* Delete a line a la curses, make sure to put the information
* line and other screens back.
getyx(stdscr
, oldy
, oldx
);
MOVE(sp
, INFOLINE(sp
) - 1, 0);
* Scroll the SMAP down count logical lines.
svi_sm_down(sp
, ep
, rp
, count
, cursor_move
)
/* Set the default return position. */
* There are two forms of this command, one where the cursor follows
* the line, and one where it doesn't. In the latter, we try and keep
* the cursor at the same position on the screen, but, if the screen
* is small enough and the line length large enough, the cursor can
* end up in very strange places. Probably not worth fixing.
* Find the line in the SMAP.
"Line %lu not on the screen", sp
->lno
);
for (svmap
= *p
, scrolled
= 0;; scrolled
= 1) {
/* If the line doesn't exist, we're done. */
if (HMAP
->lno
== 1 && HMAP
->off
== 1)
/* Scroll down one logical line. */
if (svi_sm_1down(sp
, ep
))
if (!cursor_move
&& p
< TMAP
)
* If didn't move enough lines, it's an error if we're at the
* SOF, else move there. Otherwise, try and place the cursor
* roughly where it was before.
if (!scrolled
|| count
) {
if (sp
->lno
== HMAP
->lno
) {
* If the line itself moved, invalidate the cursor, because
* the comparison with the old line/new line won't be right
F_SET(sp
, S_CUR_INVALID
);
/* It's an error if we didn't scroll enough. */
if (!scrolled
|| count
) {
/* If the cursor moved off the screen, move it to the bottom. */
* On a logical movement, we try and keep the cursor as close as
* possible to the last position, but also set it up so that the
* next "real" movement will return the cursor to the closest position
* to the last real movement.
if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
* Scroll the SMAP down one.
* Clear the bottom line of the screen, insert a line at the top
* of the screen. Shift the screen map down, display a new line
* at the top of the screen.
memmove(HMAP
+ 1, HMAP
, sp
->rows
* sizeof(SMAP
));
if (svi_sm_prev(sp
, ep
, HMAP
+ 1, HMAP
))
if (svi_line(sp
, ep
, HMAP
, NULL
, 0, NULL
, NULL
))
* Insert a line a la curses, make sure to put the information
* line and other screens back.
getyx(stdscr
, oldy
, oldx
);
MOVE(sp
, INFOLINE(sp
) - 1, 0);
* Fill in the next entry in the SMAP.
svi_sm_next(sp
, ep
, p
, t
)
if (O_ISSET(sp
, O_LEFTRIGHT
)) {
lcnt
= svi_screens(sp
, ep
, p
->lno
, NULL
);
* Fill in the previous entry in the SMAP.
svi_sm_prev(sp
, ep
, p
, t
)
if (O_ISSET(sp
, O_LEFTRIGHT
)) {
} else if (p
->off
!= 1) {
t
->off
= svi_screens(sp
, ep
, t
->lno
, NULL
);
* Return the line number of the top, middle or last line on the screen.
* (The vi H, M and L commands.) Here because only the screen routines
* know what's really out there.
svi_sm_position(sp
, ep
, lnop
, cnt
, pos
)
* Note, the top line number may not be at the top of the
* screen, because we search for a line that starts on the
* screen. It works that way because that's how the historic
* Set t to point at the map entry one past the last legal
if (file_lline(sp
, ep
, &last
))
for (t
= HMAP
; t
->lno
<= last
; ++t
);
/* Step past cnt start-of-lines, stopping at t. */
for (p
= HMAP
- 1; cnt
; --cnt
)
"No such line on the screen.");
* Note, the middle line number may not be anywhere near the
* middle of the screen, because that's how the historic vi
* Check for less than a full screen of lines.
if (file_lline(sp
, ep
, &last
))
down
= (last
- HMAP
->lno
+ 1) / 2;
if (down
== 0 && HMAP
->off
!= 1) {
msgq(sp
, M_ERR
, "No such line on the screen.");
*lnop
= HMAP
->lno
+ down
;
/* Set p to point at the last legal entry in the map. */
if (file_lline(sp
, ep
, &last
))
for (p
= HMAP
; p
->lno
<= last
; ++p
);
/* Step past cnt start-of-lines, stopping at HMAP. */
"No such line on the screen.");
* Return the number of screen lines from an SMAP entry to the
* start of some file line, less than a maximum value.
svi_sm_nlines(sp
, ep
, from_sp
, to_lno
, max
)
if (O_ISSET(sp
, O_LEFTRIGHT
))
if (from_sp
->lno
> to_lno
)
return (from_sp
->lno
- to_lno
);
return (to_lno
- from_sp
->lno
);
if (from_sp
->lno
== to_lno
)
return (from_sp
->off
- 1);
if (from_sp
->lno
> to_lno
) {
lcnt
= from_sp
->off
- 1; /* Correct for off-by-one. */
for (lno
= from_sp
->lno
; --lno
>= to_lno
&& lcnt
<= max
;)
lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);
lcnt
= (svi_screens(sp
, ep
, lno
, NULL
) - from_sp
->off
) + 1;
for (; ++lno
< to_lno
&& lcnt
<= max
;)
lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);