3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: vs_smap.c,v 8.23 1993/11/03 17:15:36 bostic Exp $ (Berkeley) $Date: 1993/11/03 17:15:36 $";
12 #include <sys/types.h>
20 #include "svi_screen.h"
22 static int svi_deleteln
__P((SCR
*, int));
23 static int svi_insertln
__P((SCR
*, int));
27 * Make a change to the screen.
30 svi_change(sp
, ep
, lno
, op
)
39 /* Appending is the same as inserting, if the line is incremented. */
40 if (op
== LINE_APPEND
) {
45 /* Ignore the change if the line is after the map. */
50 * If the line is before the map, and it's a decrement, decrement
51 * the map. If it's an increment, increment the map. Otherwise,
54 if (lno
< HMAP
->lno
) {
60 for (p
= HMAP
; p
<= TMAP
; ++p
)
66 for (p
= HMAP
; p
<= TMAP
; ++p
)
77 F_SET(SVP(sp
), SVI_SCREENDIRTY
);
79 /* Flush cached information from svi_screens(). */
80 SVP(sp
)->ss_lno
= OOBLNO
;
82 /* Invalidate the cursor, if it's on this line. */
84 F_SET(SVP(sp
), SVI_CUR_INVALID
);
86 getyx(stdscr
, oldy
, oldx
);
90 if (svi_sm_delete(sp
, ep
, lno
))
94 if (svi_sm_insert(sp
, ep
, lno
))
98 if (svi_sm_reset(sp
, ep
, lno
))
105 MOVEA(sp
, oldy
, oldx
);
112 * Fill in the screen map, placing the specified line at the
113 * right position. There isn't any way to tell if an SMAP
114 * entry has been filled in, so this routine had better be
115 * called with P_FILL set before anything else is done.
118 * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
119 * slot is already filled in, P_BOTTOM means that the TMAP slot is
120 * already filled in, and we just finish up the job.
123 svi_sm_fill(sp
, ep
, lno
, pos
)
131 /* Flush all cached information from the SMAP. */
132 for (p
= HMAP
; p
<= TMAP
; ++p
)
140 /* See if less than half a screen from the top. */
141 if (svi_sm_nlines(sp
, ep
,
142 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
147 /* See if less than half a screen from the bottom. */
148 if (file_lline(sp
, ep
, &tmp
.lno
))
150 tmp
.off
= svi_screens(sp
, ep
, tmp
.lno
, NULL
);
151 if (svi_sm_nlines(sp
, ep
,
152 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
160 top
: HMAP
->lno
= lno
;
163 /* If we fail, just punt. */
164 for (p
= HMAP
; p
< TMAP
; ++p
)
165 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
169 /* If we fail, guess that the file is too small. */
170 middle
: p
= HMAP
+ (TMAP
- HMAP
) / 2;
171 for (p
->lno
= lno
, p
->off
= 1; p
> HMAP
; --p
)
172 if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
177 /* If we fail, just punt. */
178 p
= HMAP
+ (TMAP
- HMAP
) / 2;
179 for (; p
< TMAP
; ++p
)
180 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
186 TMAP
->off
= svi_screens(sp
, ep
, lno
, NULL
);
188 /* If we fail, guess that the file is too small. */
189 bottom
: for (p
= TMAP
; p
> HMAP
; --p
)
190 if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
199 * Try and put *something* on the screen. If this fails,
200 * we have a serious hard error.
204 for (p
= HMAP
; p
< TMAP
; ++p
)
205 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
211 * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
212 * screen only contains one line, or, if the line is the entire screen, this
213 * gets fairly exciting. Skip the fun and simply return if there's only one
214 * line in the screen, or just call fill. Fill may not be entirely accurate,
215 * i.e. we may be painting the screen with something not even close to the
216 * cursor, but it's not like we're into serious performance issues here, and
217 * the refresh routine will fix it for us.
219 #define TOO_WEIRD { \
220 if (cnt_orig >= sp->t_rows) { \
223 if (file_gline(sp, ep, lno, NULL) == NULL) \
224 if (file_lline(sp, ep, &lno)) \
226 F_SET(sp, S_REDRAW); \
227 return (svi_sm_fill(sp, ep, lno, P_TOP)); \
233 * Delete a line out of the SMAP.
236 svi_sm_delete(sp
, ep
, lno
)
245 * Find the line in the map, and count the number of screen lines
246 * which display any part of the deleted line.
248 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
249 for (cnt_orig
= 1, t
= p
+ 1;
250 t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
);
254 /* Delete that many lines from the screen. */
255 MOVE(sp
, p
- HMAP
, 0);
256 if (svi_deleteln(sp
, cnt_orig
))
259 /* Shift the screen map up. */
260 memmove(p
, p
+ cnt_orig
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
262 /* Decrement the line numbers for the rest of the map. */
263 for (t
= TMAP
- cnt_orig
; p
<= t
; ++p
)
266 /* Display the new lines. */
267 for (p
= TMAP
- cnt_orig
;;) {
268 if (p
< TMAP
&& svi_sm_next(sp
, ep
, p
, p
+ 1))
270 if (svi_line(sp
, ep
, ++p
, NULL
, NULL
))
280 * Insert a line into the SMAP.
283 svi_sm_insert(sp
, ep
, lno
)
289 size_t cnt_orig
, cnt
;
292 * Find the line in the map, find out how many screen lines
293 * needed to display the line.
295 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
296 cnt_orig
= svi_screens(sp
, ep
, lno
, NULL
);
301 * The lines left in the screen override the number of screen
302 * lines in the inserted line.
304 cnt
= (TMAP
- p
) + 1;
308 /* Push down that many lines. */
309 MOVE(sp
, p
- HMAP
, 0);
310 if (svi_insertln(sp
, cnt_orig
))
313 /* Shift the screen map down. */
314 memmove(p
+ cnt_orig
, p
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
316 /* Increment the line numbers for the rest of the map. */
317 for (t
= p
+ cnt_orig
; t
<= TMAP
; ++t
)
320 /* Fill in the SMAP for the new lines, and display. */
321 for (cnt
= 1, t
= p
; cnt
<= cnt_orig
; ++t
, ++cnt
) {
325 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
333 * Reset a line in the SMAP.
336 svi_sm_reset(sp
, ep
, lno
)
342 size_t cnt_orig
, cnt_new
, cnt
, diff
;
345 * See if the number of on-screen rows taken up by the old display
346 * for the line is the same as the number needed for the new one.
347 * If so, repaint, otherwise do it the hard way.
349 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
350 for (cnt_orig
= 0, t
= p
;
351 t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
)
353 cnt_new
= svi_screens(sp
, ep
, lno
, NULL
);
357 if (cnt_orig
== cnt_new
) {
359 if (svi_line(sp
, ep
, p
, NULL
, NULL
))
365 if (cnt_orig
< cnt_new
) {
366 /* Get the difference. */
367 diff
= cnt_new
- cnt_orig
;
370 * The lines left in the screen override the number of screen
371 * lines in the inserted line.
373 cnt
= (TMAP
- p
) + 1;
377 /* Push down the extra lines. */
378 MOVE(sp
, p
- HMAP
, 0);
379 if (svi_insertln(sp
, diff
))
382 /* Shift the screen map down. */
383 memmove(p
+ diff
, p
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
385 /* Fill in the SMAP for the replaced line, and display. */
386 for (cnt
= 1, t
= p
; cnt_new
-- && t
<= TMAP
; ++t
, ++cnt
) {
389 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
393 /* Get the difference. */
394 diff
= cnt_orig
- cnt_new
;
396 /* Delete that many lines from the screen. */
397 MOVE(sp
, p
- HMAP
, 0);
398 if (svi_deleteln(sp
, diff
))
401 /* Shift the screen map up. */
402 memmove(p
, p
+ diff
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
404 /* Fill in the SMAP for the replaced line, and display. */
405 for (cnt
= 1, t
= p
; cnt_new
--; ++t
, ++cnt
) {
408 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
412 /* Display the new lines at the bottom of the screen. */
413 for (t
= TMAP
- diff
;;) {
414 if (t
< TMAP
&& svi_sm_next(sp
, ep
, t
, t
+ 1))
416 if (svi_line(sp
, ep
, ++t
, NULL
, NULL
))
427 * Scroll the SMAP up count logical lines.
430 svi_sm_up(sp
, ep
, rp
, count
, cursor_move
)
440 /* Set the default return position. */
445 * There are two forms of this command, one where the cursor tries to
446 * follow the line, and one where it doesn't. In the latter, we try
447 * and keep the cursor at the same position on the screen, but, if the
448 * screen is small enough and the line length large enough, the cursor
449 * can end up in very strange places. Probably not worth fixing.
451 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
454 if (svi_sm_cursor(sp
, ep
, &p
))
463 /* Check to see if movement is possible. */
464 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
466 if (tmp
.lno
> TMAP
->lno
&&
467 !file_gline(sp
, ep
, tmp
.lno
, NULL
) ||
468 tmp
.off
> svi_screens(sp
, ep
, tmp
.lno
, NULL
)) {
469 if (!cursor_move
|| ignore_cursor
|| p
== TMAP
) {
476 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
478 * If it's a small screen, and the movement is small, open up the
479 * screen. Otherwise, compress and repaint. If we compress, we
480 * ignore the cursor, the movement is too large to care.
482 if (ISSMALLSCREEN(sp
))
483 if (count
<= HALFTEXT(sp
)) {
484 for (; count
&& sp
->t_rows
!= sp
->t_maxrows
;
485 --count
, ++sp
->t_rows
) {
486 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
488 if (TMAP
->lno
!= tmp
.lno
&&
489 !file_gline(sp
, ep
, tmp
.lno
, NULL
))
492 if (svi_line(sp
, ep
, TMAP
, NULL
, NULL
))
498 MOVE(sp
, INFOLINE(sp
), 0);
501 sp
->t_rows
> sp
->t_minrows
; --sp
->t_rows
, --TMAP
) {
502 MOVE(sp
, TMAP
- HMAP
, 0);
508 for (; count
; --count
) {
509 /* Decide what would show up on the screen. */
510 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
513 /* If the line doesn't exist, we're done. */
514 if (TMAP
->lno
!= tmp
.lno
&& !file_gline(sp
, ep
, tmp
.lno
, NULL
))
517 /* Scroll the screen cursor up one logical line. */
518 if (svi_sm_1up(sp
, ep
))
520 if (!cursor_move
&& !ignore_cursor
&& p
> HMAP
)
524 /* If ignoring the cursor, we're done. */
529 /* If we didn't move enough, head toward EOF. */
530 for (; count
; --count
, ++p
)
535 * If the line itself moved, invalidate the cursor, because
536 * the comparison with the old line/new line won't be right
538 F_SET(SVP(sp
), SVI_CUR_INVALID
);
540 /* If didn't move enough, it's an error. */
546 /* If the cursor moved off the screen, move it to the top. */
547 if (sp
->lno
< HMAP
->lno
)
551 * On a logical movement, we try and keep the cursor as close as
552 * possible to the last position, but also set it up so that the
553 * next "real" movement will return the cursor to the closest position
554 * to the last real movement.
556 if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
558 rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
565 * Scroll the SMAP up one.
573 * Delete the top line of the screen. Shift the screen map up.
574 * Display a new line at the bottom of the screen.
577 if (svi_deleteln(sp
, 1))
580 /* One-line screens can fail. */
582 if (svi_sm_next(sp
, ep
, TMAP
, TMAP
))
585 memmove(HMAP
, HMAP
+ 1, (sp
->rows
- 1) * sizeof(SMAP
));
586 if (svi_sm_next(sp
, ep
, TMAP
- 1, TMAP
))
589 if (svi_line(sp
, ep
, TMAP
, NULL
, NULL
))
596 * Delete a line a la curses, make sure to put the information
597 * line and other screens back.
600 svi_deleteln(sp
, cnt
)
606 getyx(stdscr
, oldy
, oldx
);
609 MOVE(sp
, INFOLINE(sp
) - 1, 0);
611 MOVEA(sp
, oldy
, oldx
);
618 * Scroll the SMAP down count logical lines.
621 svi_sm_down(sp
, ep
, rp
, count
, cursor_move
)
631 /* Set the default return position. */
636 * There are two forms of this command, one where the cursor tries to
637 * follow the line, and one where it doesn't. In the latter, we try
638 * and keep the cursor at the same position on the screen, but, if the
639 * screen is small enough and the line length large enough, the cursor
640 * can end up in very strange places. Probably not worth fixing.
642 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
645 if (svi_sm_cursor(sp
, ep
, &p
))
654 /* Check to see if movement is possible. */
655 if (HMAP
->lno
== 1 && HMAP
->off
== 1 &&
656 !cursor_move
|| ignore_cursor
|| p
== HMAP
) {
662 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
664 * If it's a small screen, and the movement is small, open up the
665 * screen. Otherwise, compress and repaint. If we compress, we
666 * ignore the cursor, the movement is too large to care.
668 if (ISSMALLSCREEN(sp
))
669 if (count
<= HALFTEXT(sp
)) {
670 for (; count
&& sp
->t_rows
!= sp
->t_maxrows
&&
671 (HMAP
->lno
> 1 || HMAP
->off
> 1);
672 --count
, ++sp
->t_rows
) {
674 if (svi_sm_1down(sp
, ep
))
682 MOVE(sp
, INFOLINE(sp
), 0);
685 sp
->t_rows
> sp
->t_minrows
; --sp
->t_rows
, --TMAP
) {
686 MOVE(sp
, TMAP
- HMAP
, 0);
692 for (; count
; --count
) {
693 /* If the line doesn't exist, we're done. */
694 if (HMAP
->lno
== 1 && HMAP
->off
== 1)
697 /* Scroll the screen and cursor down one logical line. */
698 if (svi_sm_1down(sp
, ep
))
700 if (!cursor_move
&& !ignore_cursor
&& p
< TMAP
)
704 /* If ignoring the cursor, we're done. */
709 /* If we didn't move enough, move to SOF. */
714 * If the line itself moved, invalidate the cursor, because
715 * the comparison with the old line/new line won't be right.
717 F_SET(SVP(sp
), SVI_CUR_INVALID
);
719 /* If didn't move enough, it's an error. */
725 /* If the cursor moved off the screen, move it to the bottom. */
726 if (sp
->lno
> TMAP
->lno
)
731 * On a logical movement, we try and keep the cursor as close as
732 * possible to the last position, but also set it up so that the
733 * next "real" movement will return the cursor to the closest position
734 * to the last real movement.
736 if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
738 rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
745 * Scroll the SMAP down one.
753 * Clear the bottom line of the screen, insert a line at the top
754 * of the screen. Shift the screen map down, display a new line
755 * at the top of the screen.
757 MOVE(sp
, sp
->t_rows
, 0);
760 if (svi_insertln(sp
, 1))
762 memmove(HMAP
+ 1, HMAP
, (sp
->rows
- 1) * sizeof(SMAP
));
763 if (svi_sm_prev(sp
, ep
, HMAP
+ 1, HMAP
))
765 if (svi_line(sp
, ep
, HMAP
, NULL
, NULL
))
772 * Insert a line a la curses, make sure to put the information
773 * line and other screens back.
776 svi_insertln(sp
, cnt
)
782 getyx(stdscr
, oldy
, oldx
);
784 MOVE(sp
, INFOLINE(sp
) - 1, 0);
786 MOVEA(sp
, oldy
, oldx
);
794 * Fill in the next entry in the SMAP.
797 svi_sm_next(sp
, ep
, p
, t
)
805 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
809 lcnt
= svi_screens(sp
, ep
, p
->lno
, NULL
);
810 if (lcnt
== p
->off
) {
823 * Fill in the previous entry in the SMAP.
826 svi_sm_prev(sp
, ep
, p
, t
)
832 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
835 } else if (p
->off
!= 1) {
840 t
->off
= svi_screens(sp
, ep
, t
->lno
, NULL
);
842 return (t
->lno
== 0);
847 * Return the SMAP entry referenced by the cursor.
850 svi_sm_cursor(sp
, ep
, smp
)
857 /* See if the cursor is not in the map. */
858 if (sp
->lno
< HMAP
->lno
|| sp
->lno
> TMAP
->lno
) {
863 /* Find the first occurence of the line. */
864 for (p
= HMAP
; p
->lno
!= sp
->lno
; ++p
);
866 /* Fill in the map information until we find the right line. */
867 for (; p
<= TMAP
; ++p
) {
868 /* Short lines are common and easy to detect. */
869 if (p
!= TMAP
&& (p
+ 1)->lno
!= p
->lno
) {
873 if (!SMAP_CACHE(p
) && svi_line(sp
, ep
, p
, NULL
, NULL
))
875 if (p
->c_eboff
>= sp
->cno
) {
881 /* It was past the end of the map after all. */
888 * Return the line/column of the top, middle or last line on the screen.
889 * (The vi H, M and L commands.) Here because only the screen routines
890 * know what's really out there.
893 svi_sm_position(sp
, ep
, rp
, cnt
, pos
)
905 if (cnt
> TMAP
- HMAP
)
910 if (cnt
> (TMAP
- HMAP
) / 2)
912 smp
= (HMAP
+ (TMAP
- HMAP
) / 2) + cnt
;
915 if (cnt
> TMAP
- HMAP
) {
916 err
: msgq(sp
, M_BERR
, "Movement past the end-of-screen.");
920 eof
: if (file_gline(sp
, ep
, smp
->lno
, NULL
) == NULL
) {
921 if (file_lline(sp
, ep
, &last
))
923 for (; smp
->lno
> last
&& smp
> HMAP
; --smp
);
930 if (!SMAP_CACHE(smp
) && svi_line(sp
, ep
, smp
, NULL
, NULL
))
933 rp
->cno
= smp
->c_sboff
;
940 * Return the number of screen lines from an SMAP entry to the
941 * start of some file line, less than a maximum value.
944 svi_sm_nlines(sp
, ep
, from_sp
, to_lno
, max
)
953 if (O_ISSET(sp
, O_LEFTRIGHT
))
954 return (from_sp
->lno
> to_lno
?
955 from_sp
->lno
- to_lno
: to_lno
- from_sp
->lno
);
957 if (from_sp
->lno
== to_lno
)
958 return (from_sp
->off
- 1);
960 if (from_sp
->lno
> to_lno
) {
961 lcnt
= from_sp
->off
- 1; /* Correct for off-by-one. */
962 for (lno
= from_sp
->lno
; --lno
>= to_lno
&& lcnt
<= max
;)
963 lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);
966 lcnt
= (svi_screens(sp
, ep
, lno
, NULL
) - from_sp
->off
) + 1;
967 for (; ++lno
< to_lno
&& lcnt
<= max
;)
968 lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);