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