initialize keys written so if we fail we know
[nvi.git] / vi / vs_split.c
blobf71d13602ab956b7431e86345d931e8ad7ca1894
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.27 1993/12/16 15:41:39 bostic Exp $ (Berkeley) $Date: 1993/12/16 15:41:39 $";
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, 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 MALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
48 if (_HMAP(tsp) == NULL)
49 goto mem2;
51 /* INITIALIZED AT SCREEN CREATE. */
53 /* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
55 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
56 * Set a flag so we know to fix the screen up later.
58 issmallscreen = ISSMALLSCREEN(sp);
61 * Split the screen, and link the screens together. If the cursor
62 * is in the top half of the current screen, the new screen goes
63 * under the current screen. Else, it goes above the current screen.
65 * The columns in the screen don't change.
67 tsp->cols = sp->cols;
69 cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP;
70 if (cnt <= half) { /* Parent is top half. */
71 /* Child. */
72 tsp->rows = sp->rows - half;
73 tsp->woff = sp->woff + half;
74 tsp->t_maxrows = tsp->rows - 1;
76 /* Parent. */
77 sp->rows = half;
78 sp->t_maxrows = sp->rows - 1;
80 splitup = 0;
81 } else { /* Parent is bottom half. */
82 /* Child. */
83 tsp->rows = sp->rows - half;
84 tsp->woff = sp->woff;
85 tsp->t_maxrows = tsp->rows - 1;
87 /* Parent. */
88 sp->rows = half;
89 sp->woff += tsp->rows;
90 sp->t_maxrows = sp->rows - 1;
92 /* Shift the parent's map down. */
93 memmove(_HMAP(sp),
94 _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP));
96 splitup = 1;
100 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
102 * The child may have different screen options sizes than the
103 * parent, so use them. Make sure that the text counts aren't
104 * larger than the new screen sizes.
106 if (issmallscreen) {
107 /* Fix the text line count for the parent. */
108 if (splitup)
109 sp->t_rows -= tsp->rows;
111 /* Fix the parent screen. */
112 if (sp->t_rows > sp->t_maxrows)
113 sp->t_rows = sp->t_maxrows;
114 if (sp->t_minrows > sp->t_maxrows)
115 sp->t_minrows = sp->t_maxrows;
117 /* Fix the child screen. */
118 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
119 if (tsp->t_rows > tsp->t_maxrows)
120 tsp->t_rows = tsp->t_maxrows;
121 if (tsp->t_minrows > tsp->t_maxrows)
122 tsp->t_minrows = tsp->t_maxrows;
125 * If we split up, i.e. the child is on top, lines that
126 * were painted in the parent may not be painted in the
127 * child. Clear any lines not being used in the child
128 * screen.
131 if (splitup)
132 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
133 MOVE(tsp, cnt, 0)
134 clrtoeol();
136 } else {
137 sp->t_minrows = sp->t_rows = sp->rows - 1;
140 * The new screen may be a small screen, even though the
141 * parent was not. Don't complain if O_WINDOW is too large,
142 * we're splitting the screen so the screen is much smaller
143 * than normal. Clear any lines not being used in the child
144 * screen.
146 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
147 if (tsp->t_rows > tsp->rows - 1)
148 tsp->t_minrows = tsp->t_rows = tsp->rows - 1;
149 else
150 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
151 MOVE(tsp, cnt, 0)
152 clrtoeol();
156 /* Adjust the ends of both maps. */
157 _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1);
158 _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
161 * In any case, if the size of the scrolling region hasn't been
162 * modified by the user, reset it so it's reasonable for the split
163 * screen.
165 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) {
166 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
167 O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2;
171 * If files specified, build the file list, else, link to the
172 * current file.
174 if (argv == NULL) {
175 if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL)
176 goto mem3;
177 } else
178 for (; (*argv)->len != 0; ++argv)
179 if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL)
180 goto mem3;
182 /* Set up the argument and current FREF pointers. */
183 if ((tsp->frp = file_first(tsp)) == NULL) {
184 msgq(sp, M_ERR, "No files in the file list.");
185 goto mem3;
187 tsp->a_frp = tsp->frp;
190 * Copy the file state flags, start the file. Fill the child's
191 * screen map. If the file is unchanged, keep the screen and
192 * cursor the same.
194 if (argv == NULL) {
195 tsp->ep = sp->ep;
196 ++sp->ep->refcnt;
198 tsp->frp->flags = sp->frp->flags;
199 tsp->frp->lno = sp->lno;
200 tsp->frp->cno = sp->cno;
201 F_SET(tsp->frp, FR_CURSORSET);
203 /* Copy the parent's map into the child's map. */
204 memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP));
205 } else {
206 if (file_init(tsp, tsp->frp, NULL, 0))
207 goto mem3;
208 (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP);
211 /* Everything's initialized, put the screen on the displayed queue.*/
212 if (splitup) {
213 /* Link in before the parent. */
214 CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q);
215 } else {
216 /* Link in after the parent. */
217 CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q);
220 /* Clear the current information lines in both screens. */
221 MOVE(sp, INFOLINE(sp), 0);
222 clrtoeol();
223 MOVE(tsp, INFOLINE(tsp), 0);
224 clrtoeol();
226 /* Redraw the status line for the parent screen. */
227 (void)status(sp, sp->ep, sp->lno, 0);
229 /* Save the parent screen's cursor information. */
230 sp->frp->lno = sp->lno;
231 sp->frp->cno = sp->cno;
232 F_SET(sp->frp, FR_CURSORSET);
234 /* Completely redraw the child screen. */
235 F_SET(tsp, S_REDRAW);
237 /* Switch screens. */
238 sp->nextdisp = tsp;
239 F_SET(sp, S_SSWITCH);
240 return (0);
242 mem3: FREE(_HMAP(tsp), SIZE_HMAP(sp) * sizeof(SMAP));
243 FREE(SVP(sp), sizeof(SVI_PRIVATE));
244 mem2: (void)screen_end(tsp);
245 mem1: FREE(tsp, sizeof(SCR));
246 return (1);
250 * svi_bg --
251 * Background the screen, and switch to the next one.
254 svi_bg(csp)
255 SCR *csp;
257 SCR *sp;
259 /* Try and join with another screen. */
260 if ((svi_join(csp, &sp)))
261 return (1);
262 if (sp == NULL) {
263 msgq(csp, M_ERR,
264 "You may not background your only displayed screen.");
265 return (1);
268 /* Move the old screen to the hidden queue. */
269 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
270 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
272 /* Switch screens. */
273 csp->nextdisp = sp;
274 F_SET(csp, S_SSWITCH);
276 return (0);
280 * svi_join --
281 * Join the screen into a related screen, if one exists,
282 * and return that screen.
285 svi_join(csp, nsp)
286 SCR *csp, **nsp;
288 SCR *sp;
289 size_t cnt;
292 * If a split screen, add space to parent/child. Make no effort
293 * to clean up the screen's values. If it's not exiting, we'll
294 * get it when the user asks to show it again.
296 if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) {
297 if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) {
298 *nsp = NULL;
299 return (0);
301 sp->woff = csp->woff;
303 sp->rows += csp->rows;
304 if (ISSMALLSCREEN(sp)) {
305 sp->t_maxrows += csp->rows;
306 for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) {
307 MOVE(sp, cnt, 0);
308 clrtoeol();
310 TMAP = HMAP + (sp->t_rows - 1);
311 } else {
312 sp->t_maxrows += csp->rows;
313 sp->t_rows = sp->t_minrows = sp->t_maxrows;
314 TMAP = HMAP + (sp->t_rows - 1);
315 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
316 return (1);
317 F_SET(sp, S_REDRAW);
321 * If the size of the scrolling region hasn't been modified by
322 * the user, reset it so it's reasonable for the new screen.
324 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
325 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
327 *nsp = sp;
328 return (0);
332 * svi_fg --
333 * Background the current screen, and foreground a new one.
336 svi_fg(csp, name)
337 SCR *csp;
338 char *name;
340 SCR *sp;
342 if (svi_swap(csp, &sp, name))
343 return (1);
344 if (sp == NULL) {
345 if (name == NULL)
346 msgq(csp, M_ERR, "There are no background screens.");
347 else
348 msgq(csp, M_ERR,
349 "There's no background screen editing a file named %s.",
350 name);
351 return (1);
354 /* Move the old screen to the hidden queue. */
355 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
356 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
358 return (0);
362 * svi_swap --
363 * Swap the current screen with a hidden one.
366 svi_swap(csp, nsp, name)
367 SCR *csp, **nsp;
368 char *name;
370 SCR *sp;
371 int issmallscreen;
373 /* Find the screen, or, if name is NULL, the first screen. */
374 for (sp = csp->gp->hq.cqh_first;
375 sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next)
376 if (name == NULL || !strcmp(FILENAME(sp->frp), name))
377 break;
378 if (sp == (void *)&csp->gp->hq) {
379 *nsp = NULL;
380 return (0);
382 *nsp = sp;
384 /* Save the old screen's cursor information. */
385 csp->frp->lno = csp->lno;
386 csp->frp->cno = csp->cno;
387 F_SET(csp->frp, FR_CURSORSET);
389 /* Switch screens. */
390 csp->nextdisp = sp;
391 F_SET(csp, S_SSWITCH);
393 /* Initialize terminal information. */
394 SVP(sp)->srows = SVP(csp)->srows;
396 issmallscreen = ISSMALLSCREEN(sp);
398 /* Initialize screen information. */
399 sp->rows = csp->rows;
400 sp->cols = csp->cols;
401 sp->woff = csp->woff;
404 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
406 * The new screens may have different screen options sizes than the
407 * old one, so use them. Make sure that text counts aren't larger
408 * than the new screen sizes.
410 if (issmallscreen) {
411 sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW);
412 if (sp->t_rows > csp->t_maxrows)
413 sp->t_rows = sp->t_maxrows;
414 if (sp->t_minrows > csp->t_maxrows)
415 sp->t_minrows = sp->t_maxrows;
416 } else
417 sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1;
420 * If the size of the scrolling region hasn't been modified by
421 * the user, reset it so it's reasonable for the new screen.
423 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
424 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
427 * Don't change the screen's cursor information other than to
428 * note that the cursor is wrong.
430 F_SET(SVP(sp), SVI_CUR_INVALID);
433 * The HMAP may be NULL, if the screen got resized and
434 * a bunch of screens had to be hidden.
436 if (HMAP == NULL)
437 MALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
438 TMAP = HMAP + (sp->t_rows - 1);
440 /* Fill the map. */
441 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
442 return (1);
445 * The new screen replaces the old screen in the parent/child list.
446 * We insert the new screen after the old one. If we're exiting,
447 * the exit will delete the old one, if we're foregrounding, the fg
448 * code will move the old one to the hidden queue.
450 CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
451 CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
453 F_SET(sp, S_REDRAW);
454 return (0);
458 * svi_rabs --
459 * Change the absolute size of the current screen.
462 svi_rabs(sp, count)
463 SCR *sp;
464 long count;
466 SCR *g, *s;
469 * Figure out which screens will grow, which will shrink, and
470 * make sure it's possible.
472 if (count == 0)
473 return (0);
474 if (count < 0) {
475 count = -count;
476 s = sp;
477 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
478 goto toosmall;
479 if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
480 if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq)
481 goto toobig;
482 g->woff -= count;
483 } else
484 s->woff += count;
485 } else {
486 g = sp;
487 if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq)
488 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
489 s = NULL;
490 else
491 s->woff += count;
492 else
493 s = NULL;
494 if (s == NULL) {
495 if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
496 toobig: msgq(sp, M_BERR, "The screen cannot %s.",
497 count < 0 ? "shrink" : "grow");
498 return (1);
500 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
501 toosmall: msgq(sp, M_BERR,
502 "The screen can only shrink to %d rows.",
503 MINIMUM_SCREEN_ROWS);
504 return (1);
506 g->woff -= count;
510 /* Update the screens. */
511 g->rows += count;
512 g->t_rows += count;
513 if (g->t_minrows == g->t_maxrows)
514 g->t_minrows += count;
515 g->t_maxrows += count;
516 _TMAP(g) = _HMAP(g) + (g->t_rows - 1);
517 (void)status(g, g->ep, g->lno, 0);
518 F_SET(g, S_REDRAW);
520 s->rows -= count;
521 s->t_rows -= count;
522 s->t_maxrows -= count;
523 if (s->t_minrows > s->t_maxrows)
524 s->t_minrows = s->t_maxrows;
525 _TMAP(s) = _HMAP(s) + (s->t_rows - 1);
526 (void)status(s, s->ep, s->lno, 0);
527 F_SET(s, S_REDRAW);
529 return (0);