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