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