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