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.28 1993/11/22 17:30:13 bostic Exp $ (Berkeley) $Date: 1993/11/22 17:30:13 $";
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));
24 static int svi_sm_delete
__P((SCR
*, EXF
*, recno_t
));
25 static int svi_sm_insert
__P((SCR
*, EXF
*, recno_t
));
26 static int svi_sm_reset
__P((SCR
*, EXF
*, recno_t
));
30 * Make a change to the screen.
33 svi_change(sp
, ep
, lno
, op
)
42 /* Appending is the same as inserting, if the line is incremented. */
43 if (op
== LINE_APPEND
) {
48 /* Ignore the change if the line is after the map. */
53 * If the line is before the map, and it's a decrement, decrement
54 * the map. If it's an increment, increment the map. Otherwise,
57 if (lno
< HMAP
->lno
) {
63 for (p
= HMAP
; p
<= TMAP
; ++p
)
67 F_SET(sp
, S_RENUMBER
);
70 for (p
= HMAP
; p
<= TMAP
; ++p
)
74 F_SET(sp
, S_RENUMBER
);
82 F_SET(SVP(sp
), SVI_SCREENDIRTY
);
84 /* Flush cached information from svi_screens(). */
85 SVP(sp
)->ss_lno
= OOBLNO
;
87 /* Invalidate the cursor, if it's on this line. */
89 F_SET(SVP(sp
), SVI_CUR_INVALID
);
91 getyx(stdscr
, oldy
, oldx
);
95 if (svi_sm_delete(sp
, ep
, lno
))
97 F_SET(sp
, S_RENUMBER
);
100 if (svi_sm_insert(sp
, ep
, lno
))
102 F_SET(sp
, S_RENUMBER
);
105 if (svi_sm_reset(sp
, ep
, lno
))
112 MOVEA(sp
, oldy
, oldx
);
119 * Fill in the screen map, placing the specified line at the
120 * right position. There isn't any way to tell if an SMAP
121 * entry has been filled in, so this routine had better be
122 * called with P_FILL set before anything else is done.
125 * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
126 * slot is already filled in, P_BOTTOM means that the TMAP slot is
127 * already filled in, and we just finish up the job.
130 svi_sm_fill(sp
, ep
, lno
, pos
)
138 /* Flush all cached information from the SMAP. */
139 for (p
= HMAP
; p
<= TMAP
; ++p
)
147 /* See if less than half a screen from the top. */
148 if (svi_sm_nlines(sp
, ep
,
149 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
154 /* See if less than half a screen from the bottom. */
155 if (file_lline(sp
, ep
, &tmp
.lno
))
157 tmp
.off
= svi_screens(sp
, ep
, tmp
.lno
, NULL
);
158 if (svi_sm_nlines(sp
, ep
,
159 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
167 top
: HMAP
->lno
= lno
;
170 /* If we fail, just punt. */
171 for (p
= HMAP
; p
< TMAP
; ++p
)
172 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
176 /* If we fail, guess that the file is too small. */
177 middle
: p
= HMAP
+ (TMAP
- HMAP
) / 2;
178 for (p
->lno
= lno
, p
->off
= 1; p
> HMAP
; --p
)
179 if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
184 /* If we fail, just punt. */
185 p
= HMAP
+ (TMAP
- HMAP
) / 2;
186 for (; p
< TMAP
; ++p
)
187 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
193 TMAP
->off
= svi_screens(sp
, ep
, lno
, NULL
);
195 /* If we fail, guess that the file is too small. */
196 bottom
: for (p
= TMAP
; p
> HMAP
; --p
)
197 if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
206 * Try and put *something* on the screen. If this fails,
207 * we have a serious hard error.
211 for (p
= HMAP
; p
< TMAP
; ++p
)
212 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
218 * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
219 * screen only contains one line, or, if the line is the entire screen, this
220 * gets fairly exciting. Skip the fun and simply return if there's only one
221 * line in the screen, or just call fill. Fill may not be entirely accurate,
222 * i.e. we may be painting the screen with something not even close to the
223 * cursor, but it's not like we're into serious performance issues here, and
224 * the refresh routine will fix it for us.
226 #define TOO_WEIRD { \
227 if (cnt_orig >= sp->t_rows) { \
230 if (file_gline(sp, ep, lno, NULL) == NULL) \
231 if (file_lline(sp, ep, &lno)) \
233 F_SET(sp, S_REDRAW); \
234 return (svi_sm_fill(sp, ep, lno, P_TOP)); \
240 * Delete a line out of the SMAP.
243 svi_sm_delete(sp
, ep
, lno
)
252 * Find the line in the map, and count the number of screen lines
253 * which display any part of the deleted line.
255 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
256 for (cnt_orig
= 1, t
= p
+ 1;
257 t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
);
261 /* Delete that many lines from the screen. */
262 MOVE(sp
, p
- HMAP
, 0);
263 if (svi_deleteln(sp
, cnt_orig
))
266 /* Shift the screen map up. */
267 memmove(p
, p
+ cnt_orig
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
269 /* Decrement the line numbers for the rest of the map. */
270 for (t
= TMAP
- cnt_orig
; p
<= t
; ++p
)
273 /* Display the new lines. */
274 for (p
= TMAP
- cnt_orig
;;) {
275 if (p
< TMAP
&& svi_sm_next(sp
, ep
, p
, p
+ 1))
277 if (svi_line(sp
, ep
, ++p
, NULL
, NULL
))
287 * Insert a line into the SMAP.
290 svi_sm_insert(sp
, ep
, lno
)
296 size_t cnt_orig
, cnt
;
299 * Find the line in the map, find out how many screen lines
300 * needed to display the line.
302 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
303 cnt_orig
= svi_screens(sp
, ep
, lno
, NULL
);
308 * The lines left in the screen override the number of screen
309 * lines in the inserted line.
311 cnt
= (TMAP
- p
) + 1;
315 /* Push down that many lines. */
316 MOVE(sp
, p
- HMAP
, 0);
317 if (svi_insertln(sp
, cnt_orig
))
320 /* Shift the screen map down. */
321 memmove(p
+ cnt_orig
, p
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
323 /* Increment the line numbers for the rest of the map. */
324 for (t
= p
+ cnt_orig
; t
<= TMAP
; ++t
)
327 /* Fill in the SMAP for the new lines, and display. */
328 for (cnt
= 1, t
= p
; cnt
<= cnt_orig
; ++t
, ++cnt
) {
332 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
340 * Reset a line in the SMAP.
343 svi_sm_reset(sp
, ep
, lno
)
349 size_t cnt_orig
, cnt_new
, cnt
, diff
;
352 * See if the number of on-screen rows taken up by the old display
353 * for the line is the same as the number needed for the new one.
354 * If so, repaint, otherwise do it the hard way.
356 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
357 for (cnt_orig
= 0, t
= p
;
358 t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
)
360 cnt_new
= svi_screens(sp
, ep
, lno
, NULL
);
364 if (cnt_orig
== cnt_new
) {
366 if (svi_line(sp
, ep
, p
, NULL
, NULL
))
372 if (cnt_orig
< cnt_new
) {
373 /* Get the difference. */
374 diff
= cnt_new
- cnt_orig
;
377 * The lines left in the screen override the number of screen
378 * lines in the inserted line.
380 cnt
= (TMAP
- p
) + 1;
384 /* Push down the extra lines. */
385 MOVE(sp
, p
- HMAP
, 0);
386 if (svi_insertln(sp
, diff
))
389 /* Shift the screen map down. */
390 memmove(p
+ diff
, p
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
392 /* Fill in the SMAP for the replaced line, and display. */
393 for (cnt
= 1, t
= p
; cnt_new
-- && t
<= TMAP
; ++t
, ++cnt
) {
396 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
400 /* Get the difference. */
401 diff
= cnt_orig
- cnt_new
;
403 /* Delete that many lines from the screen. */
404 MOVE(sp
, p
- HMAP
, 0);
405 if (svi_deleteln(sp
, diff
))
408 /* Shift the screen map up. */
409 memmove(p
, p
+ diff
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
411 /* Fill in the SMAP for the replaced line, and display. */
412 for (cnt
= 1, t
= p
; cnt_new
--; ++t
, ++cnt
) {
415 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
419 /* Display the new lines at the bottom of the screen. */
420 for (t
= TMAP
- diff
;;) {
421 if (t
< TMAP
&& svi_sm_next(sp
, ep
, t
, t
+ 1))
423 if (svi_line(sp
, ep
, ++t
, NULL
, NULL
))
434 * Scroll the SMAP up count logical lines.
437 svi_sm_up(sp
, ep
, rp
, count
, cursor_move
)
447 /* Set the default return position. */
452 * Invalidate the cursor. The line is probably going to change,
453 * but if cursor_move isn't set it may not. In any case, this
454 * routine moves the cursor to draw things.
456 F_SET(SVP(sp
), SVI_CUR_INVALID
);
459 * There are two forms of this command, one where the cursor tries to
460 * follow the line, and one where it doesn't. In the latter, we try
461 * and keep the cursor at the same position on the screen, but, if the
462 * screen is small enough and the line length large enough, the cursor
463 * can end up in very strange places. Probably not worth fixing.
465 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
468 if (svi_sm_cursor(sp
, ep
, &p
))
478 * Check to see if movement is possible. Lots of checks...
480 * Find out if it's possible to move past the end of the map. If
481 * that's okay because we think that we can move the cursor down
482 * in the map, check to make sure that the map isn't mostly empty.
484 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
486 if (tmp
.lno
> TMAP
->lno
&&
487 !file_gline(sp
, ep
, tmp
.lno
, NULL
) ||
488 tmp
.off
> svi_screens(sp
, ep
, tmp
.lno
, NULL
)) {
489 if (!cursor_move
|| ignore_cursor
|| p
== TMAP
) {
493 if (svi_sm_next(sp
, ep
, p
, &tmp
))
495 if (!file_gline(sp
, ep
, tmp
.lno
, NULL
) ||
496 tmp
.off
> svi_screens(sp
, ep
, tmp
.lno
, NULL
)) {
503 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
505 * If it's a small screen, and the movement is small, open up the
506 * screen. Otherwise, compress and repaint. If we compress, we
507 * ignore the cursor, the movement is too large to care.
509 if (ISSMALLSCREEN(sp
))
510 if (count
<= HALFTEXT(sp
)) {
511 for (; count
&& sp
->t_rows
!= sp
->t_maxrows
;
512 --count
, ++sp
->t_rows
) {
513 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
515 if (TMAP
->lno
!= tmp
.lno
&&
516 !file_gline(sp
, ep
, tmp
.lno
, NULL
))
519 if (svi_line(sp
, ep
, TMAP
, NULL
, NULL
))
525 MOVE(sp
, INFOLINE(sp
), 0);
528 sp
->t_rows
> sp
->t_minrows
; --sp
->t_rows
, --TMAP
) {
529 MOVE(sp
, TMAP
- HMAP
, 0);
535 for (; count
; --count
) {
536 /* Decide what would show up on the screen. */
537 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
540 /* If the line doesn't exist, we're done. */
541 if (TMAP
->lno
!= tmp
.lno
&& !file_gline(sp
, ep
, tmp
.lno
, NULL
))
544 /* Scroll the screen cursor up one logical line. */
545 if (svi_sm_1up(sp
, ep
))
547 if (!cursor_move
&& !ignore_cursor
&& p
> HMAP
)
551 /* If ignoring the cursor, we're done. */
557 * If we didn't move enough, head toward EOF. Check to make
558 * sure the lines actually, if the file is smaller than the
559 * screen they may not.
561 for (; count
; --count
, ++p
)
562 if (p
== TMAP
|| !file_gline(sp
, ep
, p
[1].lno
, NULL
))
566 * If the line itself moved, invalidate the cursor, because
567 * the comparison with the old line/new line won't be right
569 F_SET(SVP(sp
), SVI_CUR_INVALID
);
571 /* If didn't move enough, it's an error. */
577 /* If the cursor moved off the screen, move it to the top. */
578 if (sp
->lno
< HMAP
->lno
)
582 * On a logical movement, we try and keep the cursor as close as
583 * possible to the last position, but also set it up so that the
584 * next "real" movement will return the cursor to the closest position
585 * to the last real movement.
587 if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
589 rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
596 * Scroll the SMAP up one.
604 * Delete the top line of the screen. Shift the screen map up.
605 * Display a new line at the bottom of the screen.
608 if (svi_deleteln(sp
, 1))
611 /* One-line screens can fail. */
613 if (svi_sm_next(sp
, ep
, TMAP
, TMAP
))
616 memmove(HMAP
, HMAP
+ 1, (sp
->rows
- 1) * sizeof(SMAP
));
617 if (svi_sm_next(sp
, ep
, TMAP
- 1, TMAP
))
620 if (svi_line(sp
, ep
, TMAP
, NULL
, NULL
))
627 * Delete a line a la curses, make sure to put the information
628 * line and other screens back.
631 svi_deleteln(sp
, cnt
)
637 getyx(stdscr
, oldy
, oldx
);
640 MOVE(sp
, INFOLINE(sp
) - 1, 0);
642 MOVEA(sp
, oldy
, oldx
);
649 * Scroll the SMAP down count logical lines.
652 svi_sm_down(sp
, ep
, rp
, count
, cursor_move
)
662 /* Set the default return position. */
667 * Invalidate the cursor. The line is probably going to change,
668 * but if cursor_move isn't set it may not. In any case, this
669 * routine moves the cursor to draw things.
671 F_SET(SVP(sp
), SVI_CUR_INVALID
);
674 * There are two forms of this command, one where the cursor tries to
675 * follow the line, and one where it doesn't. In the latter, we try
676 * and keep the cursor at the same position on the screen, but, if the
677 * screen is small enough and the line length large enough, the cursor
678 * can end up in very strange places. Probably not worth fixing.
680 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
683 if (svi_sm_cursor(sp
, ep
, &p
))
692 /* Check to see if movement is possible. */
693 if (HMAP
->lno
== 1 && HMAP
->off
== 1 &&
694 (!cursor_move
|| ignore_cursor
|| p
== HMAP
)) {
700 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
702 * If it's a small screen, and the movement is small, open up the
703 * screen. Otherwise, compress and repaint. If we compress, we
704 * ignore the cursor, the movement is too large to care.
706 if (ISSMALLSCREEN(sp
))
707 if (count
<= HALFTEXT(sp
)) {
708 for (; count
&& sp
->t_rows
!= sp
->t_maxrows
&&
709 (HMAP
->lno
> 1 || HMAP
->off
> 1);
710 --count
, ++sp
->t_rows
) {
712 if (svi_sm_1down(sp
, ep
))
720 MOVE(sp
, INFOLINE(sp
), 0);
723 sp
->t_rows
> sp
->t_minrows
; --sp
->t_rows
, --TMAP
) {
724 MOVE(sp
, TMAP
- HMAP
, 0);
730 for (; count
; --count
) {
731 /* If the line doesn't exist, we're done. */
732 if (HMAP
->lno
== 1 && HMAP
->off
== 1)
735 /* Scroll the screen and cursor down one logical line. */
736 if (svi_sm_1down(sp
, ep
))
738 if (!cursor_move
&& !ignore_cursor
&& p
< TMAP
)
742 /* If ignoring the cursor, we're done. */
747 /* If we didn't move enough, move to SOF. */
752 * If the line itself moved, invalidate the cursor, because
753 * the comparison with the old line/new line won't be right.
755 F_SET(SVP(sp
), SVI_CUR_INVALID
);
757 /* If didn't move enough, it's an error. */
763 /* If the cursor moved off the screen, move it to the bottom. */
764 if (sp
->lno
> TMAP
->lno
)
769 * On a logical movement, we try and keep the cursor as close as
770 * possible to the last position, but also set it up so that the
771 * next "real" movement will return the cursor to the closest position
772 * to the last real movement.
774 if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
776 rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
783 * Scroll the SMAP down one.
791 * Clear the bottom line of the screen, insert a line at the top
792 * of the screen. Shift the screen map down, display a new line
793 * at the top of the screen.
795 MOVE(sp
, sp
->t_rows
, 0);
798 if (svi_insertln(sp
, 1))
800 memmove(HMAP
+ 1, HMAP
, (sp
->rows
- 1) * sizeof(SMAP
));
801 if (svi_sm_prev(sp
, ep
, HMAP
+ 1, HMAP
))
803 if (svi_line(sp
, ep
, HMAP
, NULL
, NULL
))
810 * Insert a line a la curses, make sure to put the information
811 * line and other screens back.
814 svi_insertln(sp
, cnt
)
820 getyx(stdscr
, oldy
, oldx
);
822 MOVE(sp
, INFOLINE(sp
) - 1, 0);
824 MOVEA(sp
, oldy
, oldx
);
832 * Fill in the next entry in the SMAP.
835 svi_sm_next(sp
, ep
, p
, t
)
843 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
847 lcnt
= svi_screens(sp
, ep
, p
->lno
, NULL
);
848 if (lcnt
== p
->off
) {
861 * Fill in the previous entry in the SMAP.
864 svi_sm_prev(sp
, ep
, p
, t
)
870 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
873 } else if (p
->off
!= 1) {
878 t
->off
= svi_screens(sp
, ep
, t
->lno
, NULL
);
880 return (t
->lno
== 0);
885 * Return the SMAP entry referenced by the cursor.
888 svi_sm_cursor(sp
, ep
, smp
)
895 /* See if the cursor is not in the map. */
896 if (sp
->lno
< HMAP
->lno
|| sp
->lno
> TMAP
->lno
) {
901 /* Find the first occurence of the line. */
902 for (p
= HMAP
; p
->lno
!= sp
->lno
; ++p
);
904 /* Fill in the map information until we find the right line. */
905 for (; p
<= TMAP
; ++p
) {
906 /* Short lines are common and easy to detect. */
907 if (p
!= TMAP
&& (p
+ 1)->lno
!= p
->lno
) {
911 if (!SMAP_CACHE(p
) && svi_line(sp
, ep
, p
, NULL
, NULL
))
913 if (p
->c_eboff
>= sp
->cno
) {
919 /* It was past the end of the map after all. */
926 * Return the line/column of the top, middle or last line on the screen.
927 * (The vi H, M and L commands.) Here because only the screen routines
928 * know what's really out there.
931 svi_sm_position(sp
, ep
, rp
, cnt
, pos
)
943 if (cnt
> TMAP
- HMAP
)
948 if (cnt
> (TMAP
- HMAP
) / 2)
950 smp
= (HMAP
+ (TMAP
- HMAP
) / 2) + cnt
;
953 if (cnt
> TMAP
- HMAP
) {
954 err
: msgq(sp
, M_BERR
, "Movement past the end-of-screen.");
958 eof
: if (file_gline(sp
, ep
, smp
->lno
, NULL
) == NULL
) {
959 if (file_lline(sp
, ep
, &last
))
961 for (; smp
->lno
> last
&& smp
> HMAP
; --smp
);
968 if (!SMAP_CACHE(smp
) && svi_line(sp
, ep
, smp
, NULL
, NULL
))
971 rp
->cno
= smp
->c_sboff
;
978 * Return the number of screen lines from an SMAP entry to the
979 * start of some file line, less than a maximum value.
982 svi_sm_nlines(sp
, ep
, from_sp
, to_lno
, max
)
991 if (O_ISSET(sp
, O_LEFTRIGHT
))
992 return (from_sp
->lno
> to_lno
?
993 from_sp
->lno
- to_lno
: to_lno
- from_sp
->lno
);
995 if (from_sp
->lno
== to_lno
)
996 return (from_sp
->off
- 1);
998 if (from_sp
->lno
> to_lno
) {
999 lcnt
= from_sp
->off
- 1; /* Correct for off-by-one. */
1000 for (lno
= from_sp
->lno
; --lno
>= to_lno
&& lcnt
<= max
;)
1001 lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);
1004 lcnt
= (svi_screens(sp
, ep
, lno
, NULL
) - from_sp
->off
) + 1;
1005 for (; ++lno
< to_lno
&& lcnt
<= max
;)
1006 lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);