xref: /openbsd/usr.bin/mg/window.c (revision 133306f0)
1 /*	$OpenBSD: window.c,v 1.5 2001/01/29 01:58:10 niklas Exp $	*/
2 
3 /*
4  *		Window handling.
5  */
6 
7 #include "def.h"
8 
9 /*
10  * Reposition dot in the current window to line "n".  If the argument is
11  * positive, it is that line.  If it is negative it is that line from the
12  * bottom.  If it is 0 the window is centered (this is what the standard
13  * redisplay code does).  If GOSREC is undefined, default is 0, so it acts
14  * like GNU.  If GOSREC is defined, with no argument it defaults to 1 and
15  * works like in Gosling.
16  */
17 /* ARGSUSED */
18 int
19 reposition(f, n)
20 	int f, n;
21 {
22 #ifndef GOSREC
23 	curwp->w_force = (f & FFARG) ? (n >= 0 ? n + 1 : n) : 0;
24 #else /* !GOSREC */
25 	curwp->w_force = n;
26 #endif /* !GOSREC */
27 	curwp->w_flag |= WFFORCE;
28 	sgarbf = TRUE;
29 	return TRUE;
30 }
31 
32 /*
33  * Refresh the display.  A call is made to the "ttresize" entry in the
34  * terminal handler, which tries to reset "nrow" and "ncol".  They will,
35  * however, never be set outside of the NROW or NCOL range.  If the display
36  * changed size, arrange that everything is redone, then call "update" to
37  * fix the display.  We do this so the new size can be displayed.  In the
38  * normal case the call to "update" in "main.c" refreshes the screen, and
39  * all of the windows need not be recomputed.  Note that when you get to the
40  * "display unusable" message, the screen will be messed up. If you make the
41  * window bigger again, and send another command, everything will get fixed!
42  */
43 /* ARGSUSED */
44 int
45 refresh(f, n)
46 	int f, n;
47 {
48 	MGWIN	*wp;
49 	int	 oldnrow;
50 	int	 oldncol;
51 
52 	oldnrow = nrow;
53 	oldncol = ncol;
54 	ttresize();
55 	if (nrow != oldnrow || ncol != oldncol) {
56 
57 		/* find last */
58 		wp = wheadp;
59 		while (wp->w_wndp != NULL)
60 			wp = wp->w_wndp;
61 
62 		/* check if too small */
63 		if (nrow < wp->w_toprow + 3) {
64 			ewprintf("Display unusable");
65 			return (FALSE);
66 		}
67 		wp->w_ntrows = nrow - wp->w_toprow - 2;
68 		sgarbf = TRUE;
69 		update();
70 		ewprintf("New size %d by %d", nrow, ncol);
71 	} else
72 		sgarbf = TRUE;
73 	return TRUE;
74 }
75 
76 /*
77  * The command to make the next window (next => down the screen) the current
78  * window. There are no real errors, although the command does nothing if
79  * there is only 1 window on the screen.
80  */
81 /* ARGSUSED */
82 int
83 nextwind(f, n)
84 	int f, n;
85 {
86 	MGWIN	*wp;
87 
88 	if ((wp = curwp->w_wndp) == NULL)
89 		wp = wheadp;
90 	curwp = wp;
91 	curbp = wp->w_bufp;
92 	return TRUE;
93 }
94 
95 #ifdef	GOSMACS
96 
97 /* not in Gnu Emacs */
98 /*
99  * This command makes the previous window (previous => up the screen) the
100  * current window. There are no errors, although the command does not do
101  * a lot if there is only 1 window.
102  */
103 /* ARGSUSED */
104 int
105 prevwind(f, n)
106 	int f, n;
107 {
108 	MGWIN	*wp1, *wp2;
109 
110 	wp1 = wheadp;
111 	wp2 = curwp;
112 	if (wp1 == wp2)
113 		wp2 = NULL;
114 	while (wp1->w_wndp != wp2)
115 		wp1 = wp1->w_wndp;
116 	curwp = wp1;
117 	curbp = wp1->w_bufp;
118 	return TRUE;
119 }
120 #endif /* GOSEMACS */
121 
122 /*
123  * This command makes the current window the only window on the screen.  Try
124  * to set the framing so that "." does not have to move on the display.  Some
125  * care has to be taken to keep the values of dot and mark in the buffer
126  * structures right if the distruction of a window makes a buffer become
127  * undisplayed.
128  */
129 /* ARGSUSED */
130 int
131 onlywind(f, n)
132 	int f, n;
133 {
134 	MGWIN	*wp;
135 	LINE	*lp;
136 	int	 i;
137 
138 	while (wheadp != curwp) {
139 		wp = wheadp;
140 		wheadp = wp->w_wndp;
141 		if (--wp->w_bufp->b_nwnd == 0) {
142 			wp->w_bufp->b_dotp = wp->w_dotp;
143 			wp->w_bufp->b_doto = wp->w_doto;
144 			wp->w_bufp->b_markp = wp->w_markp;
145 			wp->w_bufp->b_marko = wp->w_marko;
146 		}
147 		free((char *)wp);
148 	}
149 	while (curwp->w_wndp != NULL) {
150 		wp = curwp->w_wndp;
151 		curwp->w_wndp = wp->w_wndp;
152 		if (--wp->w_bufp->b_nwnd == 0) {
153 			wp->w_bufp->b_dotp = wp->w_dotp;
154 			wp->w_bufp->b_doto = wp->w_doto;
155 			wp->w_bufp->b_markp = wp->w_markp;
156 			wp->w_bufp->b_marko = wp->w_marko;
157 		}
158 		free((char *)wp);
159 	}
160 	lp = curwp->w_linep;
161 	i = curwp->w_toprow;
162 	while (i != 0 && lback(lp) != curbp->b_linep) {
163 		--i;
164 		lp = lback(lp);
165 	}
166 	curwp->w_toprow = 0;
167 
168 	/* 2 = mode, echo */
169 	curwp->w_ntrows = nrow - 2;
170 	curwp->w_linep = lp;
171 	curwp->w_flag |= WFMODE | WFHARD;
172 	return TRUE;
173 }
174 
175 /*
176  * Split the current window.  A window smaller than 3 lines cannot be split.
177  * The only other error that is possible is a "malloc" failure allocating the
178  * structure for the new window.
179  */
180 /* ARGSUSED */
181 int
182 splitwind(f, n)
183 	int f, n;
184 {
185 	MGWIN	*wp, *wp1, *wp2;
186 	LINE	*lp;
187 	int	 ntru, ntrd, ntrl;
188 
189 	if (curwp->w_ntrows < 3) {
190 		ewprintf("Cannot split a %d line window", curwp->w_ntrows);
191 		return (FALSE);
192 	}
193 	if ((wp = (MGWIN *)malloc(sizeof(MGWIN))) == NULL) {
194 		ewprintf("Can't get %d", sizeof(MGWIN));
195 		return (FALSE);
196 	}
197 
198 	/* displayed twice */
199 	++curbp->b_nwnd;
200 	wp->w_bufp = curbp;
201 	wp->w_dotp = curwp->w_dotp;
202 	wp->w_doto = curwp->w_doto;
203 	wp->w_markp = curwp->w_markp;
204 	wp->w_marko = curwp->w_marko;
205 	wp->w_flag = 0;
206 	wp->w_force = 0;
207 	ntru = (curwp->w_ntrows - 1) / 2;	/* Upper size		 */
208 	ntrl = (curwp->w_ntrows - 1) - ntru;	/* Lower size		 */
209 	lp = curwp->w_linep;
210 	ntrd = 0;
211 	while (lp != curwp->w_dotp) {
212 		++ntrd;
213 		lp = lforw(lp);
214 	}
215 	lp = curwp->w_linep;
216 
217 	/* old is upper window */
218 	if (ntrd <= ntru) {
219 		/* hit mode line */
220 		if (ntrd == ntru)
221 			lp = lforw(lp);
222 		curwp->w_ntrows = ntru;
223 		wp->w_wndp = curwp->w_wndp;
224 		curwp->w_wndp = wp;
225 		wp->w_toprow = curwp->w_toprow + ntru + 1;
226 		wp->w_ntrows = ntrl;
227 	/* old is lower window */
228 	} else {
229 		wp1 = NULL;
230 		wp2 = wheadp;
231 		while (wp2 != curwp) {
232 			wp1 = wp2;
233 			wp2 = wp2->w_wndp;
234 		}
235 		if (wp1 == NULL)
236 			wheadp = wp;
237 		else
238 			wp1->w_wndp = wp;
239 		wp->w_wndp = curwp;
240 		wp->w_toprow = curwp->w_toprow;
241 		wp->w_ntrows = ntru;
242 
243 		/* mode line */
244 		++ntru;
245 		curwp->w_toprow += ntru;
246 		curwp->w_ntrows = ntrl;
247 		while (ntru--)
248 			lp = lforw(lp);
249 	}
250 
251 	/* adjust the top lines if necessary */
252 	curwp->w_linep = lp;
253 	wp->w_linep = lp;
254 
255 	curwp->w_flag |= WFMODE | WFHARD;
256 	wp->w_flag |= WFMODE | WFHARD;
257 	return TRUE;
258 }
259 
260 /*
261  * Enlarge the current window.  Find the window that loses space.  Make sure
262  * it is big enough.  If so, hack the window descriptions, and ask redisplay
263  * to do all the hard work.  You don't just set "force reframe" because dot
264  * would move.
265  */
266 /* ARGSUSED */
267 int
268 enlargewind(f, n)
269 	int f, n;
270 {
271 	MGWIN	*adjwp;
272 	LINE	*lp;
273 	int	 i;
274 
275 	if (n < 0)
276 		return shrinkwind(f, -n);
277 	if (wheadp->w_wndp == NULL) {
278 		ewprintf("Only one window");
279 		return FALSE;
280 	}
281 	if ((adjwp = curwp->w_wndp) == NULL) {
282 		adjwp = wheadp;
283 		while (adjwp->w_wndp != curwp)
284 			adjwp = adjwp->w_wndp;
285 	}
286 	if (adjwp->w_ntrows <= n) {
287 		ewprintf("Impossible change");
288 		return FALSE;
289 	}
290 
291 	/* shrink below */
292 	if (curwp->w_wndp == adjwp) {
293 		lp = adjwp->w_linep;
294 		for (i = 0; i < n && lp != adjwp->w_bufp->b_linep; ++i)
295 			lp = lforw(lp);
296 		adjwp->w_linep = lp;
297 		adjwp->w_toprow += n;
298 	/* shrink above */
299 	} else {
300 		lp = curwp->w_linep;
301 		for (i = 0; i < n && lback(lp) != curbp->b_linep; ++i)
302 			lp = lback(lp);
303 		curwp->w_linep = lp;
304 		curwp->w_toprow -= n;
305 	}
306 	curwp->w_ntrows += n;
307 	adjwp->w_ntrows -= n;
308 	curwp->w_flag |= WFMODE | WFHARD;
309 	adjwp->w_flag |= WFMODE | WFHARD;
310 	return TRUE;
311 }
312 
313 /*
314  * Shrink the current window.  Find the window that gains space.  Hack at the
315  * window descriptions. Ask the redisplay to do all the hard work.
316  */
317 int
318 shrinkwind(f, n)
319 	int f, n;
320 {
321 	MGWIN	*adjwp;
322 	LINE	*lp;
323 	int	 i;
324 
325 	if (n < 0)
326 		return enlargewind(f, -n);
327 	if (wheadp->w_wndp == NULL) {
328 		ewprintf("Only one window");
329 		return FALSE;
330 	}
331 	/*
332 	 * Bit of flakiness - KRANDOM means it was an internal call, and
333 	 * to be trusted implicitly about sizes.
334 	 */
335 	if (!(f & FFRAND) && curwp->w_ntrows <= n) {
336 		ewprintf("Impossible change");
337 		return (FALSE);
338 	}
339 	if ((adjwp = curwp->w_wndp) == NULL) {
340 		adjwp = wheadp;
341 		while (adjwp->w_wndp != curwp)
342 			adjwp = adjwp->w_wndp;
343 	}
344 
345 	/* grow below */
346 	if (curwp->w_wndp == adjwp) {
347 		lp = adjwp->w_linep;
348 		for (i = 0; i < n && lback(lp) != adjwp->w_bufp->b_linep; ++i)
349 			lp = lback(lp);
350 		adjwp->w_linep = lp;
351 		adjwp->w_toprow -= n;
352 	/* grow above */
353 	} else {
354 		lp = curwp->w_linep;
355 		for (i = 0; i < n && lp != curbp->b_linep; ++i)
356 			lp = lforw(lp);
357 		curwp->w_linep = lp;
358 		curwp->w_toprow += n;
359 	}
360 	curwp->w_ntrows -= n;
361 	adjwp->w_ntrows += n;
362 	curwp->w_flag |= WFMODE | WFHARD;
363 	adjwp->w_flag |= WFMODE | WFHARD;
364 	return (TRUE);
365 }
366 
367 /*
368  * Delete current window. Call shrink-window to do the screen updating, then
369  * throw away the window.
370  */
371 /* ARGSUSED */
372 int
373 delwind(f, n)
374 	int f, n;
375 {
376 	MGWIN	*wp, *nwp;
377 
378 	wp = curwp;		/* Cheap...		 */
379 
380 	/* shrinkwind returning false means only one window... */
381 	if (shrinkwind(FFRAND, wp->w_ntrows + 1) == FALSE)
382 		return FALSE;
383 	if (--wp->w_bufp->b_nwnd == 0) {
384 		wp->w_bufp->b_dotp = wp->w_dotp;
385 		wp->w_bufp->b_doto = wp->w_doto;
386 		wp->w_bufp->b_markp = wp->w_markp;
387 		wp->w_bufp->b_marko = wp->w_marko;
388 	}
389 
390 	/* since shrinkwind did't crap out, we know we have a second window */
391 	if (wp == wheadp)
392 		wheadp = curwp = wp->w_wndp;
393 	else if ((curwp = wp->w_wndp) == NULL)
394 		curwp = wheadp;
395 	curbp = curwp->w_bufp;
396 	for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp)
397 		if (nwp->w_wndp == wp) {
398 			nwp->w_wndp = wp->w_wndp;
399 			break;
400 		}
401 	free((char *)wp);
402 	return TRUE;
403 }
404 
405 /*
406  * Pick a window for a pop-up.  Split the screen if there is only one window.
407  * Pick the uppermost window that isn't the current window. An LRU algorithm
408  * might be better. Return a pointer, or NULL on error.
409  */
410 MGWIN *
411 wpopup()
412 {
413 	MGWIN	*wp;
414 
415 	if (wheadp->w_wndp == NULL
416 	    && splitwind(FFRAND, 0) == FALSE)
417 		return NULL;
418 
419 	/* find a window to use */
420 	wp = wheadp;
421 
422 	while (wp != NULL && wp == curwp)
423 		wp = wp->w_wndp;
424 	return wp;
425 }
426