bug fix, :resize +1 in the bottom screen dropped core
[nvi.git] / vi / vs_split.c
blob5ef1be87a7b26c68e5803154aa18919bcf5cc2ec
1 /*-
2 * Copyright (c) 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: vs_split.c,v 8.21 1993/12/02 12:15:00 bostic Exp $ (Berkeley) $Date: 1993/12/02 12:15:00 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <curses.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
19 #include "vi.h"
20 #include "svi_screen.h"
23 * svi_split --
24 * Split the screen.
26 int
27 svi_split(sp, argv)
28 SCR *sp;
29 ARGS *argv[];
31 SCR *tsp;
32 SMAP *smp;
33 size_t cnt, half;
34 int issmallscreen, nochange, splitup;
36 /* Check to see if it's possible. */
37 half = sp->rows / 2;
38 if (half < MINIMUM_SCREEN_ROWS) {
39 msgq(sp, M_ERR, "Screen must be larger than %d to split",
40 MINIMUM_SCREEN_ROWS);
41 return (1);
44 /* Get a new screen. */
45 if (screen_init(sp, &tsp, 0))
46 goto mem1;
47 if ((_HMAP(tsp) = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) {
48 msgq(sp, M_SYSERR, NULL);
49 goto mem2;
52 /* INITIALIZED AT SCREEN CREATE. */
54 /* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
56 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
57 * Set a flag so we know to fix the screen up later.
59 issmallscreen = ISSMALLSCREEN(sp);
61 /* Flag if we're changing screens. */
62 nochange = argv == NULL;
65 * Split the screen, and link the screens together. If the cursor
66 * is in the top half of the current screen, the new screen goes
67 * under the current screen. Else, it goes above the current screen.
69 * The columns in the screen don't change.
71 tsp->cols = sp->cols;
73 cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP;
74 if (cnt <= half) { /* Parent is top half. */
75 /* Child. */
76 tsp->rows = sp->rows - half;
77 tsp->woff = sp->woff + half;
78 tsp->t_maxrows = tsp->rows - 1;
80 /* Parent. */
81 sp->rows = half;
82 sp->t_maxrows = sp->rows - 1;
84 /* Link in after the parent. */
85 CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q);
86 splitup = 0;
87 } else { /* Parent is bottom half. */
88 /* Child. */
89 tsp->rows = sp->rows - half;
90 tsp->woff = sp->woff;
91 tsp->t_maxrows = tsp->rows - 1;
93 /* Parent. */
94 sp->rows = half;
95 sp->woff += tsp->rows;
96 sp->t_maxrows = sp->rows - 1;
98 /* Shift the parent's map down. */
99 memmove(_HMAP(sp),
100 _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP));
102 /* Link in before the parent. */
103 CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q);
104 splitup = 1;
108 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
110 * The child may have different screen options sizes than the
111 * parent, so use them. Make sure that the text counts aren't
112 * larger than the new screen sizes.
114 if (issmallscreen) {
115 /* Fix the text line count for the parent. */
116 if (splitup)
117 sp->t_rows -= tsp->rows;
119 /* Fix the parent screen. */
120 if (sp->t_rows > sp->t_maxrows)
121 sp->t_rows = sp->t_maxrows;
122 if (sp->t_minrows > sp->t_maxrows)
123 sp->t_minrows = sp->t_maxrows;
125 /* Fix the child screen. */
126 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
127 if (tsp->t_rows > tsp->t_maxrows)
128 tsp->t_rows = tsp->t_maxrows;
129 if (tsp->t_minrows > tsp->t_maxrows)
130 tsp->t_minrows = tsp->t_maxrows;
133 * If we split up, i.e. the child is on top, lines that
134 * were painted in the parent may not be painted in the
135 * child. Clear any lines not being used in the child
136 * screen.
139 if (splitup)
140 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
141 MOVE(tsp, cnt, 0)
142 clrtoeol();
144 } else {
145 sp->t_minrows = sp->t_rows = sp->rows - 1;
148 * The new screen may be a small screen, even though the
149 * parent was not. Don't complain if O_WINDOW is too large,
150 * we're splitting the screen so the screen is much smaller
151 * than normal. Clear any lines not being used in the child
152 * screen.
154 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
155 if (tsp->t_rows > tsp->rows - 1)
156 tsp->t_minrows = tsp->t_rows = tsp->rows - 1;
157 else
158 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
159 MOVE(tsp, cnt, 0)
160 clrtoeol();
164 /* Adjust the ends of both maps. */
165 _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1);
166 _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
169 * In any case, if the size of the scrolling region hasn't been
170 * modified by the user, reset it so it's reasonable for the split
171 * screen.
173 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) {
174 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
175 O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2;
179 * If files specified, build the file list, else, link to the
180 * current file.
182 if (argv != NULL) {
183 for (; *argv != NULL; ++argv)
184 if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL)
185 goto mem3;
186 } else
187 if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL)
188 goto mem3;
190 if ((tsp->frp = file_first(tsp, 0)) == NULL) {
191 msgq(sp, M_ERR, "No files in the file list.");
192 goto mem3;
196 * Copy the file state flags, start the file. Fill the child's
197 * screen map. If the file is unchanged, keep the screen and
198 * cursor the same.
200 if (nochange) {
201 tsp->ep = sp->ep;
202 ++sp->ep->refcnt;
204 tsp->frp->flags = sp->frp->flags;
205 tsp->frp->lno = sp->lno;
206 tsp->frp->cno = sp->cno;
207 F_SET(tsp->frp, FR_CURSORSET);
209 /* Copy the parent's map into the child's map. */
210 memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP));
211 } else {
212 if (file_init(tsp, tsp->frp, NULL, 0))
213 goto mem3;
214 (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP);
217 /* Clear the current information lines in both screens. */
218 MOVE(sp, INFOLINE(sp), 0);
219 clrtoeol();
220 MOVE(tsp, INFOLINE(tsp), 0);
221 clrtoeol();
223 /* Redraw the status line for the parent screen. */
224 (void)status(sp, sp->ep, sp->lno, 0);
226 /* Save the parent screen's cursor information. */
227 sp->frp->lno = sp->lno;
228 sp->frp->cno = sp->cno;
229 F_SET(sp->frp, FR_CURSORSET);
231 /* Completely redraw the child screen. */
232 F_SET(tsp, S_REDRAW);
234 /* Switch screens. */
235 sp->nextdisp = tsp;
236 F_SET(sp, S_SSWITCH);
237 return (0);
239 mem3: FREE(_HMAP(tsp), SIZE_HMAP(sp) * sizeof(SMAP));
240 FREE(SVP(sp), sizeof(SVI_PRIVATE));
241 mem2: (void)screen_end(tsp);
242 mem1: FREE(tsp, sizeof(SCR));
243 return (1);
247 * svi_bg --
248 * Background the screen, and switch to the next one.
251 svi_bg(csp)
252 SCR *csp;
254 SCR *sp;
256 /* Try and join with another screen. */
257 if ((svi_join(csp, &sp)))
258 return (1);
259 if (sp == NULL) {
260 msgq(csp, M_ERR,
261 "You may not background your only displayed screen.");
262 return (1);
265 /* Switch screens. */
266 csp->nextdisp = sp;
267 F_SET(csp, S_SSWITCH);
269 return (0);
273 * svi_join --
274 * Join the screen into a related screen, if one exists,
275 * and return that screen.
278 svi_join(csp, nsp)
279 SCR *csp, **nsp;
281 SCR *sp;
282 size_t cnt;
285 * If a split screen, add space to parent/child. Make no effort
286 * to clean up the screen's values. If it's not exiting, we'll
287 * get it when the user asks to show it again.
289 if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) {
290 if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) {
291 *nsp = NULL;
292 return (0);
294 sp->woff = csp->woff;
296 sp->rows += csp->rows;
297 if (ISSMALLSCREEN(sp)) {
298 sp->t_maxrows += csp->rows;
299 for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) {
300 MOVE(sp, cnt, 0);
301 clrtoeol();
303 TMAP = HMAP + (sp->t_rows - 1);
304 } else {
305 sp->t_maxrows += csp->rows;
306 sp->t_rows = sp->t_minrows = sp->t_maxrows;
307 TMAP = HMAP + (sp->t_rows - 1);
308 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
309 return (1);
310 F_SET(sp, S_REDRAW);
314 * If the size of the scrolling region hasn't been modified by
315 * the user, reset it so it's reasonable for the new screen.
317 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
318 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
320 /* Screen is no longer displayed. */
321 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
322 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
324 *nsp = sp;
325 return (0);
329 * svi_fg --
330 * Background the current screen, and foreground a new one.
333 svi_fg(csp, name)
334 SCR *csp;
335 char *name;
337 SCR *sp;
339 if (svi_swap(csp, &sp, name))
340 return (1);
341 if (sp == NULL) {
342 if (name == NULL)
343 msgq(csp, M_ERR, "There are no background screens.");
344 else
345 msgq(csp, M_ERR,
346 "There's no background screen editing a file named %s.",
347 name);
348 return (1);
350 return (0);
354 * svi_swap --
355 * Swap the current screen with a hidden one.
358 svi_swap(csp, nsp, name)
359 SCR *csp, **nsp;
360 char *name;
362 SCR *sp;
363 int issmallscreen;
365 /* Find the screen, or, if name is NULL, the first screen. */
366 for (sp = csp->gp->hq.cqh_first;
367 sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next)
368 if (name == NULL || !strcmp(FILENAME(sp->frp), name))
369 break;
370 if (sp == (void *)&csp->gp->hq) {
371 *nsp = NULL;
372 return (0);
374 *nsp = sp;
376 /* Save the old screen's cursor information. */
377 csp->frp->lno = csp->lno;
378 csp->frp->cno = csp->cno;
379 F_SET(csp->frp, FR_CURSORSET);
381 /* Switch screens. */
382 csp->nextdisp = sp;
383 F_SET(csp, S_SSWITCH);
385 /* Initialize terminal information. */
386 SVP(sp)->srows = SVP(csp)->srows;
388 issmallscreen = ISSMALLSCREEN(sp);
390 /* Initialize screen information. */
391 sp->rows = csp->rows;
392 sp->cols = csp->cols;
393 sp->woff = csp->woff;
396 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
398 * The new screens may have different screen options sizes than the
399 * old one, so use them. Make sure that text counts aren't larger
400 * than the new screen sizes.
402 if (issmallscreen) {
403 sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW);
404 if (sp->t_rows > csp->t_maxrows)
405 sp->t_rows = sp->t_maxrows;
406 if (sp->t_minrows > csp->t_maxrows)
407 sp->t_minrows = sp->t_maxrows;
408 } else
409 sp->t_rows = sp->t_maxrows = sp->rows - 1;
412 * If the size of the scrolling region hasn't been modified by
413 * the user, reset it so it's reasonable for the new screen.
415 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
416 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
419 * Don't change the screen's cursor information other than to
420 * note that the cursor is wrong.
422 F_SET(SVP(sp), SVI_CUR_INVALID);
425 * The HMAP may be NULL, if the screen got resized and
426 * a bunch of screens had to be hidden.
428 if (HMAP == NULL &&
429 (HMAP = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) {
430 msgq(sp, M_SYSERR, NULL);
431 return (1);
433 TMAP = HMAP + (sp->t_rows - 1);
435 /* Fill the map. */
436 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
437 return (1);
439 /* The new screen replaces the old screen in the parent/child list. */
440 CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
441 CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
442 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
443 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
445 F_SET(sp, S_REDRAW);
446 return (0);
450 * svi_resize --
451 * Change the size of the current screen.
454 svi_resize(sp, count)
455 SCR *sp;
456 long count;
458 SCR *g, *s;
461 * Figure out which screens will grow, which will shrink, and
462 * make sure it's possible.
464 if (count == 0)
465 return (0);
466 if (count < 0) {
467 count = -count;
468 s = sp;
469 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
470 goto toosmall;
471 if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
472 if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq)
473 goto toobig;
474 g->woff -= count;
475 } else
476 s->woff += count;
477 } else {
478 g = sp;
479 if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq)
480 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
481 s = NULL;
482 else
483 s->woff += count;
484 else
485 s = NULL;
486 if (s == NULL) {
487 if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
488 toobig: msgq(sp, M_BERR, "The screen cannot %s.",
489 count < 0 ? "shrink" : "grow");
490 return (1);
492 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
493 toosmall: msgq(sp, M_BERR,
494 "The screen can only shrink to %d rows.",
495 MINIMUM_SCREEN_ROWS);
496 return (1);
498 g->woff -= count;
502 /* Update the screens. */
503 g->rows += count;
504 g->t_rows += count;
505 if (g->t_minrows == g->t_maxrows)
506 g->t_minrows += count;
507 g->t_maxrows += count;
508 _TMAP(g) = _HMAP(g) + (g->t_rows - 1);
509 (void)status(g, g->ep, g->lno, 0);
510 F_SET(g, S_REDRAW);
512 s->rows -= count;
513 s->t_rows -= count;
514 s->t_maxrows -= count;
515 if (s->t_minrows > s->t_maxrows)
516 s->t_minrows = s->t_maxrows;
517 _TMAP(s) = _HMAP(s) + (s->t_rows - 1);
518 (void)status(s, s->ep, s->lno, 0);
519 F_SET(s, S_REDRAW);
521 return (0);