update to 1.01
[nvi.git] / vi / vs_split.c
blob7eb81485813c98fd7d5d2e551b219b486fdf3617
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.29 1993/12/22 15:12:14 bostic Exp $ (Berkeley) $Date: 1993/12/22 15:12:14 $";
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 MSG *mp, *next;
32 SCR *tsp, saved_sp;
33 SVI_PRIVATE saved_svp;
34 SMAP *smp;
35 size_t cnt, half;
36 int issmallscreen, splitup;
38 /* Check to see if it's possible. */
39 half = sp->rows / 2;
40 if (half < MINIMUM_SCREEN_ROWS) {
41 msgq(sp, M_ERR, "Screen must be larger than %d to split",
42 MINIMUM_SCREEN_ROWS);
43 return (1);
46 /* Get a new screen. */
47 if (screen_init(sp, &tsp, 0))
48 return (1);
49 MALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
50 if (_HMAP(tsp) == NULL)
51 return (1);
54 * We're about to modify the current screen. Save the contents
55 * in case something goes horribly, senselessly wrong.
57 saved_sp = *sp;
58 saved_svp = *SVP(sp);
60 /* INITIALIZED AT SCREEN CREATE. */
62 /* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
64 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
65 * Set a flag so we know to fix the screen up later.
67 issmallscreen = ISSMALLSCREEN(sp);
70 * Split the screen, and link the screens together. If the cursor
71 * is in the top half of the current screen, the new screen goes
72 * under the current screen. Else, it goes above the current screen.
74 * The columns in the screen don't change.
76 tsp->cols = sp->cols;
78 cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP;
79 if (cnt <= half) { /* Parent is top half. */
80 /* Child. */
81 tsp->rows = sp->rows - half;
82 tsp->woff = sp->woff + half;
83 tsp->t_maxrows = tsp->rows - 1;
85 /* Parent. */
86 sp->rows = half;
87 sp->t_maxrows = sp->rows - 1;
89 splitup = 0;
90 } else { /* Parent is bottom half. */
91 /* Child. */
92 tsp->rows = sp->rows - half;
93 tsp->woff = sp->woff;
94 tsp->t_maxrows = tsp->rows - 1;
96 /* Parent. */
97 sp->rows = half;
98 sp->woff += tsp->rows;
99 sp->t_maxrows = sp->rows - 1;
101 /* Shift the parent's map down. */
102 memmove(_HMAP(sp),
103 _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP));
105 splitup = 1;
109 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
111 * The child may have different screen options sizes than the
112 * parent, so use them. Make sure that the text counts aren't
113 * larger than the new screen sizes.
115 if (issmallscreen) {
116 /* Fix the text line count for the parent. */
117 if (splitup)
118 sp->t_rows -= tsp->rows;
120 /* Fix the parent screen. */
121 if (sp->t_rows > sp->t_maxrows)
122 sp->t_rows = sp->t_maxrows;
123 if (sp->t_minrows > sp->t_maxrows)
124 sp->t_minrows = sp->t_maxrows;
126 /* Fix the child screen. */
127 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
128 if (tsp->t_rows > tsp->t_maxrows)
129 tsp->t_rows = tsp->t_maxrows;
130 if (tsp->t_minrows > tsp->t_maxrows)
131 tsp->t_minrows = tsp->t_maxrows;
134 * If we split up, i.e. the child is on top, lines that
135 * were painted in the parent may not be painted in the
136 * child. Clear any lines not being used in the child
137 * screen.
140 if (splitup)
141 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
142 MOVE(tsp, cnt, 0)
143 clrtoeol();
145 } else {
146 sp->t_minrows = sp->t_rows = sp->rows - 1;
149 * The new screen may be a small screen, even though the
150 * parent was not. Don't complain if O_WINDOW is too large,
151 * we're splitting the screen so the screen is much smaller
152 * than normal. Clear any lines not being used in the child
153 * screen.
155 tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
156 if (tsp->t_rows > tsp->rows - 1)
157 tsp->t_minrows = tsp->t_rows = tsp->rows - 1;
158 else
159 for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
160 MOVE(tsp, cnt, 0)
161 clrtoeol();
165 /* Adjust the ends of both maps. */
166 _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1);
167 _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
170 * In any case, if the size of the scrolling region hasn't been
171 * modified by the user, reset it so it's reasonable for the split
172 * screen.
174 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) {
175 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
176 O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2;
180 * If files specified, build the file list, else, link to the
181 * current file.
183 if (argv == NULL) {
184 if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL)
185 goto err;
186 } else
187 for (; (*argv)->len != 0; ++argv)
188 if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL)
189 goto err;
191 /* Set up the argument and current FREF pointers. */
192 if ((tsp->frp = file_first(tsp)) == NULL) {
193 msgq(sp, M_ERR, "No files in the file list.");
194 goto err;
197 tsp->a_frp = tsp->frp;
200 * Copy the file state flags, start the file. Fill the child's
201 * screen map. If the file is unchanged, keep the screen and
202 * cursor the same.
204 if (argv == NULL) {
205 tsp->ep = sp->ep;
206 ++sp->ep->refcnt;
208 tsp->frp->flags = sp->frp->flags;
209 tsp->frp->lno = sp->lno;
210 tsp->frp->cno = sp->cno;
211 F_SET(tsp->frp, FR_CURSORSET);
213 /* Copy the parent's map into the child's map. */
214 memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP));
215 } else {
216 if (file_init(tsp, tsp->frp, NULL, 0))
217 goto err;
218 (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP);
221 /* Everything's initialized, put the screen on the displayed queue.*/
222 if (splitup) {
223 /* Link in before the parent. */
224 CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q);
225 } else {
226 /* Link in after the parent. */
227 CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q);
230 /* Clear the current information lines in both screens. */
231 MOVE(sp, INFOLINE(sp), 0);
232 clrtoeol();
233 MOVE(tsp, INFOLINE(tsp), 0);
234 clrtoeol();
236 /* Redraw the status line for the parent screen. */
237 (void)status(sp, sp->ep, sp->lno, 0);
239 /* Save the parent screen's cursor information. */
240 sp->frp->lno = sp->lno;
241 sp->frp->cno = sp->cno;
242 F_SET(sp->frp, FR_CURSORSET);
244 /* Completely redraw the child screen. */
245 F_SET(tsp, S_REDRAW);
247 /* Switch screens. */
248 sp->nextdisp = tsp;
249 F_SET(sp, S_SSWITCH);
250 return (0);
252 /* Recover the original screen. */
253 err: *sp = saved_sp;
254 *SVP(sp) = saved_svp;
256 /* Copy any (probably error) messages in the new screen. */
257 for (mp = tsp->msgq.lh_first; mp != NULL; mp = next) {
258 if (!F_ISSET(mp, M_EMPTY))
259 msg_app(sp->gp, sp,
260 mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
261 next = mp->q.le_next;
262 if (mp->mbuf != NULL)
263 free(mp->mbuf);
264 free(mp);
267 /* Free the new screen. */
268 free(_HMAP(tsp));
269 free(SVP(tsp));
270 FREE(tsp, sizeof(SCR));
271 return (1);
275 * svi_bg --
276 * Background the screen, and switch to the next one.
279 svi_bg(csp)
280 SCR *csp;
282 SCR *sp;
284 /* Try and join with another screen. */
285 if ((svi_join(csp, &sp)))
286 return (1);
287 if (sp == NULL) {
288 msgq(csp, M_ERR,
289 "You may not background your only displayed screen.");
290 return (1);
293 /* Move the old screen to the hidden queue. */
294 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
295 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
297 /* Switch screens. */
298 csp->nextdisp = sp;
299 F_SET(csp, S_SSWITCH);
301 return (0);
305 * svi_join --
306 * Join the screen into a related screen, if one exists,
307 * and return that screen.
310 svi_join(csp, nsp)
311 SCR *csp, **nsp;
313 SCR *sp;
314 size_t cnt;
317 * If a split screen, add space to parent/child. Make no effort
318 * to clean up the screen's values. If it's not exiting, we'll
319 * get it when the user asks to show it again.
321 if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) {
322 if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) {
323 *nsp = NULL;
324 return (0);
326 sp->woff = csp->woff;
328 sp->rows += csp->rows;
329 if (ISSMALLSCREEN(sp)) {
330 sp->t_maxrows += csp->rows;
331 for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) {
332 MOVE(sp, cnt, 0);
333 clrtoeol();
335 TMAP = HMAP + (sp->t_rows - 1);
336 } else {
337 sp->t_maxrows += csp->rows;
338 sp->t_rows = sp->t_minrows = sp->t_maxrows;
339 TMAP = HMAP + (sp->t_rows - 1);
340 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
341 return (1);
342 F_SET(sp, S_REDRAW);
346 * If the size of the scrolling region hasn't been modified by
347 * the user, reset it so it's reasonable for the new screen.
349 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
350 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
352 *nsp = sp;
353 return (0);
357 * svi_fg --
358 * Background the current screen, and foreground a new one.
361 svi_fg(csp, name)
362 SCR *csp;
363 CHAR_T *name;
365 SCR *sp;
367 if (svi_swap(csp, &sp, name))
368 return (1);
369 if (sp == NULL) {
370 if (name == NULL)
371 msgq(csp, M_ERR, "There are no background screens.");
372 else
373 msgq(csp, M_ERR,
374 "There's no background screen editing a file named %s.",
375 name);
376 return (1);
379 /* Move the old screen to the hidden queue. */
380 CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
381 CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
383 return (0);
387 * svi_swap --
388 * Swap the current screen with a hidden one.
391 svi_swap(csp, nsp, name)
392 SCR *csp, **nsp;
393 char *name;
395 SCR *sp;
396 int issmallscreen;
398 /* Find the screen, or, if name is NULL, the first screen. */
399 for (sp = csp->gp->hq.cqh_first;
400 sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next)
401 if (name == NULL || !strcmp(FILENAME(sp->frp), name))
402 break;
403 if (sp == (void *)&csp->gp->hq) {
404 *nsp = NULL;
405 return (0);
407 *nsp = sp;
409 /* Save the old screen's cursor information. */
410 csp->frp->lno = csp->lno;
411 csp->frp->cno = csp->cno;
412 F_SET(csp->frp, FR_CURSORSET);
414 /* Switch screens. */
415 csp->nextdisp = sp;
416 F_SET(csp, S_SSWITCH);
418 /* Initialize terminal information. */
419 SVP(sp)->srows = SVP(csp)->srows;
421 issmallscreen = ISSMALLSCREEN(sp);
423 /* Initialize screen information. */
424 sp->rows = csp->rows;
425 sp->cols = csp->cols;
426 sp->woff = csp->woff;
429 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
431 * The new screens may have different screen options sizes than the
432 * old one, so use them. Make sure that text counts aren't larger
433 * than the new screen sizes.
435 if (issmallscreen) {
436 sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW);
437 if (sp->t_rows > csp->t_maxrows)
438 sp->t_rows = sp->t_maxrows;
439 if (sp->t_minrows > csp->t_maxrows)
440 sp->t_minrows = sp->t_maxrows;
441 } else
442 sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1;
445 * If the size of the scrolling region hasn't been modified by
446 * the user, reset it so it's reasonable for the new screen.
448 if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
449 O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
452 * Don't change the screen's cursor information other than to
453 * note that the cursor is wrong.
455 F_SET(SVP(sp), SVI_CUR_INVALID);
458 * The HMAP may be NULL, if the screen got resized and
459 * a bunch of screens had to be hidden.
461 if (HMAP == NULL)
462 MALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
463 TMAP = HMAP + (sp->t_rows - 1);
465 /* Fill the map. */
466 if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
467 return (1);
470 * The new screen replaces the old screen in the parent/child list.
471 * We insert the new screen after the old one. If we're exiting,
472 * the exit will delete the old one, if we're foregrounding, the fg
473 * code will move the old one to the hidden queue.
475 CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
476 CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
478 F_SET(sp, S_REDRAW);
479 return (0);
483 * svi_rabs --
484 * Change the absolute size of the current screen.
487 svi_rabs(sp, count)
488 SCR *sp;
489 long count;
491 SCR *g, *s;
494 * Figure out which screens will grow, which will shrink, and
495 * make sure it's possible.
497 if (count == 0)
498 return (0);
499 if (count < 0) {
500 count = -count;
501 s = sp;
502 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
503 goto toosmall;
504 if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
505 if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq)
506 goto toobig;
507 g->woff -= count;
508 } else
509 s->woff += count;
510 } else {
511 g = sp;
512 if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq)
513 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
514 s = NULL;
515 else
516 s->woff += count;
517 else
518 s = NULL;
519 if (s == NULL) {
520 if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
521 toobig: msgq(sp, M_BERR, "The screen cannot %s.",
522 count < 0 ? "shrink" : "grow");
523 return (1);
525 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
526 toosmall: msgq(sp, M_BERR,
527 "The screen can only shrink to %d rows.",
528 MINIMUM_SCREEN_ROWS);
529 return (1);
531 g->woff -= count;
535 /* Update the screens. */
536 g->rows += count;
537 g->t_rows += count;
538 if (g->t_minrows == g->t_maxrows)
539 g->t_minrows += count;
540 g->t_maxrows += count;
541 _TMAP(g) = _HMAP(g) + (g->t_rows - 1);
542 (void)status(g, g->ep, g->lno, 0);
543 F_SET(g, S_REDRAW);
545 s->rows -= count;
546 s->t_rows -= count;
547 s->t_maxrows -= count;
548 if (s->t_minrows > s->t_maxrows)
549 s->t_minrows = s->t_maxrows;
550 _TMAP(s) = _HMAP(s) + (s->t_rows - 1);
551 (void)status(s, s->ep, s->lno, 0);
552 F_SET(s, S_REDRAW);
554 return (0);