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