1 /* $NetBSD: vs_split.c,v 1.7 2014/01/26 21:43:45 christos Exp $ */
2 /*-
3 * Copyright (c) 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved.
7 *
8 * See the LICENSE file for redistribution information.
9 */
10
11 #include "config.h"
12
13 #include <sys/cdefs.h>
14 #if 0
15 #ifndef lint
16 static const char sccsid[] = "Id: vs_split.c,v 10.42 2001/06/25 15:19:38 skimo Exp (Berkeley) Date: 2001/06/25 15:19:38 ";
17 #endif /* not lint */
18 #else
19 __RCSID("$NetBSD: vs_split.c,v 1.7 2014/01/26 21:43:45 christos Exp $");
20 #endif
21
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/time.h>
25
26 #include <bitstring.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "../common/common.h"
34 #include "vi.h"
35
36 typedef enum { HORIZ_FOLLOW, HORIZ_PRECEDE, VERT_FOLLOW, VERT_PRECEDE } jdir_t;
37
38 static SCR *vs_getbg __P((SCR *, const char *));
39 static void vs_insert __P((SCR *sp, WIN *wp));
40 static int vs_join __P((SCR *, SCR **, jdir_t *));
41
42 /*
43 * vs_split --
44 * Create a new screen, horizontally.
45 *
46 * PUBLIC: int vs_split __P((SCR *, SCR *, int));
47 */
48 int
vs_split(SCR * sp,SCR * new,int ccl)49 vs_split(SCR *sp, SCR *new, int ccl)
50
51 /* Colon-command line split. */
52 {
53 GS *gp;
54 SMAP *smp;
55 size_t half;
56 int issmallscreen, splitup;
57
58 gp = sp->gp;
59
60 /* Check to see if it's possible. */
61 /* XXX: The IS_ONELINE fix will change this, too. */
62 if (sp->rows < 4) {
63 msgq(sp, M_ERR,
64 "222|Screen must be larger than %d lines to split", 4 - 1);
65 return (1);
66 }
67
68 /* Wait for any messages in the screen. */
69 vs_resolve(sp, NULL, 1);
70
71 /* Get a new screen map. */
72 CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
73 if (_HMAP(new) == NULL)
74 return (1);
75 _HMAP(new)->lno = sp->lno;
76 _HMAP(new)->coff = 0;
77 _HMAP(new)->soff = 1;
78
79 /* Split the screen in half. */
80 half = sp->rows / 2;
81 if (ccl && half > 6)
82 half = 6;
83
84 /*
85 * Small screens: see vs_refresh.c section 6a. Set a flag so
86 * we know to fix the screen up later.
87 */
88 issmallscreen = IS_SMALL(sp);
89
90 /* The columns in the screen don't change. */
91 new->coff = sp->coff;
92 new->cols = sp->cols;
93
94 /*
95 * Split the screen, and link the screens together. If creating a
96 * screen to edit the colon command line or the cursor is in the top
97 * half of the current screen, the new screen goes under the current
98 * screen. Else, it goes above the current screen.
99 *
100 * Recalculate current cursor position based on sp->lno, we're called
101 * with the cursor on the colon command line. Then split the screen
102 * in half and update the shared information.
103 */
104 splitup =
105 !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (size_t)(smp - HMAP) + 1) >= half;
106 if (splitup) { /* Old is bottom half. */
107 new->rows = sp->rows - half; /* New. */
108 new->roff = sp->roff;
109 sp->rows = half; /* Old. */
110 sp->roff += new->rows;
111
112 /*
113 * If the parent is the bottom half of the screen, shift
114 * the map down to match on-screen text.
115 */
116 memcpy(_HMAP(sp), _HMAP(sp) + new->rows,
117 (sp->t_maxrows - new->rows) * sizeof(SMAP));
118 } else { /* Old is top half. */
119 new->rows = half; /* New. */
120 sp->rows -= half; /* Old. */
121 new->roff = sp->roff + sp->rows;
122 }
123
124 /* Adjust maximum text count. */
125 sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
126 new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1;
127
128 /*
129 * Small screens: see vs_refresh.c, section 6a.
130 *
131 * The child may have different screen options sizes than the parent,
132 * so use them. Guarantee that text counts aren't larger than the
133 * new screen sizes.
134 */
135 if (issmallscreen) {
136 /* Fix the text line count for the parent. */
137 if (splitup)
138 sp->t_rows -= new->rows;
139
140 /* Fix the parent screen. */
141 if (sp->t_rows > sp->t_maxrows)
142 sp->t_rows = sp->t_maxrows;
143 if (sp->t_minrows > sp->t_maxrows)
144 sp->t_minrows = sp->t_maxrows;
145
146 /* Fix the child screen. */
147 new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
148 if (new->t_rows > new->t_maxrows)
149 new->t_rows = new->t_maxrows;
150 if (new->t_minrows > new->t_maxrows)
151 new->t_minrows = new->t_maxrows;
152 } else {
153 sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
154
155 /*
156 * The new screen may be a small screen, even if the parent
157 * was not. Don't complain if O_WINDOW is too large, we're
158 * splitting the screen so the screen is much smaller than
159 * normal.
160 */
161 new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
162 if (new->t_rows > new->rows - 1)
163 new->t_minrows = new->t_rows =
164 IS_ONELINE(new) ? 1 : new->rows - 1;
165 }
166
167 /* Adjust the ends of the new and old maps. */
168 _TMAP(sp) = IS_ONELINE(sp) ?
169 _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1);
170 _TMAP(new) = IS_ONELINE(new) ?
171 _HMAP(new) : _HMAP(new) + (new->t_rows - 1);
172
173 /* Reset the length of the default scroll. */
174 if ((sp->defscroll = sp->t_maxrows / 2) == 0)
175 sp->defscroll = 1;
176 if ((new->defscroll = new->t_maxrows / 2) == 0)
177 new->defscroll = 1;
178
179 /* Fit the screen into the logical chain. */
180 vs_insert(new, sp->wp);
181
182 /* Tell the display that we're splitting. */
183 (void)gp->scr_split(sp, new);
184
185 /*
186 * Initialize the screen flags:
187 *
188 * If we're in vi mode in one screen, we don't have to reinitialize.
189 * This isn't just a cosmetic fix. The path goes like this:
190 *
191 * return into vi(), SC_SSWITCH set
192 * call vs_refresh() with SC_STATUS set
193 * call vs_resolve to display the status message
194 * call vs_refresh() because the SC_SCR_VI bit isn't set
195 *
196 * Things go downhill at this point.
197 *
198 * Draw the new screen from scratch, and add a status line.
199 */
200 F_SET(new,
201 SC_SCR_REFORMAT | SC_STATUS |
202 F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
203 return (0);
204 }
205
206 /*
207 * vs_vsplit --
208 * Create a new screen, vertically.
209 *
210 * PUBLIC: int vs_vsplit __P((SCR *, SCR *));
211 */
212 int
vs_vsplit(SCR * sp,SCR * new)213 vs_vsplit(SCR *sp, SCR *new)
214 {
215 GS *gp;
216 size_t cols;
217
218 gp = sp->gp;
219
220 /* Check to see if it's possible. */
221 if (sp->cols / 2 <= MINIMUM_SCREEN_COLS) {
222 msgq(sp, M_ERR,
223 "288|Screen must be larger than %d columns to split",
224 MINIMUM_SCREEN_COLS * 2);
225 return (1);
226 }
227
228 /* Wait for any messages in the screen. */
229 vs_resolve(sp, NULL, 1);
230
231 /* Get a new screen map. */
232 CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
233 if (_HMAP(new) == NULL)
234 return (1);
235 _HMAP(new)->lno = sp->lno;
236 _HMAP(new)->coff = 0;
237 _HMAP(new)->soff = 1;
238
239 /*
240 * Split the screen in half; we have to sacrifice a column to delimit
241 * the screens.
242 *
243 * XXX
244 * We always split to the right... that makes more sense to me, and
245 * I don't want to play the stupid games that I play when splitting
246 * horizontally.
247 *
248 * XXX
249 * We reserve a column for the screen, "knowing" that curses needs
250 * one. This should be worked out with the display interface.
251 */
252 cols = sp->cols / 2;
253 new->cols = sp->cols - cols - 1;
254 sp->cols = cols;
255 new->coff = sp->coff + cols + 1;
256 sp->cno = 0;
257
258 /* Nothing else changes. */
259 new->rows = sp->rows;
260 new->t_rows = sp->t_rows;
261 new->t_maxrows = sp->t_maxrows;
262 new->t_minrows = sp->t_minrows;
263 new->roff = sp->roff;
264 new->defscroll = sp->defscroll;
265 _TMAP(new) = _HMAP(new) + (new->t_rows - 1);
266
267 /* Fit the screen into the logical chain. */
268 vs_insert(new, sp->wp);
269
270 /* Tell the display that we're splitting. */
271 (void)gp->scr_split(sp, new);
272
273 /* Redraw the old screen from scratch. */
274 F_SET(sp, SC_SCR_REFORMAT | SC_STATUS);
275
276 /*
277 * Initialize the screen flags:
278 *
279 * If we're in vi mode in one screen, we don't have to reinitialize.
280 * This isn't just a cosmetic fix. The path goes like this:
281 *
282 * return into vi(), SC_SSWITCH set
283 * call vs_refresh() with SC_STATUS set
284 * call vs_resolve to display the status message
285 * call vs_refresh() because the SC_SCR_VI bit isn't set
286 *
287 * Things go downhill at this point.
288 *
289 * Draw the new screen from scratch, and add a status line.
290 */
291 F_SET(new,
292 SC_SCR_REFORMAT | SC_STATUS |
293 F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
294 return (0);
295 }
296
297 /*
298 * vs_insert --
299 * Insert the new screen into the correct place in the logical
300 * chain.
301 */
302 static void
vs_insert(SCR * sp,WIN * wp)303 vs_insert(SCR *sp, WIN *wp)
304 {
305 SCR *tsp;
306
307 sp->wp = wp;
308
309 /* Move past all screens with lower row numbers. */
310 TAILQ_FOREACH(tsp, &wp->scrq, q)
311 if (tsp->roff >= sp->roff)
312 break;
313 /*
314 * Move past all screens with the same row number and lower
315 * column numbers.
316 */
317 for (; tsp != NULL; tsp = TAILQ_NEXT(tsp, q))
318 if (tsp->roff != sp->roff || tsp->coff > sp->coff)
319 break;
320
321 /*
322 * If we reached the end, this screen goes there. Otherwise,
323 * put it before or after the screen where we stopped.
324 */
325 if (tsp == NULL) {
326 TAILQ_INSERT_TAIL(&wp->scrq, sp, q);
327 } else if (tsp->roff < sp->roff ||
328 (tsp->roff == sp->roff && tsp->coff < sp->coff)) {
329 TAILQ_INSERT_AFTER(&wp->scrq, tsp, sp, q);
330 } else
331 TAILQ_INSERT_BEFORE(tsp, sp, q);
332 }
333
334 /*
335 * vs_discard --
336 * Discard the screen, folding the real-estate into a related screen,
337 * if one exists, and return that screen.
338 *
339 * PUBLIC: int vs_discard __P((SCR *, SCR **));
340 */
341 int
vs_discard(SCR * sp,SCR ** spp)342 vs_discard(SCR *sp, SCR **spp)
343 {
344 GS *gp;
345 SCR *tsp, **lp, *list[100];
346 jdir_t jdir;
347
348 gp = sp->gp;
349
350 /*
351 * Save the old screen's cursor information.
352 *
353 * XXX
354 * If called after file_end(), and the underlying file was a tmp
355 * file, it may have gone away.
356 */
357 if (sp->frp != NULL) {
358 sp->frp->lno = sp->lno;
359 sp->frp->cno = sp->cno;
360 F_SET(sp->frp, FR_CURSORSET);
361 }
362
363 /* If no other screens to join, we're done. */
364 if (!IS_SPLIT(sp)) {
365 (void)gp->scr_discard(sp, NULL);
366
367 if (spp != NULL)
368 *spp = NULL;
369 return (0);
370 }
371
372 /*
373 * Find a set of screens that cover one of the screen's borders.
374 * Check the vertical axis first, for no particular reason.
375 *
376 * XXX
377 * It's possible (I think?), to create a screen that shares no full
378 * border with any other set of screens, so we can't discard it. We
379 * just complain at the user until they clean it up.
380 */
381 if (vs_join(sp, list, &jdir))
382 return (1);
383
384 /*
385 * Modify the affected screens. Redraw the modified screen(s) from
386 * scratch, setting a status line. If this is ever a performance
387 * problem we could play games with the map, but I wrote that code
388 * before and it was never clean or easy.
389 *
390 * Don't clean up the discarded screen's information. If the screen
391 * isn't exiting, we'll do the work when the user redisplays it.
392 */
393 switch (jdir) {
394 case HORIZ_FOLLOW:
395 case HORIZ_PRECEDE:
396 for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) {
397 /*
398 * Small screens: see vs_refresh.c section 6a. Adjust
399 * text line info, unless it's a small screen.
400 *
401 * Reset the length of the default scroll.
402 *
403 * Reset the map references.
404 */
405 tsp->rows += sp->rows;
406 if (!IS_SMALL(tsp))
407 tsp->t_rows = tsp->t_minrows = tsp->rows - 1;
408 tsp->t_maxrows = tsp->rows - 1;
409
410 tsp->defscroll = tsp->t_maxrows / 2;
411
412 *(_HMAP(tsp) + (tsp->t_rows - 1)) = *_TMAP(tsp);
413 _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
414
415 switch (jdir) {
416 case HORIZ_FOLLOW:
417 tsp->roff = sp->roff;
418 vs_sm_fill(tsp, OOBLNO, P_TOP);
419 break;
420 case HORIZ_PRECEDE:
421 vs_sm_fill(tsp, OOBLNO, P_BOTTOM);
422 break;
423 default:
424 abort();
425 }
426 F_SET(tsp, SC_STATUS);
427 }
428 break;
429 case VERT_FOLLOW:
430 case VERT_PRECEDE:
431 for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) {
432 if (jdir == VERT_FOLLOW)
433 tsp->coff = sp->coff;
434 tsp->cols += sp->cols + 1; /* XXX: DIVIDER */
435 vs_sm_fill(tsp, OOBLNO, P_TOP);
436 F_SET(tsp, SC_STATUS);
437 }
438 break;
439 default:
440 abort();
441 }
442
443 /* Find the closest screen that changed and move to it. */
444 tsp = list[0];
445 if (spp != NULL)
446 *spp = tsp;
447
448 /* Tell the display that we're discarding a screen. */
449 (void)gp->scr_discard(sp, list);
450
451 return (0);
452 }
453
454 /*
455 * vs_join --
456 * Find a set of screens that covers a screen's border.
457 */
458 static int
vs_join(SCR * sp,SCR ** listp,jdir_t * jdirp)459 vs_join(SCR *sp, SCR **listp, jdir_t *jdirp)
460 {
461 WIN *wp;
462 SCR **lp, *tsp;
463 int first;
464 size_t tlen;
465
466 wp = sp->wp;
467
468 /* Check preceding vertical. */
469 lp = listp;
470 tlen = sp->rows;
471 TAILQ_FOREACH(tsp, &wp->scrq, q) {
472 if (sp == tsp)
473 continue;
474 /* Test if precedes the screen vertically. */
475 if (tsp->coff + tsp->cols + 1 != sp->coff)
476 continue;
477 /*
478 * Test if a subset on the vertical axis. If overlaps the
479 * beginning or end, we can't join on this axis at all.
480 */
481 if (tsp->roff > sp->roff + sp->rows)
482 continue;
483 if (tsp->roff < sp->roff) {
484 if (tsp->roff + tsp->rows >= sp->roff)
485 break;
486 continue;
487 }
488 if (tsp->roff + tsp->rows > sp->roff + sp->rows)
489 break;
490 #ifdef DEBUG
491 if (tlen < tsp->rows)
492 abort();
493 #endif
494 tlen -= tsp->rows;
495 *lp++ = tsp;
496 }
497 if (tlen == 0) {
498 *lp = NULL;
499 *jdirp = VERT_PRECEDE;
500 return (0);
501 }
502
503 /* Check following vertical. */
504 lp = listp;
505 tlen = sp->rows;
506 TAILQ_FOREACH(tsp, &wp->scrq, q) {
507 if (sp == tsp)
508 continue;
509 /* Test if follows the screen vertically. */
510 if (tsp->coff != sp->coff + sp->cols + 1)
511 continue;
512 /*
513 * Test if a subset on the vertical axis. If overlaps the
514 * beginning or end, we can't join on this axis at all.
515 */
516 if (tsp->roff > sp->roff + sp->rows)
517 continue;
518 if (tsp->roff < sp->roff) {
519 if (tsp->roff + tsp->rows >= sp->roff)
520 break;
521 continue;
522 }
523 if (tsp->roff + tsp->rows > sp->roff + sp->rows)
524 break;
525 #ifdef DEBUG
526 if (tlen < tsp->rows)
527 abort();
528 #endif
529 tlen -= tsp->rows;
530 *lp++ = tsp;
531 }
532 if (tlen == 0) {
533 *lp = NULL;
534 *jdirp = VERT_FOLLOW;
535 return (0);
536 }
537
538 /* Check preceding horizontal. */
539 first = 0;
540 lp = listp;
541 tlen = sp->cols;
542 TAILQ_FOREACH(tsp, &wp->scrq, q) {
543 if (sp == tsp)
544 continue;
545 /* Test if precedes the screen horizontally. */
546 if (tsp->roff + tsp->rows != sp->roff)
547 continue;
548 /*
549 * Test if a subset on the horizontal axis. If overlaps the
550 * beginning or end, we can't join on this axis at all.
551 */
552 if (tsp->coff > sp->coff + sp->cols)
553 continue;
554 if (tsp->coff < sp->coff) {
555 if (tsp->coff + tsp->cols >= sp->coff)
556 break;
557 continue;
558 }
559 if (tsp->coff + tsp->cols > sp->coff + sp->cols)
560 break;
561 #ifdef DEBUG
562 if (tlen < tsp->cols)
563 abort();
564 #endif
565 tlen -= tsp->cols + first;
566 first = 1;
567 *lp++ = tsp;
568 }
569 if (tlen == 0) {
570 *lp = NULL;
571 *jdirp = HORIZ_PRECEDE;
572 return (0);
573 }
574
575 /* Check following horizontal. */
576 first = 0;
577 lp = listp;
578 tlen = sp->cols;
579 TAILQ_FOREACH(tsp, &wp->scrq, q) {
580 if (sp == tsp)
581 continue;
582 /* Test if precedes the screen horizontally. */
583 if (tsp->roff != sp->roff + sp->rows)
584 continue;
585 /*
586 * Test if a subset on the horizontal axis. If overlaps the
587 * beginning or end, we can't join on this axis at all.
588 */
589 if (tsp->coff > sp->coff + sp->cols)
590 continue;
591 if (tsp->coff < sp->coff) {
592 if (tsp->coff + tsp->cols >= sp->coff)
593 break;
594 continue;
595 }
596 if (tsp->coff + tsp->cols > sp->coff + sp->cols)
597 break;
598 #ifdef DEBUG
599 if (tlen < tsp->cols)
600 abort();
601 #endif
602 tlen -= tsp->cols + first;
603 first = 1;
604 *lp++ = tsp;
605 }
606 if (tlen == 0) {
607 *lp = NULL;
608 *jdirp = HORIZ_FOLLOW;
609 return (0);
610 }
611 return (1);
612 }
613
614 /*
615 * vs_fg --
616 * Background the current screen, and foreground a new one.
617 *
618 * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int));
619 */
620 int
vs_fg(SCR * sp,SCR ** nspp,CHAR_T * name,int newscreen)621 vs_fg(SCR *sp, SCR **nspp, CHAR_T *name, int newscreen)
622 {
623 GS *gp;
624 WIN *wp;
625 SCR *nsp;
626 const char *np;
627 size_t nlen;
628
629 gp = sp->gp;
630 wp = sp->wp;
631
632 if (name)
633 INT2CHAR(sp, name, STRLEN(name) + 1, np, nlen);
634 else
635 np = NULL;
636 if (newscreen)
637 /* Get the specified background screen. */
638 nsp = vs_getbg(sp, np);
639 else
640 /* Swap screens. */
641 if (vs_swap(sp, &nsp, np))
642 return (1);
643
644 if ((*nspp = nsp) == NULL) {
645 msgq_wstr(sp, M_ERR, name,
646 name == NULL ?
647 "223|There are no background screens" :
648 "224|There's no background screen editing a file named %s");
649 return (1);
650 }
651
652 if (newscreen) {
653 /* Remove the new screen from the background queue. */
654 TAILQ_REMOVE(&gp->hq, nsp, q);
655
656 /* Split the screen; if we fail, hook the screen back in. */
657 if (vs_split(sp, nsp, 0)) {
658 TAILQ_INSERT_TAIL(&gp->hq, nsp, q);
659 return (1);
660 }
661 } else {
662 /* Move the old screen to the background queue. */
663 TAILQ_REMOVE(&wp->scrq, sp, q);
664 TAILQ_INSERT_TAIL(&gp->hq, sp, q);
665 }
666 return (0);
667 }
668
669 /*
670 * vs_bg --
671 * Background the screen, and switch to the next one.
672 *
673 * PUBLIC: int vs_bg __P((SCR *));
674 */
675 int
vs_bg(SCR * sp)676 vs_bg(SCR *sp)
677 {
678 GS *gp;
679 WIN *wp;
680 SCR *nsp;
681
682 gp = sp->gp;
683 wp = sp->wp;
684
685 /* Try and join with another screen. */
686 if (vs_discard(sp, &nsp))
687 return (1);
688 if (nsp == NULL) {
689 msgq(sp, M_ERR,
690 "225|You may not background your only displayed screen");
691 return (1);
692 }
693
694 /* Move the old screen to the background queue. */
695 TAILQ_REMOVE(&wp->scrq, sp, q);
696 TAILQ_INSERT_TAIL(&gp->hq, sp, q);
697
698 /* Toss the screen map. */
699 free(_HMAP(sp));
700 _HMAP(sp) = NULL;
701
702 /* Switch screens. */
703 sp->nextdisp = nsp;
704 F_SET(sp, SC_SSWITCH);
705
706 return (0);
707 }
708
709 /*
710 * vs_swap --
711 * Swap the current screen with a backgrounded one.
712 *
713 * PUBLIC: int vs_swap __P((SCR *, SCR **, const char *));
714 */
715 int
vs_swap(SCR * sp,SCR ** nspp,const char * name)716 vs_swap(SCR *sp, SCR **nspp, const char *name)
717 {
718 GS *gp;
719 WIN *wp;
720 SCR *nsp, *list[2];
721
722 gp = sp->gp;
723 wp = sp->wp;
724
725 /* Get the specified background screen. */
726 if ((*nspp = nsp = vs_getbg(sp, name)) == NULL)
727 return (0);
728
729 /*
730 * Save the old screen's cursor information.
731 *
732 * XXX
733 * If called after file_end(), and the underlying file was a tmp
734 * file, it may have gone away.
735 */
736 if (sp->frp != NULL) {
737 sp->frp->lno = sp->lno;
738 sp->frp->cno = sp->cno;
739 F_SET(sp->frp, FR_CURSORSET);
740 }
741
742 /* Switch screens. */
743 sp->nextdisp = nsp;
744 F_SET(sp, SC_SSWITCH);
745
746 /* Initialize terminal information. */
747 VIP(nsp)->srows = VIP(sp)->srows;
748
749 /* Initialize screen information. */
750 nsp->cols = sp->cols;
751 nsp->rows = sp->rows; /* XXX: Only place in vi that sets rows. */
752 nsp->roff = sp->roff;
753
754 /*
755 * Small screens: see vs_refresh.c, section 6a.
756 *
757 * The new screens may have different screen options sizes than the
758 * old one, so use them. Make sure that text counts aren't larger
759 * than the new screen sizes.
760 */
761 if (IS_SMALL(nsp)) {
762 nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW);
763 if (nsp->t_rows > sp->t_maxrows)
764 nsp->t_rows = nsp->t_maxrows;
765 if (nsp->t_minrows > sp->t_maxrows)
766 nsp->t_minrows = nsp->t_maxrows;
767 } else
768 nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1;
769
770 /* Reset the length of the default scroll. */
771 nsp->defscroll = nsp->t_maxrows / 2;
772
773 /* Allocate a new screen map. */
774 CALLOC_RET(nsp, _HMAP(nsp), SMAP *, SIZE_HMAP(nsp), sizeof(SMAP));
775 _TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1);
776
777 /* Fill the map. */
778 nsp->wp = sp->wp;
779 if (vs_sm_fill(nsp, nsp->lno, P_FILL))
780 return (1);
781
782 /*
783 * The new screen replaces the old screen in the parent/child list.
784 * We insert the new screen after the old one. If we're exiting,
785 * the exit will delete the old one, if we're foregrounding, the fg
786 * code will move the old one to the background queue.
787 */
788 TAILQ_REMOVE(&gp->hq, nsp, q);
789 TAILQ_INSERT_AFTER(&wp->scrq, sp, nsp, q);
790
791 /*
792 * Don't change the screen's cursor information other than to
793 * note that the cursor is wrong.
794 */
795 F_SET(VIP(nsp), VIP_CUR_INVALID);
796
797 /* Draw the new screen from scratch, and add a status line. */
798 F_SET(nsp, SC_SCR_REDRAW | SC_STATUS);
799
800 list[0] = nsp; list[1] = NULL;
801 (void)gp->scr_discard(sp, list);
802
803 return (0);
804 }
805
806 /*
807 * vs_resize --
808 * Change the absolute size of the current screen.
809 *
810 * PUBLIC: int vs_resize __P((SCR *, long, adj_t));
811 */
812 int
vs_resize(SCR * sp,long int count,adj_t adj)813 vs_resize(SCR *sp, long int count, adj_t adj)
814 {
815 GS *gp;
816 SCR *g, *s, *prev, *next, *list[3] = {NULL, NULL, NULL};
817 size_t g_off, s_off;
818
819 gp = sp->gp;
820
821 /*
822 * Figure out which screens will grow, which will shrink, and
823 * make sure it's possible.
824 */
825 if (count == 0)
826 return (0);
827 if (adj == A_SET) {
828 if (sp->t_maxrows == (size_t)count)
829 return (0);
830 if (sp->t_maxrows > (size_t)count) {
831 adj = A_DECREASE;
832 count = sp->t_maxrows - count;
833 } else {
834 adj = A_INCREASE;
835 count = count - sp->t_maxrows;
836 }
837 }
838
839 /* Find first overlapping screen */
840 for (next = TAILQ_NEXT(sp, q);
841 next != NULL &&
842 (next->coff >= sp->coff + sp->cols ||
843 next->coff + next->cols <= sp->coff);
844 next = TAILQ_NEXT(next, q))
845 continue;
846 /* See if we can use it */
847 if (next != NULL &&
848 (sp->coff != next->coff || sp->cols != next->cols))
849 next = NULL;
850 for (prev = TAILQ_PREV(sp, _scrh, q);
851 prev != NULL &&
852 (prev->coff >= sp->coff + sp->cols ||
853 prev->coff + prev->cols <= sp->coff);
854 prev = TAILQ_PREV(prev, _scrh, q))
855 continue;
856 if (prev != NULL &&
857 (sp->coff != prev->coff || sp->cols != prev->cols))
858 prev = NULL;
859
860 g_off = s_off = 0;
861 if (adj == A_DECREASE) {
862 if (count < 0)
863 count = -count;
864 s = sp;
865 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + (size_t)count)
866 goto toosmall;
867 if ((g = prev) == NULL) {
868 if ((g = next) == NULL)
869 goto toobig;
870 g_off = -count;
871 } else
872 s_off = count;
873 } else {
874 g = sp;
875 if ((s = next) != NULL &&
876 s->t_maxrows >= MINIMUM_SCREEN_ROWS + (size_t)count)
877 s_off = count;
878 else
879 s = NULL;
880 if (s == NULL) {
881 if ((s = prev) == NULL) {
882 toobig: msgq(sp, M_BERR, adj == A_DECREASE ?
883 "227|The screen cannot shrink" :
884 "228|The screen cannot grow");
885 return (1);
886 }
887 if (s->t_maxrows < MINIMUM_SCREEN_ROWS + (size_t)count) {
888 toosmall: msgq(sp, M_BERR,
889 "226|The screen can only shrink to %d rows",
890 MINIMUM_SCREEN_ROWS);
891 return (1);
892 }
893 g_off = -count;
894 }
895 }
896
897 /*
898 * Fix up the screens; we could optimize the reformatting of the
899 * screen, but this isn't likely to be a common enough operation
900 * to make it worthwhile.
901 */
902 s->rows += -count;
903 s->roff += s_off;
904 g->rows += count;
905 g->roff += g_off;
906
907 g->t_rows += count;
908 if (g->t_minrows == g->t_maxrows)
909 g->t_minrows += count;
910 g->t_maxrows += count;
911 _TMAP(g) += count;
912 F_SET(g, SC_SCR_REFORMAT | SC_STATUS);
913
914 s->t_rows -= count;
915 s->t_maxrows -= count;
916 if (s->t_minrows > s->t_maxrows)
917 s->t_minrows = s->t_maxrows;
918 _TMAP(s) -= count;
919 F_SET(s, SC_SCR_REFORMAT | SC_STATUS);
920
921 /* XXXX */
922 list[0] = g; list[1] = s;
923 gp->scr_discard(0, list);
924
925 return (0);
926 }
927
928 /*
929 * vs_getbg --
930 * Get the specified background screen, or, if name is NULL, the first
931 * background screen.
932 */
933 static SCR *
vs_getbg(SCR * sp,const char * name)934 vs_getbg(SCR *sp, const char *name)
935 {
936 GS *gp;
937 SCR *nsp;
938 char *p;
939
940 gp = sp->gp;
941
942 /* If name is NULL, return the first background screen on the list. */
943 if (name == NULL) {
944 return TAILQ_FIRST(&gp->hq);
945 }
946
947 /* Search for a full match. */
948 TAILQ_FOREACH(nsp, &gp->hq, q)
949 if (!strcmp(nsp->frp->name, name))
950 break;
951 if (nsp != NULL)
952 return (nsp);
953
954 /* Search for a last-component match. */
955 TAILQ_FOREACH(nsp, &gp->hq, q) {
956 if ((p = strrchr(nsp->frp->name, '/')) == NULL)
957 p = nsp->frp->name;
958 else
959 ++p;
960 if (!strcmp(p, name))
961 break;
962 }
963 return nsp;
964 }
965