3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: vs_line.c,v 8.14 1993/11/29 14:15:43 bostic Exp $ (Berkeley) $Date: 1993/11/29 14:15:43 $";
12 #include <sys/types.h>
18 #include "svi_screen.h"
20 #if defined(DEBUG) && 0
22 #define TABSTR "--------------------"
30 * Update one line on the screen.
33 svi_line(sp
, ep
, smp
, yp
, xp
)
41 size_t chlen
, cols_per_screen
, cno_cnt
, len
, scno
, skip_screens
;
42 size_t offset_in_char
, offset_in_line
;
44 int ch
, is_cached
, is_infoline
, is_partial
, is_tab
, listset
;
48 #if defined(DEBUG) && 0
49 TRACE(sp
, "svi_line: row %u: line: %u off: %u\n",
50 smp
- HMAP
, smp
->lno
, smp
->off
);
54 * Assume that, if the cache entry for the line is filled in, the
55 * line is already on the screen, and all we need to do is return
56 * the cursor position. If the calling routine doesn't need the
57 * cursor position, we can just return.
59 is_cached
= SMAP_CACHE(smp
);
60 if (yp
== NULL
&& is_cached
)
64 * A nasty side effect of this routine is that it returns the screen
65 * position for the "current" character. Not pretty, but this is the
66 * only routine that really knows what's out there.
68 * Move to the line. This routine can be called by svi_sm_position(),
69 * which uses it to fill in the cache entry so it can figure out what
70 * the real contents of the screen are. Because of this, we have to
71 * return to whereever we started from.
73 getyx(stdscr
, oldy
, oldx
);
74 MOVE(sp
, smp
- HMAP
, 0);
76 /* Get the character map. */
77 cname
= sp
->gp
->cname
;
79 /* Get a copy of the line. */
80 p
= file_gline(sp
, ep
, smp
->lno
, &len
);
83 * Special case if we're printing the info/mode line. Skip printing
84 * the leading number, as well as other minor setup. If painting the
85 * line between two screens, it's always in reverse video. The only
86 * time this code paints the mode line is when the user is entering
87 * text for a ":" command, so we can put the code here instead of
88 * dealing with the empty line logic below. This is a kludge, but it's
89 * pretty much confined to this module.
91 * Set the number of screens to skip until a character is displayed.
92 * Left-right screens are special, because we don't bother building
93 * a buffer to be skipped over.
95 * Set the number of columns for this screen.
98 cols_per_screen
= sp
->cols
;
99 if (is_infoline
= ISINFOLINE(sp
, smp
)) {
100 if (sp
->q
.cqe_next
!= (void *)&sp
->gp
->dq
) {
105 if (O_ISSET(sp
, O_LEFTRIGHT
))
108 skip_screens
= smp
->off
- 1;
110 listset
= O_ISSET(sp
, O_LIST
);
111 skip_screens
= smp
->off
- 1;
114 * If O_NUMBER is set and it's line number 1 or the line exists
115 * and this is the first screen of a folding line or any left-
116 * right line, display the line number.
118 if (O_ISSET(sp
, O_NUMBER
) &&
119 (smp
->lno
== 1 || p
!= NULL
) && skip_screens
== 0) {
120 cols_per_screen
-= O_NUMBER_LENGTH
;
122 sizeof(nbuf
), O_NUMBER_FMT
, smp
->lno
);
128 * Special case non-existent lines and the first line of an empty
129 * file. In both cases, the cursor position is 0, but corrected
130 * for the O_NUMBER field if it was displayed.
132 if (p
== NULL
|| len
== 0) {
133 /* Fill in the cursor. */
134 if (yp
!= NULL
&& smp
->lno
== sp
->lno
) {
136 *xp
= sp
->cols
- cols_per_screen
;
139 /* If the line is on the screen, quit. */
143 /* Set line cacheing information. */
144 smp
->c_sboff
= smp
->c_eboff
= 0;
145 smp
->c_scoff
= smp
->c_eclen
= 0;
149 ADDCH(listset
&& skip_screens
== 0 ? '$' : '~');
150 } else if (listset
&& skip_screens
== 0)
154 MOVEA(sp
, oldy
, oldx
);
159 * If we wrote a line that's this or a previous one, we can do this
160 * much more quickly -- we cached the starting and ending positions
161 * of that line. The way it works is we keep information about the
162 * lines displayed in the SMAP. If we're painting the screen in
163 * the forward, this saves us from reformatting the physical line for
164 * every line on the screen. This wins big on binary files with 10K
167 * Test for the first screen of the line, then the current screen line,
168 * then the line behind us, then do the hard work. Note, it doesn't
169 * do us any good to have a line in front of us -- it would be really
170 * hard to try and figure out tabs in the reverse direction, i.e. how
171 * many spaces a tab takes up in the reverse direction depends on
172 * what characters preceded it.
175 smp
->c_sboff
= offset_in_line
= 0;
176 smp
->c_scoff
= offset_in_char
= 0;
177 p
= &p
[offset_in_line
];
178 } else if (is_cached
) {
179 offset_in_line
= smp
->c_sboff
;
180 offset_in_char
= smp
->c_scoff
;
181 p
= &p
[offset_in_line
];
182 } else if (smp
!= HMAP
&&
183 SMAP_CACHE(tsmp
= smp
- 1) && tsmp
->lno
== smp
->lno
) {
184 if (tsmp
->c_eclen
!= tsmp
->c_ecsize
) {
185 offset_in_line
= tsmp
->c_eboff
;
186 offset_in_char
= tsmp
->c_eclen
;
188 offset_in_line
= tsmp
->c_eboff
+ 1;
192 /* Put starting info for this line in the cache. */
193 smp
->c_sboff
= offset_in_line
;
194 smp
->c_scoff
= offset_in_char
;
195 p
= &p
[offset_in_line
];
200 /* This is the loop that skips through screens. */
201 if (skip_screens
== 0) {
202 smp
->c_sboff
= offset_in_line
;
203 smp
->c_scoff
= offset_in_char
;
204 } else for (scno
= 0; offset_in_line
< len
; ++offset_in_line
) {
206 (ch
= *(u_char
*)p
++) == '\t' && !listset
?
207 TAB_OFF(sp
, scno
) : cname
[ch
].len
;
208 if (scno
< cols_per_screen
)
211 * Reset cols_per_screen to second and subsequent line
214 scno
-= cols_per_screen
;
215 cols_per_screen
= sp
->cols
;
218 * If crossed the last skipped screen boundary, start
219 * displaying the characters.
224 /* Put starting info for this line in the cache. */
226 smp
->c_sboff
= offset_in_line
;
227 smp
->c_scoff
= offset_in_char
= chlen
- scno
;
230 smp
->c_sboff
= ++offset_in_line
;
238 * Set the number of characters to skip before reaching the cursor
239 * character. Offset by 1 and use 0 as a flag value. Svi_line is
240 * called repeatedly with a valid pointer to a cursor position.
241 * Don't fill anything in unless it's the right line and the right
242 * character, and the right part of the character...
245 smp
->lno
!= sp
->lno
|| sp
->cno
< offset_in_line
||
246 offset_in_line
+ cols_per_screen
< sp
->cno
) {
248 /* If the line is on the screen, quit. */
252 cno_cnt
= (sp
->cno
- offset_in_line
) + 1;
254 /* This is the loop that actually displays characters. */
255 for (is_partial
= 0, scno
= 0;
256 offset_in_line
< len
; ++offset_in_line
, offset_in_char
= 0) {
257 if ((ch
= *(u_char
*)p
++) == '\t' && !listset
) {
258 scno
+= chlen
= TAB_OFF(sp
, scno
) - offset_in_char
;
261 scno
+= chlen
= cname
[ch
].len
- offset_in_char
;
266 * Only display up to the right-hand column. Set a flag if
267 * the entire character wasn't displayed for use in setting
268 * the cursor. If reached the end of the line, set the cache
269 * info for the screen. Don't worry about there not being
270 * characters to display on the next screen, its lno/off won't
271 * match up in that case.
273 if (scno
>= cols_per_screen
) {
274 smp
->c_ecsize
= chlen
;
275 chlen
-= scno
- cols_per_screen
;
276 smp
->c_eclen
= chlen
;
277 smp
->c_eboff
= offset_in_line
;
278 if (scno
> cols_per_screen
)
281 /* Terminate the loop. */
282 offset_in_line
= len
;
286 * If the caller wants the cursor value, and this was the
287 * cursor character, set the value. There are two ways to
288 * put the cursor on a character -- if it's normal display
289 * mode, it goes on the last column of the character. If
290 * it's input mode, it goes on the first. In normal mode,
291 * set the cursor only if the entire character was displayed.
294 --cno_cnt
== 0 && (F_ISSET(sp
, S_INPUT
) || !is_partial
)) {
296 if (F_ISSET(sp
, S_INPUT
))
300 if (O_ISSET(sp
, O_NUMBER
) &&
301 !is_infoline
&& smp
->off
== 1)
302 *xp
+= O_NUMBER_LENGTH
;
304 /* If the line is on the screen, quit. */
309 /* If the line is on the screen, don't display anything. */
314 * Display the character. If it's a tab and tabs aren't some
315 * ridiculous length, do it fast. (We do tab expansion here
316 * because curses doesn't have a way to set the tab length.)
319 if (chlen
<= sizeof(TABSTR
) - 1) {
320 ADDNSTR(TABSTR
, chlen
);
325 ADDNSTR(cname
[ch
].name
+ offset_in_char
, chlen
);
328 if (scno
< cols_per_screen
) {
329 /* If didn't paint the whole line, update the cache. */
330 smp
->c_ecsize
= smp
->c_eclen
= cname
[ch
].len
;
331 smp
->c_eboff
= len
- 1;
334 * If not the info/mode line, and O_LIST set, and at the
335 * end of the line, and the line ended on this screen,
343 /* If still didn't paint the whole line, clear the rest. */
344 if (scno
< cols_per_screen
)
348 ret
: if (reverse_video
)
350 MOVEA(sp
, oldy
, oldx
);
356 * Repaint the numbers on all the lines.
369 * Try and avoid getting the last line in the file, by getting the
370 * line after the last line in the screen -- if it exists, we know
371 * we have to to number all the lines in the screen. Get the one
372 * after the last instead of the last, so that the info line doesn't
375 * If that test fails, we have to check each line for existence.
378 * The problem is that file_lline will lie, and tell us that the
379 * info line is the last line in the file.
381 if ((p
= file_gline(sp
, ep
, TMAP
->lno
- 1, NULL
)) != NULL
)
384 getyx(stdscr
, oldy
, oldx
);
385 for (smp
= HMAP
; smp
<= TMAP
; ++smp
) {
388 if (ISINFOLINE(sp
, smp
))
396 file_gline(sp
, ep
, smp
->lno
, NULL
)) == NULL
)
400 MOVE(sp
, smp
- HMAP
, 0);
401 (void)snprintf(nbuf
, sizeof(nbuf
), O_NUMBER_FMT
, smp
->lno
);
404 MOVEA(sp
, oldy
, oldx
);