2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
13 static const char sccsid
[] = "@(#)vs_split.c 10.31 (Berkeley) 10/13/96";
16 #include <sys/types.h>
17 #include <sys/queue.h>
20 #include <bitstring.h>
27 #include "../common/common.h"
30 static SCR
*vs_getbg
__P((SCR
*, char *));
34 * Create a new screen.
36 * PUBLIC: int vs_split __P((SCR *, SCR *, int));
39 vs_split(sp
, new, ccl
)
41 int ccl
; /* Colon-command line split. */
46 int issmallscreen
, splitup
;
50 /* Check to see if it's possible. */
51 /* XXX: The IS_ONELINE fix will change this, too. */
54 "222|Screen must be larger than %d lines to split", 4 - 1);
58 /* Wait for any messages in the screen. */
59 vs_resolve(sp
, NULL
, 1);
65 /* Get a new screen map. */
66 CALLOC(sp
, _HMAP(new), SMAP
*, SIZE_HMAP(sp
), sizeof(SMAP
));
67 if (_HMAP(new) == NULL
)
69 _HMAP(new)->lno
= sp
->lno
;
74 * Small screens: see vs_refresh.c section 6a. Set a flag so
75 * we know to fix the screen up later.
77 issmallscreen
= IS_SMALL(sp
);
79 /* The columns in the screen don't change. */
83 * Split the screen, and link the screens together. If creating a
84 * screen to edit the colon command line or the cursor is in the top
85 * half of the current screen, the new screen goes under the current
86 * screen. Else, it goes above the current screen.
88 * Recalculate current cursor position based on sp->lno, we're called
89 * with the cursor on the colon command line. Then split the screen
90 * in half and update the shared information.
93 !ccl
&& (vs_sm_cursor(sp
, &smp
) ? 0 : (smp
- HMAP
) + 1) >= half
;
94 if (splitup
) { /* Old is bottom half. */
95 new->rows
= sp
->rows
- half
; /* New. */
97 sp
->rows
= half
; /* Old. */
98 sp
->woff
+= new->rows
;
99 /* Link in before old. */
100 CIRCLEQ_INSERT_BEFORE(&gp
->dq
, sp
, new, q
);
103 * If the parent is the bottom half of the screen, shift
104 * the map down to match on-screen text.
106 memmove(_HMAP(sp
), _HMAP(sp
) + new->rows
,
107 (sp
->t_maxrows
- new->rows
) * sizeof(SMAP
));
108 } else { /* Old is top half. */
109 new->rows
= half
; /* New. */
110 sp
->rows
-= half
; /* Old. */
111 new->woff
= sp
->woff
+ sp
->rows
;
112 /* Link in after old. */
113 CIRCLEQ_INSERT_AFTER(&gp
->dq
, sp
, new, q
);
116 /* Adjust maximum text count. */
117 sp
->t_maxrows
= IS_ONELINE(sp
) ? 1 : sp
->rows
- 1;
118 new->t_maxrows
= IS_ONELINE(new) ? 1 : new->rows
- 1;
121 * Small screens: see vs_refresh.c, section 6a.
123 * The child may have different screen options sizes than the parent,
124 * so use them. Guarantee that text counts aren't larger than the
128 /* Fix the text line count for the parent. */
130 sp
->t_rows
-= new->rows
;
132 /* Fix the parent screen. */
133 if (sp
->t_rows
> sp
->t_maxrows
)
134 sp
->t_rows
= sp
->t_maxrows
;
135 if (sp
->t_minrows
> sp
->t_maxrows
)
136 sp
->t_minrows
= sp
->t_maxrows
;
138 /* Fix the child screen. */
139 new->t_minrows
= new->t_rows
= O_VAL(sp
, O_WINDOW
);
140 if (new->t_rows
> new->t_maxrows
)
141 new->t_rows
= new->t_maxrows
;
142 if (new->t_minrows
> new->t_maxrows
)
143 new->t_minrows
= new->t_maxrows
;
145 sp
->t_minrows
= sp
->t_rows
= IS_ONELINE(sp
) ? 1 : sp
->rows
- 1;
148 * The new screen may be a small screen, even if the parent
149 * was not. Don't complain if O_WINDOW is too large, we're
150 * splitting the screen so the screen is much smaller than
153 new->t_minrows
= new->t_rows
= O_VAL(sp
, O_WINDOW
);
154 if (new->t_rows
> new->rows
- 1)
155 new->t_minrows
= new->t_rows
=
156 IS_ONELINE(new) ? 1 : new->rows
- 1;
159 /* Adjust the ends of the new and old maps. */
160 _TMAP(sp
) = IS_ONELINE(sp
) ?
161 _HMAP(sp
) : _HMAP(sp
) + (sp
->t_rows
- 1);
162 _TMAP(new) = IS_ONELINE(new) ?
163 _HMAP(new) : _HMAP(new) + (new->t_rows
- 1);
165 /* Reset the length of the default scroll. */
166 if ((sp
->defscroll
= sp
->t_maxrows
/ 2) == 0)
168 if ((new->defscroll
= new->t_maxrows
/ 2) == 0)
172 * Initialize the screen flags:
174 * If we're in vi mode in one screen, we don't have to reinitialize.
175 * This isn't just a cosmetic fix. The path goes like this:
177 * return into vi(), SC_SSWITCH set
178 * call vs_refresh() with SC_STATUS set
179 * call vs_resolve to display the status message
180 * call vs_refresh() because the SC_SCR_VI bit isn't set
182 * Things go downhill at this point.
184 * Draw the new screen from scratch, and add a status line.
187 SC_SCR_REFORMAT
| SC_STATUS
|
188 F_ISSET(sp
, SC_EX
| SC_VI
| SC_SCR_VI
| SC_SCR_EX
));
194 * Discard the screen, folding the real-estate into a related screen,
195 * if one exists, and return that screen.
197 * PUBLIC: int vs_discard __P((SCR *, SCR **));
207 * Save the old screen's cursor information.
210 * If called after file_end(), and the underlying file was a tmp
211 * file, it may have gone away.
213 if (sp
->frp
!= NULL
) {
214 sp
->frp
->lno
= sp
->lno
;
215 sp
->frp
->cno
= sp
->cno
;
216 F_SET(sp
->frp
, FR_CURSORSET
);
220 * Add into a previous screen and then into a subsequent screen, as
221 * they're the closest to the current screen. If that doesn't work,
222 * there was no screen to join.
224 if ((nsp
= sp
->q
.cqe_prev
) != (void *)&sp
->gp
->dq
) {
225 nsp
->rows
+= sp
->rows
;
228 } else if ((nsp
= sp
->q
.cqe_next
) != (void *)&sp
->gp
->dq
) {
229 nsp
->woff
= sp
->woff
;
230 nsp
->rows
+= sp
->rows
;
242 * Make no effort to clean up the discarded screen's information. If
243 * it's not exiting, we'll do the work when the user redisplays it.
245 * Small screens: see vs_refresh.c section 6a. Adjust text line info,
246 * unless it's a small screen.
248 * Reset the length of the default scroll.
251 sp
->t_rows
= sp
->t_minrows
= sp
->rows
- 1;
252 sp
->t_maxrows
= sp
->rows
- 1;
253 sp
->defscroll
= sp
->t_maxrows
/ 2;
254 *(HMAP
+ (sp
->t_rows
- 1)) = *TMAP
;
255 TMAP
= HMAP
+ (sp
->t_rows
- 1);
258 * Draw the new screen from scratch, and add a status line.
261 * We could play games with the map, if this were ever to be a
262 * performance problem, but I wrote the code a few times and it
263 * was never clean or easy.
267 vs_sm_fill(sp
, OOBLNO
, P_TOP
);
270 vs_sm_fill(sp
, OOBLNO
, P_BOTTOM
);
276 F_SET(sp
, SC_STATUS
);
282 * Background the current screen, and foreground a new one.
284 * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int));
287 vs_fg(sp
, nspp
, name
, newscreen
)
298 /* Get the specified background screen. */
299 nsp
= vs_getbg(sp
, name
);
302 if (vs_swap(sp
, &nsp
, name
))
305 if ((*nspp
= nsp
) == NULL
) {
306 msgq_str(sp
, M_ERR
, name
,
308 "223|There are no background screens" :
309 "224|There's no background screen editing a file named %s");
314 /* Remove the new screen from the background queue. */
315 CIRCLEQ_REMOVE(&gp
->hq
, nsp
, q
);
317 /* Split the screen; if we fail, hook the screen back in. */
318 if (vs_split(sp
, nsp
, 0)) {
319 CIRCLEQ_INSERT_TAIL(&gp
->hq
, nsp
, q
);
323 /* Move the old screen to the background queue. */
324 CIRCLEQ_REMOVE(&gp
->dq
, sp
, q
);
325 CIRCLEQ_INSERT_TAIL(&gp
->hq
, sp
, q
);
332 * Background the screen, and switch to the next one.
334 * PUBLIC: int vs_bg __P((SCR *));
345 /* Try and join with another screen. */
346 if (vs_discard(sp
, &nsp
))
350 "225|You may not background your only displayed screen");
354 /* Move the old screen to the background queue. */
355 CIRCLEQ_REMOVE(&gp
->dq
, sp
, q
);
356 CIRCLEQ_INSERT_TAIL(&gp
->hq
, sp
, q
);
358 /* Toss the screen map. */
362 /* Switch screens. */
364 F_SET(sp
, SC_SSWITCH
);
371 * Swap the current screen with a backgrounded one.
373 * PUBLIC: int vs_swap __P((SCR *, SCR **, char *));
376 vs_swap(sp
, nspp
, name
)
385 /* Get the specified background screen. */
386 if ((*nspp
= nsp
= vs_getbg(sp
, name
)) == NULL
)
390 * Save the old screen's cursor information.
393 * If called after file_end(), and the underlying file was a tmp
394 * file, it may have gone away.
396 if (sp
->frp
!= NULL
) {
397 sp
->frp
->lno
= sp
->lno
;
398 sp
->frp
->cno
= sp
->cno
;
399 F_SET(sp
->frp
, FR_CURSORSET
);
402 /* Switch screens. */
404 F_SET(sp
, SC_SSWITCH
);
406 /* Initialize terminal information. */
407 VIP(nsp
)->srows
= VIP(sp
)->srows
;
409 /* Initialize screen information. */
410 nsp
->cols
= sp
->cols
;
411 nsp
->rows
= sp
->rows
; /* XXX: Only place in vi that sets rows. */
412 nsp
->woff
= sp
->woff
;
415 * Small screens: see vs_refresh.c, section 6a.
417 * The new screens may have different screen options sizes than the
418 * old one, so use them. Make sure that text counts aren't larger
419 * than the new screen sizes.
422 nsp
->t_minrows
= nsp
->t_rows
= O_VAL(nsp
, O_WINDOW
);
423 if (nsp
->t_rows
> sp
->t_maxrows
)
424 nsp
->t_rows
= nsp
->t_maxrows
;
425 if (nsp
->t_minrows
> sp
->t_maxrows
)
426 nsp
->t_minrows
= nsp
->t_maxrows
;
428 nsp
->t_rows
= nsp
->t_maxrows
= nsp
->t_minrows
= nsp
->rows
- 1;
430 /* Reset the length of the default scroll. */
431 nsp
->defscroll
= nsp
->t_maxrows
/ 2;
433 /* Allocate a new screen map. */
434 CALLOC_RET(nsp
, _HMAP(nsp
), SMAP
*, SIZE_HMAP(nsp
), sizeof(SMAP
));
435 _TMAP(nsp
) = _HMAP(nsp
) + (nsp
->t_rows
- 1);
438 if (vs_sm_fill(nsp
, nsp
->lno
, P_FILL
))
442 * The new screen replaces the old screen in the parent/child list.
443 * We insert the new screen after the old one. If we're exiting,
444 * the exit will delete the old one, if we're foregrounding, the fg
445 * code will move the old one to the background queue.
447 CIRCLEQ_REMOVE(&gp
->hq
, nsp
, q
);
448 CIRCLEQ_INSERT_AFTER(&gp
->dq
, sp
, nsp
, q
);
451 * Don't change the screen's cursor information other than to
452 * note that the cursor is wrong.
454 F_SET(VIP(nsp
), VIP_CUR_INVALID
);
456 /* Draw the new screen from scratch, and add a status line. */
457 F_SET(nsp
, SC_SCR_REDRAW
| SC_STATUS
);
463 * Change the absolute size of the current screen.
465 * PUBLIC: int vs_resize __P((SCR *, long, adj_t));
468 vs_resize(sp
, count
, adj
)
480 * Figure out which screens will grow, which will shrink, and
481 * make sure it's possible.
486 if (sp
->t_maxrows
== count
)
488 if (sp
->t_maxrows
> count
) {
490 count
= sp
->t_maxrows
- count
;
493 count
= count
- sp
->t_maxrows
;
498 if (adj
== A_DECREASE
) {
502 if (s
->t_maxrows
< MINIMUM_SCREEN_ROWS
+ count
)
504 if ((g
= sp
->q
.cqe_prev
) == (void *)&gp
->dq
) {
505 if ((g
= sp
->q
.cqe_next
) == (void *)&gp
->dq
)
512 if ((s
= sp
->q
.cqe_next
) != (void *)&gp
->dq
)
513 if (s
->t_maxrows
< MINIMUM_SCREEN_ROWS
+ count
)
520 if ((s
= sp
->q
.cqe_prev
) == (void *)&gp
->dq
) {
521 toobig
: msgq(sp
, M_BERR
, adj
== A_DECREASE
?
522 "227|The screen cannot shrink" :
523 "228|The screen cannot grow");
526 if (s
->t_maxrows
< MINIMUM_SCREEN_ROWS
+ count
) {
527 toosmall
: msgq(sp
, M_BERR
,
528 "226|The screen can only shrink to %d rows",
529 MINIMUM_SCREEN_ROWS
);
537 * Fix up the screens; we could optimize the reformatting of the
538 * screen, but this isn't likely to be a common enough operation
539 * to make it worthwhile.
547 if (g
->t_minrows
== g
->t_maxrows
)
548 g
->t_minrows
+= count
;
549 g
->t_maxrows
+= count
;
551 F_SET(g
, SC_SCR_REFORMAT
| SC_STATUS
);
554 s
->t_maxrows
-= count
;
555 if (s
->t_minrows
> s
->t_maxrows
)
556 s
->t_minrows
= s
->t_maxrows
;
558 F_SET(s
, SC_SCR_REFORMAT
| SC_STATUS
);
565 * Get the specified background screen, or, if name is NULL, the first
579 /* If name is NULL, return the first background screen on the list. */
581 nsp
= gp
->hq
.cqh_first
;
582 return (nsp
== (void *)&gp
->hq
? NULL
: nsp
);
585 /* Search for a full match. */
586 for (nsp
= gp
->hq
.cqh_first
;
587 nsp
!= (void *)&gp
->hq
; nsp
= nsp
->q
.cqe_next
)
588 if (!strcmp(nsp
->frp
->name
, name
))
590 if (nsp
!= (void *)&gp
->hq
)
593 /* Search for a last-component match. */
594 for (nsp
= gp
->hq
.cqh_first
;
595 nsp
!= (void *)&gp
->hq
; nsp
= nsp
->q
.cqe_next
) {
596 if ((p
= strrchr(nsp
->frp
->name
, '/')) == NULL
)
600 if (!strcmp(p
, name
))
603 if (nsp
!= (void *)&gp
->hq
)