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