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