xref: /original-bsd/usr.bin/ex/ex_vwind.c (revision d25e1985)
1 /* Copyright (c) 1980 Regents of the University of California */
2 static char *sccsid = "@(#)ex_vwind.c	5.1 08/20/80";
3 #include "ex.h"
4 #include "ex_tty.h"
5 #include "ex_vis.h"
6 
7 /*
8  * Routines to adjust the window, showing specified lines
9  * in certain positions on the screen, and scrolling in both
10  * directions.  Code here is very dependent on mode (open versus visual).
11  */
12 
13 /*
14  * Move in a nonlocal way to line addr.
15  * If it isn't on screen put it in specified context.
16  * New position for cursor is curs.
17  * Like most routines here, we vsave().
18  */
19 vmoveto(addr, curs, context)
20 	register line *addr;
21 	char *curs;
22 	char context;
23 {
24 
25 	markit(addr);
26 	vsave();
27 	vjumpto(addr, curs, context);
28 }
29 
30 /*
31  * Vjumpto is like vmoveto, but doesn't mark previous
32  * context or save linebuf as current line.
33  */
34 vjumpto(addr, curs, context)
35 	register line *addr;
36 	char *curs;
37 	char context;
38 {
39 
40 	noteit(0);
41 	if (context != 0)
42 		vcontext(addr, context);
43 	else
44 		vshow(addr, NOLINE);
45 	noteit(1);
46 	vnline(curs);
47 }
48 
49 /*
50  * Go up or down cnt (negative is up) to new position curs.
51  */
52 vupdown(cnt, curs)
53 	register int cnt;
54 	char *curs;
55 {
56 
57 	if (cnt > 0)
58 		vdown(cnt, 0, 0);
59 	else if (cnt < 0)
60 		vup(-cnt, 0, 0);
61 	if (vcnt == 0)
62 		vrepaint(curs);
63 	else
64 		vnline(curs);
65 }
66 
67 /*
68  * Go up cnt lines, afterwards preferring to be ind
69  * logical lines from the top of the screen.
70  * If scroll, then we MUST use a scroll.
71  * Otherwise clear and redraw if motion is far.
72  */
73 vup(cnt, ind, scroll)
74 	register int cnt, ind;
75 	bool scroll;
76 {
77 	register int i, tot;
78 
79 	if (dot == one) {
80 		beep();
81 		return;
82 	}
83 	vsave();
84 	i = lineDOT() - 1;
85 	if (cnt > i) {
86 		ind -= cnt - i;
87 		if (ind < 0)
88 			ind = 0;
89 		cnt = i;
90 	}
91 	if (!scroll && cnt <= vcline) {
92 		vshow(dot - cnt, NOLINE);
93 		return;
94 	}
95 	cnt -= vcline, dot -= vcline, vcline = 0;
96 	if (hold & HOLDWIG)
97 		goto contxt;
98 	if (state == VISUAL && !AL && !SR &&
99 	    cnt <= WTOP - ZERO && vfit(dot - cnt, cnt) <= WTOP - ZERO)
100 		goto okr;
101 	tot = WECHO - ZERO;
102 	if (state != VISUAL || (!AL && !SR) || (!scroll && (cnt > tot || vfit(dot - cnt, cnt) > tot / 3 + 1))) {
103 		if (ind > basWLINES / 2)
104 			ind = basWLINES / 3;
105 contxt:
106 		vcontext(dot + ind - cnt, '.');
107 		return;
108 	}
109 okr:
110 	vrollR(cnt);
111 	if (scroll) {
112 		vcline += ind, dot += ind;
113 		if (vcline >= vcnt)
114 			dot -= vcline - vcnt + 1, vcline = vcnt - 1;
115 		getDOT();
116 	}
117 }
118 
119 /*
120  * Like vup, but scrolling down.
121  */
122 vdown(cnt, ind, scroll)
123 	register int cnt, ind;
124 	bool scroll;
125 {
126 	register int i, tot;
127 
128 	if (dot == dol) {
129 		beep();
130 		return;
131 	}
132 	vsave();
133 	i = dol - dot;
134 	if (cnt > i) {
135 		ind -= cnt - i;
136 		if (ind < 0)
137 			ind = 0;
138 		cnt = i;
139 	}
140 	i = vcnt - vcline - 1;
141 	if (!scroll && cnt <= i) {
142 		vshow(dot + cnt, NOLINE);
143 		return;
144 	}
145 	cnt -= i, dot += i, vcline += i;
146 	if (hold & HOLDWIG)
147 		goto dcontxt;
148 	if (!scroll) {
149 		tot = WECHO - ZERO;
150 		if (state != VISUAL || cnt - tot > 0 || vfit(dot, cnt) > tot / 3 + 1) {
151 dcontxt:
152 			vcontext(dot + cnt, '.');
153 			return;
154 		}
155 	}
156 	if (cnt > 0)
157 		vroll(cnt);
158 	if (state == VISUAL && scroll) {
159 		vcline -= ind, dot -= ind;
160 		if (vcline < 0)
161 			dot -= vcline, vcline = 0;
162 		getDOT();
163 	}
164 }
165 
166 /*
167  * Show line addr in context where on the screen.
168  * Work here is in determining new top line implied by
169  * this placement of line addr, since we always draw from the top.
170  */
171 vcontext(addr, where)
172 	register line *addr;
173 	char where;
174 {
175 	register line *top;
176 
177 	getline(*addr);
178 	if (state != VISUAL)
179 		top = addr;
180 	else switch (where) {
181 
182 	case '^':
183 		addr = vback(addr, basWLINES - vdepth());
184 		getline(*addr);
185 		/* fall into ... */
186 
187 	case '-':
188 		top = vback(addr, basWLINES - vdepth());
189 		getline(*addr);
190 		break;
191 
192 	case '.':
193 		top = vback(addr, basWLINES / 2 - vdepth());
194 		getline(*addr);
195 		break;
196 
197 	default:
198 		top = addr;
199 		break;
200 	}
201 	if (state == ONEOPEN && LINE(0) == WBOT)
202 		vup1();
203 	vcnt = vcline = 0;
204 	vclean();
205 	if (state == CRTOPEN)
206 		vup1();
207 	vshow(addr, top);
208 }
209 
210 /*
211  * Get a clean line.  If we are in a hard open
212  * we may be able to reuse the line we are on
213  * if it is blank.  This is a real win.
214  */
215 vclean()
216 {
217 
218 	if (state != VISUAL && state != CRTOPEN) {
219 		destcol = 0;
220 		if (!ateopr())
221 			vup1();
222 		vcnt = 0;
223 	}
224 }
225 
226 /*
227  * Show line addr with the specified top line on the screen.
228  * Top may be 0; in this case have vcontext compute the top
229  * (and call us recursively).  Eventually, we clear the screen
230  * (or its open mode equivalent) and redraw.
231  */
232 vshow(addr, top)
233 	line *addr, *top;
234 {
235 #ifndef CBREAK
236 	register bool fried = 0;
237 #endif
238 	register int cnt = addr - dot;
239 	register int i = vcline + cnt;
240 	short oldhold = hold;
241 
242 	if (state != HARDOPEN && state != ONEOPEN && i >= 0 && i < vcnt) {
243 		dot = addr;
244 		getDOT();
245 		vcline = i;
246 		return;
247 	}
248 	if (state != VISUAL) {
249 		dot = addr;
250 		vopen(dot, WBOT);
251 		return;
252 	}
253 	if (top == 0) {
254 		vcontext(addr, '.');
255 		return;
256 	}
257 	dot = top;
258 #ifndef CBREAK
259 	if (vcookit(2))
260 		fried++, vcook();
261 #endif
262 	oldhold = hold;
263 	hold |= HOLDAT;
264 	vclear();
265 	vreset(0);
266 	vredraw(WTOP);
267 	/* error if vcline >= vcnt ! */
268 	vcline = addr - top;
269 	dot = addr;
270 	getDOT();
271 	hold = oldhold;
272 	vsync(LASTLINE);
273 #ifndef CBREAK
274 	if (fried)
275 		flusho(), vraw();
276 #endif
277 }
278 
279 /*
280  * reset the state.
281  * If inecho then leave us at the beginning of the echo
282  * area;  we are called this way in the middle of a :e escape
283  * from visual, e.g.
284  */
285 vreset(inecho)
286 	bool inecho;
287 {
288 
289 	vcnt = vcline = 0;
290 	WTOP = basWTOP;
291 	WLINES = basWLINES;
292 	if (inecho)
293 		splitw = 1, vgoto(WECHO, 0);
294 }
295 
296 /*
297  * Starting from which line preceding tp uses almost (but not more
298  * than) cnt physical lines?
299  */
300 line *
301 vback(tp, cnt)
302 	register int cnt;
303 	register line *tp;
304 {
305 	register int d;
306 
307 	if (cnt > 0)
308 		for (; tp > one; tp--) {
309 			getline(tp[-1]);
310 			d = vdepth();
311 			if (d > cnt)
312 				break;
313 			cnt -= d;
314 		}
315 	return (tp);
316 }
317 
318 /*
319  * How much scrolling will it take to roll cnt lines starting at tp?
320  */
321 vfit(tp, cnt)
322 	register line *tp;
323 	int cnt;
324 {
325 	register int j;
326 
327 	j = 0;
328 	while (cnt > 0) {
329 		cnt--;
330 		getline(tp[cnt]);
331 		j += vdepth();
332 	}
333 	if (tp > dot)
334 		j -= WBOT - LASTLINE;
335 	return (j);
336 }
337 
338 /*
339  * Roll cnt lines onto the screen.
340  */
341 vroll(cnt)
342 	register int cnt;
343 {
344 #ifndef CBREAK
345 	register bool fried = 0;
346 #endif
347 	short oldhold = hold;
348 
349 #ifdef ADEBUG
350 	if (trace)
351 		tfixnl(), fprintf(trace, "vroll(%d)\n", cnt);
352 #endif
353 	if (state != VISUAL)
354 		hold |= HOLDAT|HOLDROL;
355 	if (WBOT == WECHO) {
356 		vcnt = 0;
357 		if (state == ONEOPEN)
358 			vup1();
359 	}
360 #ifndef CBREAK
361 	if (vcookit(cnt))
362 		fried++, vcook();
363 #endif
364 	for (; cnt > 0 && Peekkey != ATTN; cnt--) {
365 		dot++, vcline++;
366 		vopen(dot, LASTLINE);
367 		vscrap();
368 	}
369 	hold = oldhold;
370 	if (state == HARDOPEN)
371 		sethard();
372 	vsyncCL();
373 #ifndef CBREAK
374 	if (fried)
375 		flusho(), vraw();
376 #endif
377 }
378 
379 /*
380  * Roll backwards (scroll up).
381  */
382 vrollR(cnt)
383 	register int cnt;
384 {
385 	register bool fried = 0;
386 	short oldhold = hold;
387 
388 #ifdef ADEBUG
389 	if (trace)
390 		tfixnl(), fprintf(trace, "vrollR(%d), dot=%d\n", cnt, lineDOT());
391 #endif
392 #ifndef CBREAK
393 	if (vcookit(cnt))
394 		fried++, vcook();
395 #endif
396 	if (WBOT == WECHO)
397 		vcnt = 0;
398 	heldech = 0;
399 	hold |= HOLDAT|HOLDECH;
400 	for (; cnt > 0 && Peekkey != ATTN; cnt--) {
401 		dot--;
402 		vopen(dot, WTOP);
403 		vscrap();
404 	}
405 	hold = oldhold;
406 	if (heldech)
407 		vclrech(0);
408 	vsync(LINE(vcnt-1));
409 #ifndef CBREAK
410 	if (fried)
411 		flusho(), vraw();
412 #endif
413 }
414 
415 /*
416  * Go into cooked mode (allow interrupts) during
417  * a scroll if we are at less than 1200 baud and not
418  * a 'vi' command, of if we are in a 'vi' command and the
419  * scroll is more than 2 full screens.
420  *
421  * BUG:		An interrupt during a scroll in this way
422  *		dumps to command mode.
423  */
424 vcookit(cnt)
425 	register int cnt;
426 {
427 
428 	return (cnt > 1 && (ospeed < B1200 && !initev || cnt > LINES * 2));
429 }
430 
431 /*
432  * Determine displayed depth of current line.
433  */
434 vdepth()
435 {
436 	register int d;
437 
438 	d = (column(NOSTR) + WCOLS - 1 + (Putchar == listchar) + IN) / WCOLS;
439 #ifdef ADEBUG
440 	if (trace)
441 		tfixnl(), fprintf(trace, "vdepth returns %d\n", d == 0 ? 1 : d);
442 #endif
443 	return (d == 0 ? 1 : d);
444 }
445 
446 /*
447  * Move onto a new line, with cursor at position curs.
448  */
449 vnline(curs)
450 	char *curs;
451 {
452 
453 	if (curs)
454 		wcursor = curs;
455 	else if (vmoving)
456 		wcursor = vfindcol(vmovcol);
457 	else
458 		wcursor = vskipwh(linebuf);
459 	cursor = linebuf;
460 	vmove();
461 }
462