119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: vs_split.c,v 10.42 2001/06/25 15:19:38 skimo Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <errno.h>
2219304Speter#include <limits.h>
2319304Speter#include <stdio.h>
2419304Speter#include <stdlib.h>
2519304Speter#include <string.h>
2619304Speter
2719304Speter#include "../common/common.h"
2819304Speter#include "vi.h"
2919304Speter
30254225Spetertypedef enum { HORIZ_FOLLOW, HORIZ_PRECEDE, VERT_FOLLOW, VERT_PRECEDE } jdir_t;
3119304Speter
32254225Speterstatic SCR	*vs_getbg __P((SCR *, char *));
33254225Speterstatic void      vs_insert __P((SCR *sp, GS *gp));
34254225Speterstatic int	 vs_join __P((SCR *, SCR **, jdir_t *));
35254225Speter
3619304Speter/*
3719304Speter * vs_split --
38254225Speter *	Create a new screen, horizontally.
3919304Speter *
4019304Speter * PUBLIC: int vs_split __P((SCR *, SCR *, int));
4119304Speter */
4219304Speterint
43254225Spetervs_split(
44254225Speter	SCR *sp,
45254225Speter	SCR *new,
46254225Speter	int ccl)		/* Colon-command line split. */
4719304Speter{
4819304Speter	GS *gp;
4919304Speter	SMAP *smp;
5019304Speter	size_t half;
5119304Speter	int issmallscreen, splitup;
5219304Speter
5319304Speter	gp = sp->gp;
5419304Speter
5519304Speter	/* Check to see if it's possible. */
5619304Speter	/* XXX: The IS_ONELINE fix will change this, too. */
5719304Speter	if (sp->rows < 4) {
5819304Speter		msgq(sp, M_ERR,
5919304Speter		    "222|Screen must be larger than %d lines to split", 4 - 1);
6019304Speter		return (1);
6119304Speter	}
6219304Speter
6319304Speter	/* Wait for any messages in the screen. */
6419304Speter	vs_resolve(sp, NULL, 1);
6519304Speter
6619304Speter	/* Get a new screen map. */
6719304Speter	CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
6819304Speter	if (_HMAP(new) == NULL)
6919304Speter		return (1);
7019304Speter	_HMAP(new)->lno = sp->lno;
7119304Speter	_HMAP(new)->coff = 0;
7219304Speter	_HMAP(new)->soff = 1;
7319304Speter
74254225Speter	/* Split the screen in half. */
75254225Speter	half = sp->rows / 2;
76254225Speter	if (ccl && half > 6)
77254225Speter		half = 6;
78254225Speter
7919304Speter	/*
8019304Speter	 * Small screens: see vs_refresh.c section 6a.  Set a flag so
8119304Speter	 * we know to fix the screen up later.
8219304Speter	 */
8319304Speter	issmallscreen = IS_SMALL(sp);
8419304Speter
8519304Speter	/* The columns in the screen don't change. */
86254225Speter	new->coff = sp->coff;
8719304Speter	new->cols = sp->cols;
8819304Speter
8919304Speter	/*
9019304Speter	 * Split the screen, and link the screens together.  If creating a
9119304Speter	 * screen to edit the colon command line or the cursor is in the top
9219304Speter	 * half of the current screen, the new screen goes under the current
9319304Speter	 * screen.  Else, it goes above the current screen.
9419304Speter	 *
9519304Speter	 * Recalculate current cursor position based on sp->lno, we're called
9619304Speter	 * with the cursor on the colon command line.  Then split the screen
9719304Speter	 * in half and update the shared information.
9819304Speter	 */
9919304Speter	splitup =
10019304Speter	    !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (smp - HMAP) + 1) >= half;
10119304Speter	if (splitup) {				/* Old is bottom half. */
10219304Speter		new->rows = sp->rows - half;	/* New. */
103254225Speter		new->roff = sp->roff;
10419304Speter		sp->rows = half;		/* Old. */
105254225Speter		sp->roff += new->rows;
10619304Speter
10719304Speter		/*
10819304Speter		 * If the parent is the bottom half of the screen, shift
10919304Speter		 * the map down to match on-screen text.
11019304Speter		 */
111254225Speter		memcpy(_HMAP(sp), _HMAP(sp) + new->rows,
11219304Speter		    (sp->t_maxrows - new->rows) * sizeof(SMAP));
11319304Speter	} else {				/* Old is top half. */
11419304Speter		new->rows = half;		/* New. */
11519304Speter		sp->rows -= half;		/* Old. */
116254225Speter		new->roff = sp->roff + sp->rows;
11719304Speter	}
11819304Speter
11919304Speter	/* Adjust maximum text count. */
12019304Speter	sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
12119304Speter	new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1;
12219304Speter
12319304Speter	/*
12419304Speter	 * Small screens: see vs_refresh.c, section 6a.
12519304Speter	 *
12619304Speter	 * The child may have different screen options sizes than the parent,
12719304Speter	 * so use them.  Guarantee that text counts aren't larger than the
12819304Speter	 * new screen sizes.
12919304Speter	 */
13019304Speter	if (issmallscreen) {
13119304Speter		/* Fix the text line count for the parent. */
13219304Speter		if (splitup)
13319304Speter			sp->t_rows -= new->rows;
13419304Speter
13519304Speter		/* Fix the parent screen. */
13619304Speter		if (sp->t_rows > sp->t_maxrows)
13719304Speter			sp->t_rows = sp->t_maxrows;
13819304Speter		if (sp->t_minrows > sp->t_maxrows)
13919304Speter			sp->t_minrows = sp->t_maxrows;
14019304Speter
14119304Speter		/* Fix the child screen. */
14219304Speter		new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
14319304Speter		if (new->t_rows > new->t_maxrows)
14419304Speter			new->t_rows = new->t_maxrows;
14519304Speter		if (new->t_minrows > new->t_maxrows)
14619304Speter			new->t_minrows = new->t_maxrows;
14719304Speter	} else {
14819304Speter		sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
14919304Speter
15019304Speter		/*
15119304Speter		 * The new screen may be a small screen, even if the parent
15219304Speter		 * was not.  Don't complain if O_WINDOW is too large, we're
15319304Speter		 * splitting the screen so the screen is much smaller than
15419304Speter		 * normal.
15519304Speter		 */
15619304Speter		new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
15719304Speter		if (new->t_rows > new->rows - 1)
15819304Speter			new->t_minrows = new->t_rows =
15919304Speter			    IS_ONELINE(new) ? 1 : new->rows - 1;
16019304Speter	}
16119304Speter
16219304Speter	/* Adjust the ends of the new and old maps. */
16319304Speter	_TMAP(sp) = IS_ONELINE(sp) ?
16419304Speter	    _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1);
16519304Speter	_TMAP(new) = IS_ONELINE(new) ?
16619304Speter	    _HMAP(new) : _HMAP(new) + (new->t_rows - 1);
16719304Speter
16819304Speter	/* Reset the length of the default scroll. */
16919304Speter	if ((sp->defscroll = sp->t_maxrows / 2) == 0)
17019304Speter		sp->defscroll = 1;
17119304Speter	if ((new->defscroll = new->t_maxrows / 2) == 0)
17219304Speter		new->defscroll = 1;
17319304Speter
174254225Speter	/* Fit the screen into the logical chain. */
175254225Speter	vs_insert(new, sp->gp);
176254225Speter
177254225Speter	/* Tell the display that we're splitting. */
178254225Speter	(void)gp->scr_split(sp, new);
179254225Speter
18019304Speter	/*
18119304Speter	 * Initialize the screen flags:
18219304Speter	 *
18319304Speter	 * If we're in vi mode in one screen, we don't have to reinitialize.
18419304Speter	 * This isn't just a cosmetic fix.  The path goes like this:
18519304Speter	 *
18619304Speter	 *	return into vi(), SC_SSWITCH set
18719304Speter	 *	call vs_refresh() with SC_STATUS set
18819304Speter	 *	call vs_resolve to display the status message
18919304Speter	 *	call vs_refresh() because the SC_SCR_VI bit isn't set
19019304Speter	 *
19119304Speter	 * Things go downhill at this point.
19219304Speter	 *
19319304Speter	 * Draw the new screen from scratch, and add a status line.
19419304Speter	 */
19519304Speter	F_SET(new,
19619304Speter	    SC_SCR_REFORMAT | SC_STATUS |
19719304Speter	    F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
19819304Speter	return (0);
19919304Speter}
20019304Speter
20119304Speter/*
202254225Speter * vs_vsplit --
203254225Speter *	Create a new screen, vertically.
204254225Speter *
205254225Speter * PUBLIC: int vs_vsplit __P((SCR *, SCR *));
206254225Speter */
207254225Speterint
208254225Spetervs_vsplit(SCR *sp, SCR *new)
209254225Speter{
210254225Speter	GS *gp;
211254225Speter	size_t cols;
212254225Speter
213254225Speter	gp = sp->gp;
214254225Speter
215254225Speter	/* Check to see if it's possible. */
216254225Speter	if (sp->cols / 2 <= MINIMUM_SCREEN_COLS) {
217254225Speter		msgq(sp, M_ERR,
218254225Speter		    "288|Screen must be larger than %d columns to split",
219254225Speter		    MINIMUM_SCREEN_COLS * 2);
220254225Speter		return (1);
221254225Speter	}
222254225Speter
223254225Speter	/* Wait for any messages in the screen. */
224254225Speter	vs_resolve(sp, NULL, 1);
225254225Speter
226254225Speter	/* Get a new screen map. */
227254225Speter	CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
228254225Speter	if (_HMAP(new) == NULL)
229254225Speter		return (1);
230254225Speter	_HMAP(new)->lno = sp->lno;
231254225Speter	_HMAP(new)->coff = 0;
232254225Speter	_HMAP(new)->soff = 1;
233254225Speter
234254225Speter	/*
235254225Speter	 * Split the screen in half; we have to sacrifice a column to delimit
236254225Speter	 * the screens.
237254225Speter	 *
238254225Speter	 * XXX
239254225Speter	 * We always split to the right... that makes more sense to me, and
240254225Speter	 * I don't want to play the stupid games that I play when splitting
241254225Speter	 * horizontally.
242254225Speter	 *
243254225Speter	 * XXX
244254225Speter	 * We reserve a column for the screen, "knowing" that curses needs
245254225Speter	 * one.  This should be worked out with the display interface.
246254225Speter	 */
247254225Speter	cols = sp->cols / 2;
248254225Speter	new->cols = sp->cols - cols - 1;
249254225Speter	sp->cols = cols;
250254225Speter	new->coff = sp->coff + cols + 1;
251254225Speter	sp->cno = 0;
252254225Speter
253254225Speter	/* Nothing else changes. */
254254225Speter	new->rows = sp->rows;
255254225Speter	new->t_rows = sp->t_rows;
256254225Speter	new->t_maxrows = sp->t_maxrows;
257254225Speter	new->t_minrows = sp->t_minrows;
258254225Speter	new->roff = sp->roff;
259254225Speter	new->defscroll = sp->defscroll;
260254225Speter	_TMAP(new) = _HMAP(new) + (new->t_rows - 1);
261254225Speter
262254225Speter	/* Fit the screen into the logical chain. */
263254225Speter	vs_insert(new, sp->gp);
264254225Speter
265254225Speter	/* Tell the display that we're splitting. */
266254225Speter	(void)gp->scr_split(sp, new);
267254225Speter
268254225Speter	/* Redraw the old screen from scratch. */
269254225Speter	F_SET(sp, SC_SCR_REFORMAT | SC_STATUS);
270254225Speter
271254225Speter	/*
272254225Speter	 * Initialize the screen flags:
273254225Speter	 *
274254225Speter	 * If we're in vi mode in one screen, we don't have to reinitialize.
275254225Speter	 * This isn't just a cosmetic fix.  The path goes like this:
276254225Speter	 *
277254225Speter	 *	return into vi(), SC_SSWITCH set
278254225Speter	 *	call vs_refresh() with SC_STATUS set
279254225Speter	 *	call vs_resolve to display the status message
280254225Speter	 *	call vs_refresh() because the SC_SCR_VI bit isn't set
281254225Speter	 *
282254225Speter	 * Things go downhill at this point.
283254225Speter	 *
284254225Speter	 * Draw the new screen from scratch, and add a status line.
285254225Speter	 */
286254225Speter	F_SET(new,
287254225Speter	    SC_SCR_REFORMAT | SC_STATUS |
288254225Speter	    F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
289254225Speter	return (0);
290254225Speter}
291254225Speter
292254225Speter/*
293254225Speter * vs_insert --
294254225Speter *	Insert the new screen into the correct place in the logical
295254225Speter *	chain.
296254225Speter */
297254225Speterstatic void
298254225Spetervs_insert(SCR *sp, GS *gp)
299254225Speter{
300254225Speter	SCR *tsp;
301254225Speter
302254225Speter	gp = sp->gp;
303254225Speter
304254225Speter	/* Move past all screens with lower row numbers. */
305254225Speter	TAILQ_FOREACH(tsp, gp->dq, q)
306254225Speter		if (tsp->roff >= sp->roff)
307254225Speter			break;
308254225Speter	/*
309254225Speter	 * Move past all screens with the same row number and lower
310254225Speter	 * column numbers.
311254225Speter	 */
312254225Speter	for (; tsp != NULL; tsp = TAILQ_NEXT(tsp, q))
313254225Speter		if (tsp->roff != sp->roff || tsp->coff > sp->coff)
314254225Speter			break;
315254225Speter
316254225Speter	/*
317254225Speter	 * If we reached the end, this screen goes there.  Otherwise,
318254225Speter	 * put it before or after the screen where we stopped.
319254225Speter	 */
320254225Speter	if (tsp == NULL) {
321254225Speter		TAILQ_INSERT_TAIL(gp->dq, sp, q);
322254225Speter	} else if (tsp->roff < sp->roff ||
323254225Speter	    (tsp->roff == sp->roff && tsp->coff < sp->coff)) {
324254225Speter		TAILQ_INSERT_AFTER(gp->dq, tsp, sp, q);
325254225Speter	} else
326254225Speter		TAILQ_INSERT_BEFORE(tsp, sp, q);
327254225Speter}
328254225Speter
329254225Speter/*
33019304Speter * vs_discard --
33119304Speter *	Discard the screen, folding the real-estate into a related screen,
33219304Speter *	if one exists, and return that screen.
33319304Speter *
33419304Speter * PUBLIC: int vs_discard __P((SCR *, SCR **));
33519304Speter */
33619304Speterint
337254225Spetervs_discard(SCR *sp, SCR **spp)
33819304Speter{
339254225Speter	GS *gp;
340254225Speter	SCR *tsp, **lp, *list[100];
341254225Speter	jdir_t jdir;
34219304Speter
343254225Speter	gp = sp->gp;
344254225Speter
34519304Speter	/*
34619304Speter	 * Save the old screen's cursor information.
34719304Speter	 *
34819304Speter	 * XXX
34919304Speter	 * If called after file_end(), and the underlying file was a tmp
35019304Speter	 * file, it may have gone away.
35119304Speter	 */
35219304Speter	if (sp->frp != NULL) {
35319304Speter		sp->frp->lno = sp->lno;
35419304Speter		sp->frp->cno = sp->cno;
35519304Speter		F_SET(sp->frp, FR_CURSORSET);
35619304Speter	}
35719304Speter
358254225Speter	/* If no other screens to join, we're done. */
359254225Speter	if (!IS_SPLIT(sp)) {
360254225Speter		(void)gp->scr_discard(sp, NULL);
36119304Speter
362254225Speter		if (spp != NULL)
363254225Speter			*spp = NULL;
36419304Speter		return (0);
365254225Speter	}
366254225Speter
36719304Speter	/*
368254225Speter	 * Find a set of screens that cover one of the screen's borders.
369254225Speter	 * Check the vertical axis first, for no particular reason.
37019304Speter	 *
371254225Speter	 * XXX
372254225Speter	 * It's possible (I think?), to create a screen that shares no full
373254225Speter	 * border with any other set of screens, so we can't discard it.  We
374254225Speter	 * just complain at the user until they clean it up.
37519304Speter	 */
376254225Speter	if (vs_join(sp, list, &jdir))
377254225Speter		return (1);
37819304Speter
37919304Speter	/*
380254225Speter	 * Modify the affected screens.  Redraw the modified screen(s) from
381254225Speter	 * scratch, setting a status line.  If this is ever a performance
382254225Speter	 * problem we could play games with the map, but I wrote that code
383254225Speter	 * before and it was never clean or easy.
38419304Speter	 *
385254225Speter	 * Don't clean up the discarded screen's information.  If the screen
386254225Speter	 * isn't exiting, we'll do the work when the user redisplays it.
38719304Speter	 */
388254225Speter	switch (jdir) {
389254225Speter	case HORIZ_FOLLOW:
390254225Speter	case HORIZ_PRECEDE:
391254225Speter		for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) {
392254225Speter			/*
393254225Speter			 * Small screens: see vs_refresh.c section 6a.  Adjust
394254225Speter			 * text line info, unless it's a small screen.
395254225Speter			 *
396254225Speter			 * Reset the length of the default scroll.
397254225Speter			 *
398254225Speter			 * Reset the map references.
399254225Speter			 */
400254225Speter			tsp->rows += sp->rows;
401254225Speter			if (!IS_SMALL(tsp))
402254225Speter				tsp->t_rows = tsp->t_minrows = tsp->rows - 1;
403254225Speter			tsp->t_maxrows = tsp->rows - 1;
404254225Speter
405254225Speter			tsp->defscroll = tsp->t_maxrows / 2;
406254225Speter
407254225Speter			*(_HMAP(tsp) + (tsp->t_rows - 1)) = *_TMAP(tsp);
408254225Speter			_TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
409254225Speter
410254225Speter			switch (jdir) {
411254225Speter			case HORIZ_FOLLOW:
412254225Speter				tsp->roff = sp->roff;
413254225Speter				vs_sm_fill(tsp, OOBLNO, P_TOP);
414254225Speter				break;
415254225Speter			case HORIZ_PRECEDE:
416254225Speter				vs_sm_fill(tsp, OOBLNO, P_BOTTOM);
417254225Speter				break;
418254225Speter			default:
419254225Speter				abort();
420254225Speter			}
421254225Speter			F_SET(tsp, SC_STATUS);
422254225Speter		}
42319304Speter		break;
424254225Speter	case VERT_FOLLOW:
425254225Speter	case VERT_PRECEDE:
426254225Speter		for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) {
427254225Speter			if (jdir == VERT_FOLLOW)
428254225Speter				tsp->coff = sp->coff;
429254225Speter			tsp->cols += sp->cols + 1;	/* XXX: DIVIDER */
430254225Speter			vs_sm_fill(tsp, OOBLNO, P_TOP);
431254225Speter			F_SET(tsp, SC_STATUS);
432254225Speter		}
43319304Speter		break;
43419304Speter	default:
43519304Speter		abort();
43619304Speter	}
43719304Speter
438254225Speter	/* Find the closest screen that changed and move to it. */
439254225Speter	tsp = list[0];
440254225Speter	if (spp != NULL)
441254225Speter		*spp = tsp;
442254225Speter
443254225Speter	/* Tell the display that we're discarding a screen. */
444254225Speter	(void)gp->scr_discard(sp, list);
445254225Speter
44619304Speter	return (0);
44719304Speter}
44819304Speter
44919304Speter/*
450254225Speter * vs_join --
451254225Speter *	Find a set of screens that covers a screen's border.
452254225Speter */
453254225Speterstatic int
454254225Spetervs_join(SCR *sp, SCR **listp, jdir_t *jdirp)
455254225Speter{
456254225Speter	GS *gp;
457254225Speter	SCR **lp, *tsp;
458254225Speter	int first;
459254225Speter	size_t tlen;
460254225Speter
461254225Speter	gp = sp->gp;
462254225Speter
463254225Speter	/* Check preceding vertical. */
464254225Speter	for (lp = listp, tlen = sp->rows,
465254225Speter	    tsp = TAILQ_FIRST(gp->dq);
466254225Speter	    tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) {
467254225Speter		if (sp == tsp)
468254225Speter			continue;
469254225Speter		/* Test if precedes the screen vertically. */
470254225Speter		if (tsp->coff + tsp->cols + 1 != sp->coff)
471254225Speter			continue;
472254225Speter		/*
473254225Speter		 * Test if a subset on the vertical axis.  If overlaps the
474254225Speter		 * beginning or end, we can't join on this axis at all.
475254225Speter		 */
476254225Speter		if (tsp->roff > sp->roff + sp->rows)
477254225Speter			continue;
478254225Speter		if (tsp->roff < sp->roff) {
479254225Speter			if (tsp->roff + tsp->rows >= sp->roff)
480254225Speter				break;
481254225Speter			continue;
482254225Speter		}
483254225Speter		if (tsp->roff + tsp->rows > sp->roff + sp->rows)
484254225Speter			break;
485254225Speter#ifdef DEBUG
486254225Speter		if (tlen < tsp->rows)
487254225Speter			abort();
488254225Speter#endif
489254225Speter		tlen -= tsp->rows;
490254225Speter		*lp++ = tsp;
491254225Speter	}
492254225Speter	if (tlen == 0) {
493254225Speter		*lp = NULL;
494254225Speter		*jdirp = VERT_PRECEDE;
495254225Speter		return (0);
496254225Speter	}
497254225Speter
498254225Speter	/* Check following vertical. */
499254225Speter	for (lp = listp, tlen = sp->rows,
500254225Speter	    tsp = TAILQ_FIRST(gp->dq);
501254225Speter	    tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) {
502254225Speter		if (sp == tsp)
503254225Speter			continue;
504254225Speter		/* Test if follows the screen vertically. */
505254225Speter		if (tsp->coff != sp->coff + sp->cols + 1)
506254225Speter			continue;
507254225Speter		/*
508254225Speter		 * Test if a subset on the vertical axis.  If overlaps the
509254225Speter		 * beginning or end, we can't join on this axis at all.
510254225Speter		 */
511254225Speter		if (tsp->roff > sp->roff + sp->rows)
512254225Speter			continue;
513254225Speter		if (tsp->roff < sp->roff) {
514254225Speter			if (tsp->roff + tsp->rows >= sp->roff)
515254225Speter				break;
516254225Speter			continue;
517254225Speter		}
518254225Speter		if (tsp->roff + tsp->rows > sp->roff + sp->rows)
519254225Speter			break;
520254225Speter#ifdef DEBUG
521254225Speter		if (tlen < tsp->rows)
522254225Speter			abort();
523254225Speter#endif
524254225Speter		tlen -= tsp->rows;
525254225Speter		*lp++ = tsp;
526254225Speter	}
527254225Speter	if (tlen == 0) {
528254225Speter		*lp = NULL;
529254225Speter		*jdirp = VERT_FOLLOW;
530254225Speter		return (0);
531254225Speter	}
532254225Speter
533254225Speter	/* Check preceding horizontal. */
534254225Speter	for (first = 0, lp = listp, tlen = sp->cols,
535254225Speter	    tsp = TAILQ_FIRST(gp->dq);
536254225Speter	    tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) {
537254225Speter		if (sp == tsp)
538254225Speter			continue;
539254225Speter		/* Test if precedes the screen horizontally. */
540254225Speter		if (tsp->roff + tsp->rows != sp->roff)
541254225Speter			continue;
542254225Speter		/*
543254225Speter		 * Test if a subset on the horizontal axis.  If overlaps the
544254225Speter		 * beginning or end, we can't join on this axis at all.
545254225Speter		 */
546254225Speter		if (tsp->coff > sp->coff + sp->cols)
547254225Speter			continue;
548254225Speter		if (tsp->coff < sp->coff) {
549254225Speter			if (tsp->coff + tsp->cols >= sp->coff)
550254225Speter				break;
551254225Speter			continue;
552254225Speter		}
553254225Speter		if (tsp->coff + tsp->cols > sp->coff + sp->cols)
554254225Speter			break;
555254225Speter#ifdef DEBUG
556254225Speter		if (tlen < tsp->cols)
557254225Speter			abort();
558254225Speter#endif
559254225Speter		tlen -= tsp->cols + first;
560254225Speter		first = 1;
561254225Speter		*lp++ = tsp;
562254225Speter	}
563254225Speter	if (tlen == 0) {
564254225Speter		*lp = NULL;
565254225Speter		*jdirp = HORIZ_PRECEDE;
566254225Speter		return (0);
567254225Speter	}
568254225Speter
569254225Speter	/* Check following horizontal. */
570254225Speter	for (first = 0, lp = listp, tlen = sp->cols,
571254225Speter	    tsp = TAILQ_FIRST(gp->dq);
572254225Speter	    tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) {
573254225Speter		if (sp == tsp)
574254225Speter			continue;
575254225Speter		/* Test if precedes the screen horizontally. */
576254225Speter		if (tsp->roff != sp->roff + sp->rows)
577254225Speter			continue;
578254225Speter		/*
579254225Speter		 * Test if a subset on the horizontal axis.  If overlaps the
580254225Speter		 * beginning or end, we can't join on this axis at all.
581254225Speter		 */
582254225Speter		if (tsp->coff > sp->coff + sp->cols)
583254225Speter			continue;
584254225Speter		if (tsp->coff < sp->coff) {
585254225Speter			if (tsp->coff + tsp->cols >= sp->coff)
586254225Speter				break;
587254225Speter			continue;
588254225Speter		}
589254225Speter		if (tsp->coff + tsp->cols > sp->coff + sp->cols)
590254225Speter			break;
591254225Speter#ifdef DEBUG
592254225Speter		if (tlen < tsp->cols)
593254225Speter			abort();
594254225Speter#endif
595254225Speter		tlen -= tsp->cols + first;
596254225Speter		first = 1;
597254225Speter		*lp++ = tsp;
598254225Speter	}
599254225Speter	if (tlen == 0) {
600254225Speter		*lp = NULL;
601254225Speter		*jdirp = HORIZ_FOLLOW;
602254225Speter		return (0);
603254225Speter	}
604254225Speter	return (1);
605254225Speter}
606254225Speter
607254225Speter/*
60819304Speter * vs_fg --
60919304Speter *	Background the current screen, and foreground a new one.
61019304Speter *
61119304Speter * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int));
61219304Speter */
61319304Speterint
614254225Spetervs_fg(SCR *sp, SCR **nspp, CHAR_T *name, int newscreen)
61519304Speter{
61619304Speter	GS *gp;
61719304Speter	SCR *nsp;
618254225Speter	char *np;
619254225Speter	size_t nlen;
62019304Speter
62119304Speter	gp = sp->gp;
62219304Speter
623254225Speter	if (name)
624254225Speter	    INT2CHAR(sp, name, STRLEN(name) + 1, np, nlen);
625254225Speter	else
626254225Speter	    np = NULL;
62719304Speter	if (newscreen)
62819304Speter		/* Get the specified background screen. */
629254225Speter		nsp = vs_getbg(sp, np);
63019304Speter	else
63119304Speter		/* Swap screens. */
632254225Speter		if (vs_swap(sp, &nsp, np))
63319304Speter			return (1);
63419304Speter
63519304Speter	if ((*nspp = nsp) == NULL) {
636254225Speter		msgq_wstr(sp, M_ERR, name,
63719304Speter		    name == NULL ?
63819304Speter		    "223|There are no background screens" :
63919304Speter		    "224|There's no background screen editing a file named %s");
64019304Speter		return (1);
64119304Speter	}
64219304Speter
64319304Speter	if (newscreen) {
64419304Speter		/* Remove the new screen from the background queue. */
645254225Speter		TAILQ_REMOVE(gp->hq, nsp, q);
64619304Speter
64719304Speter		/* Split the screen; if we fail, hook the screen back in. */
64819304Speter		if (vs_split(sp, nsp, 0)) {
649254225Speter			TAILQ_INSERT_TAIL(gp->hq, nsp, q);
65019304Speter			return (1);
65119304Speter		}
65219304Speter	} else {
65319304Speter		/* Move the old screen to the background queue. */
654254225Speter		TAILQ_REMOVE(gp->dq, sp, q);
655254225Speter		TAILQ_INSERT_TAIL(gp->hq, sp, q);
65619304Speter	}
65719304Speter	return (0);
65819304Speter}
65919304Speter
66019304Speter/*
66119304Speter * vs_bg --
66219304Speter *	Background the screen, and switch to the next one.
66319304Speter *
66419304Speter * PUBLIC: int vs_bg __P((SCR *));
66519304Speter */
66619304Speterint
667254225Spetervs_bg(SCR *sp)
66819304Speter{
66919304Speter	GS *gp;
67019304Speter	SCR *nsp;
67119304Speter
67219304Speter	gp = sp->gp;
67319304Speter
67419304Speter	/* Try and join with another screen. */
67519304Speter	if (vs_discard(sp, &nsp))
67619304Speter		return (1);
67719304Speter	if (nsp == NULL) {
67819304Speter		msgq(sp, M_ERR,
67919304Speter		    "225|You may not background your only displayed screen");
68019304Speter		return (1);
68119304Speter	}
68219304Speter
68319304Speter	/* Move the old screen to the background queue. */
684254225Speter	TAILQ_REMOVE(gp->dq, sp, q);
685254225Speter	TAILQ_INSERT_TAIL(gp->hq, sp, q);
68619304Speter
68719304Speter	/* Toss the screen map. */
68819304Speter	free(_HMAP(sp));
68919304Speter	_HMAP(sp) = NULL;
69019304Speter
69119304Speter	/* Switch screens. */
69219304Speter	sp->nextdisp = nsp;
69319304Speter	F_SET(sp, SC_SSWITCH);
69419304Speter
69519304Speter	return (0);
69619304Speter}
69719304Speter
69819304Speter/*
69919304Speter * vs_swap --
70019304Speter *	Swap the current screen with a backgrounded one.
70119304Speter *
70219304Speter * PUBLIC: int vs_swap __P((SCR *, SCR **, char *));
70319304Speter */
70419304Speterint
705254225Spetervs_swap(SCR *sp, SCR **nspp, char *name)
70619304Speter{
70719304Speter	GS *gp;
708254225Speter	SCR *nsp, *list[2];
70919304Speter
71019304Speter	gp = sp->gp;
71119304Speter
71219304Speter	/* Get the specified background screen. */
71319304Speter	if ((*nspp = nsp = vs_getbg(sp, name)) == NULL)
71419304Speter		return (0);
71519304Speter
71619304Speter	/*
71719304Speter	 * Save the old screen's cursor information.
71819304Speter	 *
71919304Speter	 * XXX
72019304Speter	 * If called after file_end(), and the underlying file was a tmp
72119304Speter	 * file, it may have gone away.
72219304Speter	 */
72319304Speter	if (sp->frp != NULL) {
72419304Speter		sp->frp->lno = sp->lno;
72519304Speter		sp->frp->cno = sp->cno;
72619304Speter		F_SET(sp->frp, FR_CURSORSET);
72719304Speter	}
72819304Speter
72919304Speter	/* Switch screens. */
73019304Speter	sp->nextdisp = nsp;
73119304Speter	F_SET(sp, SC_SSWITCH);
73219304Speter
73319304Speter	/* Initialize terminal information. */
73419304Speter	VIP(nsp)->srows = VIP(sp)->srows;
73519304Speter
73619304Speter	/* Initialize screen information. */
73719304Speter	nsp->cols = sp->cols;
73819304Speter	nsp->rows = sp->rows;	/* XXX: Only place in vi that sets rows. */
739254225Speter	nsp->roff = sp->roff;
74019304Speter
74119304Speter	/*
74219304Speter	 * Small screens: see vs_refresh.c, section 6a.
74319304Speter	 *
74419304Speter	 * The new screens may have different screen options sizes than the
74519304Speter	 * old one, so use them.  Make sure that text counts aren't larger
74619304Speter	 * than the new screen sizes.
74719304Speter	 */
74819304Speter	if (IS_SMALL(nsp)) {
74919304Speter		nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW);
75019304Speter		if (nsp->t_rows > sp->t_maxrows)
75119304Speter			nsp->t_rows = nsp->t_maxrows;
75219304Speter		if (nsp->t_minrows > sp->t_maxrows)
75319304Speter			nsp->t_minrows = nsp->t_maxrows;
75419304Speter	} else
75519304Speter		nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1;
75619304Speter
75719304Speter	/* Reset the length of the default scroll. */
75819304Speter	nsp->defscroll = nsp->t_maxrows / 2;
75919304Speter
76019304Speter	/* Allocate a new screen map. */
76119304Speter	CALLOC_RET(nsp, _HMAP(nsp), SMAP *, SIZE_HMAP(nsp), sizeof(SMAP));
76219304Speter	_TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1);
76319304Speter
76419304Speter	/* Fill the map. */
765254225Speter	nsp->gp = sp->gp;
76619304Speter	if (vs_sm_fill(nsp, nsp->lno, P_FILL))
76719304Speter		return (1);
76819304Speter
76919304Speter	/*
77019304Speter	 * The new screen replaces the old screen in the parent/child list.
77119304Speter	 * We insert the new screen after the old one.  If we're exiting,
77219304Speter	 * the exit will delete the old one, if we're foregrounding, the fg
77319304Speter	 * code will move the old one to the background queue.
77419304Speter	 */
775254225Speter	TAILQ_REMOVE(gp->hq, nsp, q);
776254225Speter	TAILQ_INSERT_AFTER(gp->dq, sp, nsp, q);
77719304Speter
77819304Speter	/*
77919304Speter	 * Don't change the screen's cursor information other than to
78019304Speter	 * note that the cursor is wrong.
78119304Speter	 */
78219304Speter	F_SET(VIP(nsp), VIP_CUR_INVALID);
78319304Speter
78419304Speter	/* Draw the new screen from scratch, and add a status line. */
78519304Speter	F_SET(nsp, SC_SCR_REDRAW | SC_STATUS);
786254225Speter
787254225Speter	list[0] = nsp; list[1] = NULL;
788254225Speter	(void)gp->scr_discard(sp, list);
789254225Speter
79019304Speter	return (0);
79119304Speter}
79219304Speter
79319304Speter/*
79419304Speter * vs_resize --
79519304Speter *	Change the absolute size of the current screen.
79619304Speter *
79719304Speter * PUBLIC: int vs_resize __P((SCR *, long, adj_t));
79819304Speter */
79919304Speterint
800254225Spetervs_resize(SCR *sp, long int count, adj_t adj)
80119304Speter{
80219304Speter	GS *gp;
803254225Speter	SCR *g, *s, *prev, *next, *list[3] = {NULL, NULL, NULL};
80419304Speter	size_t g_off, s_off;
80519304Speter
80619304Speter	gp = sp->gp;
80719304Speter
80819304Speter	/*
80919304Speter	 * Figure out which screens will grow, which will shrink, and
81019304Speter	 * make sure it's possible.
81119304Speter	 */
81219304Speter	if (count == 0)
81319304Speter		return (0);
81419304Speter	if (adj == A_SET) {
81519304Speter		if (sp->t_maxrows == count)
81619304Speter			return (0);
81719304Speter		if (sp->t_maxrows > count) {
81819304Speter			adj = A_DECREASE;
81919304Speter			count = sp->t_maxrows - count;
82019304Speter		} else {
82119304Speter			adj = A_INCREASE;
82219304Speter			count = count - sp->t_maxrows;
82319304Speter		}
82419304Speter	}
82519304Speter
826254225Speter	/* Find first overlapping screen */
827254225Speter	for (next = TAILQ_NEXT(sp, q); next != NULL &&
828254225Speter	     (next->coff >= sp->coff + sp->cols ||
829254225Speter	      next->coff + next->cols <= sp->coff);
830254225Speter	     next = TAILQ_NEXT(next, q));
831254225Speter	/* See if we can use it */
832254225Speter	if (next != NULL &&
833254225Speter	    (sp->coff != next->coff || sp->cols != next->cols))
834254225Speter		next = NULL;
835254225Speter	for (prev = TAILQ_PREV(sp, _dqh, q); prev != NULL &&
836254225Speter	     (prev->coff >= sp->coff + sp->cols ||
837254225Speter	      prev->coff + prev->cols <= sp->coff);
838254225Speter	     prev = TAILQ_PREV(prev, _dqh, q));
839254225Speter	if (prev != NULL &&
840254225Speter	    (sp->coff != prev->coff || sp->cols != prev->cols))
841254225Speter		prev = NULL;
842254225Speter
84319304Speter	g_off = s_off = 0;
84419304Speter	if (adj == A_DECREASE) {
84519304Speter		if (count < 0)
84619304Speter			count = -count;
84719304Speter		s = sp;
84819304Speter		if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
84919304Speter			goto toosmall;
850254225Speter		if ((g = prev) == NULL) {
851254225Speter			if ((g = next) == NULL)
85219304Speter				goto toobig;
85319304Speter			g_off = -count;
85419304Speter		} else
85519304Speter			s_off = count;
85619304Speter	} else {
85719304Speter		g = sp;
858254225Speter		if ((s = next) != NULL &&
859254225Speter		    s->t_maxrows >= MINIMUM_SCREEN_ROWS + count)
86019304Speter				s_off = count;
86119304Speter		else
86219304Speter			s = NULL;
86319304Speter		if (s == NULL) {
864254225Speter			if ((s = prev) == NULL) {
86519304Spetertoobig:				msgq(sp, M_BERR, adj == A_DECREASE ?
86619304Speter				    "227|The screen cannot shrink" :
86719304Speter				    "228|The screen cannot grow");
86819304Speter				return (1);
86919304Speter			}
87019304Speter			if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
87119304Spetertoosmall:			msgq(sp, M_BERR,
87219304Speter				    "226|The screen can only shrink to %d rows",
87319304Speter				    MINIMUM_SCREEN_ROWS);
87419304Speter				return (1);
87519304Speter			}
87619304Speter			g_off = -count;
87719304Speter		}
87819304Speter	}
87919304Speter
88019304Speter	/*
88119304Speter	 * Fix up the screens; we could optimize the reformatting of the
88219304Speter	 * screen, but this isn't likely to be a common enough operation
88319304Speter	 * to make it worthwhile.
88419304Speter	 */
88519304Speter	s->rows += -count;
886254225Speter	s->roff += s_off;
88719304Speter	g->rows += count;
888254225Speter	g->roff += g_off;
88919304Speter
89019304Speter	g->t_rows += count;
89119304Speter	if (g->t_minrows == g->t_maxrows)
89219304Speter		g->t_minrows += count;
89319304Speter	g->t_maxrows += count;
89419304Speter	_TMAP(g) += count;
89519304Speter	F_SET(g, SC_SCR_REFORMAT | SC_STATUS);
89619304Speter
89719304Speter	s->t_rows -= count;
89819304Speter	s->t_maxrows -= count;
89919304Speter	if (s->t_minrows > s->t_maxrows)
90019304Speter		s->t_minrows = s->t_maxrows;
90119304Speter	_TMAP(s) -= count;
90219304Speter	F_SET(s, SC_SCR_REFORMAT | SC_STATUS);
90319304Speter
904254225Speter	/* XXXX */
905254225Speter	list[0] = g; list[1] = s;
906254225Speter	gp->scr_discard(0, list);
907254225Speter
90819304Speter	return (0);
90919304Speter}
91019304Speter
91119304Speter/*
91219304Speter * vs_getbg --
91319304Speter *	Get the specified background screen, or, if name is NULL, the first
91419304Speter *	background screen.
91519304Speter */
91619304Speterstatic SCR *
917254225Spetervs_getbg(SCR *sp, char *name)
91819304Speter{
91919304Speter	GS *gp;
92019304Speter	SCR *nsp;
92119304Speter	char *p;
92219304Speter
92319304Speter	gp = sp->gp;
92419304Speter
92519304Speter	/* If name is NULL, return the first background screen on the list. */
926254225Speter	if (name == NULL)
927254225Speter		return (TAILQ_FIRST(gp->hq));
92819304Speter
92919304Speter	/* Search for a full match. */
930254225Speter	TAILQ_FOREACH(nsp, gp->hq, q)
93119304Speter		if (!strcmp(nsp->frp->name, name))
93219304Speter			break;
933254225Speter	if (nsp != NULL)
93419304Speter		return (nsp);
93519304Speter
93619304Speter	/* Search for a last-component match. */
937254225Speter	TAILQ_FOREACH(nsp, gp->hq, q) {
93819304Speter		if ((p = strrchr(nsp->frp->name, '/')) == NULL)
93919304Speter			p = nsp->frp->name;
94019304Speter		else
94119304Speter			++p;
94219304Speter		if (!strcmp(p, name))
94319304Speter			break;
94419304Speter	}
945254225Speter	if (nsp != NULL)
94619304Speter		return (nsp);
94719304Speter
94819304Speter	return (NULL);
94919304Speter}
950