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