s_resize -> s_rabs, add s_rrel
[nvi.git] / vi / vs_split.c
blob565749f846a7e6431f82d27f5119bec021a69412
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.23 1993/12/02 15:14:42 bostic Exp $ (Berkeley) $Date: 1993/12/02 15:14:42 $";
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 splitup = 0;
85 } else { /* Parent is bottom half. */
86 /* Child. */
87 tsp->rows = sp->rows - half;
88 tsp->woff = sp->woff;
89 tsp->t_maxrows = tsp->rows - 1;
91 /* Parent. */
92 sp->rows = half;
93 sp->woff += tsp->rows;
94 sp->t_maxrows = sp->rows - 1;
96 /* Shift the parent's map down. */
97 memmove(_HMAP(sp),
98 _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP));
100 splitup = 1;
104 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
106 * The child may have different screen options sizes than the
107 * parent, so use them. Make sure that the text counts aren't
108 * larger than the new screen sizes.
110 if (issmallscreen) {
111 /* Fix the text line count for the parent. */
112 if (splitup)
113 sp->t_rows -= tsp->rows;
115 /* Fix the parent screen. */
116 if (sp->t_rows > sp->t_maxrows)
117 sp->t_rows = sp->t_maxrows;
118 if (sp->t_minrows > sp->t_maxrows)
119 sp->t_minrows = sp->t_maxrows;
121 /* Fix the child screen. */
122 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
123 if (tsp->t_rows > tsp->t_maxrows)
124 tsp->t_rows = tsp->t_maxrows;
125 if (tsp->t_minrows > tsp->t_maxrows)
126 tsp->t_minrows = tsp->t_maxrows;
129 * If we split up, i.e. the child is on top, lines that
130 * were painted in the parent may not be painted in the
131 * child. Clear any lines not being used in the child
132 * screen.
135 if (splitup)
136 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
137 MOVE(tsp, cnt, 0)
138 clrtoeol();
140 } else {
141 sp->t_minrows = sp->t_rows = sp->rows - 1;
144 * The new screen may be a small screen, even though the
145 * parent was not. Don't complain if O_WINDOW is too large,
146 * we're splitting the screen so the screen is much smaller
147 * than normal. Clear any lines not being used in the child
148 * screen.
150 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
151 if (tsp->t_rows > tsp->rows - 1)
152 tsp->t_minrows = tsp->t_rows = tsp->rows - 1;
153 else
154 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
155 MOVE(tsp, cnt, 0)
156 clrtoeol();
160 /* Adjust the ends of both maps. */
161 _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1);
162 _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
165 * In any case, if the size of the scrolling region hasn't been
166 * modified by the user, reset it so it's reasonable for the split
167 * screen.
169 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) {
170 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
171 O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2;
175 * If files specified, build the file list, else, link to the
176 * current file.
178 if (argv != NULL) {
179 for (; *argv != NULL; ++argv)
180 if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL)
181 goto mem3;
182 } else
183 if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL)
184 goto mem3;
186 if ((tsp->frp = file_first(tsp, 0)) == NULL) {
187 msgq(sp, M_ERR, "No files in the file list.");
188 goto mem3;
192 * Copy the file state flags, start the file. Fill the child's
193 * screen map. If the file is unchanged, keep the screen and
194 * cursor the same.
196 if (nochange) {
197 tsp->ep = sp->ep;
198 ++sp->ep->refcnt;
200 tsp->frp->flags = sp->frp->flags;
201 tsp->frp->lno = sp->lno;
202 tsp->frp->cno = sp->cno;
203 F_SET(tsp->frp, FR_CURSORSET);
205 /* Copy the parent's map into the child's map. */
206 memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP));
207 } else {
208 if (file_init(tsp, tsp->frp, NULL, 0))
209 goto mem3;
210 (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP);
213 /* Everything's initialized, put the screen on the displayed queue.*/
214 if (splitup) {
215 /* Link in before the parent. */
216 CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q);
217 } else {
218 /* Link in after the parent. */
219 CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q);
222 /* Clear the current information lines in both screens. */
223 MOVE(sp, INFOLINE(sp), 0);
224 clrtoeol();
225 MOVE(tsp, INFOLINE(tsp), 0);
226 clrtoeol();
228 /* Redraw the status line for the parent screen. */
229 (void)status(sp, sp->ep, sp->lno, 0);
231 /* Save the parent screen's cursor information. */
232 sp->frp->lno = sp->lno;
233 sp->frp->cno = sp->cno;
234 F_SET(sp->frp, FR_CURSORSET);
236 /* Completely redraw the child screen. */
237 F_SET(tsp, S_REDRAW);
239 /* Switch screens. */
240 sp->nextdisp = tsp;
241 F_SET(sp, S_SSWITCH);
242 return (0);
244 mem3: FREE(_HMAP(tsp), SIZE_HMAP(sp) * sizeof(SMAP));
245 FREE(SVP(sp), sizeof(SVI_PRIVATE));
246 mem2: (void)screen_end(tsp);
247 mem1: FREE(tsp, sizeof(SCR));
248 return (1);
252 * svi_bg --
253 * Background the screen, and switch to the next one.
256 svi_bg(csp)
257 SCR *csp;
259 SCR *sp;
261 /* Try and join with another screen. */
262 if ((svi_join(csp, &sp)))
263 return (1);
264 if (sp == NULL) {
265 msgq(csp, M_ERR,
266 "You may not background your only displayed screen.");
267 return (1);
270 /* Move the old screen to the hidden queue. */
271 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
272 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
274 /* Switch screens. */
275 csp->nextdisp = sp;
276 F_SET(csp, S_SSWITCH);
278 return (0);
282 * svi_join --
283 * Join the screen into a related screen, if one exists,
284 * and return that screen.
287 svi_join(csp, nsp)
288 SCR *csp, **nsp;
290 SCR *sp;
291 size_t cnt;
294 * If a split screen, add space to parent/child. Make no effort
295 * to clean up the screen's values. If it's not exiting, we'll
296 * get it when the user asks to show it again.
298 if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) {
299 if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) {
300 *nsp = NULL;
301 return (0);
303 sp->woff = csp->woff;
305 sp->rows += csp->rows;
306 if (ISSMALLSCREEN(sp)) {
307 sp->t_maxrows += csp->rows;
308 for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) {
309 MOVE(sp, cnt, 0);
310 clrtoeol();
312 TMAP = HMAP + (sp->t_rows - 1);
313 } else {
314 sp->t_maxrows += csp->rows;
315 sp->t_rows = sp->t_minrows = sp->t_maxrows;
316 TMAP = HMAP + (sp->t_rows - 1);
317 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
318 return (1);
319 F_SET(sp, S_REDRAW);
323 * If the size of the scrolling region hasn't been modified by
324 * the user, reset it so it's reasonable for the new screen.
326 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
327 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
329 *nsp = sp;
330 return (0);
334 * svi_fg --
335 * Background the current screen, and foreground a new one.
338 svi_fg(csp, name)
339 SCR *csp;
340 char *name;
342 SCR *sp;
344 if (svi_swap(csp, &sp, name))
345 return (1);
346 if (sp == NULL) {
347 if (name == NULL)
348 msgq(csp, M_ERR, "There are no background screens.");
349 else
350 msgq(csp, M_ERR,
351 "There's no background screen editing a file named %s.",
352 name);
353 return (1);
356 /* Move the old screen to the hidden queue. */
357 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
358 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
360 return (0);
364 * svi_swap --
365 * Swap the current screen with a hidden one.
368 svi_swap(csp, nsp, name)
369 SCR *csp, **nsp;
370 char *name;
372 SCR *sp;
373 int issmallscreen;
375 /* Find the screen, or, if name is NULL, the first screen. */
376 for (sp = csp->gp->hq.cqh_first;
377 sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next)
378 if (name == NULL || !strcmp(FILENAME(sp->frp), name))
379 break;
380 if (sp == (void *)&csp->gp->hq) {
381 *nsp = NULL;
382 return (0);
384 *nsp = sp;
386 /* Save the old screen's cursor information. */
387 csp->frp->lno = csp->lno;
388 csp->frp->cno = csp->cno;
389 F_SET(csp->frp, FR_CURSORSET);
391 /* Switch screens. */
392 csp->nextdisp = sp;
393 F_SET(csp, S_SSWITCH);
395 /* Initialize terminal information. */
396 SVP(sp)->srows = SVP(csp)->srows;
398 issmallscreen = ISSMALLSCREEN(sp);
400 /* Initialize screen information. */
401 sp->rows = csp->rows;
402 sp->cols = csp->cols;
403 sp->woff = csp->woff;
406 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
408 * The new screens may have different screen options sizes than the
409 * old one, so use them. Make sure that text counts aren't larger
410 * than the new screen sizes.
412 if (issmallscreen) {
413 sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW);
414 if (sp->t_rows > csp->t_maxrows)
415 sp->t_rows = sp->t_maxrows;
416 if (sp->t_minrows > csp->t_maxrows)
417 sp->t_minrows = sp->t_maxrows;
418 } else
419 sp->t_rows = sp->t_maxrows = sp->rows - 1;
422 * If the size of the scrolling region hasn't been modified by
423 * the user, reset it so it's reasonable for the new screen.
425 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
426 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
429 * Don't change the screen's cursor information other than to
430 * note that the cursor is wrong.
432 F_SET(SVP(sp), SVI_CUR_INVALID);
435 * The HMAP may be NULL, if the screen got resized and
436 * a bunch of screens had to be hidden.
438 if (HMAP == NULL &&
439 (HMAP = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) {
440 msgq(sp, M_SYSERR, NULL);
441 return (1);
443 TMAP = HMAP + (sp->t_rows - 1);
445 /* Fill the map. */
446 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
447 return (1);
450 * The new screen replaces the old screen in the parent/child list.
451 * We insert the new screen after the old one. If we're exiting,
452 * the exit will delete the old one, if we're foregrounding, the fg
453 * code will move the old one to the hidden queue.
455 CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
456 CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
458 F_SET(sp, S_REDRAW);
459 return (0);
463 * svi_rabs --
464 * Change the absolute size of the current screen.
467 svi_rabs(sp, count)
468 SCR *sp;
469 long count;
471 SCR *g, *s;
474 * Figure out which screens will grow, which will shrink, and
475 * make sure it's possible.
477 if (count == 0)
478 return (0);
479 if (count < 0) {
480 count = -count;
481 s = sp;
482 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
483 goto toosmall;
484 if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
485 if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq)
486 goto toobig;
487 g->woff -= count;
488 } else
489 s->woff += count;
490 } else {
491 g = sp;
492 if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq)
493 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
494 s = NULL;
495 else
496 s->woff += count;
497 else
498 s = NULL;
499 if (s == NULL) {
500 if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
501 toobig: msgq(sp, M_BERR, "The screen cannot %s.",
502 count < 0 ? "shrink" : "grow");
503 return (1);
505 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
506 toosmall: msgq(sp, M_BERR,
507 "The screen can only shrink to %d rows.",
508 MINIMUM_SCREEN_ROWS);
509 return (1);
511 g->woff -= count;
515 /* Update the screens. */
516 g->rows += count;
517 g->t_rows += count;
518 if (g->t_minrows == g->t_maxrows)
519 g->t_minrows += count;
520 g->t_maxrows += count;
521 _TMAP(g) = _HMAP(g) + (g->t_rows - 1);
522 (void)status(g, g->ep, g->lno, 0);
523 F_SET(g, S_REDRAW);
525 s->rows -= count;
526 s->t_rows -= count;
527 s->t_maxrows -= count;
528 if (s->t_minrows > s->t_maxrows)
529 s->t_minrows = s->t_maxrows;
530 _TMAP(s) = _HMAP(s) + (s->t_rows - 1);
531 (void)status(s, s->ep, s->lno, 0);
532 F_SET(s, S_REDRAW);
534 return (0);