xref: /vim-8.2.3635/src/window.c (revision 89015a67)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read a list of people who contributed.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 #include "vim.h"
11 
12 static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, long Prenum);
13 static void win_init(win_T *newp, win_T *oldp, int flags);
14 static void win_init_some(win_T *newp, win_T *oldp);
15 static void frame_comp_pos(frame_T *topfrp, int *row, int *col);
16 static void frame_setheight(frame_T *curfrp, int height);
17 static void frame_setwidth(frame_T *curfrp, int width);
18 static void win_exchange(long);
19 static void win_rotate(int, int);
20 static void win_totop(int size, int flags);
21 static void win_equal_rec(win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height);
22 static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp);
23 static frame_T *win_altframe(win_T *win, tabpage_T *tp);
24 static tabpage_T *alt_tabpage(void);
25 static win_T *frame2win(frame_T *frp);
26 static int frame_has_win(frame_T *frp, win_T *wp);
27 static void frame_new_height(frame_T *topfrp, int height, int topfirst, int wfh);
28 static int frame_fixed_height(frame_T *frp);
29 static int frame_fixed_width(frame_T *frp);
30 static void frame_add_statusline(frame_T *frp);
31 static void frame_new_width(frame_T *topfrp, int width, int leftfirst, int wfw);
32 static void frame_add_vsep(frame_T *frp);
33 static int frame_minwidth(frame_T *topfrp, win_T *next_curwin);
34 static void frame_fix_width(win_T *wp);
35 static int win_alloc_firstwin(win_T *oldwin);
36 static void new_frame(win_T *wp);
37 static tabpage_T *alloc_tabpage(void);
38 static int leave_tabpage(buf_T *new_curbuf, int trigger_leave_autocmds);
39 static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_autocmds, int trigger_leave_autocmds);
40 static void frame_fix_height(win_T *wp);
41 static int frame_minheight(frame_T *topfrp, win_T *next_curwin);
42 static int may_open_tabpage(void);
43 static void win_enter_ext(win_T *wp, int undo_sync, int no_curwin, int trigger_new_autocmds, int trigger_enter_autocmds, int trigger_leave_autocmds);
44 static void win_free(win_T *wp, tabpage_T *tp);
45 static int win_unlisted(win_T *wp);
46 static void win_append(win_T *after, win_T *wp);
47 static void frame_append(frame_T *after, frame_T *frp);
48 static void frame_insert(frame_T *before, frame_T *frp);
49 static void frame_remove(frame_T *frp);
50 static void win_goto_ver(int up, long count);
51 static void win_goto_hor(int left, long count);
52 static void frame_add_height(frame_T *frp, int n);
53 static void last_status_rec(frame_T *fr, int statusline);
54 
55 static void make_snapshot_rec(frame_T *fr, frame_T **frp);
56 static void clear_snapshot(tabpage_T *tp, int idx);
57 static void clear_snapshot_rec(frame_T *fr);
58 static int check_snapshot_rec(frame_T *sn, frame_T *fr);
59 static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr);
60 
61 static int frame_check_height(frame_T *topfrp, int height);
62 static int frame_check_width(frame_T *topfrp, int width);
63 
64 static win_T *win_alloc(win_T *after, int hidden);
65 
66 #define NOWIN		(win_T *)-1	// non-existing window
67 
68 #define ROWS_AVAIL (Rows - p_ch - tabline_height())
69 
70 static char *m_onlyone = N_("Already only one window");
71 
72 // When non-zero splitting a window is forbidden.  Used to avoid that nasty
73 // autocommands mess up the window structure.
74 static int split_disallowed = 0;
75 
76 // #define WIN_DEBUG
77 #ifdef WIN_DEBUG
78 /*
79  * Call this method to log the current window layout.
80  */
81     static void
82 log_frame_layout(frame_T *frame)
83 {
84     ch_log(NULL, "layout %s, wi: %d, he: %d, wwi: %d, whe: %d, id: %d",
85 	    frame->fr_layout == FR_LEAF ? "LEAF"
86 				  : frame->fr_layout == FR_ROW ? "ROW" : "COL",
87 	    frame->fr_width,
88 	    frame->fr_height,
89 	    frame->fr_win == NULL ? -1 : frame->fr_win->w_width,
90 	    frame->fr_win == NULL ? -1 : frame->fr_win->w_height,
91 	    frame->fr_win == NULL ? -1 : frame->fr_win->w_id);
92     if (frame->fr_child != NULL)
93     {
94 	ch_log(NULL, "children");
95 	log_frame_layout(frame->fr_child);
96 	if (frame->fr_next != NULL)
97 	    ch_log(NULL, "END of children");
98     }
99     if (frame->fr_next != NULL)
100 	log_frame_layout(frame->fr_next);
101 }
102 #endif
103 
104 /*
105  * All CTRL-W window commands are handled here, called from normal_cmd().
106  */
107     void
108 do_window(
109     int		nchar,
110     long	Prenum,
111     int		xchar)	    // extra char from ":wincmd gx" or NUL
112 {
113     long	Prenum1;
114     win_T	*wp;
115 #if defined(FEAT_SEARCHPATH) || defined(FEAT_FIND_ID)
116     char_u	*ptr;
117     linenr_T    lnum = -1;
118 #endif
119 #ifdef FEAT_FIND_ID
120     int		type = FIND_DEFINE;
121     int		len;
122 #endif
123     char_u	cbuf[40];
124 
125     if (ERROR_IF_ANY_POPUP_WINDOW)
126 	return;
127 
128 #ifdef FEAT_CMDWIN
129 # define CHECK_CMDWIN \
130     do { \
131 	if (cmdwin_type != 0) \
132 	{ \
133 	    emsg(_(e_cmdwin)); \
134 	    return; \
135 	} \
136     } while (0)
137 #else
138 # define CHECK_CMDWIN do { /**/ } while (0)
139 #endif
140 
141     Prenum1 = Prenum == 0 ? 1 : Prenum;
142 
143     switch (nchar)
144     {
145 // split current window in two parts, horizontally
146     case 'S':
147     case Ctrl_S:
148     case 's':
149 		CHECK_CMDWIN;
150 		reset_VIsual_and_resel();	// stop Visual mode
151 #ifdef FEAT_QUICKFIX
152 		// When splitting the quickfix window open a new buffer in it,
153 		// don't replicate the quickfix buffer.
154 		if (bt_quickfix(curbuf))
155 		    goto newwindow;
156 #endif
157 #ifdef FEAT_GUI
158 		need_mouse_correct = TRUE;
159 #endif
160 		(void)win_split((int)Prenum, 0);
161 		break;
162 
163 // split current window in two parts, vertically
164     case Ctrl_V:
165     case 'v':
166 		CHECK_CMDWIN;
167 		reset_VIsual_and_resel();	// stop Visual mode
168 #ifdef FEAT_QUICKFIX
169 		// When splitting the quickfix window open a new buffer in it,
170 		// don't replicate the quickfix buffer.
171 		if (bt_quickfix(curbuf))
172 		    goto newwindow;
173 #endif
174 #ifdef FEAT_GUI
175 		need_mouse_correct = TRUE;
176 #endif
177 		(void)win_split((int)Prenum, WSP_VERT);
178 		break;
179 
180 // split current window and edit alternate file
181     case Ctrl_HAT:
182     case '^':
183 		CHECK_CMDWIN;
184 		reset_VIsual_and_resel();	// stop Visual mode
185 
186 		if (buflist_findnr(Prenum == 0
187 					? curwin->w_alt_fnum : Prenum) == NULL)
188 		{
189 		    if (Prenum == 0)
190 			emsg(_(e_noalt));
191 		    else
192 			semsg(_("E92: Buffer %ld not found"), Prenum);
193 		    break;
194 		}
195 
196 		if (!curbuf_locked() && win_split(0, 0) == OK)
197 		    (void)buflist_getfile(
198 			    Prenum == 0 ? curwin->w_alt_fnum : Prenum,
199 			    (linenr_T)0, GETF_ALT, FALSE);
200 		break;
201 
202 // open new window
203     case Ctrl_N:
204     case 'n':
205 		CHECK_CMDWIN;
206 		reset_VIsual_and_resel();	// stop Visual mode
207 #ifdef FEAT_QUICKFIX
208 newwindow:
209 #endif
210 		if (Prenum)
211 		    // window height
212 		    vim_snprintf((char *)cbuf, sizeof(cbuf) - 5, "%ld", Prenum);
213 		else
214 		    cbuf[0] = NUL;
215 #if defined(FEAT_QUICKFIX)
216 		if (nchar == 'v' || nchar == Ctrl_V)
217 		    STRCAT(cbuf, "v");
218 #endif
219 		STRCAT(cbuf, "new");
220 		do_cmdline_cmd(cbuf);
221 		break;
222 
223 // quit current window
224     case Ctrl_Q:
225     case 'q':
226 		reset_VIsual_and_resel();	// stop Visual mode
227 		cmd_with_count("quit", cbuf, sizeof(cbuf), Prenum);
228 		do_cmdline_cmd(cbuf);
229 		break;
230 
231 // close current window
232     case Ctrl_C:
233     case 'c':
234 		reset_VIsual_and_resel();	// stop Visual mode
235 		cmd_with_count("close", cbuf, sizeof(cbuf), Prenum);
236 		do_cmdline_cmd(cbuf);
237 		break;
238 
239 #if defined(FEAT_QUICKFIX)
240 // close preview window
241     case Ctrl_Z:
242     case 'z':
243 		CHECK_CMDWIN;
244 		reset_VIsual_and_resel();	// stop Visual mode
245 		do_cmdline_cmd((char_u *)"pclose");
246 		break;
247 
248 // cursor to preview window
249     case 'P':
250 		FOR_ALL_WINDOWS(wp)
251 		    if (wp->w_p_pvw)
252 			break;
253 		if (wp == NULL)
254 		    emsg(_("E441: There is no preview window"));
255 		else
256 		    win_goto(wp);
257 		break;
258 #endif
259 
260 // close all but current window
261     case Ctrl_O:
262     case 'o':
263 		CHECK_CMDWIN;
264 		reset_VIsual_and_resel();	// stop Visual mode
265 		cmd_with_count("only", cbuf, sizeof(cbuf), Prenum);
266 		do_cmdline_cmd(cbuf);
267 		break;
268 
269 // cursor to next window with wrap around
270     case Ctrl_W:
271     case 'w':
272 // cursor to previous window with wrap around
273     case 'W':
274 		CHECK_CMDWIN;
275 		if (ONE_WINDOW && Prenum != 1)	// just one window
276 		    beep_flush();
277 		else
278 		{
279 		    if (Prenum)			// go to specified window
280 		    {
281 			for (wp = firstwin; --Prenum > 0; )
282 			{
283 			    if (wp->w_next == NULL)
284 				break;
285 			    else
286 				wp = wp->w_next;
287 			}
288 		    }
289 		    else
290 		    {
291 			if (nchar == 'W')	    // go to previous window
292 			{
293 			    wp = curwin->w_prev;
294 			    if (wp == NULL)
295 				wp = lastwin;	    // wrap around
296 			}
297 			else			    // go to next window
298 			{
299 			    wp = curwin->w_next;
300 			    if (wp == NULL)
301 				wp = firstwin;	    // wrap around
302 			}
303 		    }
304 		    win_goto(wp);
305 		}
306 		break;
307 
308 // cursor to window below
309     case 'j':
310     case K_DOWN:
311     case Ctrl_J:
312 		CHECK_CMDWIN;
313 		win_goto_ver(FALSE, Prenum1);
314 		break;
315 
316 // cursor to window above
317     case 'k':
318     case K_UP:
319     case Ctrl_K:
320 		CHECK_CMDWIN;
321 		win_goto_ver(TRUE, Prenum1);
322 		break;
323 
324 // cursor to left window
325     case 'h':
326     case K_LEFT:
327     case Ctrl_H:
328     case K_BS:
329 		CHECK_CMDWIN;
330 		win_goto_hor(TRUE, Prenum1);
331 		break;
332 
333 // cursor to right window
334     case 'l':
335     case K_RIGHT:
336     case Ctrl_L:
337 		CHECK_CMDWIN;
338 		win_goto_hor(FALSE, Prenum1);
339 		break;
340 
341 // move window to new tab page
342     case 'T':
343 		CHECK_CMDWIN;
344 		if (one_window())
345 		    msg(_(m_onlyone));
346 		else
347 		{
348 		    tabpage_T	*oldtab = curtab;
349 		    tabpage_T	*newtab;
350 
351 		    // First create a new tab with the window, then go back to
352 		    // the old tab and close the window there.
353 		    wp = curwin;
354 		    if (win_new_tabpage((int)Prenum) == OK
355 						     && valid_tabpage(oldtab))
356 		    {
357 			newtab = curtab;
358 			goto_tabpage_tp(oldtab, TRUE, TRUE);
359 			if (curwin == wp)
360 			    win_close(curwin, FALSE);
361 			if (valid_tabpage(newtab))
362 			    goto_tabpage_tp(newtab, TRUE, TRUE);
363 		    }
364 		}
365 		break;
366 
367 // cursor to top-left window
368     case 't':
369     case Ctrl_T:
370 		win_goto(firstwin);
371 		break;
372 
373 // cursor to bottom-right window
374     case 'b':
375     case Ctrl_B:
376 		win_goto(lastwin);
377 		break;
378 
379 // cursor to last accessed (previous) window
380     case 'p':
381     case Ctrl_P:
382 		if (!win_valid(prevwin))
383 		    beep_flush();
384 		else
385 		    win_goto(prevwin);
386 		break;
387 
388 // exchange current and next window
389     case 'x':
390     case Ctrl_X:
391 		CHECK_CMDWIN;
392 		win_exchange(Prenum);
393 		break;
394 
395 // rotate windows downwards
396     case Ctrl_R:
397     case 'r':
398 		CHECK_CMDWIN;
399 		reset_VIsual_and_resel();	// stop Visual mode
400 		win_rotate(FALSE, (int)Prenum1);    // downwards
401 		break;
402 
403 // rotate windows upwards
404     case 'R':
405 		CHECK_CMDWIN;
406 		reset_VIsual_and_resel();	// stop Visual mode
407 		win_rotate(TRUE, (int)Prenum1);	    // upwards
408 		break;
409 
410 // move window to the very top/bottom/left/right
411     case 'K':
412     case 'J':
413     case 'H':
414     case 'L':
415 		CHECK_CMDWIN;
416 		win_totop((int)Prenum,
417 			((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
418 			| ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT));
419 		break;
420 
421 // make all windows the same height
422     case '=':
423 #ifdef FEAT_GUI
424 		need_mouse_correct = TRUE;
425 #endif
426 		win_equal(NULL, FALSE, 'b');
427 		break;
428 
429 // increase current window height
430     case '+':
431 #ifdef FEAT_GUI
432 		need_mouse_correct = TRUE;
433 #endif
434 		win_setheight(curwin->w_height + (int)Prenum1);
435 		break;
436 
437 // decrease current window height
438     case '-':
439 #ifdef FEAT_GUI
440 		need_mouse_correct = TRUE;
441 #endif
442 		win_setheight(curwin->w_height - (int)Prenum1);
443 		break;
444 
445 // set current window height
446     case Ctrl__:
447     case '_':
448 #ifdef FEAT_GUI
449 		need_mouse_correct = TRUE;
450 #endif
451 		win_setheight(Prenum ? (int)Prenum : 9999);
452 		break;
453 
454 // increase current window width
455     case '>':
456 #ifdef FEAT_GUI
457 		need_mouse_correct = TRUE;
458 #endif
459 		win_setwidth(curwin->w_width + (int)Prenum1);
460 		break;
461 
462 // decrease current window width
463     case '<':
464 #ifdef FEAT_GUI
465 		need_mouse_correct = TRUE;
466 #endif
467 		win_setwidth(curwin->w_width - (int)Prenum1);
468 		break;
469 
470 // set current window width
471     case '|':
472 #ifdef FEAT_GUI
473 		need_mouse_correct = TRUE;
474 #endif
475 		win_setwidth(Prenum != 0 ? (int)Prenum : 9999);
476 		break;
477 
478 // jump to tag and split window if tag exists (in preview window)
479 #if defined(FEAT_QUICKFIX)
480     case '}':
481 		CHECK_CMDWIN;
482 		if (Prenum)
483 		    g_do_tagpreview = Prenum;
484 		else
485 		    g_do_tagpreview = p_pvh;
486 #endif
487 		// FALLTHROUGH
488     case ']':
489     case Ctrl_RSB:
490 		CHECK_CMDWIN;
491 		// keep Visual mode, can select words to use as a tag
492 		if (Prenum)
493 		    postponed_split = Prenum;
494 		else
495 		    postponed_split = -1;
496 #ifdef FEAT_QUICKFIX
497 		if (nchar != '}')
498 		    g_do_tagpreview = 0;
499 #endif
500 
501 		// Execute the command right here, required when "wincmd ]"
502 		// was used in a function.
503 		do_nv_ident(Ctrl_RSB, NUL);
504 		break;
505 
506 #ifdef FEAT_SEARCHPATH
507 // edit file name under cursor in a new window
508     case 'f':
509     case 'F':
510     case Ctrl_F:
511 wingotofile:
512 		CHECK_CMDWIN;
513 
514 		ptr = grab_file_name(Prenum1, &lnum);
515 		if (ptr != NULL)
516 		{
517 		    tabpage_T	*oldtab = curtab;
518 		    win_T	*oldwin = curwin;
519 # ifdef FEAT_GUI
520 		    need_mouse_correct = TRUE;
521 # endif
522 		    setpcmark();
523 		    if (win_split(0, 0) == OK)
524 		    {
525 			RESET_BINDING(curwin);
526 			if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL,
527 						   ECMD_HIDE, NULL) == FAIL)
528 			{
529 			    // Failed to open the file, close the window
530 			    // opened for it.
531 			    win_close(curwin, FALSE);
532 			    goto_tabpage_win(oldtab, oldwin);
533 			}
534 			else if (nchar == 'F' && lnum >= 0)
535 			{
536 			    curwin->w_cursor.lnum = lnum;
537 			    check_cursor_lnum();
538 			    beginline(BL_SOL | BL_FIX);
539 			}
540 		    }
541 		    vim_free(ptr);
542 		}
543 		break;
544 #endif
545 
546 #ifdef FEAT_FIND_ID
547 // Go to the first occurrence of the identifier under cursor along path in a
548 // new window -- webb
549     case 'i':			    // Go to any match
550     case Ctrl_I:
551 		type = FIND_ANY;
552 		// FALLTHROUGH
553     case 'd':			    // Go to definition, using 'define'
554     case Ctrl_D:
555 		CHECK_CMDWIN;
556 		if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
557 		    break;
558 		find_pattern_in_path(ptr, 0, len, TRUE,
559 			Prenum == 0 ? TRUE : FALSE, type,
560 			Prenum1, ACTION_SPLIT, (linenr_T)1, (linenr_T)MAXLNUM);
561 		curwin->w_set_curswant = TRUE;
562 		break;
563 #endif
564 
565 // Quickfix window only: view the result under the cursor in a new split.
566 #if defined(FEAT_QUICKFIX)
567     case K_KENTER:
568     case CAR:
569 		if (bt_quickfix(curbuf))
570 		    qf_view_result(TRUE);
571 		break;
572 #endif
573 
574 // CTRL-W g  extended commands
575     case 'g':
576     case Ctrl_G:
577 		CHECK_CMDWIN;
578 #ifdef USE_ON_FLY_SCROLL
579 		dont_scroll = TRUE;		// disallow scrolling here
580 #endif
581 		++no_mapping;
582 		++allow_keys;   // no mapping for xchar, but allow key codes
583 		if (xchar == NUL)
584 		    xchar = plain_vgetc();
585 		LANGMAP_ADJUST(xchar, TRUE);
586 		--no_mapping;
587 		--allow_keys;
588 #ifdef FEAT_CMDL_INFO
589 		(void)add_to_showcmd(xchar);
590 #endif
591 		switch (xchar)
592 		{
593 #if defined(FEAT_QUICKFIX)
594 		    case '}':
595 			xchar = Ctrl_RSB;
596 			if (Prenum)
597 			    g_do_tagpreview = Prenum;
598 			else
599 			    g_do_tagpreview = p_pvh;
600 #endif
601 			// FALLTHROUGH
602 		    case ']':
603 		    case Ctrl_RSB:
604 			// keep Visual mode, can select words to use as a tag
605 			if (Prenum)
606 			    postponed_split = Prenum;
607 			else
608 			    postponed_split = -1;
609 
610 			// Execute the command right here, required when
611 			// "wincmd g}" was used in a function.
612 			do_nv_ident('g', xchar);
613 			break;
614 
615 #ifdef FEAT_SEARCHPATH
616 		    case 'f':	    // CTRL-W gf: "gf" in a new tab page
617 		    case 'F':	    // CTRL-W gF: "gF" in a new tab page
618 			cmdmod.cmod_tab = tabpage_index(curtab) + 1;
619 			nchar = xchar;
620 			goto wingotofile;
621 #endif
622 		    case 't':	    // CTRL-W gt: go to next tab page
623 			goto_tabpage((int)Prenum);
624 			break;
625 
626 		    case 'T':	    // CTRL-W gT: go to previous tab page
627 			goto_tabpage(-(int)Prenum1);
628 			break;
629 
630 		    case TAB:	    // CTRL-W g<Tab>: go to last used tab page
631 			if (goto_tabpage_lastused() == FAIL)
632 			    beep_flush();
633 			break;
634 
635 		    default:
636 			beep_flush();
637 			break;
638 		}
639 		break;
640 
641     default:	beep_flush();
642 		break;
643     }
644 }
645 
646 /*
647  * Figure out the address type for ":wincmd".
648  */
649     void
650 get_wincmd_addr_type(char_u *arg, exarg_T *eap)
651 {
652     switch (*arg)
653     {
654     case 'S':
655     case Ctrl_S:
656     case 's':
657     case Ctrl_N:
658     case 'n':
659     case 'j':
660     case Ctrl_J:
661     case 'k':
662     case Ctrl_K:
663     case 'T':
664     case Ctrl_R:
665     case 'r':
666     case 'R':
667     case 'K':
668     case 'J':
669     case '+':
670     case '-':
671     case Ctrl__:
672     case '_':
673     case '|':
674     case ']':
675     case Ctrl_RSB:
676     case 'g':
677     case Ctrl_G:
678     case Ctrl_V:
679     case 'v':
680     case 'h':
681     case Ctrl_H:
682     case 'l':
683     case Ctrl_L:
684     case 'H':
685     case 'L':
686     case '>':
687     case '<':
688 #if defined(FEAT_QUICKFIX)
689     case '}':
690 #endif
691 #ifdef FEAT_SEARCHPATH
692     case 'f':
693     case 'F':
694     case Ctrl_F:
695 #endif
696 #ifdef FEAT_FIND_ID
697     case 'i':
698     case Ctrl_I:
699     case 'd':
700     case Ctrl_D:
701 #endif
702 		// window size or any count
703 		eap->addr_type = ADDR_OTHER;
704 		break;
705 
706     case Ctrl_HAT:
707     case '^':
708 		// buffer number
709 		eap->addr_type = ADDR_BUFFERS;
710 		break;
711 
712     case Ctrl_Q:
713     case 'q':
714     case Ctrl_C:
715     case 'c':
716     case Ctrl_O:
717     case 'o':
718     case Ctrl_W:
719     case 'w':
720     case 'W':
721     case 'x':
722     case Ctrl_X:
723 		// window number
724 		eap->addr_type = ADDR_WINDOWS;
725 		break;
726 
727 #if defined(FEAT_QUICKFIX)
728     case Ctrl_Z:
729     case 'z':
730     case 'P':
731 #endif
732     case 't':
733     case Ctrl_T:
734     case 'b':
735     case Ctrl_B:
736     case 'p':
737     case Ctrl_P:
738     case '=':
739     case CAR:
740 		// no count
741 		eap->addr_type = ADDR_NONE;
742 		break;
743     }
744 }
745 
746     static void
747 cmd_with_count(
748     char	*cmd,
749     char_u	*bufp,
750     size_t	bufsize,
751     long	Prenum)
752 {
753     size_t	len = STRLEN(cmd);
754 
755     STRCPY(bufp, cmd);
756     if (Prenum > 0)
757 	vim_snprintf((char *)bufp + len, bufsize - len, "%ld", Prenum);
758 }
759 
760 /*
761  * If "split_disallowed" is set given an error and return FAIL.
762  * Otherwise return OK.
763  */
764     static int
765 check_split_disallowed()
766 {
767     if (split_disallowed > 0)
768     {
769 	emsg(_("E242: Can't split a window while closing another"));
770 	return FAIL;
771     }
772     return OK;
773 }
774 
775 /*
776  * split the current window, implements CTRL-W s and :split
777  *
778  * "size" is the height or width for the new window, 0 to use half of current
779  * height or width.
780  *
781  * "flags":
782  * WSP_ROOM: require enough room for new window
783  * WSP_VERT: vertical split.
784  * WSP_TOP:  open window at the top-left of the shell (help window).
785  * WSP_BOT:  open window at the bottom-right of the shell (quickfix window).
786  * WSP_HELP: creating the help window, keep layout snapshot
787  *
788  * return FAIL for failure, OK otherwise
789  */
790     int
791 win_split(int size, int flags)
792 {
793     if (ERROR_IF_ANY_POPUP_WINDOW)
794 	return FAIL;
795 
796     // When the ":tab" modifier was used open a new tab page instead.
797     if (may_open_tabpage() == OK)
798 	return OK;
799 
800     // Add flags from ":vertical", ":topleft" and ":botright".
801     flags |= cmdmod.cmod_split;
802     if ((flags & WSP_TOP) && (flags & WSP_BOT))
803     {
804 	emsg(_("E442: Can't split topleft and botright at the same time"));
805 	return FAIL;
806     }
807     if (check_split_disallowed() == FAIL)
808 	return FAIL;
809 
810     // When creating the help window make a snapshot of the window layout.
811     // Otherwise clear the snapshot, it's now invalid.
812     if (flags & WSP_HELP)
813 	make_snapshot(SNAP_HELP_IDX);
814     else
815 	clear_snapshot(curtab, SNAP_HELP_IDX);
816 
817     return win_split_ins(size, flags, NULL, 0);
818 }
819 
820 /*
821  * When "new_wp" is NULL: split the current window in two.
822  * When "new_wp" is not NULL: insert this window at the far
823  * top/left/right/bottom.
824  * return FAIL for failure, OK otherwise
825  */
826     int
827 win_split_ins(
828     int		size,
829     int		flags,
830     win_T	*new_wp,
831     int		dir)
832 {
833     win_T	*wp = new_wp;
834     win_T	*oldwin;
835     int		new_size = size;
836     int		i;
837     int		need_status = 0;
838     int		do_equal = FALSE;
839     int		needed;
840     int		available;
841     int		oldwin_height = 0;
842     int		layout;
843     frame_T	*frp, *curfrp, *frp2, *prevfrp;
844     int		before;
845     int		minheight;
846     int		wmh1;
847     int		did_set_fraction = FALSE;
848 
849     if (flags & WSP_TOP)
850 	oldwin = firstwin;
851     else if (flags & WSP_BOT)
852 	oldwin = lastwin;
853     else
854 	oldwin = curwin;
855 
856     // add a status line when p_ls == 1 and splitting the first window
857     if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0)
858     {
859 	if (VISIBLE_HEIGHT(oldwin) <= p_wmh && new_wp == NULL)
860 	{
861 	    emsg(_(e_noroom));
862 	    return FAIL;
863 	}
864 	need_status = STATUS_HEIGHT;
865     }
866 
867 #ifdef FEAT_GUI
868     // May be needed for the scrollbars that are going to change.
869     if (gui.in_use)
870 	out_flush();
871 #endif
872 
873     if (flags & WSP_VERT)
874     {
875 	int	wmw1;
876 	int	minwidth;
877 
878 	layout = FR_ROW;
879 
880 	/*
881 	 * Check if we are able to split the current window and compute its
882 	 * width.
883 	 */
884 	// Current window requires at least 1 space.
885 	wmw1 = (p_wmw == 0 ? 1 : p_wmw);
886 	needed = wmw1 + 1;
887 	if (flags & WSP_ROOM)
888 	    needed += p_wiw - wmw1;
889 	if (flags & (WSP_BOT | WSP_TOP))
890 	{
891 	    minwidth = frame_minwidth(topframe, NOWIN);
892 	    available = topframe->fr_width;
893 	    needed += minwidth;
894 	}
895 	else if (p_ea)
896 	{
897 	    minwidth = frame_minwidth(oldwin->w_frame, NOWIN);
898 	    prevfrp = oldwin->w_frame;
899 	    for (frp = oldwin->w_frame->fr_parent; frp != NULL;
900 							frp = frp->fr_parent)
901 	    {
902 		if (frp->fr_layout == FR_ROW)
903 		    FOR_ALL_FRAMES(frp2, frp->fr_child)
904 			if (frp2 != prevfrp)
905 			    minwidth += frame_minwidth(frp2, NOWIN);
906 		prevfrp = frp;
907 	    }
908 	    available = topframe->fr_width;
909 	    needed += minwidth;
910 	}
911 	else
912 	{
913 	    minwidth = frame_minwidth(oldwin->w_frame, NOWIN);
914 	    available = oldwin->w_frame->fr_width;
915 	    needed += minwidth;
916 	}
917 	if (available < needed && new_wp == NULL)
918 	{
919 	    emsg(_(e_noroom));
920 	    return FAIL;
921 	}
922 	if (new_size == 0)
923 	    new_size = oldwin->w_width / 2;
924 	if (new_size > available - minwidth - 1)
925 	    new_size = available - minwidth - 1;
926 	if (new_size < wmw1)
927 	    new_size = wmw1;
928 
929 	// if it doesn't fit in the current window, need win_equal()
930 	if (oldwin->w_width - new_size - 1 < p_wmw)
931 	    do_equal = TRUE;
932 
933 	// We don't like to take lines for the new window from a
934 	// 'winfixwidth' window.  Take them from a window to the left or right
935 	// instead, if possible. Add one for the separator.
936 	if (oldwin->w_p_wfw)
937 	    win_setwidth_win(oldwin->w_width + new_size + 1, oldwin);
938 
939 	// Only make all windows the same width if one of them (except oldwin)
940 	// is wider than one of the split windows.
941 	if (!do_equal && p_ea && size == 0 && *p_ead != 'v'
942 					 && oldwin->w_frame->fr_parent != NULL)
943 	{
944 	    frp = oldwin->w_frame->fr_parent->fr_child;
945 	    while (frp != NULL)
946 	    {
947 		if (frp->fr_win != oldwin && frp->fr_win != NULL
948 			&& (frp->fr_win->w_width > new_size
949 			    || frp->fr_win->w_width > oldwin->w_width
950 							      - new_size - 1))
951 		{
952 		    do_equal = TRUE;
953 		    break;
954 		}
955 		frp = frp->fr_next;
956 	    }
957 	}
958     }
959     else
960     {
961 	layout = FR_COL;
962 
963 	/*
964 	 * Check if we are able to split the current window and compute its
965 	 * height.
966 	 */
967 	// Current window requires at least 1 space.
968 	wmh1 = (p_wmh == 0 ? 1 : p_wmh) + WINBAR_HEIGHT(curwin);
969 	needed = wmh1 + STATUS_HEIGHT;
970 	if (flags & WSP_ROOM)
971 	    needed += p_wh - wmh1;
972 	if (flags & (WSP_BOT | WSP_TOP))
973 	{
974 	    minheight = frame_minheight(topframe, NOWIN) + need_status;
975 	    available = topframe->fr_height;
976 	    needed += minheight;
977 	}
978 	else if (p_ea)
979 	{
980 	    minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
981 	    prevfrp = oldwin->w_frame;
982 	    for (frp = oldwin->w_frame->fr_parent; frp != NULL;
983 							frp = frp->fr_parent)
984 	    {
985 		if (frp->fr_layout == FR_COL)
986 		    FOR_ALL_FRAMES(frp2, frp->fr_child)
987 			if (frp2 != prevfrp)
988 			    minheight += frame_minheight(frp2, NOWIN);
989 		prevfrp = frp;
990 	    }
991 	    available = topframe->fr_height;
992 	    needed += minheight;
993 	}
994 	else
995 	{
996 	    minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
997 	    available = oldwin->w_frame->fr_height;
998 	    needed += minheight;
999 	}
1000 	if (available < needed && new_wp == NULL)
1001 	{
1002 	    emsg(_(e_noroom));
1003 	    return FAIL;
1004 	}
1005 	oldwin_height = oldwin->w_height;
1006 	if (need_status)
1007 	{
1008 	    oldwin->w_status_height = STATUS_HEIGHT;
1009 	    oldwin_height -= STATUS_HEIGHT;
1010 	}
1011 	if (new_size == 0)
1012 	    new_size = oldwin_height / 2;
1013 	if (new_size > available - minheight - STATUS_HEIGHT)
1014 	    new_size = available - minheight - STATUS_HEIGHT;
1015 	if (new_size < wmh1)
1016 	    new_size = wmh1;
1017 
1018 	// if it doesn't fit in the current window, need win_equal()
1019 	if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh)
1020 	    do_equal = TRUE;
1021 
1022 	// We don't like to take lines for the new window from a
1023 	// 'winfixheight' window.  Take them from a window above or below
1024 	// instead, if possible.
1025 	if (oldwin->w_p_wfh)
1026 	{
1027 	    // Set w_fraction now so that the cursor keeps the same relative
1028 	    // vertical position using the old height.
1029 	    set_fraction(oldwin);
1030 	    did_set_fraction = TRUE;
1031 
1032 	    win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT,
1033 								      oldwin);
1034 	    oldwin_height = oldwin->w_height;
1035 	    if (need_status)
1036 		oldwin_height -= STATUS_HEIGHT;
1037 	}
1038 
1039 	// Only make all windows the same height if one of them (except oldwin)
1040 	// is higher than one of the split windows.
1041 	if (!do_equal && p_ea && size == 0 && *p_ead != 'h'
1042 	   && oldwin->w_frame->fr_parent != NULL)
1043 	{
1044 	    frp = oldwin->w_frame->fr_parent->fr_child;
1045 	    while (frp != NULL)
1046 	    {
1047 		if (frp->fr_win != oldwin && frp->fr_win != NULL
1048 			&& (frp->fr_win->w_height > new_size
1049 			    || frp->fr_win->w_height > oldwin_height - new_size
1050 							      - STATUS_HEIGHT))
1051 		{
1052 		    do_equal = TRUE;
1053 		    break;
1054 		}
1055 		frp = frp->fr_next;
1056 	    }
1057 	}
1058     }
1059 
1060     /*
1061      * allocate new window structure and link it in the window list
1062      */
1063     if ((flags & WSP_TOP) == 0
1064 	    && ((flags & WSP_BOT)
1065 		|| (flags & WSP_BELOW)
1066 		|| (!(flags & WSP_ABOVE)
1067 		    && ( (flags & WSP_VERT) ? p_spr : p_sb))))
1068     {
1069 	// new window below/right of current one
1070 	if (new_wp == NULL)
1071 	    wp = win_alloc(oldwin, FALSE);
1072 	else
1073 	    win_append(oldwin, wp);
1074     }
1075     else
1076     {
1077 	if (new_wp == NULL)
1078 	    wp = win_alloc(oldwin->w_prev, FALSE);
1079 	else
1080 	    win_append(oldwin->w_prev, wp);
1081     }
1082 
1083     if (new_wp == NULL)
1084     {
1085 	if (wp == NULL)
1086 	    return FAIL;
1087 
1088 	new_frame(wp);
1089 	if (wp->w_frame == NULL)
1090 	{
1091 	    win_free(wp, NULL);
1092 	    return FAIL;
1093 	}
1094 
1095 	// make the contents of the new window the same as the current one
1096 	win_init(wp, curwin, flags);
1097     }
1098 
1099     /*
1100      * Reorganise the tree of frames to insert the new window.
1101      */
1102     if (flags & (WSP_TOP | WSP_BOT))
1103     {
1104 	if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0)
1105 	    || (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0))
1106 	{
1107 	    curfrp = topframe->fr_child;
1108 	    if (flags & WSP_BOT)
1109 		while (curfrp->fr_next != NULL)
1110 		    curfrp = curfrp->fr_next;
1111 	}
1112 	else
1113 	    curfrp = topframe;
1114 	before = (flags & WSP_TOP);
1115     }
1116     else
1117     {
1118 	curfrp = oldwin->w_frame;
1119 	if (flags & WSP_BELOW)
1120 	    before = FALSE;
1121 	else if (flags & WSP_ABOVE)
1122 	    before = TRUE;
1123 	else if (flags & WSP_VERT)
1124 	    before = !p_spr;
1125 	else
1126 	    before = !p_sb;
1127     }
1128     if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout)
1129     {
1130 	// Need to create a new frame in the tree to make a branch.
1131 	frp = ALLOC_CLEAR_ONE(frame_T);
1132 	*frp = *curfrp;
1133 	curfrp->fr_layout = layout;
1134 	frp->fr_parent = curfrp;
1135 	frp->fr_next = NULL;
1136 	frp->fr_prev = NULL;
1137 	curfrp->fr_child = frp;
1138 	curfrp->fr_win = NULL;
1139 	curfrp = frp;
1140 	if (frp->fr_win != NULL)
1141 	    oldwin->w_frame = frp;
1142 	else
1143 	    FOR_ALL_FRAMES(frp, frp->fr_child)
1144 		frp->fr_parent = curfrp;
1145     }
1146 
1147     if (new_wp == NULL)
1148 	frp = wp->w_frame;
1149     else
1150 	frp = new_wp->w_frame;
1151     frp->fr_parent = curfrp->fr_parent;
1152 
1153     // Insert the new frame at the right place in the frame list.
1154     if (before)
1155 	frame_insert(curfrp, frp);
1156     else
1157 	frame_append(curfrp, frp);
1158 
1159     // Set w_fraction now so that the cursor keeps the same relative
1160     // vertical position.
1161     if (!did_set_fraction)
1162 	set_fraction(oldwin);
1163     wp->w_fraction = oldwin->w_fraction;
1164 
1165     if (flags & WSP_VERT)
1166     {
1167 	wp->w_p_scr = curwin->w_p_scr;
1168 
1169 	if (need_status)
1170 	{
1171 	    win_new_height(oldwin, oldwin->w_height - 1);
1172 	    oldwin->w_status_height = need_status;
1173 	}
1174 	if (flags & (WSP_TOP | WSP_BOT))
1175 	{
1176 	    // set height and row of new window to full height
1177 	    wp->w_winrow = tabline_height();
1178 	    win_new_height(wp, curfrp->fr_height - (p_ls > 0)
1179 							  - WINBAR_HEIGHT(wp));
1180 	    wp->w_status_height = (p_ls > 0);
1181 	}
1182 	else
1183 	{
1184 	    // height and row of new window is same as current window
1185 	    wp->w_winrow = oldwin->w_winrow;
1186 	    win_new_height(wp, VISIBLE_HEIGHT(oldwin));
1187 	    wp->w_status_height = oldwin->w_status_height;
1188 	}
1189 	frp->fr_height = curfrp->fr_height;
1190 
1191 	// "new_size" of the current window goes to the new window, use
1192 	// one column for the vertical separator
1193 	win_new_width(wp, new_size);
1194 	if (before)
1195 	    wp->w_vsep_width = 1;
1196 	else
1197 	{
1198 	    wp->w_vsep_width = oldwin->w_vsep_width;
1199 	    oldwin->w_vsep_width = 1;
1200 	}
1201 	if (flags & (WSP_TOP | WSP_BOT))
1202 	{
1203 	    if (flags & WSP_BOT)
1204 		frame_add_vsep(curfrp);
1205 	    // Set width of neighbor frame
1206 	    frame_new_width(curfrp, curfrp->fr_width
1207 		     - (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP,
1208 								       FALSE);
1209 	}
1210 	else
1211 	    win_new_width(oldwin, oldwin->w_width - (new_size + 1));
1212 	if (before)	// new window left of current one
1213 	{
1214 	    wp->w_wincol = oldwin->w_wincol;
1215 	    oldwin->w_wincol += new_size + 1;
1216 	}
1217 	else		// new window right of current one
1218 	    wp->w_wincol = oldwin->w_wincol + oldwin->w_width + 1;
1219 	frame_fix_width(oldwin);
1220 	frame_fix_width(wp);
1221     }
1222     else
1223     {
1224 	// width and column of new window is same as current window
1225 	if (flags & (WSP_TOP | WSP_BOT))
1226 	{
1227 	    wp->w_wincol = 0;
1228 	    win_new_width(wp, Columns);
1229 	    wp->w_vsep_width = 0;
1230 	}
1231 	else
1232 	{
1233 	    wp->w_wincol = oldwin->w_wincol;
1234 	    win_new_width(wp, oldwin->w_width);
1235 	    wp->w_vsep_width = oldwin->w_vsep_width;
1236 	}
1237 	frp->fr_width = curfrp->fr_width;
1238 
1239 	// "new_size" of the current window goes to the new window, use
1240 	// one row for the status line
1241 	win_new_height(wp, new_size);
1242 	if (flags & (WSP_TOP | WSP_BOT))
1243 	{
1244 	    int new_fr_height = curfrp->fr_height - new_size
1245 							  + WINBAR_HEIGHT(wp) ;
1246 
1247 	    if (!((flags & WSP_BOT) && p_ls == 0))
1248 		new_fr_height -= STATUS_HEIGHT;
1249 	    frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, FALSE);
1250 	}
1251 	else
1252 	    win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
1253 	if (before)	// new window above current one
1254 	{
1255 	    wp->w_winrow = oldwin->w_winrow;
1256 	    wp->w_status_height = STATUS_HEIGHT;
1257 	    oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
1258 	}
1259 	else		// new window below current one
1260 	{
1261 	    wp->w_winrow = oldwin->w_winrow + VISIBLE_HEIGHT(oldwin)
1262 							       + STATUS_HEIGHT;
1263 	    wp->w_status_height = oldwin->w_status_height;
1264 	    if (!(flags & WSP_BOT))
1265 		oldwin->w_status_height = STATUS_HEIGHT;
1266 	}
1267 	if (flags & WSP_BOT)
1268 	    frame_add_statusline(curfrp);
1269 	frame_fix_height(wp);
1270 	frame_fix_height(oldwin);
1271     }
1272 
1273     if (flags & (WSP_TOP | WSP_BOT))
1274 	(void)win_comp_pos();
1275 
1276      // Both windows need redrawing.  Update all status lines, in case they
1277      // show something related to the window count or position.
1278     redraw_win_later(wp, NOT_VALID);
1279     redraw_win_later(oldwin, NOT_VALID);
1280     status_redraw_all();
1281 
1282     if (need_status)
1283     {
1284 	msg_row = Rows - 1;
1285 	msg_col = sc_col;
1286 	msg_clr_eos_force();	// Old command/ruler may still be there
1287 	comp_col();
1288 	msg_row = Rows - 1;
1289 	msg_col = 0;	// put position back at start of line
1290     }
1291 
1292     /*
1293      * equalize the window sizes.
1294      */
1295     if (do_equal || dir != 0)
1296 	win_equal(wp, TRUE,
1297 		(flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
1298 		: dir == 'h' ? 'b' : 'v');
1299 
1300     // Don't change the window height/width to 'winheight' / 'winwidth' if a
1301     // size was given.
1302     if (flags & WSP_VERT)
1303     {
1304 	i = p_wiw;
1305 	if (size != 0)
1306 	    p_wiw = size;
1307 
1308 # ifdef FEAT_GUI
1309 	// When 'guioptions' includes 'L' or 'R' may have to add scrollbars.
1310 	if (gui.in_use)
1311 	    gui_init_which_components(NULL);
1312 # endif
1313     }
1314     else
1315     {
1316 	i = p_wh;
1317 	if (size != 0)
1318 	    p_wh = size;
1319     }
1320 
1321 #ifdef FEAT_JUMPLIST
1322     // Keep same changelist position in new window.
1323     wp->w_changelistidx = oldwin->w_changelistidx;
1324 #endif
1325 
1326     /*
1327      * make the new window the current window
1328      */
1329     win_enter_ext(wp, FALSE, FALSE, TRUE, TRUE, TRUE);
1330     if (flags & WSP_VERT)
1331 	p_wiw = i;
1332     else
1333 	p_wh = i;
1334 
1335     return OK;
1336 }
1337 
1338 
1339 /*
1340  * Initialize window "newp" from window "oldp".
1341  * Used when splitting a window and when creating a new tab page.
1342  * The windows will both edit the same buffer.
1343  * WSP_NEWLOC may be specified in flags to prevent the location list from
1344  * being copied.
1345  */
1346     static void
1347 win_init(win_T *newp, win_T *oldp, int flags UNUSED)
1348 {
1349     int		i;
1350 
1351     newp->w_buffer = oldp->w_buffer;
1352 #ifdef FEAT_SYN_HL
1353     newp->w_s = &(oldp->w_buffer->b_s);
1354 #endif
1355     oldp->w_buffer->b_nwindows++;
1356     newp->w_cursor = oldp->w_cursor;
1357     newp->w_valid = 0;
1358     newp->w_curswant = oldp->w_curswant;
1359     newp->w_set_curswant = oldp->w_set_curswant;
1360     newp->w_topline = oldp->w_topline;
1361 #ifdef FEAT_DIFF
1362     newp->w_topfill = oldp->w_topfill;
1363 #endif
1364     newp->w_leftcol = oldp->w_leftcol;
1365     newp->w_pcmark = oldp->w_pcmark;
1366     newp->w_prev_pcmark = oldp->w_prev_pcmark;
1367     newp->w_alt_fnum = oldp->w_alt_fnum;
1368     newp->w_wrow = oldp->w_wrow;
1369     newp->w_fraction = oldp->w_fraction;
1370     newp->w_prev_fraction_row = oldp->w_prev_fraction_row;
1371 #ifdef FEAT_JUMPLIST
1372     copy_jumplist(oldp, newp);
1373 #endif
1374 #ifdef FEAT_QUICKFIX
1375     if (flags & WSP_NEWLOC)
1376     {
1377 	// Don't copy the location list.
1378 	newp->w_llist = NULL;
1379 	newp->w_llist_ref = NULL;
1380     }
1381     else
1382 	copy_loclist_stack(oldp, newp);
1383 #endif
1384     newp->w_localdir = (oldp->w_localdir == NULL)
1385 				    ? NULL : vim_strsave(oldp->w_localdir);
1386     newp->w_prevdir = (oldp->w_prevdir == NULL)
1387 				    ? NULL : vim_strsave(oldp->w_prevdir);
1388 
1389     // copy tagstack and folds
1390     for (i = 0; i < oldp->w_tagstacklen; i++)
1391     {
1392 	taggy_T	*tag = &newp->w_tagstack[i];
1393 	*tag = oldp->w_tagstack[i];
1394 	if (tag->tagname != NULL)
1395 	    tag->tagname = vim_strsave(tag->tagname);
1396 	if (tag->user_data != NULL)
1397 	    tag->user_data = vim_strsave(tag->user_data);
1398     }
1399     newp->w_tagstackidx = oldp->w_tagstackidx;
1400     newp->w_tagstacklen = oldp->w_tagstacklen;
1401 #ifdef FEAT_FOLDING
1402     copyFoldingState(oldp, newp);
1403 #endif
1404 
1405     win_init_some(newp, oldp);
1406 
1407 #ifdef FEAT_SYN_HL
1408     check_colorcolumn(newp);
1409 #endif
1410 }
1411 
1412 /*
1413  * Initialize window "newp" from window "old".
1414  * Only the essential things are copied.
1415  */
1416     static void
1417 win_init_some(win_T *newp, win_T *oldp)
1418 {
1419     // Use the same argument list.
1420     newp->w_alist = oldp->w_alist;
1421     ++newp->w_alist->al_refcount;
1422     newp->w_arg_idx = oldp->w_arg_idx;
1423 
1424     // copy options from existing window
1425     win_copy_options(oldp, newp);
1426 }
1427 
1428 /*
1429  * Return TRUE if "win" is a global popup or a popup in the current tab page.
1430  */
1431     int
1432 win_valid_popup(win_T *win UNUSED)
1433 {
1434 #ifdef FEAT_PROP_POPUP
1435     win_T	*wp;
1436 
1437     FOR_ALL_POPUPWINS(wp)
1438 	if (wp == win)
1439 	    return TRUE;
1440     FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
1441 	if (wp == win)
1442 	    return TRUE;
1443 #endif
1444     return FALSE;
1445 }
1446 
1447 /*
1448  * Check if "win" is a pointer to an existing window in the current tab page.
1449  */
1450     int
1451 win_valid(win_T *win)
1452 {
1453     win_T	*wp;
1454 
1455     if (win == NULL)
1456 	return FALSE;
1457     FOR_ALL_WINDOWS(wp)
1458 	if (wp == win)
1459 	    return TRUE;
1460     return win_valid_popup(win);
1461 }
1462 
1463 /*
1464  * Find window "id" in the current tab page.
1465  * Also find popup windows.
1466  * Return NULL if not found.
1467  */
1468     win_T *
1469 win_find_by_id(int id)
1470 {
1471     win_T   *wp;
1472 
1473     FOR_ALL_WINDOWS(wp)
1474 	if (wp->w_id == id)
1475 	    return wp;
1476 #ifdef FEAT_PROP_POPUP
1477     FOR_ALL_POPUPWINS(wp)
1478 	if (wp->w_id == id)
1479 	    return wp;
1480     FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
1481 	if (wp->w_id == id)
1482 	    return wp;
1483 #endif
1484     return NULL;
1485 }
1486 
1487 /*
1488  * Check if "win" is a pointer to an existing window in any tab page.
1489  */
1490     int
1491 win_valid_any_tab(win_T *win)
1492 {
1493     win_T	*wp;
1494     tabpage_T	*tp;
1495 
1496     if (win == NULL)
1497 	return FALSE;
1498     FOR_ALL_TABPAGES(tp)
1499     {
1500 	FOR_ALL_WINDOWS_IN_TAB(tp, wp)
1501 	{
1502 	    if (wp == win)
1503 		return TRUE;
1504 	}
1505 #ifdef FEAT_PROP_POPUP
1506 	FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
1507 	    if (wp == win)
1508 		return TRUE;
1509 #endif
1510     }
1511     return win_valid_popup(win);
1512 }
1513 
1514 /*
1515  * Return the number of windows.
1516  */
1517     int
1518 win_count(void)
1519 {
1520     win_T	*wp;
1521     int		count = 0;
1522 
1523     FOR_ALL_WINDOWS(wp)
1524 	++count;
1525     return count;
1526 }
1527 
1528 /*
1529  * Make "count" windows on the screen.
1530  * Return actual number of windows on the screen.
1531  * Must be called when there is just one window, filling the whole screen
1532  * (excluding the command line).
1533  */
1534     int
1535 make_windows(
1536     int		count,
1537     int		vertical UNUSED)  // split windows vertically if TRUE
1538 {
1539     int		maxcount;
1540     int		todo;
1541 
1542     if (vertical)
1543     {
1544 	// Each windows needs at least 'winminwidth' lines and a separator
1545 	// column.
1546 	maxcount = (curwin->w_width + curwin->w_vsep_width
1547 					     - (p_wiw - p_wmw)) / (p_wmw + 1);
1548     }
1549     else
1550     {
1551 	// Each window needs at least 'winminheight' lines and a status line.
1552 	maxcount = (VISIBLE_HEIGHT(curwin) + curwin->w_status_height
1553 				  - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
1554     }
1555 
1556     if (maxcount < 2)
1557 	maxcount = 2;
1558     if (count > maxcount)
1559 	count = maxcount;
1560 
1561     /*
1562      * add status line now, otherwise first window will be too big
1563      */
1564     if (count > 1)
1565 	last_status(TRUE);
1566 
1567     /*
1568      * Don't execute autocommands while creating the windows.  Must do that
1569      * when putting the buffers in the windows.
1570      */
1571     block_autocmds();
1572 
1573     // todo is number of windows left to create
1574     for (todo = count - 1; todo > 0; --todo)
1575 	if (vertical)
1576 	{
1577 	    if (win_split(curwin->w_width - (curwin->w_width - todo)
1578 			/ (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL)
1579 		break;
1580 	}
1581 	else
1582 	{
1583 	    if (win_split(curwin->w_height - (curwin->w_height - todo
1584 			    * STATUS_HEIGHT) / (todo + 1)
1585 			- STATUS_HEIGHT, WSP_ABOVE) == FAIL)
1586 		break;
1587 	}
1588 
1589     unblock_autocmds();
1590 
1591     // return actual number of windows
1592     return (count - todo);
1593 }
1594 
1595 /*
1596  * Exchange current and next window
1597  */
1598     static void
1599 win_exchange(long Prenum)
1600 {
1601     frame_T	*frp;
1602     frame_T	*frp2;
1603     win_T	*wp;
1604     win_T	*wp2;
1605     int		temp;
1606 
1607     if (ERROR_IF_ANY_POPUP_WINDOW)
1608 	return;
1609     if (ONE_WINDOW)	    // just one window
1610     {
1611 	beep_flush();
1612 	return;
1613     }
1614 
1615 #ifdef FEAT_GUI
1616     need_mouse_correct = TRUE;
1617 #endif
1618 
1619     /*
1620      * find window to exchange with
1621      */
1622     if (Prenum)
1623     {
1624 	frp = curwin->w_frame->fr_parent->fr_child;
1625 	while (frp != NULL && --Prenum > 0)
1626 	    frp = frp->fr_next;
1627     }
1628     else if (curwin->w_frame->fr_next != NULL)	// Swap with next
1629 	frp = curwin->w_frame->fr_next;
1630     else    // Swap last window in row/col with previous
1631 	frp = curwin->w_frame->fr_prev;
1632 
1633     // We can only exchange a window with another window, not with a frame
1634     // containing windows.
1635     if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin)
1636 	return;
1637     wp = frp->fr_win;
1638 
1639 /*
1640  * 1. remove curwin from the list. Remember after which window it was in wp2
1641  * 2. insert curwin before wp in the list
1642  * if wp != wp2
1643  *    3. remove wp from the list
1644  *    4. insert wp after wp2
1645  * 5. exchange the status line height and vsep width.
1646  */
1647     wp2 = curwin->w_prev;
1648     frp2 = curwin->w_frame->fr_prev;
1649     if (wp->w_prev != curwin)
1650     {
1651 	win_remove(curwin, NULL);
1652 	frame_remove(curwin->w_frame);
1653 	win_append(wp->w_prev, curwin);
1654 	frame_insert(frp, curwin->w_frame);
1655     }
1656     if (wp != wp2)
1657     {
1658 	win_remove(wp, NULL);
1659 	frame_remove(wp->w_frame);
1660 	win_append(wp2, wp);
1661 	if (frp2 == NULL)
1662 	    frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame);
1663 	else
1664 	    frame_append(frp2, wp->w_frame);
1665     }
1666     temp = curwin->w_status_height;
1667     curwin->w_status_height = wp->w_status_height;
1668     wp->w_status_height = temp;
1669     temp = curwin->w_vsep_width;
1670     curwin->w_vsep_width = wp->w_vsep_width;
1671     wp->w_vsep_width = temp;
1672 
1673     // If the windows are not in the same frame, exchange the sizes to avoid
1674     // messing up the window layout.  Otherwise fix the frame sizes.
1675     if (curwin->w_frame->fr_parent != wp->w_frame->fr_parent)
1676     {
1677 	temp = curwin->w_height;
1678 	curwin->w_height = wp->w_height;
1679 	wp->w_height = temp;
1680 	temp = curwin->w_width;
1681 	curwin->w_width = wp->w_width;
1682 	wp->w_width = temp;
1683     }
1684     else
1685     {
1686 	frame_fix_height(curwin);
1687 	frame_fix_height(wp);
1688 	frame_fix_width(curwin);
1689 	frame_fix_width(wp);
1690     }
1691 
1692     (void)win_comp_pos();		// recompute window positions
1693 
1694     win_enter(wp, TRUE);
1695     redraw_all_later(NOT_VALID);
1696 }
1697 
1698 /*
1699  * rotate windows: if upwards TRUE the second window becomes the first one
1700  *		   if upwards FALSE the first window becomes the second one
1701  */
1702     static void
1703 win_rotate(int upwards, int count)
1704 {
1705     win_T	*wp1;
1706     win_T	*wp2;
1707     frame_T	*frp;
1708     int		n;
1709 
1710     if (ONE_WINDOW)		// nothing to do
1711     {
1712 	beep_flush();
1713 	return;
1714     }
1715 
1716 #ifdef FEAT_GUI
1717     need_mouse_correct = TRUE;
1718 #endif
1719 
1720     // Check if all frames in this row/col have one window.
1721     FOR_ALL_FRAMES(frp, curwin->w_frame->fr_parent->fr_child)
1722 	if (frp->fr_win == NULL)
1723 	{
1724 	    emsg(_("E443: Cannot rotate when another window is split"));
1725 	    return;
1726 	}
1727 
1728     while (count--)
1729     {
1730 	if (upwards)		// first window becomes last window
1731 	{
1732 	    // remove first window/frame from the list
1733 	    frp = curwin->w_frame->fr_parent->fr_child;
1734 	    wp1 = frp->fr_win;
1735 	    win_remove(wp1, NULL);
1736 	    frame_remove(frp);
1737 
1738 	    // find last frame and append removed window/frame after it
1739 	    for ( ; frp->fr_next != NULL; frp = frp->fr_next)
1740 		;
1741 	    win_append(frp->fr_win, wp1);
1742 	    frame_append(frp, wp1->w_frame);
1743 
1744 	    wp2 = frp->fr_win;		// previously last window
1745 	}
1746 	else			// last window becomes first window
1747 	{
1748 	    // find last window/frame in the list and remove it
1749 	    for (frp = curwin->w_frame; frp->fr_next != NULL;
1750 							   frp = frp->fr_next)
1751 		;
1752 	    wp1 = frp->fr_win;
1753 	    wp2 = wp1->w_prev;		    // will become last window
1754 	    win_remove(wp1, NULL);
1755 	    frame_remove(frp);
1756 
1757 	    // append the removed window/frame before the first in the list
1758 	    win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1);
1759 	    frame_insert(frp->fr_parent->fr_child, frp);
1760 	}
1761 
1762 	// exchange status height and vsep width of old and new last window
1763 	n = wp2->w_status_height;
1764 	wp2->w_status_height = wp1->w_status_height;
1765 	wp1->w_status_height = n;
1766 	frame_fix_height(wp1);
1767 	frame_fix_height(wp2);
1768 	n = wp2->w_vsep_width;
1769 	wp2->w_vsep_width = wp1->w_vsep_width;
1770 	wp1->w_vsep_width = n;
1771 	frame_fix_width(wp1);
1772 	frame_fix_width(wp2);
1773 
1774 	// recompute w_winrow and w_wincol for all windows
1775 	(void)win_comp_pos();
1776     }
1777 
1778     redraw_all_later(NOT_VALID);
1779 }
1780 
1781 /*
1782  * Move the current window to the very top/bottom/left/right of the screen.
1783  */
1784     static void
1785 win_totop(int size, int flags)
1786 {
1787     int		dir;
1788     int		height = curwin->w_height;
1789 
1790     if (ONE_WINDOW)
1791     {
1792 	beep_flush();
1793 	return;
1794     }
1795     if (check_split_disallowed() == FAIL)
1796 	return;
1797 
1798     // Remove the window and frame from the tree of frames.
1799     (void)winframe_remove(curwin, &dir, NULL);
1800     win_remove(curwin, NULL);
1801     last_status(FALSE);	    // may need to remove last status line
1802     (void)win_comp_pos();   // recompute window positions
1803 
1804     // Split a window on the desired side and put the window there.
1805     (void)win_split_ins(size, flags, curwin, dir);
1806     if (!(flags & WSP_VERT))
1807     {
1808 	win_setheight(height);
1809 	if (p_ea)
1810 	    win_equal(curwin, TRUE, 'v');
1811     }
1812 
1813 #if defined(FEAT_GUI)
1814     // When 'guioptions' includes 'L' or 'R' may have to remove or add
1815     // scrollbars.  Have to update them anyway.
1816     gui_may_update_scrollbars();
1817 #endif
1818 }
1819 
1820 /*
1821  * Move window "win1" to below/right of "win2" and make "win1" the current
1822  * window.  Only works within the same frame!
1823  */
1824     void
1825 win_move_after(win_T *win1, win_T *win2)
1826 {
1827     int		height;
1828 
1829     // check if the arguments are reasonable
1830     if (win1 == win2)
1831 	return;
1832 
1833     // check if there is something to do
1834     if (win2->w_next != win1)
1835     {
1836 	if (win1->w_frame->fr_parent != win2->w_frame->fr_parent)
1837 	{
1838 	    iemsg("INTERNAL: trying to move a window into another frame");
1839 	    return;
1840 	}
1841 
1842 	// may need to move the status line/vertical separator of the last
1843 	// window
1844 	if (win1 == lastwin)
1845 	{
1846 	    height = win1->w_prev->w_status_height;
1847 	    win1->w_prev->w_status_height = win1->w_status_height;
1848 	    win1->w_status_height = height;
1849 	    if (win1->w_prev->w_vsep_width == 1)
1850 	    {
1851 		// Remove the vertical separator from the last-but-one window,
1852 		// add it to the last window.  Adjust the frame widths.
1853 		win1->w_prev->w_vsep_width = 0;
1854 		win1->w_prev->w_frame->fr_width -= 1;
1855 		win1->w_vsep_width = 1;
1856 		win1->w_frame->fr_width += 1;
1857 	    }
1858 	}
1859 	else if (win2 == lastwin)
1860 	{
1861 	    height = win1->w_status_height;
1862 	    win1->w_status_height = win2->w_status_height;
1863 	    win2->w_status_height = height;
1864 	    if (win1->w_vsep_width == 1)
1865 	    {
1866 		// Remove the vertical separator from win1, add it to the last
1867 		// window, win2.  Adjust the frame widths.
1868 		win2->w_vsep_width = 1;
1869 		win2->w_frame->fr_width += 1;
1870 		win1->w_vsep_width = 0;
1871 		win1->w_frame->fr_width -= 1;
1872 	    }
1873 	}
1874 	win_remove(win1, NULL);
1875 	frame_remove(win1->w_frame);
1876 	win_append(win2, win1);
1877 	frame_append(win2->w_frame, win1->w_frame);
1878 
1879 	(void)win_comp_pos();	// recompute w_winrow for all windows
1880 	redraw_later(NOT_VALID);
1881     }
1882     win_enter(win1, FALSE);
1883 }
1884 
1885 /*
1886  * Make all windows the same height.
1887  * 'next_curwin' will soon be the current window, make sure it has enough
1888  * rows.
1889  */
1890     void
1891 win_equal(
1892     win_T	*next_curwin,	// pointer to current window to be or NULL
1893     int		current,	// do only frame with current window
1894     int		dir)		// 'v' for vertically, 'h' for horizontally,
1895 				// 'b' for both, 0 for using p_ead
1896 {
1897     if (dir == 0)
1898 	dir = *p_ead;
1899     win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
1900 		      topframe, dir, 0, tabline_height(),
1901 					   (int)Columns, topframe->fr_height);
1902 }
1903 
1904 /*
1905  * Set a frame to a new position and height, spreading the available room
1906  * equally over contained frames.
1907  * The window "next_curwin" (if not NULL) should at least get the size from
1908  * 'winheight' and 'winwidth' if possible.
1909  */
1910     static void
1911 win_equal_rec(
1912     win_T	*next_curwin,	// pointer to current window to be or NULL
1913     int		current,	// do only frame with current window
1914     frame_T	*topfr,		// frame to set size off
1915     int		dir,		// 'v', 'h' or 'b', see win_equal()
1916     int		col,		// horizontal position for frame
1917     int		row,		// vertical position for frame
1918     int		width,		// new width of frame
1919     int		height)		// new height of frame
1920 {
1921     int		n, m;
1922     int		extra_sep = 0;
1923     int		wincount, totwincount = 0;
1924     frame_T	*fr;
1925     int		next_curwin_size = 0;
1926     int		room = 0;
1927     int		new_size;
1928     int		has_next_curwin = 0;
1929     int		hnc;
1930 
1931     if (topfr->fr_layout == FR_LEAF)
1932     {
1933 	// Set the width/height of this frame.
1934 	// Redraw when size or position changes
1935 	if (topfr->fr_height != height || topfr->fr_win->w_winrow != row
1936 		|| topfr->fr_width != width || topfr->fr_win->w_wincol != col
1937 	   )
1938 	{
1939 	    topfr->fr_win->w_winrow = row;
1940 	    frame_new_height(topfr, height, FALSE, FALSE);
1941 	    topfr->fr_win->w_wincol = col;
1942 	    frame_new_width(topfr, width, FALSE, FALSE);
1943 	    redraw_all_later(NOT_VALID);
1944 	}
1945     }
1946     else if (topfr->fr_layout == FR_ROW)
1947     {
1948 	topfr->fr_width = width;
1949 	topfr->fr_height = height;
1950 
1951 	if (dir != 'v')			// equalize frame widths
1952 	{
1953 	    // Compute the maximum number of windows horizontally in this
1954 	    // frame.
1955 	    n = frame_minwidth(topfr, NOWIN);
1956 	    // add one for the rightmost window, it doesn't have a separator
1957 	    if (col + width == Columns)
1958 		extra_sep = 1;
1959 	    else
1960 		extra_sep = 0;
1961 	    totwincount = (n + extra_sep) / (p_wmw + 1);
1962 	    has_next_curwin = frame_has_win(topfr, next_curwin);
1963 
1964 	    /*
1965 	     * Compute width for "next_curwin" window and room available for
1966 	     * other windows.
1967 	     * "m" is the minimal width when counting p_wiw for "next_curwin".
1968 	     */
1969 	    m = frame_minwidth(topfr, next_curwin);
1970 	    room = width - m;
1971 	    if (room < 0)
1972 	    {
1973 		next_curwin_size = p_wiw + room;
1974 		room = 0;
1975 	    }
1976 	    else
1977 	    {
1978 		next_curwin_size = -1;
1979 		FOR_ALL_FRAMES(fr, topfr->fr_child)
1980 		{
1981 		    // If 'winfixwidth' set keep the window width if
1982 		    // possible.
1983 		    // Watch out for this window being the next_curwin.
1984 		    if (frame_fixed_width(fr))
1985 		    {
1986 			n = frame_minwidth(fr, NOWIN);
1987 			new_size = fr->fr_width;
1988 			if (frame_has_win(fr, next_curwin))
1989 			{
1990 			    room += p_wiw - p_wmw;
1991 			    next_curwin_size = 0;
1992 			    if (new_size < p_wiw)
1993 				new_size = p_wiw;
1994 			}
1995 			else
1996 			    // These windows don't use up room.
1997 			    totwincount -= (n + (fr->fr_next == NULL
1998 					      ? extra_sep : 0)) / (p_wmw + 1);
1999 			room -= new_size - n;
2000 			if (room < 0)
2001 			{
2002 			    new_size += room;
2003 			    room = 0;
2004 			}
2005 			fr->fr_newwidth = new_size;
2006 		    }
2007 		}
2008 		if (next_curwin_size == -1)
2009 		{
2010 		    if (!has_next_curwin)
2011 			next_curwin_size = 0;
2012 		    else if (totwincount > 1
2013 			    && (room + (totwincount - 2))
2014 						  / (totwincount - 1) > p_wiw)
2015 		    {
2016 			// Can make all windows wider than 'winwidth', spread
2017 			// the room equally.
2018 			next_curwin_size = (room + p_wiw
2019 					    + (totwincount - 1) * p_wmw
2020 					    + (totwincount - 1)) / totwincount;
2021 			room -= next_curwin_size - p_wiw;
2022 		    }
2023 		    else
2024 			next_curwin_size = p_wiw;
2025 		}
2026 	    }
2027 
2028 	    if (has_next_curwin)
2029 		--totwincount;		// don't count curwin
2030 	}
2031 
2032 	FOR_ALL_FRAMES(fr, topfr->fr_child)
2033 	{
2034 	    wincount = 1;
2035 	    if (fr->fr_next == NULL)
2036 		// last frame gets all that remains (avoid roundoff error)
2037 		new_size = width;
2038 	    else if (dir == 'v')
2039 		new_size = fr->fr_width;
2040 	    else if (frame_fixed_width(fr))
2041 	    {
2042 		new_size = fr->fr_newwidth;
2043 		wincount = 0;	    // doesn't count as a sizeable window
2044 	    }
2045 	    else
2046 	    {
2047 		// Compute the maximum number of windows horiz. in "fr".
2048 		n = frame_minwidth(fr, NOWIN);
2049 		wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
2050 								/ (p_wmw + 1);
2051 		m = frame_minwidth(fr, next_curwin);
2052 		if (has_next_curwin)
2053 		    hnc = frame_has_win(fr, next_curwin);
2054 		else
2055 		    hnc = FALSE;
2056 		if (hnc)	    // don't count next_curwin
2057 		    --wincount;
2058 		if (totwincount == 0)
2059 		    new_size = room;
2060 		else
2061 		    new_size = (wincount * room + ((unsigned)totwincount >> 1))
2062 								/ totwincount;
2063 		if (hnc)	    // add next_curwin size
2064 		{
2065 		    next_curwin_size -= p_wiw - (m - n);
2066 		    new_size += next_curwin_size;
2067 		    room -= new_size - next_curwin_size;
2068 		}
2069 		else
2070 		    room -= new_size;
2071 		new_size += n;
2072 	    }
2073 
2074 	    // Skip frame that is full width when splitting or closing a
2075 	    // window, unless equalizing all frames.
2076 	    if (!current || dir != 'v' || topfr->fr_parent != NULL
2077 		    || (new_size != fr->fr_width)
2078 		    || frame_has_win(fr, next_curwin))
2079 		win_equal_rec(next_curwin, current, fr, dir, col, row,
2080 							    new_size, height);
2081 	    col += new_size;
2082 	    width -= new_size;
2083 	    totwincount -= wincount;
2084 	}
2085     }
2086     else // topfr->fr_layout == FR_COL
2087     {
2088 	topfr->fr_width = width;
2089 	topfr->fr_height = height;
2090 
2091 	if (dir != 'h')			// equalize frame heights
2092 	{
2093 	    // Compute maximum number of windows vertically in this frame.
2094 	    n = frame_minheight(topfr, NOWIN);
2095 	    // add one for the bottom window if it doesn't have a statusline
2096 	    if (row + height == cmdline_row && p_ls == 0)
2097 		extra_sep = 1;
2098 	    else
2099 		extra_sep = 0;
2100 	    totwincount = (n + extra_sep) / (p_wmh + 1);
2101 	    has_next_curwin = frame_has_win(topfr, next_curwin);
2102 
2103 	    /*
2104 	     * Compute height for "next_curwin" window and room available for
2105 	     * other windows.
2106 	     * "m" is the minimal height when counting p_wh for "next_curwin".
2107 	     */
2108 	    m = frame_minheight(topfr, next_curwin);
2109 	    room = height - m;
2110 	    if (room < 0)
2111 	    {
2112 		// The room is less then 'winheight', use all space for the
2113 		// current window.
2114 		next_curwin_size = p_wh + room;
2115 		room = 0;
2116 	    }
2117 	    else
2118 	    {
2119 		next_curwin_size = -1;
2120 		FOR_ALL_FRAMES(fr, topfr->fr_child)
2121 		{
2122 		    // If 'winfixheight' set keep the window height if
2123 		    // possible.
2124 		    // Watch out for this window being the next_curwin.
2125 		    if (frame_fixed_height(fr))
2126 		    {
2127 			n = frame_minheight(fr, NOWIN);
2128 			new_size = fr->fr_height;
2129 			if (frame_has_win(fr, next_curwin))
2130 			{
2131 			    room += p_wh - p_wmh;
2132 			    next_curwin_size = 0;
2133 			    if (new_size < p_wh)
2134 				new_size = p_wh;
2135 			}
2136 			else
2137 			    // These windows don't use up room.
2138 			    totwincount -= (n + (fr->fr_next == NULL
2139 					      ? extra_sep : 0)) / (p_wmh + 1);
2140 			room -= new_size - n;
2141 			if (room < 0)
2142 			{
2143 			    new_size += room;
2144 			    room = 0;
2145 			}
2146 			fr->fr_newheight = new_size;
2147 		    }
2148 		}
2149 		if (next_curwin_size == -1)
2150 		{
2151 		    if (!has_next_curwin)
2152 			next_curwin_size = 0;
2153 		    else if (totwincount > 1
2154 			    && (room + (totwincount - 2))
2155 						   / (totwincount - 1) > p_wh)
2156 		    {
2157 			// can make all windows higher than 'winheight',
2158 			// spread the room equally.
2159 			next_curwin_size = (room + p_wh
2160 					   + (totwincount - 1) * p_wmh
2161 					   + (totwincount - 1)) / totwincount;
2162 			room -= next_curwin_size - p_wh;
2163 		    }
2164 		    else
2165 			next_curwin_size = p_wh;
2166 		}
2167 	    }
2168 
2169 	    if (has_next_curwin)
2170 		--totwincount;		// don't count curwin
2171 	}
2172 
2173 	FOR_ALL_FRAMES(fr, topfr->fr_child)
2174 	{
2175 	    wincount = 1;
2176 	    if (fr->fr_next == NULL)
2177 		// last frame gets all that remains (avoid roundoff error)
2178 		new_size = height;
2179 	    else if (dir == 'h')
2180 		new_size = fr->fr_height;
2181 	    else if (frame_fixed_height(fr))
2182 	    {
2183 		new_size = fr->fr_newheight;
2184 		wincount = 0;	    // doesn't count as a sizeable window
2185 	    }
2186 	    else
2187 	    {
2188 		// Compute the maximum number of windows vert. in "fr".
2189 		n = frame_minheight(fr, NOWIN);
2190 		wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
2191 								/ (p_wmh + 1);
2192 		m = frame_minheight(fr, next_curwin);
2193 		if (has_next_curwin)
2194 		    hnc = frame_has_win(fr, next_curwin);
2195 		else
2196 		    hnc = FALSE;
2197 		if (hnc)	    // don't count next_curwin
2198 		    --wincount;
2199 		if (totwincount == 0)
2200 		    new_size = room;
2201 		else
2202 		    new_size = (wincount * room + ((unsigned)totwincount >> 1))
2203 								/ totwincount;
2204 		if (hnc)	    // add next_curwin size
2205 		{
2206 		    next_curwin_size -= p_wh - (m - n);
2207 		    new_size += next_curwin_size;
2208 		    room -= new_size - next_curwin_size;
2209 		}
2210 		else
2211 		    room -= new_size;
2212 		new_size += n;
2213 	    }
2214 	    // Skip frame that is full width when splitting or closing a
2215 	    // window, unless equalizing all frames.
2216 	    if (!current || dir != 'h' || topfr->fr_parent != NULL
2217 		    || (new_size != fr->fr_height)
2218 		    || frame_has_win(fr, next_curwin))
2219 		win_equal_rec(next_curwin, current, fr, dir, col, row,
2220 							     width, new_size);
2221 	    row += new_size;
2222 	    height -= new_size;
2223 	    totwincount -= wincount;
2224 	}
2225     }
2226 }
2227 
2228 #ifdef FEAT_JOB_CHANNEL
2229     static void
2230 leaving_window(win_T *win)
2231 {
2232     // Only matters for a prompt window.
2233     if (!bt_prompt(win->w_buffer))
2234 	return;
2235 
2236     // When leaving a prompt window stop Insert mode and perhaps restart
2237     // it when entering that window again.
2238     win->w_buffer->b_prompt_insert = restart_edit;
2239     if (restart_edit != 0 && mode_displayed)
2240 	clear_cmdline = TRUE;		// unshow mode later
2241     restart_edit = NUL;
2242 
2243     // When leaving the window (or closing the window) was done from a
2244     // callback we need to break out of the Insert mode loop and restart Insert
2245     // mode when entering the window again.
2246     if (State & INSERT)
2247     {
2248 	stop_insert_mode = TRUE;
2249 	if (win->w_buffer->b_prompt_insert == NUL)
2250 	    win->w_buffer->b_prompt_insert = 'A';
2251     }
2252 }
2253 
2254     void
2255 entering_window(win_T *win)
2256 {
2257     // Only matters for a prompt window.
2258     if (!bt_prompt(win->w_buffer))
2259 	return;
2260 
2261     // When switching to a prompt buffer that was in Insert mode, don't stop
2262     // Insert mode, it may have been set in leaving_window().
2263     if (win->w_buffer->b_prompt_insert != NUL)
2264 	stop_insert_mode = FALSE;
2265 
2266     // When entering the prompt window restart Insert mode if we were in Insert
2267     // mode when we left it.
2268     restart_edit = win->w_buffer->b_prompt_insert;
2269 }
2270 #endif
2271 
2272 /*
2273  * Close all windows for buffer "buf".
2274  */
2275     void
2276 close_windows(
2277     buf_T	*buf,
2278     int		keep_curwin)	    // don't close "curwin"
2279 {
2280     win_T	*wp;
2281     tabpage_T   *tp, *nexttp;
2282     int		h = tabline_height();
2283     int		count = tabpage_index(NULL);
2284 
2285     ++RedrawingDisabled;
2286 
2287     for (wp = firstwin; wp != NULL && !ONE_WINDOW; )
2288     {
2289 	if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
2290 		&& !(wp->w_closing || wp->w_buffer->b_locked > 0))
2291 	{
2292 	    if (win_close(wp, FALSE) == FAIL)
2293 		// If closing the window fails give up, to avoid looping
2294 		// forever.
2295 		break;
2296 
2297 	    // Start all over, autocommands may change the window layout.
2298 	    wp = firstwin;
2299 	}
2300 	else
2301 	    wp = wp->w_next;
2302     }
2303 
2304     // Also check windows in other tab pages.
2305     for (tp = first_tabpage; tp != NULL; tp = nexttp)
2306     {
2307 	nexttp = tp->tp_next;
2308 	if (tp != curtab)
2309 	    FOR_ALL_WINDOWS_IN_TAB(tp, wp)
2310 		if (wp->w_buffer == buf
2311 		    && !(wp->w_closing || wp->w_buffer->b_locked > 0))
2312 		{
2313 		    win_close_othertab(wp, FALSE, tp);
2314 
2315 		    // Start all over, the tab page may be closed and
2316 		    // autocommands may change the window layout.
2317 		    nexttp = first_tabpage;
2318 		    break;
2319 		}
2320     }
2321 
2322     --RedrawingDisabled;
2323 
2324     if (count != tabpage_index(NULL))
2325 	apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
2326 
2327     redraw_tabline = TRUE;
2328     if (h != tabline_height())
2329 	shell_new_rows();
2330 }
2331 
2332 /*
2333  * Return TRUE if the current window is the only window that exists (ignoring
2334  * "aucmd_win").
2335  * Returns FALSE if there is a window, possibly in another tab page.
2336  */
2337     static int
2338 last_window(void)
2339 {
2340     return (one_window() && first_tabpage->tp_next == NULL);
2341 }
2342 
2343 /*
2344  * Return TRUE if there is only one window other than "aucmd_win" in the
2345  * current tab page.
2346  */
2347     int
2348 one_window(void)
2349 {
2350     win_T	*wp;
2351     int		seen_one = FALSE;
2352 
2353     FOR_ALL_WINDOWS(wp)
2354     {
2355 	if (wp != aucmd_win)
2356 	{
2357 	    if (seen_one)
2358 		return FALSE;
2359 	    seen_one = TRUE;
2360 	}
2361     }
2362     return TRUE;
2363 }
2364 
2365 /*
2366  * Close the possibly last window in a tab page.
2367  * Returns TRUE when the window was closed already.
2368  */
2369     static int
2370 close_last_window_tabpage(
2371     win_T	*win,
2372     int		free_buf,
2373     tabpage_T   *prev_curtab)
2374 {
2375     if (ONE_WINDOW)
2376     {
2377 	buf_T	*old_curbuf = curbuf;
2378 
2379 	/*
2380 	 * Closing the last window in a tab page.  First go to another tab
2381 	 * page and then close the window and the tab page.  This avoids that
2382 	 * curwin and curtab are invalid while we are freeing memory, they may
2383 	 * be used in GUI events.
2384 	 * Don't trigger autocommands yet, they may use wrong values, so do
2385 	 * that below.
2386 	 */
2387 	goto_tabpage_tp(alt_tabpage(), FALSE, TRUE);
2388 	redraw_tabline = TRUE;
2389 
2390 	// Safety check: Autocommands may have closed the window when jumping
2391 	// to the other tab page.
2392 	if (valid_tabpage(prev_curtab) && prev_curtab->tp_firstwin == win)
2393 	{
2394 	    int	    h = tabline_height();
2395 
2396 	    win_close_othertab(win, free_buf, prev_curtab);
2397 	    if (h != tabline_height())
2398 		shell_new_rows();
2399 	}
2400 #ifdef FEAT_JOB_CHANNEL
2401 	entering_window(curwin);
2402 #endif
2403 	// Since goto_tabpage_tp above did not trigger *Enter autocommands, do
2404 	// that now.
2405 	apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
2406 	apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
2407 	apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
2408 	if (old_curbuf != curbuf)
2409 	    apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
2410 	return TRUE;
2411     }
2412     return FALSE;
2413 }
2414 
2415 /*
2416  * Close the buffer of "win" and unload it if "action" is DOBUF_UNLOAD.
2417  * "action" can also be zero (do nothing) or DOBUF_WIPE.
2418  * "abort_if_last" is passed to close_buffer(): abort closing if all other
2419  * windows are closed.
2420  */
2421     static void
2422 win_close_buffer(win_T *win, int action, int abort_if_last)
2423 {
2424 #ifdef FEAT_SYN_HL
2425     // Free independent synblock before the buffer is freed.
2426     if (win->w_buffer != NULL)
2427 	reset_synblock(win);
2428 #endif
2429 
2430 #ifdef FEAT_QUICKFIX
2431     // When the quickfix/location list window is closed, unlist the buffer.
2432     if (win->w_buffer != NULL && bt_quickfix(win->w_buffer))
2433 	win->w_buffer->b_p_bl = FALSE;
2434 #endif
2435 
2436     // Close the link to the buffer.
2437     if (win->w_buffer != NULL)
2438     {
2439 	bufref_T    bufref;
2440 
2441 	set_bufref(&bufref, curbuf);
2442 	win->w_closing = TRUE;
2443 	close_buffer(win, win->w_buffer, action, abort_if_last, FALSE);
2444 	if (win_valid_any_tab(win))
2445 	    win->w_closing = FALSE;
2446 	// Make sure curbuf is valid. It can become invalid if 'bufhidden' is
2447 	// "wipe".
2448 	if (!bufref_valid(&bufref))
2449 	    curbuf = firstbuf;
2450     }
2451 }
2452 
2453 /*
2454  * Close window "win".  Only works for the current tab page.
2455  * If "free_buf" is TRUE related buffer may be unloaded.
2456  *
2457  * Called by :quit, :close, :xit, :wq and findtag().
2458  * Returns FAIL when the window was not closed.
2459  */
2460     int
2461 win_close(win_T *win, int free_buf)
2462 {
2463     win_T	*wp;
2464     int		other_buffer = FALSE;
2465     int		close_curwin = FALSE;
2466     int		dir;
2467     int		help_window = FALSE;
2468     tabpage_T   *prev_curtab = curtab;
2469     frame_T	*win_frame = win->w_frame->fr_parent;
2470 #ifdef FEAT_DIFF
2471     int		had_diffmode = win->w_p_diff;
2472 #endif
2473 
2474 #if defined(FEAT_TERMINAL) && defined(FEAT_PROP_POPUP)
2475     // Can close a popup window with a terminal if the job has finished.
2476     if (may_close_term_popup() == OK)
2477 	return OK;
2478 #endif
2479     if (ERROR_IF_ANY_POPUP_WINDOW)
2480 	return FAIL;
2481 
2482     if (last_window())
2483     {
2484 	emsg(_("E444: Cannot close last window"));
2485 	return FAIL;
2486     }
2487 
2488     if (win->w_closing || (win->w_buffer != NULL
2489 					       && win->w_buffer->b_locked > 0))
2490 	return FAIL; // window is already being closed
2491     if (win_unlisted(win))
2492     {
2493 	emsg(_(e_autocmd_close));
2494 	return FAIL;
2495     }
2496     if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window())
2497     {
2498 	emsg(_("E814: Cannot close window, only autocmd window would remain"));
2499 	return FAIL;
2500     }
2501 
2502     // When closing the last window in a tab page first go to another tab page
2503     // and then close the window and the tab page to avoid that curwin and
2504     // curtab are invalid while we are freeing memory.
2505     if (close_last_window_tabpage(win, free_buf, prev_curtab))
2506       return FAIL;
2507 
2508     // When closing the help window, try restoring a snapshot after closing
2509     // the window.  Otherwise clear the snapshot, it's now invalid.
2510     if (bt_help(win->w_buffer))
2511 	help_window = TRUE;
2512     else
2513 	clear_snapshot(curtab, SNAP_HELP_IDX);
2514 
2515     if (win == curwin)
2516     {
2517 #ifdef FEAT_JOB_CHANNEL
2518 	leaving_window(curwin);
2519 #endif
2520 	/*
2521 	 * Guess which window is going to be the new current window.
2522 	 * This may change because of the autocommands (sigh).
2523 	 */
2524 	wp = frame2win(win_altframe(win, NULL));
2525 
2526 	/*
2527 	 * Be careful: If autocommands delete the window or cause this window
2528 	 * to be the last one left, return now.
2529 	 */
2530 	if (wp->w_buffer != curbuf)
2531 	{
2532 	    other_buffer = TRUE;
2533 	    win->w_closing = TRUE;
2534 	    apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
2535 	    if (!win_valid(win))
2536 		return FAIL;
2537 	    win->w_closing = FALSE;
2538 	    if (last_window())
2539 		return FAIL;
2540 	}
2541 	win->w_closing = TRUE;
2542 	apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
2543 	if (!win_valid(win))
2544 	    return FAIL;
2545 	win->w_closing = FALSE;
2546 	if (last_window())
2547 	    return FAIL;
2548 #ifdef FEAT_EVAL
2549 	// autocmds may abort script processing
2550 	if (aborting())
2551 	    return FAIL;
2552 #endif
2553     }
2554 
2555 #ifdef FEAT_GUI
2556     // Avoid trouble with scrollbars that are going to be deleted in
2557     // win_free().
2558     if (gui.in_use)
2559 	out_flush();
2560 #endif
2561 
2562 #ifdef FEAT_PROP_POPUP
2563     if (popup_win_closed(win) && !win_valid(win))
2564 	return FAIL;
2565 #endif
2566     win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, TRUE);
2567 
2568     if (only_one_window() && win_valid(win) && win->w_buffer == NULL
2569 	    && (last_window() || curtab != prev_curtab
2570 		|| close_last_window_tabpage(win, free_buf, prev_curtab)))
2571     {
2572 	// Autocommands have closed all windows, quit now.  Restore
2573 	// curwin->w_buffer, otherwise writing viminfo may fail.
2574 	if (curwin->w_buffer == NULL)
2575 	    curwin->w_buffer = curbuf;
2576 	getout(0);
2577     }
2578 
2579     // Autocommands may have moved to another tab page.
2580     if (curtab != prev_curtab && win_valid_any_tab(win)
2581 						      && win->w_buffer == NULL)
2582     {
2583 	// Need to close the window anyway, since the buffer is NULL.
2584 	win_close_othertab(win, FALSE, prev_curtab);
2585 	return FAIL;
2586     }
2587 
2588     // Autocommands may have closed the window already or closed the only
2589     // other window.
2590     if (!win_valid(win) || last_window()
2591 	    || close_last_window_tabpage(win, free_buf, prev_curtab))
2592 	return FAIL;
2593 
2594     // Now we are really going to close the window.  Disallow any autocommand
2595     // to split a window to avoid trouble.
2596     // Also bail out of parse_queued_messages() to avoid it tries to update the
2597     // screen.
2598     ++split_disallowed;
2599 #ifdef MESSAGE_QUEUE
2600     ++dont_parse_messages;
2601 #endif
2602 
2603     // Free the memory used for the window and get the window that received
2604     // the screen space.
2605     wp = win_free_mem(win, &dir, NULL);
2606 
2607     // Make sure curwin isn't invalid.  It can cause severe trouble when
2608     // printing an error message.  For win_equal() curbuf needs to be valid
2609     // too.
2610     if (win == curwin)
2611     {
2612 	curwin = wp;
2613 #ifdef FEAT_QUICKFIX
2614 	if (wp->w_p_pvw || bt_quickfix(wp->w_buffer))
2615 	{
2616 	    /*
2617 	     * If the cursor goes to the preview or the quickfix window, try
2618 	     * finding another window to go to.
2619 	     */
2620 	    for (;;)
2621 	    {
2622 		if (wp->w_next == NULL)
2623 		    wp = firstwin;
2624 		else
2625 		    wp = wp->w_next;
2626 		if (wp == curwin)
2627 		    break;
2628 		if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer))
2629 		{
2630 		    curwin = wp;
2631 		    break;
2632 		}
2633 	    }
2634 	}
2635 #endif
2636 	curbuf = curwin->w_buffer;
2637 	close_curwin = TRUE;
2638 
2639 	// The cursor position may be invalid if the buffer changed after last
2640 	// using the window.
2641 	check_cursor();
2642     }
2643     if (p_ea && (*p_ead == 'b' || *p_ead == dir))
2644 	// If the frame of the closed window contains the new current window,
2645 	// only resize that frame.  Otherwise resize all windows.
2646 	win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
2647     else
2648 	win_comp_pos();
2649     if (close_curwin)
2650     {
2651 	win_enter_ext(wp, FALSE, TRUE, FALSE, TRUE, TRUE);
2652 	if (other_buffer)
2653 	    // careful: after this wp and win may be invalid!
2654 	    apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
2655     }
2656 
2657     --split_disallowed;
2658 #ifdef MESSAGE_QUEUE
2659     --dont_parse_messages;
2660 #endif
2661 
2662     /*
2663      * If last window has a status line now and we don't want one,
2664      * remove the status line.
2665      */
2666     last_status(FALSE);
2667 
2668     // After closing the help window, try restoring the window layout from
2669     // before it was opened.
2670     if (help_window)
2671 	restore_snapshot(SNAP_HELP_IDX, close_curwin);
2672 
2673 #ifdef FEAT_DIFF
2674     // If the window had 'diff' set and now there is only one window left in
2675     // the tab page with 'diff' set, and "closeoff" is in 'diffopt', then
2676     // execute ":diffoff!".
2677     if (diffopt_closeoff() && had_diffmode && curtab == prev_curtab)
2678     {
2679 	int	diffcount = 0;
2680 	win_T	*dwin;
2681 
2682 	FOR_ALL_WINDOWS(dwin)
2683 	    if (dwin->w_p_diff)
2684 		++diffcount;
2685 	if (diffcount == 1)
2686 	    do_cmdline_cmd((char_u *)"diffoff!");
2687     }
2688 #endif
2689 
2690 #if defined(FEAT_GUI)
2691     // When 'guioptions' includes 'L' or 'R' may have to remove scrollbars.
2692     if (gui.in_use && !win_hasvertsplit())
2693 	gui_init_which_components(NULL);
2694 #endif
2695 
2696     redraw_all_later(NOT_VALID);
2697     return OK;
2698 }
2699 
2700 /*
2701  * Close window "win" in tab page "tp", which is not the current tab page.
2702  * This may be the last window in that tab page and result in closing the tab,
2703  * thus "tp" may become invalid!
2704  * Caller must check if buffer is hidden and whether the tabline needs to be
2705  * updated.
2706  */
2707     void
2708 win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
2709 {
2710     win_T	*wp;
2711     int		dir;
2712     tabpage_T   *ptp = NULL;
2713     int		free_tp = FALSE;
2714 
2715     // Get here with win->w_buffer == NULL when win_close() detects the tab
2716     // page changed.
2717     if (win->w_closing || (win->w_buffer != NULL
2718 					       && win->w_buffer->b_locked > 0))
2719 	return; // window is already being closed
2720 
2721     if (win->w_buffer != NULL)
2722 	// Close the link to the buffer.
2723 	close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0,
2724 								 FALSE, FALSE);
2725 
2726     // Careful: Autocommands may have closed the tab page or made it the
2727     // current tab page.
2728     for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next)
2729 	;
2730     if (ptp == NULL || tp == curtab)
2731 	return;
2732 
2733     // Autocommands may have closed the window already.
2734     for (wp = tp->tp_firstwin; wp != NULL && wp != win; wp = wp->w_next)
2735 	;
2736     if (wp == NULL)
2737 	return;
2738 
2739     // When closing the last window in a tab page remove the tab page.
2740     if (tp->tp_firstwin == tp->tp_lastwin)
2741     {
2742 	if (tp == first_tabpage)
2743 	    first_tabpage = tp->tp_next;
2744 	else
2745 	{
2746 	    for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp;
2747 							   ptp = ptp->tp_next)
2748 		;
2749 	    if (ptp == NULL)
2750 	    {
2751 		internal_error("win_close_othertab()");
2752 		return;
2753 	    }
2754 	    ptp->tp_next = tp->tp_next;
2755 	}
2756 	free_tp = TRUE;
2757     }
2758 
2759     // Free the memory used for the window.
2760     win_free_mem(win, &dir, tp);
2761 
2762     if (free_tp)
2763 	free_tabpage(tp);
2764 }
2765 
2766 /*
2767  * Free the memory used for a window.
2768  * Returns a pointer to the window that got the freed up space.
2769  */
2770     static win_T *
2771 win_free_mem(
2772     win_T	*win,
2773     int		*dirp,		// set to 'v' or 'h' for direction if 'ea'
2774     tabpage_T	*tp)		// tab page "win" is in, NULL for current
2775 {
2776     frame_T	*frp;
2777     win_T	*wp;
2778     tabpage_T	*win_tp = tp == NULL ? curtab : tp;
2779 
2780     // Remove the window and its frame from the tree of frames.
2781     frp = win->w_frame;
2782     wp = winframe_remove(win, dirp, tp);
2783     vim_free(frp);
2784     win_free(win, tp);
2785 
2786     // When deleting the current window in the tab, select a new current
2787     // window.
2788     if (win == win_tp->tp_curwin)
2789 	win_tp->tp_curwin = wp;
2790 
2791     return wp;
2792 }
2793 
2794 #if defined(EXITFREE) || defined(PROTO)
2795     void
2796 win_free_all(void)
2797 {
2798     int		dummy;
2799 
2800     while (first_tabpage->tp_next != NULL)
2801 	tabpage_close(TRUE);
2802 
2803     if (aucmd_win != NULL)
2804     {
2805 	(void)win_free_mem(aucmd_win, &dummy, NULL);
2806 	aucmd_win = NULL;
2807     }
2808 
2809     while (firstwin != NULL)
2810 	(void)win_free_mem(firstwin, &dummy, NULL);
2811 
2812     // No window should be used after this. Set curwin to NULL to crash
2813     // instead of using freed memory.
2814     curwin = NULL;
2815 }
2816 #endif
2817 
2818 /*
2819  * Remove a window and its frame from the tree of frames.
2820  * Returns a pointer to the window that got the freed up space.
2821  */
2822     win_T *
2823 winframe_remove(
2824     win_T	*win,
2825     int		*dirp UNUSED,	// set to 'v' or 'h' for direction if 'ea'
2826     tabpage_T	*tp)		// tab page "win" is in, NULL for current
2827 {
2828     frame_T	*frp, *frp2, *frp3;
2829     frame_T	*frp_close = win->w_frame;
2830     win_T	*wp;
2831 
2832     /*
2833      * If there is only one window there is nothing to remove.
2834      */
2835     if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
2836 	return NULL;
2837 
2838     /*
2839      * Remove the window from its frame.
2840      */
2841     frp2 = win_altframe(win, tp);
2842     wp = frame2win(frp2);
2843 
2844     // Remove this frame from the list of frames.
2845     frame_remove(frp_close);
2846 
2847     if (frp_close->fr_parent->fr_layout == FR_COL)
2848     {
2849 	// When 'winfixheight' is set, try to find another frame in the column
2850 	// (as close to the closed frame as possible) to distribute the height
2851 	// to.
2852 	if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfh)
2853 	{
2854 	    frp = frp_close->fr_prev;
2855 	    frp3 = frp_close->fr_next;
2856 	    while (frp != NULL || frp3 != NULL)
2857 	    {
2858 		if (frp != NULL)
2859 		{
2860 		    if (!frame_fixed_height(frp))
2861 		    {
2862 			frp2 = frp;
2863 			wp = frame2win(frp2);
2864 			break;
2865 		    }
2866 		    frp = frp->fr_prev;
2867 		}
2868 		if (frp3 != NULL)
2869 		{
2870 		    if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfh)
2871 		    {
2872 			frp2 = frp3;
2873 			wp = frp3->fr_win;
2874 			break;
2875 		    }
2876 		    frp3 = frp3->fr_next;
2877 		}
2878 	    }
2879 	}
2880 	frame_new_height(frp2, frp2->fr_height + frp_close->fr_height,
2881 			    frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE);
2882 	*dirp = 'v';
2883     }
2884     else
2885     {
2886 	// When 'winfixwidth' is set, try to find another frame in the column
2887 	// (as close to the closed frame as possible) to distribute the width
2888 	// to.
2889 	if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfw)
2890 	{
2891 	    frp = frp_close->fr_prev;
2892 	    frp3 = frp_close->fr_next;
2893 	    while (frp != NULL || frp3 != NULL)
2894 	    {
2895 		if (frp != NULL)
2896 		{
2897 		    if (!frame_fixed_width(frp))
2898 		    {
2899 			frp2 = frp;
2900 			wp = frame2win(frp2);
2901 			break;
2902 		    }
2903 		    frp = frp->fr_prev;
2904 		}
2905 		if (frp3 != NULL)
2906 		{
2907 		    if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfw)
2908 		    {
2909 			frp2 = frp3;
2910 			wp = frp3->fr_win;
2911 			break;
2912 		    }
2913 		    frp3 = frp3->fr_next;
2914 		}
2915 	    }
2916 	}
2917 	frame_new_width(frp2, frp2->fr_width + frp_close->fr_width,
2918 			    frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE);
2919 	*dirp = 'h';
2920     }
2921 
2922     // If rows/columns go to a window below/right its positions need to be
2923     // updated.  Can only be done after the sizes have been updated.
2924     if (frp2 == frp_close->fr_next)
2925     {
2926 	int row = win->w_winrow;
2927 	int col = win->w_wincol;
2928 
2929 	frame_comp_pos(frp2, &row, &col);
2930     }
2931 
2932     if (frp2->fr_next == NULL && frp2->fr_prev == NULL)
2933     {
2934 	// There is no other frame in this list, move its info to the parent
2935 	// and remove it.
2936 	frp2->fr_parent->fr_layout = frp2->fr_layout;
2937 	frp2->fr_parent->fr_child = frp2->fr_child;
2938 	FOR_ALL_FRAMES(frp, frp2->fr_child)
2939 	    frp->fr_parent = frp2->fr_parent;
2940 	frp2->fr_parent->fr_win = frp2->fr_win;
2941 	if (frp2->fr_win != NULL)
2942 	    frp2->fr_win->w_frame = frp2->fr_parent;
2943 	frp = frp2->fr_parent;
2944 	if (topframe->fr_child == frp2)
2945 	    topframe->fr_child = frp;
2946 	vim_free(frp2);
2947 
2948 	frp2 = frp->fr_parent;
2949 	if (frp2 != NULL && frp2->fr_layout == frp->fr_layout)
2950 	{
2951 	    // The frame above the parent has the same layout, have to merge
2952 	    // the frames into this list.
2953 	    if (frp2->fr_child == frp)
2954 		frp2->fr_child = frp->fr_child;
2955 	    frp->fr_child->fr_prev = frp->fr_prev;
2956 	    if (frp->fr_prev != NULL)
2957 		frp->fr_prev->fr_next = frp->fr_child;
2958 	    for (frp3 = frp->fr_child; ; frp3 = frp3->fr_next)
2959 	    {
2960 		frp3->fr_parent = frp2;
2961 		if (frp3->fr_next == NULL)
2962 		{
2963 		    frp3->fr_next = frp->fr_next;
2964 		    if (frp->fr_next != NULL)
2965 			frp->fr_next->fr_prev = frp3;
2966 		    break;
2967 		}
2968 	    }
2969 	    if (topframe->fr_child == frp)
2970 		topframe->fr_child = frp2;
2971 	    vim_free(frp);
2972 	}
2973     }
2974 
2975     return wp;
2976 }
2977 
2978 /*
2979  * Return a pointer to the frame that will receive the empty screen space that
2980  * is left over after "win" is closed.
2981  *
2982  * If 'splitbelow' or 'splitright' is set, the space goes above or to the left
2983  * by default.  Otherwise, the free space goes below or to the right.  The
2984  * result is that opening a window and then immediately closing it will
2985  * preserve the initial window layout.  The 'wfh' and 'wfw' settings are
2986  * respected when possible.
2987  */
2988     static frame_T *
2989 win_altframe(
2990     win_T	*win,
2991     tabpage_T	*tp)		// tab page "win" is in, NULL for current
2992 {
2993     frame_T	*frp;
2994     frame_T	*other_fr, *target_fr;
2995 
2996     if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
2997 	return alt_tabpage()->tp_curwin->w_frame;
2998 
2999     frp = win->w_frame;
3000 
3001     if (frp->fr_prev == NULL)
3002 	return frp->fr_next;
3003     if (frp->fr_next == NULL)
3004 	return frp->fr_prev;
3005 
3006     // By default the next window will get the space that was abandoned by this
3007     // window
3008     target_fr = frp->fr_next;
3009     other_fr  = frp->fr_prev;
3010 
3011     // If this is part of a column of windows and 'splitbelow' is true then the
3012     // previous window will get the space.
3013     if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_COL && p_sb)
3014     {
3015 	target_fr = frp->fr_prev;
3016 	other_fr  = frp->fr_next;
3017     }
3018 
3019     // If this is part of a row of windows, and 'splitright' is true then the
3020     // previous window will get the space.
3021     if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW && p_spr)
3022     {
3023 	target_fr = frp->fr_prev;
3024 	other_fr  = frp->fr_next;
3025     }
3026 
3027     // If 'wfh' or 'wfw' is set for the target and not for the alternate
3028     // window, reverse the selection.
3029     if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW)
3030     {
3031 	if (frame_fixed_width(target_fr) && !frame_fixed_width(other_fr))
3032 	    target_fr = other_fr;
3033     }
3034     else
3035     {
3036 	if (frame_fixed_height(target_fr) && !frame_fixed_height(other_fr))
3037 	    target_fr = other_fr;
3038     }
3039 
3040     return target_fr;
3041 }
3042 
3043 /*
3044  * Return the tabpage that will be used if the current one is closed.
3045  */
3046     static tabpage_T *
3047 alt_tabpage(void)
3048 {
3049     tabpage_T	*tp;
3050 
3051     // Use the next tab page if possible.
3052     if (curtab->tp_next != NULL)
3053 	return curtab->tp_next;
3054 
3055     // Find the last but one tab page.
3056     for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next)
3057 	;
3058     return tp;
3059 }
3060 
3061 /*
3062  * Find the left-upper window in frame "frp".
3063  */
3064     static win_T *
3065 frame2win(frame_T *frp)
3066 {
3067     while (frp->fr_win == NULL)
3068 	frp = frp->fr_child;
3069     return frp->fr_win;
3070 }
3071 
3072 /*
3073  * Return TRUE if frame "frp" contains window "wp".
3074  */
3075     static int
3076 frame_has_win(frame_T *frp, win_T *wp)
3077 {
3078     frame_T	*p;
3079 
3080     if (frp->fr_layout == FR_LEAF)
3081 	return frp->fr_win == wp;
3082 
3083     FOR_ALL_FRAMES(p, frp->fr_child)
3084 	if (frame_has_win(p, wp))
3085 	    return TRUE;
3086     return FALSE;
3087 }
3088 
3089 /*
3090  * Set a new height for a frame.  Recursively sets the height for contained
3091  * frames and windows.  Caller must take care of positions.
3092  */
3093     static void
3094 frame_new_height(
3095     frame_T	*topfrp,
3096     int		height,
3097     int		topfirst,	// resize topmost contained frame first
3098     int		wfh)		// obey 'winfixheight' when there is a choice;
3099 				// may cause the height not to be set
3100 {
3101     frame_T	*frp;
3102     int		extra_lines;
3103     int		h;
3104 
3105     if (topfrp->fr_win != NULL)
3106     {
3107 	// Simple case: just one window.
3108 	win_new_height(topfrp->fr_win,
3109 				    height - topfrp->fr_win->w_status_height
3110 					      - WINBAR_HEIGHT(topfrp->fr_win));
3111     }
3112     else if (topfrp->fr_layout == FR_ROW)
3113     {
3114 	do
3115 	{
3116 	    // All frames in this row get the same new height.
3117 	    FOR_ALL_FRAMES(frp, topfrp->fr_child)
3118 	    {
3119 		frame_new_height(frp, height, topfirst, wfh);
3120 		if (frp->fr_height > height)
3121 		{
3122 		    // Could not fit the windows, make the whole row higher.
3123 		    height = frp->fr_height;
3124 		    break;
3125 		}
3126 	    }
3127 	}
3128 	while (frp != NULL);
3129     }
3130     else    // fr_layout == FR_COL
3131     {
3132 	// Complicated case: Resize a column of frames.  Resize the bottom
3133 	// frame first, frames above that when needed.
3134 
3135 	frp = topfrp->fr_child;
3136 	if (wfh)
3137 	    // Advance past frames with one window with 'wfh' set.
3138 	    while (frame_fixed_height(frp))
3139 	    {
3140 		frp = frp->fr_next;
3141 		if (frp == NULL)
3142 		    return;	    // no frame without 'wfh', give up
3143 	    }
3144 	if (!topfirst)
3145 	{
3146 	    // Find the bottom frame of this column
3147 	    while (frp->fr_next != NULL)
3148 		frp = frp->fr_next;
3149 	    if (wfh)
3150 		// Advance back for frames with one window with 'wfh' set.
3151 		while (frame_fixed_height(frp))
3152 		    frp = frp->fr_prev;
3153 	}
3154 
3155 	extra_lines = height - topfrp->fr_height;
3156 	if (extra_lines < 0)
3157 	{
3158 	    // reduce height of contained frames, bottom or top frame first
3159 	    while (frp != NULL)
3160 	    {
3161 		h = frame_minheight(frp, NULL);
3162 		if (frp->fr_height + extra_lines < h)
3163 		{
3164 		    extra_lines += frp->fr_height - h;
3165 		    frame_new_height(frp, h, topfirst, wfh);
3166 		}
3167 		else
3168 		{
3169 		    frame_new_height(frp, frp->fr_height + extra_lines,
3170 							       topfirst, wfh);
3171 		    break;
3172 		}
3173 		if (topfirst)
3174 		{
3175 		    do
3176 			frp = frp->fr_next;
3177 		    while (wfh && frp != NULL && frame_fixed_height(frp));
3178 		}
3179 		else
3180 		{
3181 		    do
3182 			frp = frp->fr_prev;
3183 		    while (wfh && frp != NULL && frame_fixed_height(frp));
3184 		}
3185 		// Increase "height" if we could not reduce enough frames.
3186 		if (frp == NULL)
3187 		    height -= extra_lines;
3188 	    }
3189 	}
3190 	else if (extra_lines > 0)
3191 	{
3192 	    // increase height of bottom or top frame
3193 	    frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh);
3194 	}
3195     }
3196     topfrp->fr_height = height;
3197 }
3198 
3199 /*
3200  * Return TRUE if height of frame "frp" should not be changed because of
3201  * the 'winfixheight' option.
3202  */
3203     static int
3204 frame_fixed_height(frame_T *frp)
3205 {
3206     // frame with one window: fixed height if 'winfixheight' set.
3207     if (frp->fr_win != NULL)
3208 	return frp->fr_win->w_p_wfh;
3209 
3210     if (frp->fr_layout == FR_ROW)
3211     {
3212 	// The frame is fixed height if one of the frames in the row is fixed
3213 	// height.
3214 	FOR_ALL_FRAMES(frp, frp->fr_child)
3215 	    if (frame_fixed_height(frp))
3216 		return TRUE;
3217 	return FALSE;
3218     }
3219 
3220     // frp->fr_layout == FR_COL: The frame is fixed height if all of the
3221     // frames in the row are fixed height.
3222     FOR_ALL_FRAMES(frp, frp->fr_child)
3223 	if (!frame_fixed_height(frp))
3224 	    return FALSE;
3225     return TRUE;
3226 }
3227 
3228 /*
3229  * Return TRUE if width of frame "frp" should not be changed because of
3230  * the 'winfixwidth' option.
3231  */
3232     static int
3233 frame_fixed_width(frame_T *frp)
3234 {
3235     // frame with one window: fixed width if 'winfixwidth' set.
3236     if (frp->fr_win != NULL)
3237 	return frp->fr_win->w_p_wfw;
3238 
3239     if (frp->fr_layout == FR_COL)
3240     {
3241 	// The frame is fixed width if one of the frames in the row is fixed
3242 	// width.
3243 	FOR_ALL_FRAMES(frp, frp->fr_child)
3244 	    if (frame_fixed_width(frp))
3245 		return TRUE;
3246 	return FALSE;
3247     }
3248 
3249     // frp->fr_layout == FR_ROW: The frame is fixed width if all of the
3250     // frames in the row are fixed width.
3251     FOR_ALL_FRAMES(frp, frp->fr_child)
3252 	if (!frame_fixed_width(frp))
3253 	    return FALSE;
3254     return TRUE;
3255 }
3256 
3257 /*
3258  * Add a status line to windows at the bottom of "frp".
3259  * Note: Does not check if there is room!
3260  */
3261     static void
3262 frame_add_statusline(frame_T *frp)
3263 {
3264     win_T	*wp;
3265 
3266     if (frp->fr_layout == FR_LEAF)
3267     {
3268 	wp = frp->fr_win;
3269 	if (wp->w_status_height == 0)
3270 	{
3271 	    if (wp->w_height > 0)	// don't make it negative
3272 		--wp->w_height;
3273 	    wp->w_status_height = STATUS_HEIGHT;
3274 	}
3275     }
3276     else if (frp->fr_layout == FR_ROW)
3277     {
3278 	// Handle all the frames in the row.
3279 	FOR_ALL_FRAMES(frp, frp->fr_child)
3280 	    frame_add_statusline(frp);
3281     }
3282     else // frp->fr_layout == FR_COL
3283     {
3284 	// Only need to handle the last frame in the column.
3285 	for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next)
3286 	    ;
3287 	frame_add_statusline(frp);
3288     }
3289 }
3290 
3291 /*
3292  * Set width of a frame.  Handles recursively going through contained frames.
3293  * May remove separator line for windows at the right side (for win_close()).
3294  */
3295     static void
3296 frame_new_width(
3297     frame_T	*topfrp,
3298     int		width,
3299     int		leftfirst,	// resize leftmost contained frame first
3300     int		wfw)		// obey 'winfixwidth' when there is a choice;
3301 				// may cause the width not to be set
3302 {
3303     frame_T	*frp;
3304     int		extra_cols;
3305     int		w;
3306     win_T	*wp;
3307 
3308     if (topfrp->fr_layout == FR_LEAF)
3309     {
3310 	// Simple case: just one window.
3311 	wp = topfrp->fr_win;
3312 	// Find out if there are any windows right of this one.
3313 	for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent)
3314 	    if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL)
3315 		break;
3316 	if (frp->fr_parent == NULL)
3317 	    wp->w_vsep_width = 0;
3318 	win_new_width(wp, width - wp->w_vsep_width);
3319     }
3320     else if (topfrp->fr_layout == FR_COL)
3321     {
3322 	do
3323 	{
3324 	    // All frames in this column get the same new width.
3325 	    FOR_ALL_FRAMES(frp, topfrp->fr_child)
3326 	    {
3327 		frame_new_width(frp, width, leftfirst, wfw);
3328 		if (frp->fr_width > width)
3329 		{
3330 		    // Could not fit the windows, make whole column wider.
3331 		    width = frp->fr_width;
3332 		    break;
3333 		}
3334 	    }
3335 	} while (frp != NULL);
3336     }
3337     else    // fr_layout == FR_ROW
3338     {
3339 	// Complicated case: Resize a row of frames.  Resize the rightmost
3340 	// frame first, frames left of it when needed.
3341 
3342 	frp = topfrp->fr_child;
3343 	if (wfw)
3344 	    // Advance past frames with one window with 'wfw' set.
3345 	    while (frame_fixed_width(frp))
3346 	    {
3347 		frp = frp->fr_next;
3348 		if (frp == NULL)
3349 		    return;	    // no frame without 'wfw', give up
3350 	    }
3351 	if (!leftfirst)
3352 	{
3353 	    // Find the rightmost frame of this row
3354 	    while (frp->fr_next != NULL)
3355 		frp = frp->fr_next;
3356 	    if (wfw)
3357 		// Advance back for frames with one window with 'wfw' set.
3358 		while (frame_fixed_width(frp))
3359 		    frp = frp->fr_prev;
3360 	}
3361 
3362 	extra_cols = width - topfrp->fr_width;
3363 	if (extra_cols < 0)
3364 	{
3365 	    // reduce frame width, rightmost frame first
3366 	    while (frp != NULL)
3367 	    {
3368 		w = frame_minwidth(frp, NULL);
3369 		if (frp->fr_width + extra_cols < w)
3370 		{
3371 		    extra_cols += frp->fr_width - w;
3372 		    frame_new_width(frp, w, leftfirst, wfw);
3373 		}
3374 		else
3375 		{
3376 		    frame_new_width(frp, frp->fr_width + extra_cols,
3377 							      leftfirst, wfw);
3378 		    break;
3379 		}
3380 		if (leftfirst)
3381 		{
3382 		    do
3383 			frp = frp->fr_next;
3384 		    while (wfw && frp != NULL && frame_fixed_width(frp));
3385 		}
3386 		else
3387 		{
3388 		    do
3389 			frp = frp->fr_prev;
3390 		    while (wfw && frp != NULL && frame_fixed_width(frp));
3391 		}
3392 		// Increase "width" if we could not reduce enough frames.
3393 		if (frp == NULL)
3394 		    width -= extra_cols;
3395 	    }
3396 	}
3397 	else if (extra_cols > 0)
3398 	{
3399 	    // increase width of rightmost frame
3400 	    frame_new_width(frp, frp->fr_width + extra_cols, leftfirst, wfw);
3401 	}
3402     }
3403     topfrp->fr_width = width;
3404 }
3405 
3406 /*
3407  * Add the vertical separator to windows at the right side of "frp".
3408  * Note: Does not check if there is room!
3409  */
3410     static void
3411 frame_add_vsep(frame_T *frp)
3412 {
3413     win_T	*wp;
3414 
3415     if (frp->fr_layout == FR_LEAF)
3416     {
3417 	wp = frp->fr_win;
3418 	if (wp->w_vsep_width == 0)
3419 	{
3420 	    if (wp->w_width > 0)	// don't make it negative
3421 		--wp->w_width;
3422 	    wp->w_vsep_width = 1;
3423 	}
3424     }
3425     else if (frp->fr_layout == FR_COL)
3426     {
3427 	// Handle all the frames in the column.
3428 	FOR_ALL_FRAMES(frp, frp->fr_child)
3429 	    frame_add_vsep(frp);
3430     }
3431     else // frp->fr_layout == FR_ROW
3432     {
3433 	// Only need to handle the last frame in the row.
3434 	frp = frp->fr_child;
3435 	while (frp->fr_next != NULL)
3436 	    frp = frp->fr_next;
3437 	frame_add_vsep(frp);
3438     }
3439 }
3440 
3441 /*
3442  * Set frame width from the window it contains.
3443  */
3444     static void
3445 frame_fix_width(win_T *wp)
3446 {
3447     wp->w_frame->fr_width = wp->w_width + wp->w_vsep_width;
3448 }
3449 
3450 /*
3451  * Set frame height from the window it contains.
3452  */
3453     static void
3454 frame_fix_height(win_T *wp)
3455 {
3456     wp->w_frame->fr_height = VISIBLE_HEIGHT(wp) + wp->w_status_height;
3457 }
3458 
3459 /*
3460  * Compute the minimal height for frame "topfrp".
3461  * Uses the 'winminheight' option.
3462  * When "next_curwin" isn't NULL, use p_wh for this window.
3463  * When "next_curwin" is NOWIN, don't use at least one line for the current
3464  * window.
3465  */
3466     static int
3467 frame_minheight(frame_T *topfrp, win_T *next_curwin)
3468 {
3469     frame_T	*frp;
3470     int		m;
3471     int		n;
3472 
3473     if (topfrp->fr_win != NULL)
3474     {
3475 	if (topfrp->fr_win == next_curwin)
3476 	    m = p_wh + topfrp->fr_win->w_status_height;
3477 	else
3478 	{
3479 	    // window: minimal height of the window plus status line
3480 	    m = p_wmh + topfrp->fr_win->w_status_height;
3481 	    if (topfrp->fr_win == curwin && next_curwin == NULL)
3482 	    {
3483 		// Current window is minimal one line high and WinBar is
3484 		// visible.
3485 		if (p_wmh == 0)
3486 		    ++m;
3487 		m += WINBAR_HEIGHT(curwin);
3488 	    }
3489 	}
3490     }
3491     else if (topfrp->fr_layout == FR_ROW)
3492     {
3493 	// get the minimal height from each frame in this row
3494 	m = 0;
3495 	FOR_ALL_FRAMES(frp, topfrp->fr_child)
3496 	{
3497 	    n = frame_minheight(frp, next_curwin);
3498 	    if (n > m)
3499 		m = n;
3500 	}
3501     }
3502     else
3503     {
3504 	// Add up the minimal heights for all frames in this column.
3505 	m = 0;
3506 	FOR_ALL_FRAMES(frp, topfrp->fr_child)
3507 	    m += frame_minheight(frp, next_curwin);
3508     }
3509 
3510     return m;
3511 }
3512 
3513 /*
3514  * Compute the minimal width for frame "topfrp".
3515  * When "next_curwin" isn't NULL, use p_wiw for this window.
3516  * When "next_curwin" is NOWIN, don't use at least one column for the current
3517  * window.
3518  */
3519     static int
3520 frame_minwidth(
3521     frame_T	*topfrp,
3522     win_T	*next_curwin)	// use p_wh and p_wiw for next_curwin
3523 {
3524     frame_T	*frp;
3525     int		m, n;
3526 
3527     if (topfrp->fr_win != NULL)
3528     {
3529 	if (topfrp->fr_win == next_curwin)
3530 	    m = p_wiw + topfrp->fr_win->w_vsep_width;
3531 	else
3532 	{
3533 	    // window: minimal width of the window plus separator column
3534 	    m = p_wmw + topfrp->fr_win->w_vsep_width;
3535 	    // Current window is minimal one column wide
3536 	    if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL)
3537 		++m;
3538 	}
3539     }
3540     else if (topfrp->fr_layout == FR_COL)
3541     {
3542 	// get the minimal width from each frame in this column
3543 	m = 0;
3544 	FOR_ALL_FRAMES(frp, topfrp->fr_child)
3545 	{
3546 	    n = frame_minwidth(frp, next_curwin);
3547 	    if (n > m)
3548 		m = n;
3549 	}
3550     }
3551     else
3552     {
3553 	// Add up the minimal widths for all frames in this row.
3554 	m = 0;
3555 	FOR_ALL_FRAMES(frp, topfrp->fr_child)
3556 	    m += frame_minwidth(frp, next_curwin);
3557     }
3558 
3559     return m;
3560 }
3561 
3562 
3563 /*
3564  * Try to close all windows except current one.
3565  * Buffers in the other windows become hidden if 'hidden' is set, or '!' is
3566  * used and the buffer was modified.
3567  *
3568  * Used by ":bdel" and ":only".
3569  */
3570     void
3571 close_others(
3572     int		message,
3573     int		forceit)	    // always hide all other windows
3574 {
3575     win_T	*wp;
3576     win_T	*nextwp;
3577     int		r;
3578 
3579     if (one_window())
3580     {
3581 	if (message && !autocmd_busy)
3582 	    msg(_(m_onlyone));
3583 	return;
3584     }
3585 
3586     // Be very careful here: autocommands may change the window layout.
3587     for (wp = firstwin; win_valid(wp); wp = nextwp)
3588     {
3589 	nextwp = wp->w_next;
3590 	if (wp != curwin)		// don't close current window
3591 	{
3592 
3593 	    // Check if it's allowed to abandon this window
3594 	    r = can_abandon(wp->w_buffer, forceit);
3595 	    if (!win_valid(wp))		// autocommands messed wp up
3596 	    {
3597 		nextwp = firstwin;
3598 		continue;
3599 	    }
3600 	    if (!r)
3601 	    {
3602 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
3603 		if (message && (p_confirm
3604 			     || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
3605 		{
3606 		    dialog_changed(wp->w_buffer, FALSE);
3607 		    if (!win_valid(wp))		// autocommands messed wp up
3608 		    {
3609 			nextwp = firstwin;
3610 			continue;
3611 		    }
3612 		}
3613 		if (bufIsChanged(wp->w_buffer))
3614 #endif
3615 		    continue;
3616 	    }
3617 	    win_close(wp, !buf_hide(wp->w_buffer)
3618 					       && !bufIsChanged(wp->w_buffer));
3619 	}
3620     }
3621 
3622     if (message && !ONE_WINDOW)
3623 	emsg(_("E445: Other window contains changes"));
3624 }
3625 
3626     static void
3627 win_init_empty(win_T *wp)
3628 {
3629     redraw_win_later(wp, NOT_VALID);
3630     wp->w_lines_valid = 0;
3631     wp->w_cursor.lnum = 1;
3632     wp->w_curswant = wp->w_cursor.col = 0;
3633     wp->w_cursor.coladd = 0;
3634     wp->w_pcmark.lnum = 1;	// pcmark not cleared but set to line 1
3635     wp->w_pcmark.col = 0;
3636     wp->w_prev_pcmark.lnum = 0;
3637     wp->w_prev_pcmark.col = 0;
3638     wp->w_topline = 1;
3639 #ifdef FEAT_DIFF
3640     wp->w_topfill = 0;
3641 #endif
3642     wp->w_botline = 2;
3643 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
3644     wp->w_s = &wp->w_buffer->b_s;
3645 #endif
3646 }
3647 
3648 /*
3649  * Init the current window "curwin".
3650  * Called when a new file is being edited.
3651  */
3652     void
3653 curwin_init(void)
3654 {
3655     win_init_empty(curwin);
3656 }
3657 
3658 /*
3659  * Allocate the first window and put an empty buffer in it.
3660  * Called from main().
3661  * Return FAIL when something goes wrong (out of memory).
3662  */
3663     int
3664 win_alloc_first(void)
3665 {
3666     if (win_alloc_firstwin(NULL) == FAIL)
3667 	return FAIL;
3668 
3669     first_tabpage = alloc_tabpage();
3670     if (first_tabpage == NULL)
3671 	return FAIL;
3672     first_tabpage->tp_topframe = topframe;
3673     curtab = first_tabpage;
3674     curtab->tp_firstwin = firstwin;
3675     curtab->tp_lastwin = lastwin;
3676     curtab->tp_curwin = curwin;
3677 
3678     return OK;
3679 }
3680 
3681 /*
3682  * Allocate and init a window that is not a regular window.
3683  * This can only be done after the first window is fully initialized, thus it
3684  * can't be in win_alloc_first().
3685  */
3686     win_T *
3687 win_alloc_popup_win(void)
3688 {
3689     win_T *wp;
3690 
3691     wp = win_alloc(NULL, TRUE);
3692     if (wp != NULL)
3693     {
3694 	// We need to initialize options with something, using the current
3695 	// window makes most sense.
3696 	win_init_some(wp, curwin);
3697 
3698 	RESET_BINDING(wp);
3699 	new_frame(wp);
3700     }
3701     return wp;
3702 }
3703 
3704 /*
3705  * Initialize window "wp" to display buffer "buf".
3706  */
3707     void
3708 win_init_popup_win(win_T *wp, buf_T *buf)
3709 {
3710     wp->w_buffer = buf;
3711     ++buf->b_nwindows;
3712     win_init_empty(wp); // set cursor and topline to safe values
3713 
3714     // Make sure w_localdir and globaldir are NULL to avoid a chdir() in
3715     // win_enter_ext().
3716     VIM_CLEAR(wp->w_localdir);
3717 }
3718 
3719 /*
3720  * Allocate the first window or the first window in a new tab page.
3721  * When "oldwin" is NULL create an empty buffer for it.
3722  * When "oldwin" is not NULL copy info from it to the new window.
3723  * Return FAIL when something goes wrong (out of memory).
3724  */
3725     static int
3726 win_alloc_firstwin(win_T *oldwin)
3727 {
3728     curwin = win_alloc(NULL, FALSE);
3729     if (oldwin == NULL)
3730     {
3731 	// Very first window, need to create an empty buffer for it and
3732 	// initialize from scratch.
3733 	curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED);
3734 	if (curwin == NULL || curbuf == NULL)
3735 	    return FAIL;
3736 	curwin->w_buffer = curbuf;
3737 #ifdef FEAT_SYN_HL
3738 	curwin->w_s = &(curbuf->b_s);
3739 #endif
3740 	curbuf->b_nwindows = 1;	// there is one window
3741 	curwin->w_alist = &global_alist;
3742 	curwin_init();		// init current window
3743     }
3744     else
3745     {
3746 	// First window in new tab page, initialize it from "oldwin".
3747 	win_init(curwin, oldwin, 0);
3748 
3749 	// We don't want cursor- and scroll-binding in the first window.
3750 	RESET_BINDING(curwin);
3751     }
3752 
3753     new_frame(curwin);
3754     if (curwin->w_frame == NULL)
3755 	return FAIL;
3756     topframe = curwin->w_frame;
3757     topframe->fr_width = Columns;
3758     topframe->fr_height = Rows - p_ch;
3759 
3760     return OK;
3761 }
3762 
3763 /*
3764  * Create a frame for window "wp".
3765  */
3766     static void
3767 new_frame(win_T *wp)
3768 {
3769     frame_T *frp = ALLOC_CLEAR_ONE(frame_T);
3770 
3771     wp->w_frame = frp;
3772     if (frp != NULL)
3773     {
3774 	frp->fr_layout = FR_LEAF;
3775 	frp->fr_win = wp;
3776     }
3777 }
3778 
3779 /*
3780  * Initialize the window and frame size to the maximum.
3781  */
3782     void
3783 win_init_size(void)
3784 {
3785     firstwin->w_height = ROWS_AVAIL;
3786     topframe->fr_height = ROWS_AVAIL;
3787     firstwin->w_width = Columns;
3788     topframe->fr_width = Columns;
3789 }
3790 
3791 /*
3792  * Allocate a new tabpage_T and init the values.
3793  * Returns NULL when out of memory.
3794  */
3795     static tabpage_T *
3796 alloc_tabpage(void)
3797 {
3798     tabpage_T	*tp;
3799 # ifdef FEAT_GUI
3800     int		i;
3801 # endif
3802 
3803 
3804     tp = ALLOC_CLEAR_ONE(tabpage_T);
3805     if (tp == NULL)
3806 	return NULL;
3807 
3808 # ifdef FEAT_EVAL
3809     // init t: variables
3810     tp->tp_vars = dict_alloc();
3811     if (tp->tp_vars == NULL)
3812     {
3813 	vim_free(tp);
3814 	return NULL;
3815     }
3816     init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE);
3817 # endif
3818 
3819 # ifdef FEAT_GUI
3820     for (i = 0; i < 3; i++)
3821 	tp->tp_prev_which_scrollbars[i] = -1;
3822 # endif
3823 # ifdef FEAT_DIFF
3824     tp->tp_diff_invalid = TRUE;
3825 # endif
3826     tp->tp_ch_used = p_ch;
3827 
3828     return tp;
3829 }
3830 
3831     void
3832 free_tabpage(tabpage_T *tp)
3833 {
3834     int idx;
3835 
3836 # ifdef FEAT_DIFF
3837     diff_clear(tp);
3838 # endif
3839 # ifdef FEAT_PROP_POPUP
3840     while (tp->tp_first_popupwin != NULL)
3841 	popup_close_tabpage(tp, tp->tp_first_popupwin->w_id, TRUE);
3842 #endif
3843     for (idx = 0; idx < SNAP_COUNT; ++idx)
3844 	clear_snapshot(tp, idx);
3845 #ifdef FEAT_EVAL
3846     vars_clear(&tp->tp_vars->dv_hashtab);	// free all t: variables
3847     hash_init(&tp->tp_vars->dv_hashtab);
3848     unref_var_dict(tp->tp_vars);
3849 #endif
3850 
3851     if (tp == lastused_tabpage)
3852 	lastused_tabpage = NULL;
3853 
3854     vim_free(tp->tp_localdir);
3855     vim_free(tp->tp_prevdir);
3856 
3857 #ifdef FEAT_PYTHON
3858     python_tabpage_free(tp);
3859 #endif
3860 
3861 #ifdef FEAT_PYTHON3
3862     python3_tabpage_free(tp);
3863 #endif
3864 
3865     vim_free(tp);
3866 }
3867 
3868 /*
3869  * Create a new Tab page with one window.
3870  * It will edit the current buffer, like after ":split".
3871  * When "after" is 0 put it just after the current Tab page.
3872  * Otherwise put it just before tab page "after".
3873  * Return FAIL or OK.
3874  */
3875     int
3876 win_new_tabpage(int after)
3877 {
3878     tabpage_T	*tp = curtab;
3879     tabpage_T	*prev_tp = curtab;
3880     tabpage_T	*newtp;
3881     int		n;
3882 
3883     newtp = alloc_tabpage();
3884     if (newtp == NULL)
3885 	return FAIL;
3886 
3887     // Remember the current windows in this Tab page.
3888     if (leave_tabpage(curbuf, TRUE) == FAIL)
3889     {
3890 	vim_free(newtp);
3891 	return FAIL;
3892     }
3893     curtab = newtp;
3894 
3895     newtp->tp_localdir = (tp->tp_localdir == NULL)
3896 				    ? NULL : vim_strsave(tp->tp_localdir);
3897     // Create a new empty window.
3898     if (win_alloc_firstwin(tp->tp_curwin) == OK)
3899     {
3900 	// Make the new Tab page the new topframe.
3901 	if (after == 1)
3902 	{
3903 	    // New tab page becomes the first one.
3904 	    newtp->tp_next = first_tabpage;
3905 	    first_tabpage = newtp;
3906 	}
3907 	else
3908 	{
3909 	    if (after > 0)
3910 	    {
3911 		// Put new tab page before tab page "after".
3912 		n = 2;
3913 		for (tp = first_tabpage; tp->tp_next != NULL
3914 					       && n < after; tp = tp->tp_next)
3915 		    ++n;
3916 	    }
3917 	    newtp->tp_next = tp->tp_next;
3918 	    tp->tp_next = newtp;
3919 	}
3920 	newtp->tp_firstwin = newtp->tp_lastwin = newtp->tp_curwin = curwin;
3921 
3922 	win_init_size();
3923 	firstwin->w_winrow = tabline_height();
3924 	win_comp_scroll(curwin);
3925 
3926 	newtp->tp_topframe = topframe;
3927 	last_status(FALSE);
3928 
3929 	lastused_tabpage = prev_tp;
3930 
3931 #if defined(FEAT_GUI)
3932 	// When 'guioptions' includes 'L' or 'R' may have to remove or add
3933 	// scrollbars.  Have to update them anyway.
3934 	gui_may_update_scrollbars();
3935 #endif
3936 #ifdef FEAT_JOB_CHANNEL
3937 	entering_window(curwin);
3938 #endif
3939 
3940 	redraw_all_later(NOT_VALID);
3941 	apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf);
3942 	apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
3943 	apply_autocmds(EVENT_TABNEW, NULL, NULL, FALSE, curbuf);
3944 	apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
3945 	return OK;
3946     }
3947 
3948     // Failed, get back the previous Tab page
3949     enter_tabpage(curtab, curbuf, TRUE, TRUE);
3950     return FAIL;
3951 }
3952 
3953 /*
3954  * Open a new tab page if ":tab cmd" was used.  It will edit the same buffer,
3955  * like with ":split".
3956  * Returns OK if a new tab page was created, FAIL otherwise.
3957  */
3958     static int
3959 may_open_tabpage(void)
3960 {
3961     int		n = (cmdmod.cmod_tab == 0)
3962 				       ? postponed_split_tab : cmdmod.cmod_tab;
3963 
3964     if (n != 0)
3965     {
3966 	cmdmod.cmod_tab = 0;	    // reset it to avoid doing it twice
3967 	postponed_split_tab = 0;
3968 	return win_new_tabpage(n);
3969     }
3970     return FAIL;
3971 }
3972 
3973 /*
3974  * Create up to "maxcount" tabpages with empty windows.
3975  * Returns the number of resulting tab pages.
3976  */
3977     int
3978 make_tabpages(int maxcount)
3979 {
3980     int		count = maxcount;
3981     int		todo;
3982 
3983     // Limit to 'tabpagemax' tabs.
3984     if (count > p_tpm)
3985 	count = p_tpm;
3986 
3987     /*
3988      * Don't execute autocommands while creating the tab pages.  Must do that
3989      * when putting the buffers in the windows.
3990      */
3991     block_autocmds();
3992 
3993     for (todo = count - 1; todo > 0; --todo)
3994 	if (win_new_tabpage(0) == FAIL)
3995 	    break;
3996 
3997     unblock_autocmds();
3998 
3999     // return actual number of tab pages
4000     return (count - todo);
4001 }
4002 
4003 /*
4004  * Return TRUE when "tpc" points to a valid tab page.
4005  */
4006     int
4007 valid_tabpage(tabpage_T *tpc)
4008 {
4009     tabpage_T	*tp;
4010 
4011     FOR_ALL_TABPAGES(tp)
4012 	if (tp == tpc)
4013 	    return TRUE;
4014     return FALSE;
4015 }
4016 
4017 /*
4018  * Return TRUE when "tpc" points to a valid tab page and at least one window is
4019  * valid.
4020  */
4021     int
4022 valid_tabpage_win(tabpage_T *tpc)
4023 {
4024     tabpage_T	*tp;
4025     win_T	*wp;
4026 
4027     FOR_ALL_TABPAGES(tp)
4028     {
4029 	if (tp == tpc)
4030 	{
4031 	    FOR_ALL_WINDOWS_IN_TAB(tp, wp)
4032 	    {
4033 		if (win_valid_any_tab(wp))
4034 		    return TRUE;
4035 	    }
4036 	    return FALSE;
4037 	}
4038     }
4039     // shouldn't happen
4040     return FALSE;
4041 }
4042 
4043 /*
4044  * Close tabpage "tab", assuming it has no windows in it.
4045  * There must be another tabpage or this will crash.
4046  */
4047     void
4048 close_tabpage(tabpage_T *tab)
4049 {
4050     tabpage_T	*ptp;
4051 
4052     if (tab == first_tabpage)
4053     {
4054 	first_tabpage = tab->tp_next;
4055 	ptp = first_tabpage;
4056     }
4057     else
4058     {
4059 	for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab;
4060 							    ptp = ptp->tp_next)
4061 	    ;
4062 	assert(ptp != NULL);
4063 	ptp->tp_next = tab->tp_next;
4064     }
4065 
4066     goto_tabpage_tp(ptp, FALSE, FALSE);
4067     free_tabpage(tab);
4068 }
4069 
4070 /*
4071  * Find tab page "n" (first one is 1).  Returns NULL when not found.
4072  */
4073     tabpage_T *
4074 find_tabpage(int n)
4075 {
4076     tabpage_T	*tp;
4077     int		i = 1;
4078 
4079     if (n == 0)
4080 	return curtab;
4081 
4082     for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next)
4083 	++i;
4084     return tp;
4085 }
4086 
4087 /*
4088  * Get index of tab page "tp".  First one has index 1.
4089  * When not found returns number of tab pages plus one.
4090  */
4091     int
4092 tabpage_index(tabpage_T *ftp)
4093 {
4094     int		i = 1;
4095     tabpage_T	*tp;
4096 
4097     for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next)
4098 	++i;
4099     return i;
4100 }
4101 
4102 /*
4103  * Prepare for leaving the current tab page.
4104  * When autocommands change "curtab" we don't leave the tab page and return
4105  * FAIL.
4106  * Careful: When OK is returned need to get a new tab page very very soon!
4107  */
4108     static int
4109 leave_tabpage(
4110     buf_T	*new_curbuf UNUSED,    // what is going to be the new curbuf,
4111 				       // NULL if unknown
4112     int		trigger_leave_autocmds UNUSED)
4113 {
4114     tabpage_T	*tp = curtab;
4115 
4116 #ifdef FEAT_JOB_CHANNEL
4117     leaving_window(curwin);
4118 #endif
4119     reset_VIsual_and_resel();	// stop Visual mode
4120     if (trigger_leave_autocmds)
4121     {
4122 	if (new_curbuf != curbuf)
4123 	{
4124 	    apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
4125 	    if (curtab != tp)
4126 		return FAIL;
4127 	}
4128 	apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
4129 	if (curtab != tp)
4130 	    return FAIL;
4131 	apply_autocmds(EVENT_TABLEAVE, NULL, NULL, FALSE, curbuf);
4132 	if (curtab != tp)
4133 	    return FAIL;
4134     }
4135 #if defined(FEAT_GUI)
4136     // Remove the scrollbars.  They may be added back later.
4137     if (gui.in_use)
4138 	gui_remove_scrollbars();
4139 #endif
4140     tp->tp_curwin = curwin;
4141     tp->tp_prevwin = prevwin;
4142     tp->tp_firstwin = firstwin;
4143     tp->tp_lastwin = lastwin;
4144     tp->tp_old_Rows = Rows;
4145     tp->tp_old_Columns = Columns;
4146     firstwin = NULL;
4147     lastwin = NULL;
4148     return OK;
4149 }
4150 
4151 /*
4152  * Start using tab page "tp".
4153  * Only to be used after leave_tabpage() or freeing the current tab page.
4154  * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE.
4155  * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE.
4156  */
4157     static void
4158 enter_tabpage(
4159     tabpage_T	*tp,
4160     buf_T	*old_curbuf UNUSED,
4161     int		trigger_enter_autocmds,
4162     int		trigger_leave_autocmds)
4163 {
4164     int		row;
4165     int		old_off = tp->tp_firstwin->w_winrow;
4166     win_T	*next_prevwin = tp->tp_prevwin;
4167     tabpage_T	*last_tab = curtab;
4168 
4169     curtab = tp;
4170     firstwin = tp->tp_firstwin;
4171     lastwin = tp->tp_lastwin;
4172     topframe = tp->tp_topframe;
4173 
4174     // We would like doing the TabEnter event first, but we don't have a
4175     // valid current window yet, which may break some commands.
4176     // This triggers autocommands, thus may make "tp" invalid.
4177     win_enter_ext(tp->tp_curwin, FALSE, TRUE, FALSE,
4178 			      trigger_enter_autocmds, trigger_leave_autocmds);
4179     prevwin = next_prevwin;
4180 
4181     last_status(FALSE);		// status line may appear or disappear
4182     row = win_comp_pos();	// recompute w_winrow for all windows
4183 #ifdef FEAT_DIFF
4184     diff_need_scrollbind = TRUE;
4185 #endif
4186 
4187     // The tabpage line may have appeared or disappeared, may need to resize
4188     // the frames for that.  When the Vim window was resized need to update
4189     // frame sizes too.  Use the stored value of p_ch, so that it can be
4190     // different for each tab page.
4191     if (p_ch != curtab->tp_ch_used)
4192 	clear_cmdline = TRUE;
4193     p_ch = curtab->tp_ch_used;
4194 
4195     // When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is
4196     // changed but p_ch and tp_ch_used are not changed. Thus we also need to
4197     // check cmdline_row.
4198     if ((row < cmdline_row) && (cmdline_row <= Rows - p_ch))
4199 	clear_cmdline = TRUE;
4200 
4201     if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow
4202 #ifdef FEAT_GUI_TABLINE
4203 			    && !gui_use_tabline()
4204 #endif
4205 		))
4206 	shell_new_rows();
4207     if (curtab->tp_old_Columns != Columns && starting == 0)
4208 	shell_new_columns();	// update window widths
4209 
4210     lastused_tabpage = last_tab;
4211 
4212 #if defined(FEAT_GUI)
4213     // When 'guioptions' includes 'L' or 'R' may have to remove or add
4214     // scrollbars.  Have to update them anyway.
4215     gui_may_update_scrollbars();
4216 #endif
4217 
4218     // Apply autocommands after updating the display, when 'rows' and
4219     // 'columns' have been set correctly.
4220     if (trigger_enter_autocmds)
4221     {
4222 	apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
4223 	if (old_curbuf != curbuf)
4224 	    apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
4225     }
4226 
4227     redraw_all_later(NOT_VALID);
4228 }
4229 
4230 /*
4231  * Go to tab page "n".  For ":tab N" and "Ngt".
4232  * When "n" is 9999 go to the last tab page.
4233  */
4234     void
4235 goto_tabpage(int n)
4236 {
4237     tabpage_T	*tp = NULL;  // shut up compiler
4238     tabpage_T	*ttp;
4239     int		i;
4240 
4241     if (text_locked())
4242     {
4243 	// Not allowed when editing the command line.
4244 	text_locked_msg();
4245 	return;
4246     }
4247 
4248     // If there is only one it can't work.
4249     if (first_tabpage->tp_next == NULL)
4250     {
4251 	if (n > 1)
4252 	    beep_flush();
4253 	return;
4254     }
4255 
4256     if (n == 0)
4257     {
4258 	// No count, go to next tab page, wrap around end.
4259 	if (curtab->tp_next == NULL)
4260 	    tp = first_tabpage;
4261 	else
4262 	    tp = curtab->tp_next;
4263     }
4264     else if (n < 0)
4265     {
4266 	// "gT": go to previous tab page, wrap around end.  "N gT" repeats
4267 	// this N times.
4268 	ttp = curtab;
4269 	for (i = n; i < 0; ++i)
4270 	{
4271 	    for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL;
4272 		    tp = tp->tp_next)
4273 		;
4274 	    ttp = tp;
4275 	}
4276     }
4277     else if (n == 9999)
4278     {
4279 	// Go to last tab page.
4280 	for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next)
4281 	    ;
4282     }
4283     else
4284     {
4285 	// Go to tab page "n".
4286 	tp = find_tabpage(n);
4287 	if (tp == NULL)
4288 	{
4289 	    beep_flush();
4290 	    return;
4291 	}
4292     }
4293 
4294     goto_tabpage_tp(tp, TRUE, TRUE);
4295 
4296 #ifdef FEAT_GUI_TABLINE
4297     if (gui_use_tabline())
4298 	gui_mch_set_curtab(tabpage_index(curtab));
4299 #endif
4300 }
4301 
4302 /*
4303  * Go to tabpage "tp".
4304  * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE.
4305  * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE.
4306  * Note: doesn't update the GUI tab.
4307  */
4308     void
4309 goto_tabpage_tp(
4310     tabpage_T	*tp,
4311     int		trigger_enter_autocmds,
4312     int		trigger_leave_autocmds)
4313 {
4314     // Don't repeat a message in another tab page.
4315     set_keep_msg(NULL, 0);
4316 
4317     if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer,
4318 					trigger_leave_autocmds) == OK)
4319     {
4320 	if (valid_tabpage(tp))
4321 	    enter_tabpage(tp, curbuf, trigger_enter_autocmds,
4322 		    trigger_leave_autocmds);
4323 	else
4324 	    enter_tabpage(curtab, curbuf, trigger_enter_autocmds,
4325 		    trigger_leave_autocmds);
4326     }
4327 }
4328 
4329 /*
4330  * Go to the last accessed tab page, if there is one.
4331  * Return OK or FAIL
4332  */
4333     int
4334 goto_tabpage_lastused(void)
4335 {
4336     if (valid_tabpage(lastused_tabpage))
4337     {
4338 	goto_tabpage_tp(lastused_tabpage, TRUE, TRUE);
4339 	return OK;
4340     }
4341     return FAIL;
4342 }
4343 
4344 /*
4345  * Enter window "wp" in tab page "tp".
4346  * Also updates the GUI tab.
4347  */
4348     void
4349 goto_tabpage_win(tabpage_T *tp, win_T *wp)
4350 {
4351     goto_tabpage_tp(tp, TRUE, TRUE);
4352     if (curtab == tp && win_valid(wp))
4353     {
4354 	win_enter(wp, TRUE);
4355 # ifdef FEAT_GUI_TABLINE
4356 	if (gui_use_tabline())
4357 	    gui_mch_set_curtab(tabpage_index(curtab));
4358 # endif
4359     }
4360 }
4361 
4362 /*
4363  * Move the current tab page to after tab page "nr".
4364  */
4365     void
4366 tabpage_move(int nr)
4367 {
4368     int		n = 1;
4369     tabpage_T	*tp, *tp_dst;
4370 
4371     if (first_tabpage->tp_next == NULL)
4372 	return;
4373 
4374     for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next)
4375 	++n;
4376 
4377     if (tp == curtab || (nr > 0 && tp->tp_next != NULL
4378 						    && tp->tp_next == curtab))
4379 	return;
4380 
4381     tp_dst = tp;
4382 
4383     // Remove the current tab page from the list of tab pages.
4384     if (curtab == first_tabpage)
4385 	first_tabpage = curtab->tp_next;
4386     else
4387     {
4388 	FOR_ALL_TABPAGES(tp)
4389 	    if (tp->tp_next == curtab)
4390 		break;
4391 	if (tp == NULL)	// "cannot happen"
4392 	    return;
4393 	tp->tp_next = curtab->tp_next;
4394     }
4395 
4396     // Re-insert it at the specified position.
4397     if (nr <= 0)
4398     {
4399 	curtab->tp_next = first_tabpage;
4400 	first_tabpage = curtab;
4401     }
4402     else
4403     {
4404 	curtab->tp_next = tp_dst->tp_next;
4405 	tp_dst->tp_next = curtab;
4406     }
4407 
4408     // Need to redraw the tabline.  Tab page contents doesn't change.
4409     redraw_tabline = TRUE;
4410 }
4411 
4412 
4413 /*
4414  * Go to another window.
4415  * When jumping to another buffer, stop Visual mode.  Do this before
4416  * changing windows so we can yank the selection into the '*' register.
4417  * When jumping to another window on the same buffer, adjust its cursor
4418  * position to keep the same Visual area.
4419  */
4420     void
4421 win_goto(win_T *wp)
4422 {
4423 #ifdef FEAT_CONCEAL
4424     win_T	*owp = curwin;
4425 #endif
4426 
4427 #ifdef FEAT_PROP_POPUP
4428     if (ERROR_IF_ANY_POPUP_WINDOW)
4429 	return;
4430     if (popup_is_popup(wp))
4431     {
4432 	emsg(_("E366: Not allowed to enter a popup window"));
4433 	return;
4434     }
4435 #endif
4436     if (text_and_win_locked())
4437     {
4438 	beep_flush();
4439 	text_locked_msg();
4440 	return;
4441     }
4442     if (curbuf_locked())
4443 	return;
4444 
4445     if (wp->w_buffer != curbuf)
4446 	reset_VIsual_and_resel();
4447     else if (VIsual_active)
4448 	wp->w_cursor = curwin->w_cursor;
4449 
4450 #ifdef FEAT_GUI
4451     need_mouse_correct = TRUE;
4452 #endif
4453     win_enter(wp, TRUE);
4454 
4455 #ifdef FEAT_CONCEAL
4456     // Conceal cursor line in previous window, unconceal in current window.
4457     if (win_valid(owp) && owp->w_p_cole > 0 && !msg_scrolled)
4458 	redrawWinline(owp, owp->w_cursor.lnum);
4459     if (curwin->w_p_cole > 0 && !msg_scrolled)
4460 	need_cursor_line_redraw = TRUE;
4461 #endif
4462 }
4463 
4464 #if defined(FEAT_PERL) || defined(PROTO)
4465 /*
4466  * Find window number "winnr" (counting top to bottom).
4467  */
4468     win_T *
4469 win_find_nr(int winnr)
4470 {
4471     win_T	*wp;
4472 
4473     FOR_ALL_WINDOWS(wp)
4474 	if (--winnr == 0)
4475 	    break;
4476     return wp;
4477 }
4478 #endif
4479 
4480 #if ((defined(FEAT_PYTHON) || defined(FEAT_PYTHON3))) || defined(PROTO)
4481 /*
4482  * Find the tabpage for window "win".
4483  */
4484     tabpage_T *
4485 win_find_tabpage(win_T *win)
4486 {
4487     win_T	*wp;
4488     tabpage_T	*tp;
4489 
4490     FOR_ALL_TAB_WINDOWS(tp, wp)
4491 	    if (wp == win)
4492 		return tp;
4493     return NULL;
4494 }
4495 #endif
4496 
4497 /*
4498  * Get the above or below neighbor window of the specified window.
4499  *   up - TRUE for the above neighbor
4500  *   count - nth neighbor window
4501  * Returns the specified window if the neighbor is not found.
4502  */
4503     win_T *
4504 win_vert_neighbor(tabpage_T *tp, win_T *wp, int up, long count)
4505 {
4506     frame_T	*fr;
4507     frame_T	*nfr;
4508     frame_T	*foundfr;
4509 
4510 #ifdef FEAT_PROP_POPUP
4511     if (popup_is_popup(wp))
4512 	// popups don't have neighbors.
4513 	return NULL;
4514 #endif
4515     foundfr = wp->w_frame;
4516     while (count--)
4517     {
4518 	/*
4519 	 * First go upwards in the tree of frames until we find a upwards or
4520 	 * downwards neighbor.
4521 	 */
4522 	fr = foundfr;
4523 	for (;;)
4524 	{
4525 	    if (fr == tp->tp_topframe)
4526 		goto end;
4527 	    if (up)
4528 		nfr = fr->fr_prev;
4529 	    else
4530 		nfr = fr->fr_next;
4531 	    if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL)
4532 		break;
4533 	    fr = fr->fr_parent;
4534 	}
4535 
4536 	/*
4537 	 * Now go downwards to find the bottom or top frame in it.
4538 	 */
4539 	for (;;)
4540 	{
4541 	    if (nfr->fr_layout == FR_LEAF)
4542 	    {
4543 		foundfr = nfr;
4544 		break;
4545 	    }
4546 	    fr = nfr->fr_child;
4547 	    if (nfr->fr_layout == FR_ROW)
4548 	    {
4549 		// Find the frame at the cursor row.
4550 		while (fr->fr_next != NULL
4551 			&& frame2win(fr)->w_wincol + fr->fr_width
4552 					 <= wp->w_wincol + wp->w_wcol)
4553 		    fr = fr->fr_next;
4554 	    }
4555 	    if (nfr->fr_layout == FR_COL && up)
4556 		while (fr->fr_next != NULL)
4557 		    fr = fr->fr_next;
4558 	    nfr = fr;
4559 	}
4560     }
4561 end:
4562     return foundfr != NULL ? foundfr->fr_win : NULL;
4563 }
4564 
4565 /*
4566  * Move to window above or below "count" times.
4567  */
4568     static void
4569 win_goto_ver(
4570     int		up,		// TRUE to go to win above
4571     long	count)
4572 {
4573     win_T	*win;
4574 
4575 #ifdef FEAT_PROP_POPUP
4576     if (ERROR_IF_TERM_POPUP_WINDOW)
4577 	return;
4578 #endif
4579     win = win_vert_neighbor(curtab, curwin, up, count);
4580     if (win != NULL)
4581 	win_goto(win);
4582 }
4583 
4584 /*
4585  * Get the left or right neighbor window of the specified window.
4586  *   left - TRUE for the left neighbor
4587  *   count - nth neighbor window
4588  * Returns the specified window if the neighbor is not found.
4589  */
4590     win_T *
4591 win_horz_neighbor(tabpage_T *tp, win_T *wp, int left, long count)
4592 {
4593     frame_T	*fr;
4594     frame_T	*nfr;
4595     frame_T	*foundfr;
4596 
4597 #ifdef FEAT_PROP_POPUP
4598     if (popup_is_popup(wp))
4599 	// popups don't have neighbors.
4600 	return NULL;
4601 #endif
4602     foundfr = wp->w_frame;
4603     while (count--)
4604     {
4605 	/*
4606 	 * First go upwards in the tree of frames until we find a left or
4607 	 * right neighbor.
4608 	 */
4609 	fr = foundfr;
4610 	for (;;)
4611 	{
4612 	    if (fr == tp->tp_topframe)
4613 		goto end;
4614 	    if (left)
4615 		nfr = fr->fr_prev;
4616 	    else
4617 		nfr = fr->fr_next;
4618 	    if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
4619 		break;
4620 	    fr = fr->fr_parent;
4621 	}
4622 
4623 	/*
4624 	 * Now go downwards to find the leftmost or rightmost frame in it.
4625 	 */
4626 	for (;;)
4627 	{
4628 	    if (nfr->fr_layout == FR_LEAF)
4629 	    {
4630 		foundfr = nfr;
4631 		break;
4632 	    }
4633 	    fr = nfr->fr_child;
4634 	    if (nfr->fr_layout == FR_COL)
4635 	    {
4636 		// Find the frame at the cursor row.
4637 		while (fr->fr_next != NULL
4638 			&& frame2win(fr)->w_winrow + fr->fr_height
4639 					 <= wp->w_winrow + wp->w_wrow)
4640 		    fr = fr->fr_next;
4641 	    }
4642 	    if (nfr->fr_layout == FR_ROW && left)
4643 		while (fr->fr_next != NULL)
4644 		    fr = fr->fr_next;
4645 	    nfr = fr;
4646 	}
4647     }
4648 end:
4649     return foundfr != NULL ? foundfr->fr_win : NULL;
4650 }
4651 
4652 /*
4653  * Move to left or right window.
4654  */
4655     static void
4656 win_goto_hor(
4657     int		left,		// TRUE to go to left win
4658     long	count)
4659 {
4660     win_T	*win;
4661 
4662 #ifdef FEAT_PROP_POPUP
4663     if (ERROR_IF_TERM_POPUP_WINDOW)
4664 	return;
4665 #endif
4666     win = win_horz_neighbor(curtab, curwin, left, count);
4667     if (win != NULL)
4668 	win_goto(win);
4669 }
4670 
4671 /*
4672  * Make window "wp" the current window.
4673  */
4674     void
4675 win_enter(win_T *wp, int undo_sync)
4676 {
4677     win_enter_ext(wp, undo_sync, FALSE, FALSE, TRUE, TRUE);
4678 }
4679 
4680 /*
4681  * Make window wp the current window.
4682  * Can be called with "curwin_invalid" TRUE, which means that curwin has just
4683  * been closed and isn't valid.
4684  */
4685     static void
4686 win_enter_ext(
4687     win_T	*wp,
4688     int		undo_sync,
4689     int		curwin_invalid,
4690     int		trigger_new_autocmds,
4691     int		trigger_enter_autocmds,
4692     int		trigger_leave_autocmds)
4693 {
4694     int		other_buffer = FALSE;
4695 
4696     if (wp == curwin && !curwin_invalid)	// nothing to do
4697 	return;
4698 
4699 #ifdef FEAT_JOB_CHANNEL
4700     if (!curwin_invalid)
4701 	leaving_window(curwin);
4702 #endif
4703 
4704     if (!curwin_invalid && trigger_leave_autocmds)
4705     {
4706 	/*
4707 	 * Be careful: If autocommands delete the window, return now.
4708 	 */
4709 	if (wp->w_buffer != curbuf)
4710 	{
4711 	    apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
4712 	    other_buffer = TRUE;
4713 	    if (!win_valid(wp))
4714 		return;
4715 	}
4716 	apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
4717 	if (!win_valid(wp))
4718 	    return;
4719 #ifdef FEAT_EVAL
4720 	// autocmds may abort script processing
4721 	if (aborting())
4722 	    return;
4723 #endif
4724     }
4725 
4726     // sync undo before leaving the current buffer
4727     if (undo_sync && curbuf != wp->w_buffer)
4728 	u_sync(FALSE);
4729 
4730     // Might need to scroll the old window before switching, e.g., when the
4731     // cursor was moved.
4732     update_topline();
4733 
4734     // may have to copy the buffer options when 'cpo' contains 'S'
4735     if (wp->w_buffer != curbuf)
4736 	buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP);
4737     if (!curwin_invalid)
4738     {
4739 	prevwin = curwin;	// remember for CTRL-W p
4740 	curwin->w_redr_status = TRUE;
4741     }
4742     curwin = wp;
4743     curbuf = wp->w_buffer;
4744     check_cursor();
4745     if (!virtual_active())
4746 	curwin->w_cursor.coladd = 0;
4747     changed_line_abv_curs();	// assume cursor position needs updating
4748 
4749     if (curwin->w_localdir != NULL || curtab->tp_localdir != NULL)
4750     {
4751 	char_u	*dirname;
4752 
4753 	// Window or tab has a local directory: Save current directory as
4754 	// global directory (unless that was done already) and change to the
4755 	// local directory.
4756 	if (globaldir == NULL)
4757 	{
4758 	    char_u	cwd[MAXPATHL];
4759 
4760 	    if (mch_dirname(cwd, MAXPATHL) == OK)
4761 		globaldir = vim_strsave(cwd);
4762 	}
4763 	if (curwin->w_localdir != NULL)
4764 	    dirname = curwin->w_localdir;
4765 	else
4766 	    dirname = curtab->tp_localdir;
4767 
4768 	if (mch_chdir((char *)dirname) == 0)
4769 	    shorten_fnames(TRUE);
4770     }
4771     else if (globaldir != NULL)
4772     {
4773 	// Window doesn't have a local directory and we are not in the global
4774 	// directory: Change to the global directory.
4775 	vim_ignored = mch_chdir((char *)globaldir);
4776 	VIM_CLEAR(globaldir);
4777 	shorten_fnames(TRUE);
4778     }
4779 
4780 #ifdef FEAT_JOB_CHANNEL
4781     entering_window(curwin);
4782 #endif
4783     // Careful: autocommands may close the window and make "wp" invalid
4784     if (trigger_new_autocmds)
4785 	apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf);
4786     if (trigger_enter_autocmds)
4787     {
4788 	apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
4789 	if (other_buffer)
4790 	    apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
4791     }
4792 
4793 #ifdef FEAT_TITLE
4794     maketitle();
4795 #endif
4796     curwin->w_redr_status = TRUE;
4797 #ifdef FEAT_TERMINAL
4798     if (bt_terminal(curwin->w_buffer))
4799 	// terminal is likely in another mode
4800 	redraw_mode = TRUE;
4801 #endif
4802     redraw_tabline = TRUE;
4803     if (restart_edit)
4804 	redraw_later(VALID);	// causes status line redraw
4805 
4806     // set window height to desired minimal value
4807     if (curwin->w_height < p_wh && !curwin->w_p_wfh
4808 #ifdef FEAT_PROP_POPUP
4809 	    && !popup_is_popup(curwin)
4810 #endif
4811 	    )
4812 	win_setheight((int)p_wh);
4813     else if (curwin->w_height == 0)
4814 	win_setheight(1);
4815 
4816     // set window width to desired minimal value
4817     if (curwin->w_width < p_wiw && !curwin->w_p_wfw)
4818 	win_setwidth((int)p_wiw);
4819 
4820     setmouse();			// in case jumped to/from help buffer
4821 
4822     // Change directories when the 'acd' option is set.
4823     DO_AUTOCHDIR;
4824 }
4825 
4826 
4827 /*
4828  * Jump to the first open window that contains buffer "buf", if one exists.
4829  * Returns a pointer to the window found, otherwise NULL.
4830  */
4831     win_T *
4832 buf_jump_open_win(buf_T *buf)
4833 {
4834     win_T	*wp = NULL;
4835 
4836     if (curwin->w_buffer == buf)
4837 	wp = curwin;
4838     else
4839 	FOR_ALL_WINDOWS(wp)
4840 	    if (wp->w_buffer == buf)
4841 		break;
4842     if (wp != NULL)
4843 	win_enter(wp, FALSE);
4844     return wp;
4845 }
4846 
4847 /*
4848  * Jump to the first open window in any tab page that contains buffer "buf",
4849  * if one exists.
4850  * Returns a pointer to the window found, otherwise NULL.
4851  */
4852     win_T *
4853 buf_jump_open_tab(buf_T *buf)
4854 {
4855     win_T	*wp = buf_jump_open_win(buf);
4856     tabpage_T	*tp;
4857 
4858     if (wp != NULL)
4859 	return wp;
4860 
4861     FOR_ALL_TABPAGES(tp)
4862 	if (tp != curtab)
4863 	{
4864 	    FOR_ALL_WINDOWS_IN_TAB(tp, wp)
4865 		if (wp->w_buffer == buf)
4866 		    break;
4867 	    if (wp != NULL)
4868 	    {
4869 		goto_tabpage_win(tp, wp);
4870 		if (curwin != wp)
4871 		    wp = NULL;	// something went wrong
4872 		break;
4873 	    }
4874 	}
4875     return wp;
4876 }
4877 
4878 static int last_win_id = LOWEST_WIN_ID - 1;
4879 
4880 /*
4881  * Allocate a window structure and link it in the window list when "hidden" is
4882  * FALSE.
4883  */
4884     static win_T *
4885 win_alloc(win_T *after UNUSED, int hidden UNUSED)
4886 {
4887     win_T	*new_wp;
4888 
4889     /*
4890      * allocate window structure and linesizes arrays
4891      */
4892     new_wp = ALLOC_CLEAR_ONE(win_T);
4893     if (new_wp == NULL)
4894 	return NULL;
4895 
4896     if (win_alloc_lines(new_wp) == FAIL)
4897     {
4898 	vim_free(new_wp);
4899 	return NULL;
4900     }
4901 
4902     new_wp->w_id = ++last_win_id;
4903 
4904 #ifdef FEAT_EVAL
4905     // init w: variables
4906     new_wp->w_vars = dict_alloc();
4907     if (new_wp->w_vars == NULL)
4908     {
4909 	win_free_lsize(new_wp);
4910 	vim_free(new_wp);
4911 	return NULL;
4912     }
4913     init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE);
4914 #endif
4915 
4916     // Don't execute autocommands while the window is not properly
4917     // initialized yet.  gui_create_scrollbar() may trigger a FocusGained
4918     // event.
4919     block_autocmds();
4920 
4921     /*
4922      * link the window in the window list
4923      */
4924     if (!hidden)
4925 	win_append(after, new_wp);
4926     new_wp->w_wincol = 0;
4927     new_wp->w_width = Columns;
4928 
4929     // position the display and the cursor at the top of the file.
4930     new_wp->w_topline = 1;
4931 #ifdef FEAT_DIFF
4932     new_wp->w_topfill = 0;
4933 #endif
4934     new_wp->w_botline = 2;
4935     new_wp->w_cursor.lnum = 1;
4936     new_wp->w_scbind_pos = 1;
4937 
4938     // use global option value for global-local options
4939     new_wp->w_p_so = -1;
4940     new_wp->w_p_siso = -1;
4941 
4942     // We won't calculate w_fraction until resizing the window
4943     new_wp->w_fraction = 0;
4944     new_wp->w_prev_fraction_row = -1;
4945 
4946 #ifdef FEAT_GUI
4947     if (gui.in_use)
4948     {
4949 	gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_LEFT],
4950 		SBAR_LEFT, new_wp);
4951 	gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_RIGHT],
4952 		SBAR_RIGHT, new_wp);
4953     }
4954 #endif
4955 #ifdef FEAT_FOLDING
4956     foldInitWin(new_wp);
4957 #endif
4958     unblock_autocmds();
4959 #ifdef FEAT_SEARCH_EXTRA
4960     new_wp->w_match_head = NULL;
4961     new_wp->w_next_match_id = 4;
4962 #endif
4963     return new_wp;
4964 }
4965 
4966 /*
4967  * Remove window 'wp' from the window list and free the structure.
4968  */
4969     static void
4970 win_free(
4971     win_T	*wp,
4972     tabpage_T	*tp)		// tab page "win" is in, NULL for current
4973 {
4974     int		i;
4975     buf_T	*buf;
4976     wininfo_T	*wip;
4977 
4978 #ifdef FEAT_FOLDING
4979     clearFolding(wp);
4980 #endif
4981 
4982     // reduce the reference count to the argument list.
4983     alist_unlink(wp->w_alist);
4984 
4985     // Don't execute autocommands while the window is halfway being deleted.
4986     // gui_mch_destroy_scrollbar() may trigger a FocusGained event.
4987     block_autocmds();
4988 
4989 #ifdef FEAT_LUA
4990     lua_window_free(wp);
4991 #endif
4992 
4993 #ifdef FEAT_MZSCHEME
4994     mzscheme_window_free(wp);
4995 #endif
4996 
4997 #ifdef FEAT_PERL
4998     perl_win_free(wp);
4999 #endif
5000 
5001 #ifdef FEAT_PYTHON
5002     python_window_free(wp);
5003 #endif
5004 
5005 #ifdef FEAT_PYTHON3
5006     python3_window_free(wp);
5007 #endif
5008 
5009 #ifdef FEAT_TCL
5010     tcl_window_free(wp);
5011 #endif
5012 
5013 #ifdef FEAT_RUBY
5014     ruby_window_free(wp);
5015 #endif
5016 
5017     clear_winopt(&wp->w_onebuf_opt);
5018     clear_winopt(&wp->w_allbuf_opt);
5019 
5020 #ifdef FEAT_EVAL
5021     vars_clear(&wp->w_vars->dv_hashtab);	// free all w: variables
5022     hash_init(&wp->w_vars->dv_hashtab);
5023     unref_var_dict(wp->w_vars);
5024 #endif
5025 
5026     {
5027 	tabpage_T	*ttp;
5028 
5029 	if (prevwin == wp)
5030 	    prevwin = NULL;
5031 	FOR_ALL_TABPAGES(ttp)
5032 	    if (ttp->tp_prevwin == wp)
5033 		ttp->tp_prevwin = NULL;
5034     }
5035     win_free_lsize(wp);
5036 
5037     for (i = 0; i < wp->w_tagstacklen; ++i)
5038     {
5039 	vim_free(wp->w_tagstack[i].tagname);
5040 	vim_free(wp->w_tagstack[i].user_data);
5041     }
5042     vim_free(wp->w_localdir);
5043     vim_free(wp->w_prevdir);
5044 
5045     // Remove the window from the b_wininfo lists, it may happen that the
5046     // freed memory is re-used for another window.
5047     FOR_ALL_BUFFERS(buf)
5048 	FOR_ALL_BUF_WININFO(buf, wip)
5049 	    if (wip->wi_win == wp)
5050 	    {
5051 		wininfo_T	*wip2;
5052 
5053 		// If there already is an entry with "wi_win" set to NULL it
5054 		// must be removed, it would never be used.
5055 		for (wip2 = buf->b_wininfo; wip2 != NULL; wip2 = wip2->wi_next)
5056 		    if (wip2->wi_win == NULL)
5057 		    {
5058 			if (wip2->wi_next != NULL)
5059 			    wip2->wi_next->wi_prev = wip2->wi_prev;
5060 			if (wip2->wi_prev == NULL)
5061 			    buf->b_wininfo = wip2->wi_next;
5062 			else
5063 			    wip2->wi_prev->wi_next = wip2->wi_next;
5064 			free_wininfo(wip2);
5065 			break;
5066 		    }
5067 
5068 		wip->wi_win = NULL;
5069 	    }
5070 
5071 #ifdef FEAT_SEARCH_EXTRA
5072     clear_matches(wp);
5073 #endif
5074 
5075 #ifdef FEAT_JUMPLIST
5076     free_jumplist(wp);
5077 #endif
5078 
5079 #ifdef FEAT_QUICKFIX
5080     qf_free_all(wp);
5081 #endif
5082 
5083 #ifdef FEAT_GUI
5084     if (gui.in_use)
5085     {
5086 	gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]);
5087 	gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]);
5088     }
5089 #endif // FEAT_GUI
5090 
5091 #ifdef FEAT_MENU
5092     remove_winbar(wp);
5093 #endif
5094 #ifdef FEAT_PROP_POPUP
5095     free_callback(&wp->w_close_cb);
5096     free_callback(&wp->w_filter_cb);
5097     for (i = 0; i < 4; ++i)
5098 	VIM_CLEAR(wp->w_border_highlight[i]);
5099     vim_free(wp->w_scrollbar_highlight);
5100     vim_free(wp->w_thumb_highlight);
5101     vim_free(wp->w_popup_title);
5102     list_unref(wp->w_popup_mask);
5103     vim_free(wp->w_popup_mask_cells);
5104 #endif
5105 
5106 #ifdef FEAT_SYN_HL
5107     vim_free(wp->w_p_cc_cols);
5108 #endif
5109 
5110     if (win_valid_any_tab(wp))
5111 	win_remove(wp, tp);
5112     if (autocmd_busy)
5113     {
5114 	wp->w_next = au_pending_free_win;
5115 	au_pending_free_win = wp;
5116     }
5117     else
5118 	vim_free(wp);
5119 
5120     unblock_autocmds();
5121 }
5122 
5123 /*
5124  * Return TRUE if "wp" is not in the list of windows: the autocmd window or a
5125  * popup window.
5126  */
5127     static int
5128 win_unlisted(win_T *wp)
5129 {
5130     return wp == aucmd_win || WIN_IS_POPUP(wp);
5131 }
5132 
5133 #if defined(FEAT_PROP_POPUP) || defined(PROTO)
5134 /*
5135  * Free a popup window.  This does not take the window out of the window list
5136  * and assumes there is only one toplevel frame, no split.
5137  */
5138     void
5139 win_free_popup(win_T *win)
5140 {
5141     if (bt_popup(win->w_buffer))
5142 	win_close_buffer(win, DOBUF_WIPE_REUSE, FALSE);
5143     else
5144 	close_buffer(win, win->w_buffer, 0, FALSE, FALSE);
5145 # if defined(FEAT_TIMERS)
5146     if (win->w_popup_timer != NULL)
5147 	stop_timer(win->w_popup_timer);
5148 # endif
5149     vim_free(win->w_frame);
5150     win_free(win, NULL);
5151 }
5152 #endif
5153 
5154 /*
5155  * Append window "wp" in the window list after window "after".
5156  */
5157     static void
5158 win_append(win_T *after, win_T *wp)
5159 {
5160     win_T	*before;
5161 
5162     if (after == NULL)	    // after NULL is in front of the first
5163 	before = firstwin;
5164     else
5165 	before = after->w_next;
5166 
5167     wp->w_next = before;
5168     wp->w_prev = after;
5169     if (after == NULL)
5170 	firstwin = wp;
5171     else
5172 	after->w_next = wp;
5173     if (before == NULL)
5174 	lastwin = wp;
5175     else
5176 	before->w_prev = wp;
5177 }
5178 
5179 /*
5180  * Remove a window from the window list.
5181  */
5182     void
5183 win_remove(
5184     win_T	*wp,
5185     tabpage_T	*tp)		// tab page "win" is in, NULL for current
5186 {
5187     if (wp->w_prev != NULL)
5188 	wp->w_prev->w_next = wp->w_next;
5189     else if (tp == NULL)
5190 	firstwin = curtab->tp_firstwin = wp->w_next;
5191     else
5192 	tp->tp_firstwin = wp->w_next;
5193 
5194     if (wp->w_next != NULL)
5195 	wp->w_next->w_prev = wp->w_prev;
5196     else if (tp == NULL)
5197 	lastwin = curtab->tp_lastwin = wp->w_prev;
5198     else
5199 	tp->tp_lastwin = wp->w_prev;
5200 }
5201 
5202 /*
5203  * Append frame "frp" in a frame list after frame "after".
5204  */
5205     static void
5206 frame_append(frame_T *after, frame_T *frp)
5207 {
5208     frp->fr_next = after->fr_next;
5209     after->fr_next = frp;
5210     if (frp->fr_next != NULL)
5211 	frp->fr_next->fr_prev = frp;
5212     frp->fr_prev = after;
5213 }
5214 
5215 /*
5216  * Insert frame "frp" in a frame list before frame "before".
5217  */
5218     static void
5219 frame_insert(frame_T *before, frame_T *frp)
5220 {
5221     frp->fr_next = before;
5222     frp->fr_prev = before->fr_prev;
5223     before->fr_prev = frp;
5224     if (frp->fr_prev != NULL)
5225 	frp->fr_prev->fr_next = frp;
5226     else
5227 	frp->fr_parent->fr_child = frp;
5228 }
5229 
5230 /*
5231  * Remove a frame from a frame list.
5232  */
5233     static void
5234 frame_remove(frame_T *frp)
5235 {
5236     if (frp->fr_prev != NULL)
5237 	frp->fr_prev->fr_next = frp->fr_next;
5238     else
5239     {
5240 	frp->fr_parent->fr_child = frp->fr_next;
5241 	// special case: topframe->fr_child == frp
5242 	if (topframe->fr_child == frp)
5243 	    topframe->fr_child = frp->fr_next;
5244     }
5245     if (frp->fr_next != NULL)
5246 	frp->fr_next->fr_prev = frp->fr_prev;
5247 }
5248 
5249 /*
5250  * Allocate w_lines[] for window "wp".
5251  * Return FAIL for failure, OK for success.
5252  */
5253     int
5254 win_alloc_lines(win_T *wp)
5255 {
5256     wp->w_lines_valid = 0;
5257     wp->w_lines = ALLOC_CLEAR_MULT(wline_T, Rows );
5258     if (wp->w_lines == NULL)
5259 	return FAIL;
5260     return OK;
5261 }
5262 
5263 /*
5264  * free lsize arrays for a window
5265  */
5266     void
5267 win_free_lsize(win_T *wp)
5268 {
5269     // TODO: why would wp be NULL here?
5270     if (wp != NULL)
5271 	VIM_CLEAR(wp->w_lines);
5272 }
5273 
5274 /*
5275  * Called from win_new_shellsize() after Rows changed.
5276  * This only does the current tab page, others must be done when made active.
5277  */
5278     void
5279 shell_new_rows(void)
5280 {
5281     int		h = (int)ROWS_AVAIL;
5282 
5283     if (firstwin == NULL)	// not initialized yet
5284 	return;
5285     if (h < frame_minheight(topframe, NULL))
5286 	h = frame_minheight(topframe, NULL);
5287 
5288     // First try setting the heights of windows with 'winfixheight'.  If
5289     // that doesn't result in the right height, forget about that option.
5290     frame_new_height(topframe, h, FALSE, TRUE);
5291     if (!frame_check_height(topframe, h))
5292 	frame_new_height(topframe, h, FALSE, FALSE);
5293 
5294     (void)win_comp_pos();		// recompute w_winrow and w_wincol
5295     compute_cmdrow();
5296     curtab->tp_ch_used = p_ch;
5297 
5298 #if 0
5299     // Disabled: don't want making the screen smaller make a window larger.
5300     if (p_ea)
5301 	win_equal(curwin, FALSE, 'v');
5302 #endif
5303 }
5304 
5305 /*
5306  * Called from win_new_shellsize() after Columns changed.
5307  */
5308     void
5309 shell_new_columns(void)
5310 {
5311     if (firstwin == NULL)	// not initialized yet
5312 	return;
5313 
5314     // First try setting the widths of windows with 'winfixwidth'.  If that
5315     // doesn't result in the right width, forget about that option.
5316     frame_new_width(topframe, (int)Columns, FALSE, TRUE);
5317     if (!frame_check_width(topframe, Columns))
5318 	frame_new_width(topframe, (int)Columns, FALSE, FALSE);
5319 
5320     (void)win_comp_pos();		// recompute w_winrow and w_wincol
5321 #if 0
5322     // Disabled: don't want making the screen smaller make a window larger.
5323     if (p_ea)
5324 	win_equal(curwin, FALSE, 'h');
5325 #endif
5326 }
5327 
5328 #if defined(FEAT_CMDWIN) || defined(PROTO)
5329 /*
5330  * Save the size of all windows in "gap".
5331  */
5332     void
5333 win_size_save(garray_T *gap)
5334 
5335 {
5336     win_T	*wp;
5337 
5338     ga_init2(gap, (int)sizeof(int), 1);
5339     if (ga_grow(gap, win_count() * 2 + 1) == OK)
5340     {
5341 	// first entry is value of 'lines'
5342 	((int *)gap->ga_data)[gap->ga_len++] = Rows;
5343 
5344 	FOR_ALL_WINDOWS(wp)
5345 	{
5346 	    ((int *)gap->ga_data)[gap->ga_len++] =
5347 					       wp->w_width + wp->w_vsep_width;
5348 	    ((int *)gap->ga_data)[gap->ga_len++] = wp->w_height;
5349 	}
5350     }
5351 }
5352 
5353 /*
5354  * Restore window sizes, but only if the number of windows is still the same
5355  * and 'lines' didn't change.
5356  * Does not free the growarray.
5357  */
5358     void
5359 win_size_restore(garray_T *gap)
5360 {
5361     win_T	*wp;
5362     int		i, j;
5363 
5364     if (win_count() * 2 + 1 == gap->ga_len
5365 	    && ((int *)gap->ga_data)[0] == Rows)
5366     {
5367 	// The order matters, because frames contain other frames, but it's
5368 	// difficult to get right. The easy way out is to do it twice.
5369 	for (j = 0; j < 2; ++j)
5370 	{
5371 	    i = 1;
5372 	    FOR_ALL_WINDOWS(wp)
5373 	    {
5374 		frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]);
5375 		win_setheight_win(((int *)gap->ga_data)[i++], wp);
5376 	    }
5377 	}
5378 	// recompute the window positions
5379 	(void)win_comp_pos();
5380     }
5381 }
5382 #endif // FEAT_CMDWIN
5383 
5384 /*
5385  * Update the position for all windows, using the width and height of the
5386  * frames.
5387  * Returns the row just after the last window.
5388  */
5389     int
5390 win_comp_pos(void)
5391 {
5392     int		row = tabline_height();
5393     int		col = 0;
5394 
5395     frame_comp_pos(topframe, &row, &col);
5396     return row;
5397 }
5398 
5399 /*
5400  * Update the position of the windows in frame "topfrp", using the width and
5401  * height of the frames.
5402  * "*row" and "*col" are the top-left position of the frame.  They are updated
5403  * to the bottom-right position plus one.
5404  */
5405     static void
5406 frame_comp_pos(frame_T *topfrp, int *row, int *col)
5407 {
5408     win_T	*wp;
5409     frame_T	*frp;
5410     int		startcol;
5411     int		startrow;
5412     int		h;
5413 
5414     wp = topfrp->fr_win;
5415     if (wp != NULL)
5416     {
5417 	if (wp->w_winrow != *row || wp->w_wincol != *col)
5418 	{
5419 	    // position changed, redraw
5420 	    wp->w_winrow = *row;
5421 	    wp->w_wincol = *col;
5422 	    redraw_win_later(wp, NOT_VALID);
5423 	    wp->w_redr_status = TRUE;
5424 	}
5425 	// WinBar will not show if the window height is zero
5426 	h = VISIBLE_HEIGHT(wp) + wp->w_status_height;
5427 	*row += h > topfrp->fr_height ? topfrp->fr_height : h;
5428 	*col += wp->w_width + wp->w_vsep_width;
5429     }
5430     else
5431     {
5432 	startrow = *row;
5433 	startcol = *col;
5434 	FOR_ALL_FRAMES(frp, topfrp->fr_child)
5435 	{
5436 	    if (topfrp->fr_layout == FR_ROW)
5437 		*row = startrow;	// all frames are at the same row
5438 	    else
5439 		*col = startcol;	// all frames are at the same col
5440 	    frame_comp_pos(frp, row, col);
5441 	}
5442     }
5443 }
5444 
5445 /*
5446  * Set current window height and take care of repositioning other windows to
5447  * fit around it.
5448  */
5449     void
5450 win_setheight(int height)
5451 {
5452     win_setheight_win(height, curwin);
5453 }
5454 
5455 /*
5456  * Set the window height of window "win" and take care of repositioning other
5457  * windows to fit around it.
5458  */
5459     void
5460 win_setheight_win(int height, win_T *win)
5461 {
5462     int		row;
5463 
5464     if (win == curwin)
5465     {
5466 	// Always keep current window at least one line high, even when
5467 	// 'winminheight' is zero.
5468 	if (height < p_wmh)
5469 	    height = p_wmh;
5470 	if (height == 0)
5471 	    height = 1;
5472 	height += WINBAR_HEIGHT(curwin);
5473     }
5474 
5475     frame_setheight(win->w_frame, height + win->w_status_height);
5476 
5477     // recompute the window positions
5478     row = win_comp_pos();
5479 
5480     /*
5481      * If there is extra space created between the last window and the command
5482      * line, clear it.
5483      */
5484     if (full_screen && msg_scrolled == 0 && row < cmdline_row)
5485 	screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
5486     cmdline_row = row;
5487     msg_row = row;
5488     msg_col = 0;
5489 
5490     redraw_all_later(NOT_VALID);
5491 }
5492 
5493 /*
5494  * Set the height of a frame to "height" and take care that all frames and
5495  * windows inside it are resized.  Also resize frames on the left and right if
5496  * the are in the same FR_ROW frame.
5497  *
5498  * Strategy:
5499  * If the frame is part of a FR_COL frame, try fitting the frame in that
5500  * frame.  If that doesn't work (the FR_COL frame is too small), recursively
5501  * go to containing frames to resize them and make room.
5502  * If the frame is part of a FR_ROW frame, all frames must be resized as well.
5503  * Check for the minimal height of the FR_ROW frame.
5504  * At the top level we can also use change the command line height.
5505  */
5506     static void
5507 frame_setheight(frame_T *curfrp, int height)
5508 {
5509     int		room;		// total number of lines available
5510     int		take;		// number of lines taken from other windows
5511     int		room_cmdline;	// lines available from cmdline
5512     int		run;
5513     frame_T	*frp;
5514     int		h;
5515     int		room_reserved;
5516 
5517     // If the height already is the desired value, nothing to do.
5518     if (curfrp->fr_height == height)
5519 	return;
5520 
5521     if (curfrp->fr_parent == NULL)
5522     {
5523 	// topframe: can only change the command line
5524 	if (height > ROWS_AVAIL)
5525 	    height = ROWS_AVAIL;
5526 	if (height > 0)
5527 	    frame_new_height(curfrp, height, FALSE, FALSE);
5528     }
5529     else if (curfrp->fr_parent->fr_layout == FR_ROW)
5530     {
5531 	// Row of frames: Also need to resize frames left and right of this
5532 	// one.  First check for the minimal height of these.
5533 	h = frame_minheight(curfrp->fr_parent, NULL);
5534 	if (height < h)
5535 	    height = h;
5536 	frame_setheight(curfrp->fr_parent, height);
5537     }
5538     else
5539     {
5540 	/*
5541 	 * Column of frames: try to change only frames in this column.
5542 	 */
5543 	/*
5544 	 * Do this twice:
5545 	 * 1: compute room available, if it's not enough try resizing the
5546 	 *    containing frame.
5547 	 * 2: compute the room available and adjust the height to it.
5548 	 * Try not to reduce the height of a window with 'winfixheight' set.
5549 	 */
5550 	for (run = 1; run <= 2; ++run)
5551 	{
5552 	    room = 0;
5553 	    room_reserved = 0;
5554 	    FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child)
5555 	    {
5556 		if (frp != curfrp
5557 			&& frp->fr_win != NULL
5558 			&& frp->fr_win->w_p_wfh)
5559 		    room_reserved += frp->fr_height;
5560 		room += frp->fr_height;
5561 		if (frp != curfrp)
5562 		    room -= frame_minheight(frp, NULL);
5563 	    }
5564 	    if (curfrp->fr_width != Columns)
5565 		room_cmdline = 0;
5566 	    else
5567 	    {
5568 		room_cmdline = Rows - p_ch - (lastwin->w_winrow
5569 						+ VISIBLE_HEIGHT(lastwin)
5570 						+ lastwin->w_status_height);
5571 		if (room_cmdline < 0)
5572 		    room_cmdline = 0;
5573 	    }
5574 
5575 	    if (height <= room + room_cmdline)
5576 		break;
5577 	    if (run == 2 || curfrp->fr_width == Columns)
5578 	    {
5579 		if (height > room + room_cmdline)
5580 		    height = room + room_cmdline;
5581 		break;
5582 	    }
5583 	    frame_setheight(curfrp->fr_parent, height
5584 		+ frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1);
5585 	}
5586 
5587 	/*
5588 	 * Compute the number of lines we will take from others frames (can be
5589 	 * negative!).
5590 	 */
5591 	take = height - curfrp->fr_height;
5592 
5593 	// If there is not enough room, also reduce the height of a window
5594 	// with 'winfixheight' set.
5595 	if (height > room + room_cmdline - room_reserved)
5596 	    room_reserved = room + room_cmdline - height;
5597 	// If there is only a 'winfixheight' window and making the
5598 	// window smaller, need to make the other window taller.
5599 	if (take < 0 && room - curfrp->fr_height < room_reserved)
5600 	    room_reserved = 0;
5601 
5602 	if (take > 0 && room_cmdline > 0)
5603 	{
5604 	    // use lines from cmdline first
5605 	    if (take < room_cmdline)
5606 		room_cmdline = take;
5607 	    take -= room_cmdline;
5608 	    topframe->fr_height += room_cmdline;
5609 	}
5610 
5611 	/*
5612 	 * set the current frame to the new height
5613 	 */
5614 	frame_new_height(curfrp, height, FALSE, FALSE);
5615 
5616 	/*
5617 	 * First take lines from the frames after the current frame.  If
5618 	 * that is not enough, takes lines from frames above the current
5619 	 * frame.
5620 	 */
5621 	for (run = 0; run < 2; ++run)
5622 	{
5623 	    if (run == 0)
5624 		frp = curfrp->fr_next;	// 1st run: start with next window
5625 	    else
5626 		frp = curfrp->fr_prev;	// 2nd run: start with prev window
5627 	    while (frp != NULL && take != 0)
5628 	    {
5629 		h = frame_minheight(frp, NULL);
5630 		if (room_reserved > 0
5631 			&& frp->fr_win != NULL
5632 			&& frp->fr_win->w_p_wfh)
5633 		{
5634 		    if (room_reserved >= frp->fr_height)
5635 			room_reserved -= frp->fr_height;
5636 		    else
5637 		    {
5638 			if (frp->fr_height - room_reserved > take)
5639 			    room_reserved = frp->fr_height - take;
5640 			take -= frp->fr_height - room_reserved;
5641 			frame_new_height(frp, room_reserved, FALSE, FALSE);
5642 			room_reserved = 0;
5643 		    }
5644 		}
5645 		else
5646 		{
5647 		    if (frp->fr_height - take < h)
5648 		    {
5649 			take -= frp->fr_height - h;
5650 			frame_new_height(frp, h, FALSE, FALSE);
5651 		    }
5652 		    else
5653 		    {
5654 			frame_new_height(frp, frp->fr_height - take,
5655 								FALSE, FALSE);
5656 			take = 0;
5657 		    }
5658 		}
5659 		if (run == 0)
5660 		    frp = frp->fr_next;
5661 		else
5662 		    frp = frp->fr_prev;
5663 	    }
5664 	}
5665     }
5666 }
5667 
5668 /*
5669  * Set current window width and take care of repositioning other windows to
5670  * fit around it.
5671  */
5672     void
5673 win_setwidth(int width)
5674 {
5675     win_setwidth_win(width, curwin);
5676 }
5677 
5678     void
5679 win_setwidth_win(int width, win_T *wp)
5680 {
5681     // Always keep current window at least one column wide, even when
5682     // 'winminwidth' is zero.
5683     if (wp == curwin)
5684     {
5685 	if (width < p_wmw)
5686 	    width = p_wmw;
5687 	if (width == 0)
5688 	    width = 1;
5689     }
5690     else if (width < 0)
5691 	width = 0;
5692 
5693     frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
5694 
5695     // recompute the window positions
5696     (void)win_comp_pos();
5697 
5698     redraw_all_later(NOT_VALID);
5699 }
5700 
5701 /*
5702  * Set the width of a frame to "width" and take care that all frames and
5703  * windows inside it are resized.  Also resize frames above and below if the
5704  * are in the same FR_ROW frame.
5705  *
5706  * Strategy is similar to frame_setheight().
5707  */
5708     static void
5709 frame_setwidth(frame_T *curfrp, int width)
5710 {
5711     int		room;		// total number of lines available
5712     int		take;		// number of lines taken from other windows
5713     int		run;
5714     frame_T	*frp;
5715     int		w;
5716     int		room_reserved;
5717 
5718     // If the width already is the desired value, nothing to do.
5719     if (curfrp->fr_width == width)
5720 	return;
5721 
5722     if (curfrp->fr_parent == NULL)
5723 	// topframe: can't change width
5724 	return;
5725 
5726     if (curfrp->fr_parent->fr_layout == FR_COL)
5727     {
5728 	// Column of frames: Also need to resize frames above and below of
5729 	// this one.  First check for the minimal width of these.
5730 	w = frame_minwidth(curfrp->fr_parent, NULL);
5731 	if (width < w)
5732 	    width = w;
5733 	frame_setwidth(curfrp->fr_parent, width);
5734     }
5735     else
5736     {
5737 	/*
5738 	 * Row of frames: try to change only frames in this row.
5739 	 *
5740 	 * Do this twice:
5741 	 * 1: compute room available, if it's not enough try resizing the
5742 	 *    containing frame.
5743 	 * 2: compute the room available and adjust the width to it.
5744 	 */
5745 	for (run = 1; run <= 2; ++run)
5746 	{
5747 	    room = 0;
5748 	    room_reserved = 0;
5749 	    FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child)
5750 	    {
5751 		if (frp != curfrp
5752 			&& frp->fr_win != NULL
5753 			&& frp->fr_win->w_p_wfw)
5754 		    room_reserved += frp->fr_width;
5755 		room += frp->fr_width;
5756 		if (frp != curfrp)
5757 		    room -= frame_minwidth(frp, NULL);
5758 	    }
5759 
5760 	    if (width <= room)
5761 		break;
5762 	    if (run == 2 || curfrp->fr_height >= ROWS_AVAIL)
5763 	    {
5764 		if (width > room)
5765 		    width = room;
5766 		break;
5767 	    }
5768 	    frame_setwidth(curfrp->fr_parent, width
5769 		 + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1);
5770 	}
5771 
5772 	/*
5773 	 * Compute the number of lines we will take from others frames (can be
5774 	 * negative!).
5775 	 */
5776 	take = width - curfrp->fr_width;
5777 
5778 	// If there is not enough room, also reduce the width of a window
5779 	// with 'winfixwidth' set.
5780 	if (width > room - room_reserved)
5781 	    room_reserved = room - width;
5782 	// If there is only a 'winfixwidth' window and making the
5783 	// window smaller, need to make the other window narrower.
5784 	if (take < 0 && room - curfrp->fr_width < room_reserved)
5785 	    room_reserved = 0;
5786 
5787 	/*
5788 	 * set the current frame to the new width
5789 	 */
5790 	frame_new_width(curfrp, width, FALSE, FALSE);
5791 
5792 	/*
5793 	 * First take lines from the frames right of the current frame.  If
5794 	 * that is not enough, takes lines from frames left of the current
5795 	 * frame.
5796 	 */
5797 	for (run = 0; run < 2; ++run)
5798 	{
5799 	    if (run == 0)
5800 		frp = curfrp->fr_next;	// 1st run: start with next window
5801 	    else
5802 		frp = curfrp->fr_prev;	// 2nd run: start with prev window
5803 	    while (frp != NULL && take != 0)
5804 	    {
5805 		w = frame_minwidth(frp, NULL);
5806 		if (room_reserved > 0
5807 			&& frp->fr_win != NULL
5808 			&& frp->fr_win->w_p_wfw)
5809 		{
5810 		    if (room_reserved >= frp->fr_width)
5811 			room_reserved -= frp->fr_width;
5812 		    else
5813 		    {
5814 			if (frp->fr_width - room_reserved > take)
5815 			    room_reserved = frp->fr_width - take;
5816 			take -= frp->fr_width - room_reserved;
5817 			frame_new_width(frp, room_reserved, FALSE, FALSE);
5818 			room_reserved = 0;
5819 		    }
5820 		}
5821 		else
5822 		{
5823 		    if (frp->fr_width - take < w)
5824 		    {
5825 			take -= frp->fr_width - w;
5826 			frame_new_width(frp, w, FALSE, FALSE);
5827 		    }
5828 		    else
5829 		    {
5830 			frame_new_width(frp, frp->fr_width - take,
5831 								FALSE, FALSE);
5832 			take = 0;
5833 		    }
5834 		}
5835 		if (run == 0)
5836 		    frp = frp->fr_next;
5837 		else
5838 		    frp = frp->fr_prev;
5839 	    }
5840 	}
5841     }
5842 }
5843 
5844 /*
5845  * Check 'winminheight' for a valid value and reduce it if needed.
5846  */
5847     void
5848 win_setminheight(void)
5849 {
5850     int		room;
5851     int		needed;
5852     int		first = TRUE;
5853 
5854     // loop until there is a 'winminheight' that is possible
5855     while (p_wmh > 0)
5856     {
5857 	room = Rows - p_ch;
5858 	needed = frame_minheight(topframe, NULL);
5859 	if (room >= needed)
5860 	    break;
5861 	--p_wmh;
5862 	if (first)
5863 	{
5864 	    emsg(_(e_noroom));
5865 	    first = FALSE;
5866 	}
5867     }
5868 }
5869 
5870 /*
5871  * Check 'winminwidth' for a valid value and reduce it if needed.
5872  */
5873     void
5874 win_setminwidth(void)
5875 {
5876     int		room;
5877     int		needed;
5878     int		first = TRUE;
5879 
5880     // loop until there is a 'winminheight' that is possible
5881     while (p_wmw > 0)
5882     {
5883 	room = Columns;
5884 	needed = frame_minwidth(topframe, NULL);
5885 	if (room >= needed)
5886 	    break;
5887 	--p_wmw;
5888 	if (first)
5889 	{
5890 	    emsg(_(e_noroom));
5891 	    first = FALSE;
5892 	}
5893     }
5894 }
5895 
5896 /*
5897  * Status line of dragwin is dragged "offset" lines down (negative is up).
5898  */
5899     void
5900 win_drag_status_line(win_T *dragwin, int offset)
5901 {
5902     frame_T	*curfr;
5903     frame_T	*fr;
5904     int		room;
5905     int		row;
5906     int		up;	// if TRUE, drag status line up, otherwise down
5907     int		n;
5908 
5909     fr = dragwin->w_frame;
5910     curfr = fr;
5911     if (fr != topframe)		// more than one window
5912     {
5913 	fr = fr->fr_parent;
5914 	// When the parent frame is not a column of frames, its parent should
5915 	// be.
5916 	if (fr->fr_layout != FR_COL)
5917 	{
5918 	    curfr = fr;
5919 	    if (fr != topframe)	// only a row of windows, may drag statusline
5920 		fr = fr->fr_parent;
5921 	}
5922     }
5923 
5924     // If this is the last frame in a column, may want to resize the parent
5925     // frame instead (go two up to skip a row of frames).
5926     while (curfr != topframe && curfr->fr_next == NULL)
5927     {
5928 	if (fr != topframe)
5929 	    fr = fr->fr_parent;
5930 	curfr = fr;
5931 	if (fr != topframe)
5932 	    fr = fr->fr_parent;
5933     }
5934 
5935     if (offset < 0) // drag up
5936     {
5937 	up = TRUE;
5938 	offset = -offset;
5939 	// sum up the room of the current frame and above it
5940 	if (fr == curfr)
5941 	{
5942 	    // only one window
5943 	    room = fr->fr_height - frame_minheight(fr, NULL);
5944 	}
5945 	else
5946 	{
5947 	    room = 0;
5948 	    for (fr = fr->fr_child; ; fr = fr->fr_next)
5949 	    {
5950 		room += fr->fr_height - frame_minheight(fr, NULL);
5951 		if (fr == curfr)
5952 		    break;
5953 	    }
5954 	}
5955 	fr = curfr->fr_next;		// put fr at frame that grows
5956     }
5957     else    // drag down
5958     {
5959 	up = FALSE;
5960 	/*
5961 	 * Only dragging the last status line can reduce p_ch.
5962 	 */
5963 	room = Rows - cmdline_row;
5964 	if (curfr->fr_next == NULL)
5965 	    room -= 1;
5966 	else
5967 	    room -= p_ch;
5968 	if (room < 0)
5969 	    room = 0;
5970 	// sum up the room of frames below of the current one
5971 	FOR_ALL_FRAMES(fr, curfr->fr_next)
5972 	    room += fr->fr_height - frame_minheight(fr, NULL);
5973 	fr = curfr;			// put fr at window that grows
5974     }
5975 
5976     if (room < offset)		// Not enough room
5977 	offset = room;		// Move as far as we can
5978     if (offset <= 0)
5979 	return;
5980 
5981     /*
5982      * Grow frame fr by "offset" lines.
5983      * Doesn't happen when dragging the last status line up.
5984      */
5985     if (fr != NULL)
5986 	frame_new_height(fr, fr->fr_height + offset, up, FALSE);
5987 
5988     if (up)
5989 	fr = curfr;		// current frame gets smaller
5990     else
5991 	fr = curfr->fr_next;	// next frame gets smaller
5992 
5993     /*
5994      * Now make the other frames smaller.
5995      */
5996     while (fr != NULL && offset > 0)
5997     {
5998 	n = frame_minheight(fr, NULL);
5999 	if (fr->fr_height - offset <= n)
6000 	{
6001 	    offset -= fr->fr_height - n;
6002 	    frame_new_height(fr, n, !up, FALSE);
6003 	}
6004 	else
6005 	{
6006 	    frame_new_height(fr, fr->fr_height - offset, !up, FALSE);
6007 	    break;
6008 	}
6009 	if (up)
6010 	    fr = fr->fr_prev;
6011 	else
6012 	    fr = fr->fr_next;
6013     }
6014     row = win_comp_pos();
6015     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
6016     cmdline_row = row;
6017     p_ch = Rows - cmdline_row;
6018     if (p_ch < 1)
6019 	p_ch = 1;
6020     curtab->tp_ch_used = p_ch;
6021     redraw_all_later(SOME_VALID);
6022     showmode();
6023 }
6024 
6025 /*
6026  * Separator line of dragwin is dragged "offset" lines right (negative is left).
6027  */
6028     void
6029 win_drag_vsep_line(win_T *dragwin, int offset)
6030 {
6031     frame_T	*curfr;
6032     frame_T	*fr;
6033     int		room;
6034     int		left;	// if TRUE, drag separator line left, otherwise right
6035     int		n;
6036 
6037     fr = dragwin->w_frame;
6038     if (fr == topframe)		// only one window (cannot happen?)
6039 	return;
6040     curfr = fr;
6041     fr = fr->fr_parent;
6042     // When the parent frame is not a row of frames, its parent should be.
6043     if (fr->fr_layout != FR_ROW)
6044     {
6045 	if (fr == topframe)	// only a column of windows (cannot happen?)
6046 	    return;
6047 	curfr = fr;
6048 	fr = fr->fr_parent;
6049     }
6050 
6051     // If this is the last frame in a row, may want to resize a parent
6052     // frame instead.
6053     while (curfr->fr_next == NULL)
6054     {
6055 	if (fr == topframe)
6056 	    break;
6057 	curfr = fr;
6058 	fr = fr->fr_parent;
6059 	if (fr != topframe)
6060 	{
6061 	    curfr = fr;
6062 	    fr = fr->fr_parent;
6063 	}
6064     }
6065 
6066     if (offset < 0) // drag left
6067     {
6068 	left = TRUE;
6069 	offset = -offset;
6070 	// sum up the room of the current frame and left of it
6071 	room = 0;
6072 	for (fr = fr->fr_child; ; fr = fr->fr_next)
6073 	{
6074 	    room += fr->fr_width - frame_minwidth(fr, NULL);
6075 	    if (fr == curfr)
6076 		break;
6077 	}
6078 	fr = curfr->fr_next;		// put fr at frame that grows
6079     }
6080     else    // drag right
6081     {
6082 	left = FALSE;
6083 	// sum up the room of frames right of the current one
6084 	room = 0;
6085 	FOR_ALL_FRAMES(fr, curfr->fr_next)
6086 	    room += fr->fr_width - frame_minwidth(fr, NULL);
6087 	fr = curfr;			// put fr at window that grows
6088     }
6089 
6090     if (room < offset)		// Not enough room
6091 	offset = room;		// Move as far as we can
6092     if (offset <= 0)		// No room at all, quit.
6093 	return;
6094     if (fr == NULL)
6095 	return;			// Safety check, should not happen.
6096 
6097     // grow frame fr by offset lines
6098     frame_new_width(fr, fr->fr_width + offset, left, FALSE);
6099 
6100     // shrink other frames: current and at the left or at the right
6101     if (left)
6102 	fr = curfr;		// current frame gets smaller
6103     else
6104 	fr = curfr->fr_next;	// next frame gets smaller
6105 
6106     while (fr != NULL && offset > 0)
6107     {
6108 	n = frame_minwidth(fr, NULL);
6109 	if (fr->fr_width - offset <= n)
6110 	{
6111 	    offset -= fr->fr_width - n;
6112 	    frame_new_width(fr, n, !left, FALSE);
6113 	}
6114 	else
6115 	{
6116 	    frame_new_width(fr, fr->fr_width - offset, !left, FALSE);
6117 	    break;
6118 	}
6119 	if (left)
6120 	    fr = fr->fr_prev;
6121 	else
6122 	    fr = fr->fr_next;
6123     }
6124     (void)win_comp_pos();
6125     redraw_all_later(NOT_VALID);
6126 }
6127 
6128 #define FRACTION_MULT	16384L
6129 
6130 /*
6131  * Set wp->w_fraction for the current w_wrow and w_height.
6132  * Has no effect when the window is less than two lines.
6133  */
6134     void
6135 set_fraction(win_T *wp)
6136 {
6137     if (wp->w_height > 1)
6138 	// When cursor is in the first line the percentage is computed as if
6139 	// it's halfway that line.  Thus with two lines it is 25%, with three
6140 	// lines 17%, etc.  Similarly for the last line: 75%, 83%, etc.
6141 	wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT
6142 				     + FRACTION_MULT / 2) / (long)wp->w_height;
6143 }
6144 
6145 /*
6146  * Set the height of a window.
6147  * "height" excludes any window toolbar.
6148  * This takes care of the things inside the window, not what happens to the
6149  * window position, the frame or to other windows.
6150  */
6151     void
6152 win_new_height(win_T *wp, int height)
6153 {
6154     int		prev_height = wp->w_height;
6155 
6156     // Don't want a negative height.  Happens when splitting a tiny window.
6157     // Will equalize heights soon to fix it.
6158     if (height < 0)
6159 	height = 0;
6160     if (wp->w_height == height)
6161 	return;	    // nothing to do
6162 
6163     if (wp->w_height > 0)
6164     {
6165 	if (wp == curwin)
6166 	    // w_wrow needs to be valid. When setting 'laststatus' this may
6167 	    // call win_new_height() recursively.
6168 	    validate_cursor();
6169 	if (wp->w_height != prev_height)
6170 	    return;  // Recursive call already changed the size, bail out here
6171 		     //	to avoid the following to mess things up.
6172 	if (wp->w_wrow != wp->w_prev_fraction_row)
6173 	    set_fraction(wp);
6174     }
6175 
6176     wp->w_height = height;
6177     wp->w_skipcol = 0;
6178 
6179     // There is no point in adjusting the scroll position when exiting.  Some
6180     // values might be invalid.
6181     if (!exiting)
6182 	scroll_to_fraction(wp, prev_height);
6183 }
6184 
6185     void
6186 scroll_to_fraction(win_T *wp, int prev_height)
6187 {
6188     linenr_T	lnum;
6189     int		sline, line_size;
6190     int		height = wp->w_height;
6191 
6192     // Don't change w_topline in any of these cases:
6193     // - window height is 0
6194     // - 'scrollbind' is set and this isn't the current window
6195     // - window height is sufficient to display the whole buffer and first line
6196     //   is visible.
6197     if (height > 0
6198         && (!wp->w_p_scb || wp == curwin)
6199         && (height < wp->w_buffer->b_ml.ml_line_count || wp->w_topline > 1))
6200     {
6201 	/*
6202 	 * Find a value for w_topline that shows the cursor at the same
6203 	 * relative position in the window as before (more or less).
6204 	 */
6205 	lnum = wp->w_cursor.lnum;
6206 	if (lnum < 1)		// can happen when starting up
6207 	    lnum = 1;
6208 	wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L)
6209 							       / FRACTION_MULT;
6210 	line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
6211 	sline = wp->w_wrow - line_size;
6212 
6213 	if (sline >= 0)
6214 	{
6215 	    // Make sure the whole cursor line is visible, if possible.
6216 	    int rows = plines_win(wp, lnum, FALSE);
6217 
6218 	    if (sline > wp->w_height - rows)
6219 	    {
6220 		sline = wp->w_height - rows;
6221 		wp->w_wrow -= rows - line_size;
6222 	    }
6223 	}
6224 
6225 	if (sline < 0)
6226 	{
6227 	    /*
6228 	     * Cursor line would go off top of screen if w_wrow was this high.
6229 	     * Make cursor line the first line in the window.  If not enough
6230 	     * room use w_skipcol;
6231 	     */
6232 	    wp->w_wrow = line_size;
6233 	    if (wp->w_wrow >= wp->w_height
6234 				       && (wp->w_width - win_col_off(wp)) > 0)
6235 	    {
6236 		wp->w_skipcol += wp->w_width - win_col_off(wp);
6237 		--wp->w_wrow;
6238 		while (wp->w_wrow >= wp->w_height)
6239 		{
6240 		    wp->w_skipcol += wp->w_width - win_col_off(wp)
6241 							   + win_col_off2(wp);
6242 		    --wp->w_wrow;
6243 		}
6244 	    }
6245 	}
6246 	else if (sline > 0)
6247 	{
6248 	    while (sline > 0 && lnum > 1)
6249 	    {
6250 #ifdef FEAT_FOLDING
6251 		hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
6252 		if (lnum == 1)
6253 		{
6254 		    // first line in buffer is folded
6255 		    line_size = 1;
6256 		    --sline;
6257 		    break;
6258 		}
6259 #endif
6260 		--lnum;
6261 #ifdef FEAT_DIFF
6262 		if (lnum == wp->w_topline)
6263 		    line_size = plines_win_nofill(wp, lnum, TRUE)
6264 							      + wp->w_topfill;
6265 		else
6266 #endif
6267 		    line_size = plines_win(wp, lnum, TRUE);
6268 		sline -= line_size;
6269 	    }
6270 
6271 	    if (sline < 0)
6272 	    {
6273 		/*
6274 		 * Line we want at top would go off top of screen.  Use next
6275 		 * line instead.
6276 		 */
6277 #ifdef FEAT_FOLDING
6278 		hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL);
6279 #endif
6280 		lnum++;
6281 		wp->w_wrow -= line_size + sline;
6282 	    }
6283 	    else if (sline > 0)
6284 	    {
6285 		// First line of file reached, use that as topline.
6286 		lnum = 1;
6287 		wp->w_wrow -= sline;
6288 	    }
6289 	}
6290 	set_topline(wp, lnum);
6291     }
6292 
6293     if (wp == curwin)
6294     {
6295 	if (get_scrolloff_value())
6296 	    update_topline();
6297 	curs_columns(FALSE);	// validate w_wrow
6298     }
6299     if (prev_height > 0)
6300 	wp->w_prev_fraction_row = wp->w_wrow;
6301 
6302     win_comp_scroll(wp);
6303     redraw_win_later(wp, SOME_VALID);
6304     wp->w_redr_status = TRUE;
6305     invalidate_botline_win(wp);
6306 }
6307 
6308 /*
6309  * Set the width of a window.
6310  */
6311     void
6312 win_new_width(win_T *wp, int width)
6313 {
6314     wp->w_width = width;
6315     wp->w_lines_valid = 0;
6316     changed_line_abv_curs_win(wp);
6317     invalidate_botline_win(wp);
6318     if (wp == curwin)
6319     {
6320 	update_topline();
6321 	curs_columns(TRUE);	// validate w_wrow
6322     }
6323     redraw_win_later(wp, NOT_VALID);
6324     wp->w_redr_status = TRUE;
6325 }
6326 
6327     void
6328 win_comp_scroll(win_T *wp)
6329 {
6330 #if defined(FEAT_EVAL)
6331     int old_w_p_scr = wp->w_p_scr;
6332 #endif
6333 
6334     wp->w_p_scr = ((unsigned)wp->w_height >> 1);
6335     if (wp->w_p_scr == 0)
6336 	wp->w_p_scr = 1;
6337 #if defined(FEAT_EVAL)
6338     if (wp->w_p_scr != old_w_p_scr)
6339     {
6340 	// Used by "verbose set scroll".
6341 	wp->w_p_script_ctx[WV_SCROLL].sc_sid = SID_WINLAYOUT;
6342 	wp->w_p_script_ctx[WV_SCROLL].sc_lnum = 0;
6343     }
6344 #endif
6345 }
6346 
6347 /*
6348  * command_height: called whenever p_ch has been changed
6349  */
6350     void
6351 command_height(void)
6352 {
6353     int		h;
6354     frame_T	*frp;
6355     int		old_p_ch = curtab->tp_ch_used;
6356 
6357     // Use the value of p_ch that we remembered.  This is needed for when the
6358     // GUI starts up, we can't be sure in what order things happen.  And when
6359     // p_ch was changed in another tab page.
6360     curtab->tp_ch_used = p_ch;
6361 
6362     // Find bottom frame with width of screen.
6363     frp = lastwin->w_frame;
6364     while (frp->fr_width != Columns && frp->fr_parent != NULL)
6365 	frp = frp->fr_parent;
6366 
6367     // Avoid changing the height of a window with 'winfixheight' set.
6368     while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF
6369 						      && frp->fr_win->w_p_wfh)
6370 	frp = frp->fr_prev;
6371 
6372     if (starting != NO_SCREEN)
6373     {
6374 	cmdline_row = Rows - p_ch;
6375 
6376 	if (p_ch > old_p_ch)		    // p_ch got bigger
6377 	{
6378 	    while (p_ch > old_p_ch)
6379 	    {
6380 		if (frp == NULL)
6381 		{
6382 		    emsg(_(e_noroom));
6383 		    p_ch = old_p_ch;
6384 		    curtab->tp_ch_used = p_ch;
6385 		    cmdline_row = Rows - p_ch;
6386 		    break;
6387 		}
6388 		h = frp->fr_height - frame_minheight(frp, NULL);
6389 		if (h > p_ch - old_p_ch)
6390 		    h = p_ch - old_p_ch;
6391 		old_p_ch += h;
6392 		frame_add_height(frp, -h);
6393 		frp = frp->fr_prev;
6394 	    }
6395 
6396 	    // Recompute window positions.
6397 	    (void)win_comp_pos();
6398 
6399 	    // clear the lines added to cmdline
6400 	    if (full_screen)
6401 		screen_fill((int)(cmdline_row), (int)Rows, 0,
6402 						   (int)Columns, ' ', ' ', 0);
6403 	    msg_row = cmdline_row;
6404 	    redraw_cmdline = TRUE;
6405 	    return;
6406 	}
6407 
6408 	if (msg_row < cmdline_row)
6409 	    msg_row = cmdline_row;
6410 	redraw_cmdline = TRUE;
6411     }
6412     frame_add_height(frp, (int)(old_p_ch - p_ch));
6413 
6414     // Recompute window positions.
6415     if (frp != lastwin->w_frame)
6416 	(void)win_comp_pos();
6417 }
6418 
6419 /*
6420  * Resize frame "frp" to be "n" lines higher (negative for less high).
6421  * Also resize the frames it is contained in.
6422  */
6423     static void
6424 frame_add_height(frame_T *frp, int n)
6425 {
6426     frame_new_height(frp, frp->fr_height + n, FALSE, FALSE);
6427     for (;;)
6428     {
6429 	frp = frp->fr_parent;
6430 	if (frp == NULL)
6431 	    break;
6432 	frp->fr_height += n;
6433     }
6434 }
6435 
6436 /*
6437  * Add or remove a status line for the bottom window(s), according to the
6438  * value of 'laststatus'.
6439  */
6440     void
6441 last_status(
6442     int		morewin)	// pretend there are two or more windows
6443 {
6444     // Don't make a difference between horizontal or vertical split.
6445     last_status_rec(topframe, (p_ls == 2
6446 			  || (p_ls == 1 && (morewin || !ONE_WINDOW))));
6447 }
6448 
6449     static void
6450 last_status_rec(frame_T *fr, int statusline)
6451 {
6452     frame_T	*fp;
6453     win_T	*wp;
6454 
6455     if (fr->fr_layout == FR_LEAF)
6456     {
6457 	wp = fr->fr_win;
6458 	if (wp->w_status_height != 0 && !statusline)
6459 	{
6460 	    // remove status line
6461 	    win_new_height(wp, wp->w_height + 1);
6462 	    wp->w_status_height = 0;
6463 	    comp_col();
6464 	}
6465 	else if (wp->w_status_height == 0 && statusline)
6466 	{
6467 	    // Find a frame to take a line from.
6468 	    fp = fr;
6469 	    while (fp->fr_height <= frame_minheight(fp, NULL))
6470 	    {
6471 		if (fp == topframe)
6472 		{
6473 		    emsg(_(e_noroom));
6474 		    return;
6475 		}
6476 		// In a column of frames: go to frame above.  If already at
6477 		// the top or in a row of frames: go to parent.
6478 		if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL)
6479 		    fp = fp->fr_prev;
6480 		else
6481 		    fp = fp->fr_parent;
6482 	    }
6483 	    wp->w_status_height = 1;
6484 	    if (fp != fr)
6485 	    {
6486 		frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE);
6487 		frame_fix_height(wp);
6488 		(void)win_comp_pos();
6489 	    }
6490 	    else
6491 		win_new_height(wp, wp->w_height - 1);
6492 	    comp_col();
6493 	    redraw_all_later(SOME_VALID);
6494 	}
6495     }
6496     else if (fr->fr_layout == FR_ROW)
6497     {
6498 	// vertically split windows, set status line for each one
6499 	FOR_ALL_FRAMES(fp, fr->fr_child)
6500 	    last_status_rec(fp, statusline);
6501     }
6502     else
6503     {
6504 	// horizontally split window, set status line for last one
6505 	for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
6506 	    ;
6507 	last_status_rec(fp, statusline);
6508     }
6509 }
6510 
6511 /*
6512  * Return the number of lines used by the tab page line.
6513  */
6514     int
6515 tabline_height(void)
6516 {
6517 #ifdef FEAT_GUI_TABLINE
6518     // When the GUI has the tabline then this always returns zero.
6519     if (gui_use_tabline())
6520 	return 0;
6521 #endif
6522     switch (p_stal)
6523     {
6524 	case 0: return 0;
6525 	case 1: return (first_tabpage->tp_next == NULL) ? 0 : 1;
6526     }
6527     return 1;
6528 }
6529 
6530 /*
6531  * Return the minimal number of rows that is needed on the screen to display
6532  * the current number of windows.
6533  */
6534     int
6535 min_rows(void)
6536 {
6537     int		total;
6538     tabpage_T	*tp;
6539     int		n;
6540 
6541     if (firstwin == NULL)	// not initialized yet
6542 	return MIN_LINES;
6543 
6544     total = 0;
6545     FOR_ALL_TABPAGES(tp)
6546     {
6547 	n = frame_minheight(tp->tp_topframe, NULL);
6548 	if (total < n)
6549 	    total = n;
6550     }
6551     total += tabline_height();
6552     total += 1;		// count the room for the command line
6553     return total;
6554 }
6555 
6556 /*
6557  * Return TRUE if there is only one window and only one tab page, not
6558  * counting a help or preview window, unless it is the current window.
6559  * Does not count unlisted windows.
6560  */
6561     int
6562 only_one_window(void)
6563 {
6564     int		count = 0;
6565     win_T	*wp;
6566 
6567 #if defined(FEAT_PROP_POPUP)
6568     // If the current window is a popup then there always is another window.
6569     if (popup_is_popup(curwin))
6570 	return FALSE;
6571 #endif
6572 
6573     // If there is another tab page there always is another window.
6574     if (first_tabpage->tp_next != NULL)
6575 	return FALSE;
6576 
6577     FOR_ALL_WINDOWS(wp)
6578 	if (wp->w_buffer != NULL
6579 		&& (!((bt_help(wp->w_buffer) && !bt_help(curbuf))
6580 # ifdef FEAT_QUICKFIX
6581 		    || wp->w_p_pvw
6582 # endif
6583 	     ) || wp == curwin) && wp != aucmd_win)
6584 	    ++count;
6585     return (count <= 1);
6586 }
6587 
6588 /*
6589  * Correct the cursor line number in other windows.  Used after changing the
6590  * current buffer, and before applying autocommands.
6591  * When "do_curwin" is TRUE, also check current window.
6592  */
6593     void
6594 check_lnums(int do_curwin)
6595 {
6596     win_T	*wp;
6597     tabpage_T	*tp;
6598 
6599     FOR_ALL_TAB_WINDOWS(tp, wp)
6600 	if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf)
6601 	{
6602 	    // save the original cursor position and topline
6603 	    wp->w_save_cursor.w_cursor_save = wp->w_cursor;
6604 	    wp->w_save_cursor.w_topline_save = wp->w_topline;
6605 
6606 	    if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count)
6607 		wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
6608 	    if (wp->w_topline > curbuf->b_ml.ml_line_count)
6609 		wp->w_topline = curbuf->b_ml.ml_line_count;
6610 
6611 	    // save the corrected cursor position and topline
6612 	    wp->w_save_cursor.w_cursor_corr = wp->w_cursor;
6613 	    wp->w_save_cursor.w_topline_corr = wp->w_topline;
6614 	}
6615 }
6616 
6617 /*
6618  * Reset cursor and topline to its stored values from check_lnums().
6619  * check_lnums() must have been called first!
6620  */
6621     void
6622 reset_lnums()
6623 {
6624     win_T	*wp;
6625     tabpage_T	*tp;
6626 
6627     FOR_ALL_TAB_WINDOWS(tp, wp)
6628 	if (wp->w_buffer == curbuf)
6629 	{
6630 	    // Restore the value if the autocommand didn't change it.
6631 	    if (EQUAL_POS(wp->w_save_cursor.w_cursor_corr, wp->w_cursor))
6632 		wp->w_cursor = wp->w_save_cursor.w_cursor_save;
6633 	    if (wp->w_save_cursor.w_topline_corr == wp->w_topline)
6634 		wp->w_topline = wp->w_save_cursor.w_topline_save;
6635 	}
6636 }
6637 
6638 /*
6639  * A snapshot of the window sizes, to restore them after closing the help
6640  * window.
6641  * Only these fields are used:
6642  * fr_layout
6643  * fr_width
6644  * fr_height
6645  * fr_next
6646  * fr_child
6647  * fr_win (only valid for the old curwin, NULL otherwise)
6648  */
6649 
6650 /*
6651  * Create a snapshot of the current frame sizes.
6652  */
6653     void
6654 make_snapshot(int idx)
6655 {
6656     clear_snapshot(curtab, idx);
6657     make_snapshot_rec(topframe, &curtab->tp_snapshot[idx]);
6658 }
6659 
6660     static void
6661 make_snapshot_rec(frame_T *fr, frame_T **frp)
6662 {
6663     *frp = ALLOC_CLEAR_ONE(frame_T);
6664     if (*frp == NULL)
6665 	return;
6666     (*frp)->fr_layout = fr->fr_layout;
6667     (*frp)->fr_width = fr->fr_width;
6668     (*frp)->fr_height = fr->fr_height;
6669     if (fr->fr_next != NULL)
6670 	make_snapshot_rec(fr->fr_next, &((*frp)->fr_next));
6671     if (fr->fr_child != NULL)
6672 	make_snapshot_rec(fr->fr_child, &((*frp)->fr_child));
6673     if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin)
6674 	(*frp)->fr_win = curwin;
6675 }
6676 
6677 /*
6678  * Remove any existing snapshot.
6679  */
6680     static void
6681 clear_snapshot(tabpage_T *tp, int idx)
6682 {
6683     clear_snapshot_rec(tp->tp_snapshot[idx]);
6684     tp->tp_snapshot[idx] = NULL;
6685 }
6686 
6687     static void
6688 clear_snapshot_rec(frame_T *fr)
6689 {
6690     if (fr != NULL)
6691     {
6692 	clear_snapshot_rec(fr->fr_next);
6693 	clear_snapshot_rec(fr->fr_child);
6694 	vim_free(fr);
6695     }
6696 }
6697 
6698 /*
6699  * Restore a previously created snapshot, if there is any.
6700  * This is only done if the screen size didn't change and the window layout is
6701  * still the same.
6702  */
6703     void
6704 restore_snapshot(
6705     int		idx,
6706     int		close_curwin)	    // closing current window
6707 {
6708     win_T	*wp;
6709 
6710     if (curtab->tp_snapshot[idx] != NULL
6711 	    && curtab->tp_snapshot[idx]->fr_width == topframe->fr_width
6712 	    && curtab->tp_snapshot[idx]->fr_height == topframe->fr_height
6713 	    && check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK)
6714     {
6715 	wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe);
6716 	win_comp_pos();
6717 	if (wp != NULL && close_curwin)
6718 	    win_goto(wp);
6719 	redraw_all_later(NOT_VALID);
6720     }
6721     clear_snapshot(curtab, idx);
6722 }
6723 
6724 /*
6725  * Check if frames "sn" and "fr" have the same layout, same following frames
6726  * and same children.  And the window pointer is valid.
6727  */
6728     static int
6729 check_snapshot_rec(frame_T *sn, frame_T *fr)
6730 {
6731     if (sn->fr_layout != fr->fr_layout
6732 	    || (sn->fr_next == NULL) != (fr->fr_next == NULL)
6733 	    || (sn->fr_child == NULL) != (fr->fr_child == NULL)
6734 	    || (sn->fr_next != NULL
6735 		&& check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL)
6736 	    || (sn->fr_child != NULL
6737 		&& check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)
6738 	    || (sn->fr_win != NULL && !win_valid(sn->fr_win)))
6739 	return FAIL;
6740     return OK;
6741 }
6742 
6743 /*
6744  * Copy the size of snapshot frame "sn" to frame "fr".  Do the same for all
6745  * following frames and children.
6746  * Returns a pointer to the old current window, or NULL.
6747  */
6748     static win_T *
6749 restore_snapshot_rec(frame_T *sn, frame_T *fr)
6750 {
6751     win_T	*wp = NULL;
6752     win_T	*wp2;
6753 
6754     fr->fr_height = sn->fr_height;
6755     fr->fr_width = sn->fr_width;
6756     if (fr->fr_layout == FR_LEAF)
6757     {
6758 	frame_new_height(fr, fr->fr_height, FALSE, FALSE);
6759 	frame_new_width(fr, fr->fr_width, FALSE, FALSE);
6760 	wp = sn->fr_win;
6761     }
6762     if (sn->fr_next != NULL)
6763     {
6764 	wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next);
6765 	if (wp2 != NULL)
6766 	    wp = wp2;
6767     }
6768     if (sn->fr_child != NULL)
6769     {
6770 	wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child);
6771 	if (wp2 != NULL)
6772 	    wp = wp2;
6773     }
6774     return wp;
6775 }
6776 
6777 #if defined(FEAT_GUI) || defined(PROTO)
6778 /*
6779  * Return TRUE if there is any vertically split window.
6780  */
6781     int
6782 win_hasvertsplit(void)
6783 {
6784     frame_T	*fr;
6785 
6786     if (topframe->fr_layout == FR_ROW)
6787 	return TRUE;
6788 
6789     if (topframe->fr_layout == FR_COL)
6790 	FOR_ALL_FRAMES(fr, topframe->fr_child)
6791 	    if (fr->fr_layout == FR_ROW)
6792 		return TRUE;
6793 
6794     return FALSE;
6795 }
6796 #endif
6797 
6798 #if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) || defined(PROTO)
6799     int
6800 get_win_number(win_T *wp, win_T *first_win)
6801 {
6802     int		i = 1;
6803     win_T	*w;
6804 
6805     for (w = first_win; w != NULL && w != wp; w = W_NEXT(w))
6806 	++i;
6807 
6808     if (w == NULL)
6809 	return 0;
6810     else
6811 	return i;
6812 }
6813 
6814     int
6815 get_tab_number(tabpage_T *tp UNUSED)
6816 {
6817     int		i = 1;
6818     tabpage_T	*t;
6819 
6820     for (t = first_tabpage; t != NULL && t != tp; t = t->tp_next)
6821 	++i;
6822 
6823     if (t == NULL)
6824 	return 0;
6825     else
6826 	return i;
6827 }
6828 #endif
6829 
6830 /*
6831  * Return TRUE if "topfrp" and its children are at the right height.
6832  */
6833     static int
6834 frame_check_height(frame_T *topfrp, int height)
6835 {
6836     frame_T *frp;
6837 
6838     if (topfrp->fr_height != height)
6839 	return FALSE;
6840 
6841     if (topfrp->fr_layout == FR_ROW)
6842 	FOR_ALL_FRAMES(frp, topfrp->fr_child)
6843 	    if (frp->fr_height != height)
6844 		return FALSE;
6845 
6846     return TRUE;
6847 }
6848 
6849 /*
6850  * Return TRUE if "topfrp" and its children are at the right width.
6851  */
6852     static int
6853 frame_check_width(frame_T *topfrp, int width)
6854 {
6855     frame_T *frp;
6856 
6857     if (topfrp->fr_width != width)
6858 	return FALSE;
6859 
6860     if (topfrp->fr_layout == FR_COL)
6861 	FOR_ALL_FRAMES(frp, topfrp->fr_child)
6862 	    if (frp->fr_width != width)
6863 		return FALSE;
6864 
6865     return TRUE;
6866 }
6867 
6868 #if defined(FEAT_SYN_HL) || defined(PROTO)
6869 /*
6870  * Simple int comparison function for use with qsort()
6871  */
6872     static int
6873 int_cmp(const void *a, const void *b)
6874 {
6875     return *(const int *)a - *(const int *)b;
6876 }
6877 
6878 /*
6879  * Handle setting 'colorcolumn' or 'textwidth' in window "wp".
6880  * Returns error message, NULL if it's OK.
6881  */
6882     char *
6883 check_colorcolumn(win_T *wp)
6884 {
6885     char_u	*s;
6886     int		col;
6887     int		count = 0;
6888     int		color_cols[256];
6889     int		i;
6890     int		j = 0;
6891 
6892     if (wp->w_buffer == NULL)
6893 	return NULL;  // buffer was closed
6894 
6895     for (s = wp->w_p_cc; *s != NUL && count < 255;)
6896     {
6897 	if (*s == '-' || *s == '+')
6898 	{
6899 	    // -N and +N: add to 'textwidth'
6900 	    col = (*s == '-') ? -1 : 1;
6901 	    ++s;
6902 	    if (!VIM_ISDIGIT(*s))
6903 		return e_invarg;
6904 	    col = col * getdigits(&s);
6905 	    if (wp->w_buffer->b_p_tw == 0)
6906 		goto skip;  // 'textwidth' not set, skip this item
6907 	    col += wp->w_buffer->b_p_tw;
6908 	    if (col < 0)
6909 		goto skip;
6910 	}
6911 	else if (VIM_ISDIGIT(*s))
6912 	    col = getdigits(&s);
6913 	else
6914 	    return e_invarg;
6915 	color_cols[count++] = col - 1;  // 1-based to 0-based
6916 skip:
6917 	if (*s == NUL)
6918 	    break;
6919 	if (*s != ',')
6920 	    return e_invarg;
6921 	if (*++s == NUL)
6922 	    return e_invarg;  // illegal trailing comma as in "set cc=80,"
6923     }
6924 
6925     vim_free(wp->w_p_cc_cols);
6926     if (count == 0)
6927 	wp->w_p_cc_cols = NULL;
6928     else
6929     {
6930 	wp->w_p_cc_cols = ALLOC_MULT(int, count + 1);
6931 	if (wp->w_p_cc_cols != NULL)
6932 	{
6933 	    // sort the columns for faster usage on screen redraw inside
6934 	    // win_line()
6935 	    qsort(color_cols, count, sizeof(int), int_cmp);
6936 
6937 	    for (i = 0; i < count; ++i)
6938 		// skip duplicates
6939 		if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i])
6940 		    wp->w_p_cc_cols[j++] = color_cols[i];
6941 	    wp->w_p_cc_cols[j] = -1;  // end marker
6942 	}
6943     }
6944 
6945     return NULL;  // no error
6946 }
6947 #endif
6948