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