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