xref: /original-bsd/usr.bin/window/wwscroll.c (revision b4971bb3)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Edward Wang at The University of California, Berkeley.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)wwscroll.c	8.1 (Berkeley) 06/06/93";
13 #endif /* not lint */
14 
15 #include "ww.h"
16 #include "tt.h"
17 
18 wwscroll(w, n)
19 register struct ww *w;
20 int n;
21 {
22 	register dir;
23 	register top;
24 
25 	if (n == 0)
26 		return;
27 	dir = n < 0 ? -1 : 1;
28 	top = w->ww_b.t - n;
29 	if (top > w->ww_w.t)
30 		top = w->ww_w.t;
31 	else if (top + w->ww_b.nr < w->ww_w.b)
32 		top = w->ww_w.b - w->ww_b.nr;
33 	n = abs(top - w->ww_b.t);
34 	if (n < w->ww_i.nr) {
35 		while (--n >= 0) {
36 			(void) wwscroll1(w, w->ww_i.t, w->ww_i.b, dir, 0);
37 			w->ww_buf += dir;
38 			w->ww_b.t -= dir;
39 			w->ww_b.b -= dir;
40 		}
41 	} else {
42 		w->ww_buf -= top - w->ww_b.t;
43 		w->ww_b.t = top;
44 		w->ww_b.b = top + w->ww_b.nr;
45 		wwredrawwin(w);
46 	}
47 }
48 
49 /*
50  * Scroll one line, between 'row1' and 'row2', in direction 'dir'.
51  * Don't adjust ww_scroll.
52  * And don't redraw 'leaveit' lines.
53  */
54 wwscroll1(w, row1, row2, dir, leaveit)
55 register struct ww *w;
56 int row1, row2, dir;
57 int leaveit;
58 {
59 	register i;
60 	int row1x, row2x;
61 	int nvis;
62 	int nvismax;
63 	int scrolled = 0;
64 
65 	/*
66 	 * See how many lines on the screen are affected.
67 	 * And calculate row1x, row2x, and left at the same time.
68 	 */
69 	for (i = row1; i < row2 && w->ww_nvis[i] == 0; i++)
70 		;
71 	if (i >= row2)			/* can't do any fancy stuff */
72 		goto out;
73 	row1x = i;
74 	for (i = row2 - 1; i >= row1 && w->ww_nvis[i] == 0; i--)
75 		;
76 	if (i <= row1x)
77 		goto out;		/* just one line is easy */
78 	row2x = i + 1;
79 
80 	/*
81 	 * See how much of this window is visible.
82 	 */
83 	nvismax = wwncol * (row2x - row1x);
84 	nvis = 0;
85 	for (i = row1x; i < row2x; i++)
86 		nvis += w->ww_nvis[i];
87 
88 	/*
89 	 * If it's a good idea to scroll and the terminal can, then do it.
90 	 */
91 	if (nvis < nvismax / 2)
92 		goto no_scroll;		/* not worth it */
93 	if ((dir > 0 ? tt.tt_scroll_down == 0 : tt.tt_scroll_up == 0) ||
94 	    (tt.tt_scroll_top != row1x || tt.tt_scroll_bot != row2x - 1) &&
95 	    tt.tt_setscroll == 0)
96 		if (tt.tt_delline == 0 || tt.tt_insline == 0)
97 			goto no_scroll;
98 	xxscroll(dir, row1x, row2x);
99 	scrolled = 1;
100 	/*
101 	 * Fix up the old screen.
102 	 */
103 	{
104 		register union ww_char *tmp;
105 		register union ww_char **cpp, **cqq;
106 
107 		if (dir > 0) {
108 			cpp = &wwos[row1x];
109 			cqq = cpp + 1;
110 			tmp = *cpp;
111 			for (i = row2x - row1x; --i > 0;)
112 				*cpp++ = *cqq++;
113 			*cpp = tmp;
114 		} else {
115 			cpp = &wwos[row2x];
116 			cqq = cpp - 1;
117 			tmp = *cqq;
118 			for (i = row2x - row1x; --i > 0;)
119 				*--cpp = *--cqq;
120 			*cqq = tmp;
121 		}
122 		for (i = wwncol; --i >= 0;)
123 			tmp++->c_w = ' ';
124 	}
125 
126 no_scroll:
127 	/*
128 	 * Fix the new screen.
129 	 */
130 	if (nvis == nvismax) {
131 		/*
132 		 * Can shift whole lines.
133 		 */
134 		if (dir > 0) {
135 			{
136 				register union ww_char *tmp;
137 				register union ww_char **cpp, **cqq;
138 
139 				cpp = &wwns[row1x];
140 				cqq = cpp + 1;
141 				tmp = *cpp;
142 				for (i = row2x - row1x; --i > 0;)
143 					*cpp++ = *cqq++;
144 				*cpp = tmp;
145 			}
146 			if (scrolled) {
147 				register char *p, *q;
148 
149 				p = &wwtouched[row1x];
150 				q = p + 1;
151 				for (i = row2x - row1x; --i > 0;)
152 					*p++ = *q++;
153 				*p |= WWU_TOUCHED;
154 			} else {
155 				register char *p;
156 
157 				p = &wwtouched[row1x];
158 				for (i = row2x - row1x; --i >= 0;)
159 					*p++ |= WWU_TOUCHED;
160 			}
161 			wwredrawwin1(w, row1, row1x, dir);
162 			wwredrawwin1(w, row2x - 1, row2 - leaveit, dir);
163 		} else {
164 			{
165 				register union ww_char *tmp;
166 				register union ww_char **cpp, **cqq;
167 
168 				cpp = &wwns[row2x];
169 				cqq = cpp - 1;
170 				tmp = *cqq;
171 				for (i = row2x - row1x; --i > 0;)
172 					*--cpp = *--cqq;
173 				*cqq = tmp;
174 			}
175 			if (scrolled) {
176 				register char *p, *q;
177 
178 				p = &wwtouched[row2x];
179 				q = p - 1;
180 				for (i = row2x - row1x; --i > 0;)
181 					*--p = *--q;
182 				*q |= WWU_TOUCHED;
183 			} else {
184 				register char *p;
185 
186 				p = &wwtouched[row1x];
187 				for (i = row2x - row1x; --i >= 0;)
188 					*p++ |= WWU_TOUCHED;
189 			}
190 			wwredrawwin1(w, row1 + leaveit, row1x + 1, dir);
191 			wwredrawwin1(w, row2x, row2, dir);
192 		}
193 	} else {
194 		if (scrolled) {
195 			register char *p;
196 
197 			p = &wwtouched[row1x];
198 			for (i = row2x - row1x; --i >= 0;)
199 				*p++ |= WWU_TOUCHED;
200 		}
201 out:
202 		if (dir > 0)
203 			wwredrawwin1(w, row1, row2 - leaveit, dir);
204 		else
205 			wwredrawwin1(w, row1 + leaveit, row2, dir);
206 	}
207 	return scrolled;
208 }
209