xref: /vim-8.2.3635/src/window.c (revision fc1421eb)
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 			/* Can make all windows wider than 'winwidth', spread
1709 			 * the room equally. */
1710 			next_curwin_size = (room + p_wiw
1711 					    + (totwincount - 1) * p_wmw
1712 					    + (totwincount - 1)) / totwincount;
1713 			room -= next_curwin_size - p_wiw;
1714 		    }
1715 		    else
1716 			next_curwin_size = p_wiw;
1717 		}
1718 	    }
1719 
1720 	    if (has_next_curwin)
1721 		--totwincount;		/* don't count curwin */
1722 	}
1723 
1724 	for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next)
1725 	{
1726 	    n = m = 0;
1727 	    wincount = 1;
1728 	    if (fr->fr_next == NULL)
1729 		/* last frame gets all that remains (avoid roundoff error) */
1730 		new_size = width;
1731 	    else if (dir == 'v')
1732 		new_size = fr->fr_width;
1733 	    else if (frame_fixed_width(fr))
1734 	    {
1735 		new_size = fr->fr_newwidth;
1736 		wincount = 0;	    /* doesn't count as a sizeable window */
1737 	    }
1738 	    else
1739 	    {
1740 		/* Compute the maximum number of windows horiz. in "fr". */
1741 		n = frame_minwidth(fr, NOWIN);
1742 		wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
1743 								/ (p_wmw + 1);
1744 		m = frame_minwidth(fr, next_curwin);
1745 		if (has_next_curwin)
1746 		    hnc = frame_has_win(fr, next_curwin);
1747 		else
1748 		    hnc = FALSE;
1749 		if (hnc)	    /* don't count next_curwin */
1750 		    --wincount;
1751 		if (totwincount == 0)
1752 		    new_size = room;
1753 		else
1754 		    new_size = (wincount * room + ((unsigned)totwincount >> 1))
1755 								/ totwincount;
1756 		if (hnc)	    /* add next_curwin size */
1757 		{
1758 		    next_curwin_size -= p_wiw - (m - n);
1759 		    new_size += next_curwin_size;
1760 		    room -= new_size - next_curwin_size;
1761 		}
1762 		else
1763 		    room -= new_size;
1764 		new_size += n;
1765 	    }
1766 
1767 	    /* Skip frame that is full width when splitting or closing a
1768 	     * window, unless equalizing all frames. */
1769 	    if (!current || dir != 'v' || topfr->fr_parent != NULL
1770 		    || (new_size != fr->fr_width)
1771 		    || frame_has_win(fr, next_curwin))
1772 		win_equal_rec(next_curwin, current, fr, dir, col, row,
1773 							    new_size, height);
1774 	    col += new_size;
1775 	    width -= new_size;
1776 	    totwincount -= wincount;
1777 	}
1778     }
1779 #endif
1780     else /* topfr->fr_layout == FR_COL */
1781     {
1782 #ifdef FEAT_VERTSPLIT
1783 	topfr->fr_width = width;
1784 #endif
1785 	topfr->fr_height = height;
1786 
1787 	if (dir != 'h')			/* equalize frame heights */
1788 	{
1789 	    /* Compute maximum number of windows vertically in this frame. */
1790 	    n = frame_minheight(topfr, NOWIN);
1791 	    /* add one for the bottom window if it doesn't have a statusline */
1792 	    if (row + height == cmdline_row && p_ls == 0)
1793 		extra_sep = 1;
1794 	    else
1795 		extra_sep = 0;
1796 	    totwincount = (n + extra_sep) / (p_wmh + 1);
1797 	    has_next_curwin = frame_has_win(topfr, next_curwin);
1798 
1799 	    /*
1800 	     * Compute height for "next_curwin" window and room available for
1801 	     * other windows.
1802 	     * "m" is the minimal height when counting p_wh for "next_curwin".
1803 	     */
1804 	    m = frame_minheight(topfr, next_curwin);
1805 	    room = height - m;
1806 	    if (room < 0)
1807 	    {
1808 		/* The room is less then 'winheight', use all space for the
1809 		 * current window. */
1810 		next_curwin_size = p_wh + room;
1811 		room = 0;
1812 	    }
1813 	    else
1814 	    {
1815 		next_curwin_size = -1;
1816 		for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next)
1817 		{
1818 		    /* If 'winfixheight' set keep the window height if
1819 		     * possible.
1820 		     * Watch out for this window being the next_curwin. */
1821 		    if (frame_fixed_height(fr))
1822 		    {
1823 			n = frame_minheight(fr, NOWIN);
1824 			new_size = fr->fr_height;
1825 			if (frame_has_win(fr, next_curwin))
1826 			{
1827 			    room += p_wh - p_wmh;
1828 			    next_curwin_size = 0;
1829 			    if (new_size < p_wh)
1830 				new_size = p_wh;
1831 			}
1832 			else
1833 			    /* These windows don't use up room. */
1834 			    totwincount -= (n + (fr->fr_next == NULL
1835 					      ? extra_sep : 0)) / (p_wmh + 1);
1836 			room -= new_size - n;
1837 			if (room < 0)
1838 			{
1839 			    new_size += room;
1840 			    room = 0;
1841 			}
1842 			fr->fr_newheight = new_size;
1843 		    }
1844 		}
1845 		if (next_curwin_size == -1)
1846 		{
1847 		    if (!has_next_curwin)
1848 			next_curwin_size = 0;
1849 		    else if (totwincount > 1
1850 			    && (room + (totwincount - 2))
1851 						   / (totwincount - 1) > p_wh)
1852 		    {
1853 			/* can make all windows higher than 'winheight',
1854 			 * spread the room equally. */
1855 			next_curwin_size = (room + p_wh
1856 					   + (totwincount - 1) * p_wmh
1857 					   + (totwincount - 1)) / totwincount;
1858 			room -= next_curwin_size - p_wh;
1859 		    }
1860 		    else
1861 			next_curwin_size = p_wh;
1862 		}
1863 	    }
1864 
1865 	    if (has_next_curwin)
1866 		--totwincount;		/* don't count curwin */
1867 	}
1868 
1869 	for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next)
1870 	{
1871 	    n = m = 0;
1872 	    wincount = 1;
1873 	    if (fr->fr_next == NULL)
1874 		/* last frame gets all that remains (avoid roundoff error) */
1875 		new_size = height;
1876 	    else if (dir == 'h')
1877 		new_size = fr->fr_height;
1878 	    else if (frame_fixed_height(fr))
1879 	    {
1880 		new_size = fr->fr_newheight;
1881 		wincount = 0;	    /* doesn't count as a sizeable window */
1882 	    }
1883 	    else
1884 	    {
1885 		/* Compute the maximum number of windows vert. in "fr". */
1886 		n = frame_minheight(fr, NOWIN);
1887 		wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
1888 								/ (p_wmh + 1);
1889 		m = frame_minheight(fr, next_curwin);
1890 		if (has_next_curwin)
1891 		    hnc = frame_has_win(fr, next_curwin);
1892 		else
1893 		    hnc = FALSE;
1894 		if (hnc)	    /* don't count next_curwin */
1895 		    --wincount;
1896 		if (totwincount == 0)
1897 		    new_size = room;
1898 		else
1899 		    new_size = (wincount * room + ((unsigned)totwincount >> 1))
1900 								/ totwincount;
1901 		if (hnc)	    /* add next_curwin size */
1902 		{
1903 		    next_curwin_size -= p_wh - (m - n);
1904 		    new_size += next_curwin_size;
1905 		    room -= new_size - next_curwin_size;
1906 		}
1907 		else
1908 		    room -= new_size;
1909 		new_size += n;
1910 	    }
1911 	    /* Skip frame that is full width when splitting or closing a
1912 	     * window, unless equalizing all frames. */
1913 	    if (!current || dir != 'h' || topfr->fr_parent != NULL
1914 		    || (new_size != fr->fr_height)
1915 		    || frame_has_win(fr, next_curwin))
1916 		win_equal_rec(next_curwin, current, fr, dir, col, row,
1917 							     width, new_size);
1918 	    row += new_size;
1919 	    height -= new_size;
1920 	    totwincount -= wincount;
1921 	}
1922     }
1923 }
1924 
1925 /*
1926  * close all windows for buffer 'buf'
1927  */
1928     void
1929 close_windows(buf, keep_curwin)
1930     buf_T	*buf;
1931     int		keep_curwin;	    /* don't close "curwin" */
1932 {
1933     win_T	*wp;
1934     tabpage_T   *tp, *nexttp;
1935     int		h = tabline_height();
1936 
1937     ++RedrawingDisabled;
1938 
1939     for (wp = firstwin; wp != NULL && lastwin != firstwin; )
1940     {
1941 	if (wp->w_buffer == buf && (!keep_curwin || wp != curwin))
1942 	{
1943 	    win_close(wp, FALSE);
1944 
1945 	    /* Start all over, autocommands may change the window layout. */
1946 	    wp = firstwin;
1947 	}
1948 	else
1949 	    wp = wp->w_next;
1950     }
1951 
1952     /* Also check windows in other tab pages. */
1953     for (tp = first_tabpage; tp != NULL; tp = nexttp)
1954     {
1955 	nexttp = tp->tp_next;
1956 	if (tp != curtab)
1957 	    for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
1958 		if (wp->w_buffer == buf)
1959 		{
1960 		    win_close_othertab(wp, FALSE, tp);
1961 
1962 		    /* Start all over, the tab page may be closed and
1963 		     * autocommands may change the window layout. */
1964 		    nexttp = first_tabpage;
1965 		    break;
1966 		}
1967     }
1968 
1969     --RedrawingDisabled;
1970 
1971     if (h != tabline_height())
1972 	shell_new_rows();
1973 }
1974 
1975 /*
1976  * Return TRUE if the current window is the only window that exists.
1977  * Returns FALSE if there is a window, possibly in another tab page.
1978  */
1979     static int
1980 last_window()
1981 {
1982     return (lastwin == firstwin && first_tabpage->tp_next == NULL);
1983 }
1984 
1985 /*
1986  * Close window "win".  Only works for the current tab page.
1987  * If "free_buf" is TRUE related buffer may be unloaded.
1988  *
1989  * called by :quit, :close, :xit, :wq and findtag()
1990  */
1991     void
1992 win_close(win, free_buf)
1993     win_T	*win;
1994     int		free_buf;
1995 {
1996     win_T	*wp;
1997     buf_T	*old_curbuf = curbuf;
1998 #ifdef FEAT_AUTOCMD
1999     int		other_buffer = FALSE;
2000 #endif
2001     int		close_curwin = FALSE;
2002     int		dir;
2003     int		help_window = FALSE;
2004 
2005     if (last_window())
2006     {
2007 	EMSG(_("E444: Cannot close last window"));
2008 	return;
2009     }
2010 
2011     /* When closing the help window, try restoring a snapshot after closing
2012      * the window.  Otherwise clear the snapshot, it's now invalid. */
2013     if (win->w_buffer->b_help)
2014 	help_window = TRUE;
2015     else
2016 	clear_snapshot(curtab);
2017 
2018 #ifdef FEAT_AUTOCMD
2019     if (win == curwin)
2020     {
2021 	/*
2022 	 * Guess which window is going to be the new current window.
2023 	 * This may change because of the autocommands (sigh).
2024 	 */
2025 	wp = frame2win(win_altframe(win, NULL));
2026 
2027 	/*
2028 	 * Be careful: If autocommands delete the window, return now.
2029 	 */
2030 	if (wp->w_buffer != curbuf)
2031 	{
2032 	    other_buffer = TRUE;
2033 	    apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
2034 	    if (!win_valid(win) || last_window())
2035 		return;
2036 	}
2037 	apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
2038 	if (!win_valid(win) || last_window())
2039 	    return;
2040 # ifdef FEAT_EVAL
2041 	/* autocmds may abort script processing */
2042 	if (aborting())
2043 	    return;
2044 # endif
2045     }
2046 #endif
2047 
2048     /*
2049      * Close the link to the buffer.
2050      */
2051     close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0);
2052     /* Autocommands may have closed the window already, or closed the only
2053      * other window. */
2054     if (!win_valid(win) || last_window())
2055 	return;
2056 
2057     /* Free the memory used for the window. */
2058     wp = win_free_mem(win, &dir, NULL);
2059 
2060     /* When closing the last window in a tab page go to another tab page. */
2061     if (wp == NULL)
2062     {
2063 	tabpage_T   *ptp = NULL;
2064 	tabpage_T   *tp;
2065 	tabpage_T   *atp = alt_tabpage();
2066 
2067 	for (tp = first_tabpage; tp != curtab; tp = tp->tp_next)
2068 	    ptp = tp;
2069 	if (tp == NULL)
2070 	{
2071 	    EMSG2(_(e_intern2), "win_close()");
2072 	    return;
2073 	}
2074 	if (ptp == NULL)
2075 	    first_tabpage = tp->tp_next;
2076 	else
2077 	    ptp->tp_next = tp->tp_next;
2078 	free_tabpage(tp);
2079 
2080 	/* We don't do the window resizing stuff, let enter_tabpage() take
2081 	 * care of entering a window in another tab page. */
2082 	enter_tabpage(atp, old_curbuf);
2083 	return;
2084     }
2085 
2086     /* Make sure curwin isn't invalid.  It can cause severe trouble when
2087      * printing an error message.  For win_equal() curbuf needs to be valid
2088      * too. */
2089     else if (win == curwin)
2090     {
2091 	curwin = wp;
2092 #ifdef FEAT_QUICKFIX
2093 	if (wp->w_p_pvw || bt_quickfix(wp->w_buffer))
2094 	{
2095 	    /*
2096 	     * The cursor goes to the preview or the quickfix window, try
2097 	     * finding another window to go to.
2098 	     */
2099 	    for (;;)
2100 	    {
2101 		if (wp->w_next == NULL)
2102 		    wp = firstwin;
2103 		else
2104 		    wp = wp->w_next;
2105 		if (wp == curwin)
2106 		    break;
2107 		if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer))
2108 		{
2109 		    curwin = wp;
2110 		    break;
2111 		}
2112 	    }
2113 	}
2114 #endif
2115 	curbuf = curwin->w_buffer;
2116 	close_curwin = TRUE;
2117     }
2118     if (p_ea
2119 #ifdef FEAT_VERTSPLIT
2120 	    && (*p_ead == 'b' || *p_ead == dir)
2121 #endif
2122 	    )
2123 	win_equal(curwin, TRUE,
2124 #ifdef FEAT_VERTSPLIT
2125 		dir
2126 #else
2127 		0
2128 #endif
2129 		);
2130     else
2131 	win_comp_pos();
2132     if (close_curwin)
2133     {
2134 	win_enter_ext(wp, FALSE, TRUE);
2135 #ifdef FEAT_AUTOCMD
2136 	if (other_buffer)
2137 	    /* careful: after this wp and win may be invalid! */
2138 	    apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
2139 #endif
2140     }
2141 
2142     /*
2143      * If last window has a status line now and we don't want one,
2144      * remove the status line.
2145      */
2146     last_status(FALSE);
2147 
2148     /* After closing the help window, try restoring the window layout from
2149      * before it was opened. */
2150     if (help_window)
2151 	restore_snapshot(close_curwin);
2152 
2153 #if defined(FEAT_GUI) && defined(FEAT_VERTSPLIT)
2154     /* When 'guioptions' includes 'L' or 'R' may have to remove scrollbars. */
2155     if (gui.in_use && !win_hasvertsplit())
2156 	gui_init_which_components(NULL);
2157 #endif
2158 
2159     redraw_all_later(NOT_VALID);
2160 }
2161 
2162 /*
2163  * Close window "win" in tab page "tp", which is not the current tab page.
2164  * This may be the last window ih that tab page and result in closing the tab,
2165  * thus "tp" may become invalid!
2166  * Called must check if buffer is hidden.
2167  */
2168     void
2169 win_close_othertab(win, free_buf, tp)
2170     win_T	*win;
2171     int		free_buf;
2172     tabpage_T	*tp;
2173 {
2174     win_T	*wp;
2175     int		dir;
2176     tabpage_T   *ptp = NULL;
2177 
2178     /* Close the link to the buffer. */
2179     close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0);
2180 
2181     /* Careful: Autocommands may have closed the tab page or made it the
2182      * current tab page.  */
2183     for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next)
2184 	;
2185     if (ptp == NULL || tp == curtab)
2186 	return;
2187 
2188     /* Autocommands may have closed the window already. */
2189     for (wp = tp->tp_firstwin; wp != NULL && wp != win; wp = wp->w_next)
2190 	;
2191     if (wp == NULL)
2192 	return;
2193 
2194     /* Free the memory used for the window. */
2195     wp = win_free_mem(win, &dir, tp);
2196 
2197     /* When closing the last window in a tab page remove the tab page. */
2198     if (wp == NULL)
2199     {
2200 	if (tp == first_tabpage)
2201 	    first_tabpage = tp->tp_next;
2202 	else
2203 	{
2204 	    for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp;
2205 							   ptp = ptp->tp_next)
2206 		;
2207 	    if (ptp == NULL)
2208 	    {
2209 		EMSG2(_(e_intern2), "win_close_othertab()");
2210 		return;
2211 	    }
2212 	    ptp->tp_next = tp->tp_next;
2213 	}
2214 	vim_free(tp);
2215     }
2216 }
2217 
2218 /*
2219  * Free the memory used for a window.
2220  * Returns a pointer to the window that got the freed up space.
2221  */
2222     static win_T *
2223 win_free_mem(win, dirp, tp)
2224     win_T	*win;
2225     int		*dirp;		/* set to 'v' or 'h' for direction if 'ea' */
2226     tabpage_T	*tp;		/* tab page "win" is in, NULL for current */
2227 {
2228     frame_T	*frp;
2229     win_T	*wp;
2230 
2231 #ifdef FEAT_FOLDING
2232     clearFolding(win);
2233 #endif
2234 
2235     /* reduce the reference count to the argument list. */
2236     alist_unlink(win->w_alist);
2237 
2238     /* Remove the window and its frame from the tree of frames. */
2239     frp = win->w_frame;
2240     wp = winframe_remove(win, dirp, tp);
2241     vim_free(frp);
2242     win_free(win, tp);
2243 
2244     /* When deleting the current window of another tab page select a new
2245      * current window. */
2246     if (tp != NULL && win == tp->tp_curwin)
2247 	tp->tp_curwin = wp;
2248 
2249     return wp;
2250 }
2251 
2252 #if defined(EXITFREE) || defined(PROTO)
2253     void
2254 win_free_all()
2255 {
2256     int		dummy;
2257 
2258 # ifdef FEAT_WINDOWS
2259     while (first_tabpage->tp_next != NULL)
2260 	tabpage_close(TRUE);
2261 # endif
2262 
2263     while (firstwin != NULL)
2264 	(void)win_free_mem(firstwin, &dummy, NULL);
2265 }
2266 #endif
2267 
2268 /*
2269  * Remove a window and its frame from the tree of frames.
2270  * Returns a pointer to the window that got the freed up space.
2271  */
2272 /*ARGSUSED*/
2273     static win_T *
2274 winframe_remove(win, dirp, tp)
2275     win_T	*win;
2276     int		*dirp;		/* set to 'v' or 'h' for direction if 'ea' */
2277     tabpage_T	*tp;		/* tab page "win" is in, NULL for current */
2278 {
2279     frame_T	*frp, *frp2, *frp3;
2280     frame_T	*frp_close = win->w_frame;
2281     win_T	*wp;
2282     int		old_size = 0;
2283 
2284     /*
2285      * If there is only one window there is nothing to remove.
2286      */
2287     if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin)
2288 	return NULL;
2289 
2290     /*
2291      * Remove the window from its frame.
2292      */
2293     frp2 = win_altframe(win, tp);
2294     wp = frame2win(frp2);
2295 
2296     /* Remove this frame from the list of frames. */
2297     frame_remove(frp_close);
2298 
2299 #ifdef FEAT_VERTSPLIT
2300     if (frp_close->fr_parent->fr_layout == FR_COL)
2301     {
2302 #endif
2303 	/* When 'winfixheight' is set, remember its old size and restore
2304 	 * it later (it's a simplistic solution...).  Don't do this if the
2305 	 * window will occupy the full height of the screen. */
2306 	if (frp2->fr_win != NULL
2307 		&& (frp2->fr_next != NULL || frp2->fr_prev != NULL)
2308 		&& frp2->fr_win->w_p_wfh)
2309 	    old_size = frp2->fr_win->w_height;
2310 	frame_new_height(frp2, frp2->fr_height + frp_close->fr_height,
2311 			    frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE);
2312 	if (old_size != 0)
2313 	    win_setheight_win(old_size, frp2->fr_win);
2314 #ifdef FEAT_VERTSPLIT
2315 	*dirp = 'v';
2316     }
2317     else
2318     {
2319 	/* When 'winfixwidth' is set, remember its old size and restore
2320 	 * it later (it's a simplistic solution...).  Don't do this if the
2321 	 * window will occupy the full width of the screen. */
2322 	if (frp2->fr_win != NULL
2323 		&& (frp2->fr_next != NULL || frp2->fr_prev != NULL)
2324 		&& frp2->fr_win->w_p_wfw)
2325 	    old_size = frp2->fr_win->w_width;
2326 	frame_new_width(frp2, frp2->fr_width + frp_close->fr_width,
2327 			    frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE);
2328 	if (old_size != 0)
2329 	    win_setwidth_win(old_size, frp2->fr_win);
2330 	*dirp = 'h';
2331     }
2332 #endif
2333 
2334     /* If rows/columns go to a window below/right its positions need to be
2335      * updated.  Can only be done after the sizes have been updated. */
2336     if (frp2 == frp_close->fr_next)
2337     {
2338 	int row = win->w_winrow;
2339 	int col = W_WINCOL(win);
2340 
2341 	frame_comp_pos(frp2, &row, &col);
2342     }
2343 
2344     if (frp2->fr_next == NULL && frp2->fr_prev == NULL)
2345     {
2346 	/* There is no other frame in this list, move its info to the parent
2347 	 * and remove it. */
2348 	frp2->fr_parent->fr_layout = frp2->fr_layout;
2349 	frp2->fr_parent->fr_child = frp2->fr_child;
2350 	for (frp = frp2->fr_child; frp != NULL; frp = frp->fr_next)
2351 	    frp->fr_parent = frp2->fr_parent;
2352 	frp2->fr_parent->fr_win = frp2->fr_win;
2353 	if (frp2->fr_win != NULL)
2354 	    frp2->fr_win->w_frame = frp2->fr_parent;
2355 	frp = frp2->fr_parent;
2356 	vim_free(frp2);
2357 
2358 	frp2 = frp->fr_parent;
2359 	if (frp2 != NULL && frp2->fr_layout == frp->fr_layout)
2360 	{
2361 	    /* The frame above the parent has the same layout, have to merge
2362 	     * the frames into this list. */
2363 	    if (frp2->fr_child == frp)
2364 		frp2->fr_child = frp->fr_child;
2365 	    frp->fr_child->fr_prev = frp->fr_prev;
2366 	    if (frp->fr_prev != NULL)
2367 		frp->fr_prev->fr_next = frp->fr_child;
2368 	    for (frp3 = frp->fr_child; ; frp3 = frp3->fr_next)
2369 	    {
2370 		frp3->fr_parent = frp2;
2371 		if (frp3->fr_next == NULL)
2372 		{
2373 		    frp3->fr_next = frp->fr_next;
2374 		    if (frp->fr_next != NULL)
2375 			frp->fr_next->fr_prev = frp3;
2376 		    break;
2377 		}
2378 	    }
2379 	    vim_free(frp);
2380 	}
2381     }
2382 
2383     return wp;
2384 }
2385 
2386 /*
2387  * Find out which frame is going to get the freed up space when "win" is
2388  * closed.
2389  * if 'splitbelow'/'splitleft' the space goes to the window above/left.
2390  * if 'nosplitbelow'/'nosplitleft' the space goes to the window below/right.
2391  * This makes opening a window and closing it immediately keep the same window
2392  * layout.
2393  */
2394     static frame_T *
2395 win_altframe(win, tp)
2396     win_T	*win;
2397     tabpage_T	*tp;		/* tab page "win" is in, NULL for current */
2398 {
2399     frame_T	*frp;
2400     int		b;
2401 
2402     if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin)
2403 	/* Last window in this tab page, will go to next tab page. */
2404 	return alt_tabpage()->tp_curwin->w_frame;
2405 
2406     frp = win->w_frame;
2407 #ifdef FEAT_VERTSPLIT
2408     if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW)
2409 	b = p_spr;
2410     else
2411 #endif
2412 	b = p_sb;
2413     if ((!b && frp->fr_next != NULL) || frp->fr_prev == NULL)
2414 	return frp->fr_next;
2415     return frp->fr_prev;
2416 }
2417 
2418 /*
2419  * Return the tabpage that will be used if the current one is closed.
2420  */
2421     static tabpage_T *
2422 alt_tabpage()
2423 {
2424     tabpage_T	*tp;
2425 
2426     /* Use the next tab page if possible. */
2427     if (curtab->tp_next != NULL)
2428 	return curtab->tp_next;
2429 
2430     /* Find the last but one tab page. */
2431     for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next)
2432 	;
2433     return tp;
2434 }
2435 
2436 /*
2437  * Find the left-upper window in frame "frp".
2438  */
2439     static win_T *
2440 frame2win(frp)
2441     frame_T	*frp;
2442 {
2443     while (frp->fr_win == NULL)
2444 	frp = frp->fr_child;
2445     return frp->fr_win;
2446 }
2447 
2448 /*
2449  * Return TRUE if frame "frp" contains window "wp".
2450  */
2451     static int
2452 frame_has_win(frp, wp)
2453     frame_T	*frp;
2454     win_T	*wp;
2455 {
2456     frame_T	*p;
2457 
2458     if (frp->fr_layout == FR_LEAF)
2459 	return frp->fr_win == wp;
2460 
2461     for (p = frp->fr_child; p != NULL; p = p->fr_next)
2462 	if (frame_has_win(p, wp))
2463 	    return TRUE;
2464     return FALSE;
2465 }
2466 
2467 /*
2468  * Set a new height for a frame.  Recursively sets the height for contained
2469  * frames and windows.  Caller must take care of positions.
2470  */
2471     static void
2472 frame_new_height(topfrp, height, topfirst, wfh)
2473     frame_T	*topfrp;
2474     int		height;
2475     int		topfirst;	/* resize topmost contained frame first */
2476     int		wfh;		/* obey 'winfixheight' when there is a choice;
2477 				   may cause the height not to be set */
2478 {
2479     frame_T	*frp;
2480     int		extra_lines;
2481     int		h;
2482 
2483     if (topfrp->fr_win != NULL)
2484     {
2485 	/* Simple case: just one window. */
2486 	win_new_height(topfrp->fr_win,
2487 				    height - topfrp->fr_win->w_status_height);
2488     }
2489 #ifdef FEAT_VERTSPLIT
2490     else if (topfrp->fr_layout == FR_ROW)
2491     {
2492 	do
2493 	{
2494 	    /* All frames in this row get the same new height. */
2495 	    for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
2496 	    {
2497 		frame_new_height(frp, height, topfirst, wfh);
2498 		if (frp->fr_height > height)
2499 		{
2500 		    /* Could not fit the windows, make the whole row higher. */
2501 		    height = frp->fr_height;
2502 		    break;
2503 		}
2504 	    }
2505 	}
2506 	while (frp != NULL);
2507     }
2508 #endif
2509     else    /* fr_layout == FR_COL */
2510     {
2511 	/* Complicated case: Resize a column of frames.  Resize the bottom
2512 	 * frame first, frames above that when needed. */
2513 
2514 	frp = topfrp->fr_child;
2515 	if (wfh)
2516 	    /* Advance past frames with one window with 'wfh' set. */
2517 	    while (frame_fixed_height(frp))
2518 	    {
2519 		frp = frp->fr_next;
2520 		if (frp == NULL)
2521 		    return;	    /* no frame without 'wfh', give up */
2522 	    }
2523 	if (!topfirst)
2524 	{
2525 	    /* Find the bottom frame of this column */
2526 	    while (frp->fr_next != NULL)
2527 		frp = frp->fr_next;
2528 	    if (wfh)
2529 		/* Advance back for frames with one window with 'wfh' set. */
2530 		while (frame_fixed_height(frp))
2531 		    frp = frp->fr_prev;
2532 	}
2533 
2534 	extra_lines = height - topfrp->fr_height;
2535 	if (extra_lines < 0)
2536 	{
2537 	    /* reduce height of contained frames, bottom or top frame first */
2538 	    while (frp != NULL)
2539 	    {
2540 		h = frame_minheight(frp, NULL);
2541 		if (frp->fr_height + extra_lines < h)
2542 		{
2543 		    extra_lines += frp->fr_height - h;
2544 		    frame_new_height(frp, h, topfirst, wfh);
2545 		}
2546 		else
2547 		{
2548 		    frame_new_height(frp, frp->fr_height + extra_lines,
2549 							       topfirst, wfh);
2550 		    break;
2551 		}
2552 		if (topfirst)
2553 		{
2554 		    do
2555 			frp = frp->fr_next;
2556 		    while (wfh && frp != NULL && frame_fixed_height(frp));
2557 		}
2558 		else
2559 		{
2560 		    do
2561 			frp = frp->fr_prev;
2562 		    while (wfh && frp != NULL && frame_fixed_height(frp));
2563 		}
2564 		/* Increase "height" if we could not reduce enough frames. */
2565 		if (frp == NULL)
2566 		    height -= extra_lines;
2567 	    }
2568 	}
2569 	else if (extra_lines > 0)
2570 	{
2571 	    /* increase height of bottom or top frame */
2572 	    frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh);
2573 	}
2574     }
2575     topfrp->fr_height = height;
2576 }
2577 
2578 /*
2579  * Return TRUE if height of frame "frp" should not be changed because of
2580  * the 'winfixheight' option.
2581  */
2582     static int
2583 frame_fixed_height(frp)
2584     frame_T	*frp;
2585 {
2586     /* frame with one window: fixed height if 'winfixheight' set. */
2587     if (frp->fr_win != NULL)
2588 	return frp->fr_win->w_p_wfh;
2589 
2590     if (frp->fr_layout == FR_ROW)
2591     {
2592 	/* The frame is fixed height if one of the frames in the row is fixed
2593 	 * height. */
2594 	for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
2595 	    if (frame_fixed_height(frp))
2596 		return TRUE;
2597 	return FALSE;
2598     }
2599 
2600     /* frp->fr_layout == FR_COL: The frame is fixed height if all of the
2601      * frames in the row are fixed height. */
2602     for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
2603 	if (!frame_fixed_height(frp))
2604 	    return FALSE;
2605     return TRUE;
2606 }
2607 
2608 #ifdef FEAT_VERTSPLIT
2609 /*
2610  * Return TRUE if width of frame "frp" should not be changed because of
2611  * the 'winfixwidth' option.
2612  */
2613     static int
2614 frame_fixed_width(frp)
2615     frame_T	*frp;
2616 {
2617     /* frame with one window: fixed width if 'winfixwidth' set. */
2618     if (frp->fr_win != NULL)
2619 	return frp->fr_win->w_p_wfw;
2620 
2621     if (frp->fr_layout == FR_COL)
2622     {
2623 	/* The frame is fixed width if one of the frames in the row is fixed
2624 	 * width. */
2625 	for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
2626 	    if (frame_fixed_width(frp))
2627 		return TRUE;
2628 	return FALSE;
2629     }
2630 
2631     /* frp->fr_layout == FR_ROW: The frame is fixed width if all of the
2632      * frames in the row are fixed width. */
2633     for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
2634 	if (!frame_fixed_width(frp))
2635 	    return FALSE;
2636     return TRUE;
2637 }
2638 
2639 /*
2640  * Add a status line to windows at the bottom of "frp".
2641  * Note: Does not check if there is room!
2642  */
2643     static void
2644 frame_add_statusline(frp)
2645     frame_T	*frp;
2646 {
2647     win_T	*wp;
2648 
2649     if (frp->fr_layout == FR_LEAF)
2650     {
2651 	wp = frp->fr_win;
2652 	if (wp->w_status_height == 0)
2653 	{
2654 	    if (wp->w_height > 0)	/* don't make it negative */
2655 		--wp->w_height;
2656 	    wp->w_status_height = STATUS_HEIGHT;
2657 	}
2658     }
2659     else if (frp->fr_layout == FR_ROW)
2660     {
2661 	/* Handle all the frames in the row. */
2662 	for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
2663 	    frame_add_statusline(frp);
2664     }
2665     else /* frp->fr_layout == FR_COL */
2666     {
2667 	/* Only need to handle the last frame in the column. */
2668 	for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next)
2669 	    ;
2670 	frame_add_statusline(frp);
2671     }
2672 }
2673 
2674 /*
2675  * Set width of a frame.  Handles recursively going through contained frames.
2676  * May remove separator line for windows at the right side (for win_close()).
2677  */
2678     static void
2679 frame_new_width(topfrp, width, leftfirst, wfw)
2680     frame_T	*topfrp;
2681     int		width;
2682     int		leftfirst;	/* resize leftmost contained frame first */
2683     int		wfw;		/* obey 'winfixwidth' when there is a choice;
2684 				   may cause the width not to be set */
2685 {
2686     frame_T	*frp;
2687     int		extra_cols;
2688     int		w;
2689     win_T	*wp;
2690 
2691     if (topfrp->fr_layout == FR_LEAF)
2692     {
2693 	/* Simple case: just one window. */
2694 	wp = topfrp->fr_win;
2695 	/* Find out if there are any windows right of this one. */
2696 	for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent)
2697 	    if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL)
2698 		break;
2699 	if (frp->fr_parent == NULL)
2700 	    wp->w_vsep_width = 0;
2701 	win_new_width(wp, width - wp->w_vsep_width);
2702     }
2703     else if (topfrp->fr_layout == FR_COL)
2704     {
2705 	do
2706 	{
2707 	    /* All frames in this column get the same new width. */
2708 	    for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
2709 	    {
2710 		frame_new_width(frp, width, leftfirst, wfw);
2711 		if (frp->fr_width > width)
2712 		{
2713 		    /* Could not fit the windows, make whole column wider. */
2714 		    width = frp->fr_width;
2715 		    break;
2716 		}
2717 	    }
2718 	} while (frp != NULL);
2719     }
2720     else    /* fr_layout == FR_ROW */
2721     {
2722 	/* Complicated case: Resize a row of frames.  Resize the rightmost
2723 	 * frame first, frames left of it when needed. */
2724 
2725 	frp = topfrp->fr_child;
2726 	if (wfw)
2727 	    /* Advance past frames with one window with 'wfw' set. */
2728 	    while (frame_fixed_width(frp))
2729 	    {
2730 		frp = frp->fr_next;
2731 		if (frp == NULL)
2732 		    return;	    /* no frame without 'wfw', give up */
2733 	    }
2734 	if (!leftfirst)
2735 	{
2736 	    /* Find the rightmost frame of this row */
2737 	    while (frp->fr_next != NULL)
2738 		frp = frp->fr_next;
2739 	    if (wfw)
2740 		/* Advance back for frames with one window with 'wfw' set. */
2741 		while (frame_fixed_width(frp))
2742 		    frp = frp->fr_prev;
2743 	}
2744 
2745 	extra_cols = width - topfrp->fr_width;
2746 	if (extra_cols < 0)
2747 	{
2748 	    /* reduce frame width, rightmost frame first */
2749 	    while (frp != NULL)
2750 	    {
2751 		w = frame_minwidth(frp, NULL);
2752 		if (frp->fr_width + extra_cols < w)
2753 		{
2754 		    extra_cols += frp->fr_width - w;
2755 		    frame_new_width(frp, w, leftfirst, wfw);
2756 		}
2757 		else
2758 		{
2759 		    frame_new_width(frp, frp->fr_width + extra_cols,
2760 							      leftfirst, wfw);
2761 		    break;
2762 		}
2763 		if (leftfirst)
2764 		{
2765 		    do
2766 			frp = frp->fr_next;
2767 		    while (wfw && frp != NULL && frame_fixed_width(frp));
2768 		}
2769 		else
2770 		{
2771 		    do
2772 			frp = frp->fr_prev;
2773 		    while (wfw && frp != NULL && frame_fixed_width(frp));
2774 		}
2775 		/* Increase "width" if we could not reduce enough frames. */
2776 		if (frp == NULL)
2777 		    width -= extra_cols;
2778 	    }
2779 	}
2780 	else if (extra_cols > 0)
2781 	{
2782 	    /* increase width of rightmost frame */
2783 	    frame_new_width(frp, frp->fr_width + extra_cols, leftfirst, wfw);
2784 	}
2785     }
2786     topfrp->fr_width = width;
2787 }
2788 
2789 /*
2790  * Add the vertical separator to windows at the right side of "frp".
2791  * Note: Does not check if there is room!
2792  */
2793     static void
2794 frame_add_vsep(frp)
2795     frame_T	*frp;
2796 {
2797     win_T	*wp;
2798 
2799     if (frp->fr_layout == FR_LEAF)
2800     {
2801 	wp = frp->fr_win;
2802 	if (wp->w_vsep_width == 0)
2803 	{
2804 	    if (wp->w_width > 0)	/* don't make it negative */
2805 		--wp->w_width;
2806 	    wp->w_vsep_width = 1;
2807 	}
2808     }
2809     else if (frp->fr_layout == FR_COL)
2810     {
2811 	/* Handle all the frames in the column. */
2812 	for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
2813 	    frame_add_vsep(frp);
2814     }
2815     else /* frp->fr_layout == FR_ROW */
2816     {
2817 	/* Only need to handle the last frame in the row. */
2818 	frp = frp->fr_child;
2819 	while (frp->fr_next != NULL)
2820 	    frp = frp->fr_next;
2821 	frame_add_vsep(frp);
2822     }
2823 }
2824 
2825 /*
2826  * Set frame width from the window it contains.
2827  */
2828     static void
2829 frame_fix_width(wp)
2830     win_T	*wp;
2831 {
2832     wp->w_frame->fr_width = wp->w_width + wp->w_vsep_width;
2833 }
2834 #endif
2835 
2836 /*
2837  * Set frame height from the window it contains.
2838  */
2839     static void
2840 frame_fix_height(wp)
2841     win_T	*wp;
2842 {
2843     wp->w_frame->fr_height = wp->w_height + wp->w_status_height;
2844 }
2845 
2846 /*
2847  * Compute the minimal height for frame "topfrp".
2848  * Uses the 'winminheight' option.
2849  * When "next_curwin" isn't NULL, use p_wh for this window.
2850  * When "next_curwin" is NOWIN, don't use at least one line for the current
2851  * window.
2852  */
2853     static int
2854 frame_minheight(topfrp, next_curwin)
2855     frame_T	*topfrp;
2856     win_T	*next_curwin;
2857 {
2858     frame_T	*frp;
2859     int		m;
2860 #ifdef FEAT_VERTSPLIT
2861     int		n;
2862 #endif
2863 
2864     if (topfrp->fr_win != NULL)
2865     {
2866 	if (topfrp->fr_win == next_curwin)
2867 	    m = p_wh + topfrp->fr_win->w_status_height;
2868 	else
2869 	{
2870 	    /* window: minimal height of the window plus status line */
2871 	    m = p_wmh + topfrp->fr_win->w_status_height;
2872 	    /* Current window is minimal one line high */
2873 	    if (p_wmh == 0 && topfrp->fr_win == curwin && next_curwin == NULL)
2874 		++m;
2875 	}
2876     }
2877 #ifdef FEAT_VERTSPLIT
2878     else if (topfrp->fr_layout == FR_ROW)
2879     {
2880 	/* get the minimal height from each frame in this row */
2881 	m = 0;
2882 	for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
2883 	{
2884 	    n = frame_minheight(frp, next_curwin);
2885 	    if (n > m)
2886 		m = n;
2887 	}
2888     }
2889 #endif
2890     else
2891     {
2892 	/* Add up the minimal heights for all frames in this column. */
2893 	m = 0;
2894 	for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
2895 	    m += frame_minheight(frp, next_curwin);
2896     }
2897 
2898     return m;
2899 }
2900 
2901 #ifdef FEAT_VERTSPLIT
2902 /*
2903  * Compute the minimal width for frame "topfrp".
2904  * When "next_curwin" isn't NULL, use p_wiw for this window.
2905  * When "next_curwin" is NOWIN, don't use at least one column for the current
2906  * window.
2907  */
2908     static int
2909 frame_minwidth(topfrp, next_curwin)
2910     frame_T	*topfrp;
2911     win_T	*next_curwin;	/* use p_wh and p_wiw for next_curwin */
2912 {
2913     frame_T	*frp;
2914     int		m, n;
2915 
2916     if (topfrp->fr_win != NULL)
2917     {
2918 	if (topfrp->fr_win == next_curwin)
2919 	    m = p_wiw + topfrp->fr_win->w_vsep_width;
2920 	else
2921 	{
2922 	    /* window: minimal width of the window plus separator column */
2923 	    m = p_wmw + topfrp->fr_win->w_vsep_width;
2924 	    /* Current window is minimal one column wide */
2925 	    if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL)
2926 		++m;
2927 	}
2928     }
2929     else if (topfrp->fr_layout == FR_COL)
2930     {
2931 	/* get the minimal width from each frame in this column */
2932 	m = 0;
2933 	for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
2934 	{
2935 	    n = frame_minwidth(frp, next_curwin);
2936 	    if (n > m)
2937 		m = n;
2938 	}
2939     }
2940     else
2941     {
2942 	/* Add up the minimal widths for all frames in this row. */
2943 	m = 0;
2944 	for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
2945 	    m += frame_minwidth(frp, next_curwin);
2946     }
2947 
2948     return m;
2949 }
2950 #endif
2951 
2952 
2953 /*
2954  * Try to close all windows except current one.
2955  * Buffers in the other windows become hidden if 'hidden' is set, or '!' is
2956  * used and the buffer was modified.
2957  *
2958  * Used by ":bdel" and ":only".
2959  */
2960     void
2961 close_others(message, forceit)
2962     int		message;
2963     int		forceit;	    /* always hide all other windows */
2964 {
2965     win_T	*wp;
2966     win_T	*nextwp;
2967     int		r;
2968 
2969     if (lastwin == firstwin)
2970     {
2971 	if (message
2972 #ifdef FEAT_AUTOCMD
2973 		    && !autocmd_busy
2974 #endif
2975 				    )
2976 	    MSG(_(m_onlyone));
2977 	return;
2978     }
2979 
2980     /* Be very careful here: autocommands may change the window layout. */
2981     for (wp = firstwin; win_valid(wp); wp = nextwp)
2982     {
2983 	nextwp = wp->w_next;
2984 	if (wp != curwin)		/* don't close current window */
2985 	{
2986 
2987 	    /* Check if it's allowed to abandon this window */
2988 	    r = can_abandon(wp->w_buffer, forceit);
2989 #ifdef FEAT_AUTOCMD
2990 	    if (!win_valid(wp))		/* autocommands messed wp up */
2991 	    {
2992 		nextwp = firstwin;
2993 		continue;
2994 	    }
2995 #endif
2996 	    if (!r)
2997 	    {
2998 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2999 		if (message && (p_confirm || cmdmod.confirm) && p_write)
3000 		{
3001 		    dialog_changed(wp->w_buffer, FALSE);
3002 # ifdef FEAT_AUTOCMD
3003 		    if (!win_valid(wp))		/* autocommands messed wp up */
3004 		    {
3005 			nextwp = firstwin;
3006 			continue;
3007 		    }
3008 # endif
3009 		}
3010 		if (bufIsChanged(wp->w_buffer))
3011 #endif
3012 		    continue;
3013 	    }
3014 	    win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
3015 	}
3016     }
3017 
3018     if (message && lastwin != firstwin)
3019 	EMSG(_("E445: Other window contains changes"));
3020 }
3021 
3022 #endif /* FEAT_WINDOWS */
3023 
3024 /*
3025  * Init the current window "curwin".
3026  * Called when a new file is being edited.
3027  */
3028     void
3029 curwin_init()
3030 {
3031     redraw_win_later(curwin, NOT_VALID);
3032     curwin->w_lines_valid = 0;
3033     curwin->w_cursor.lnum = 1;
3034     curwin->w_curswant = curwin->w_cursor.col = 0;
3035 #ifdef FEAT_VIRTUALEDIT
3036     curwin->w_cursor.coladd = 0;
3037 #endif
3038     curwin->w_pcmark.lnum = 1;	/* pcmark not cleared but set to line 1 */
3039     curwin->w_pcmark.col = 0;
3040     curwin->w_prev_pcmark.lnum = 0;
3041     curwin->w_prev_pcmark.col = 0;
3042     curwin->w_topline = 1;
3043 #ifdef FEAT_DIFF
3044     curwin->w_topfill = 0;
3045 #endif
3046     curwin->w_botline = 2;
3047 #ifdef FEAT_FKMAP
3048     if (curwin->w_p_rl)
3049 	curwin->w_farsi = W_CONV + W_R_L;
3050     else
3051 	curwin->w_farsi = W_CONV;
3052 #endif
3053 }
3054 
3055 /*
3056  * Allocate the first window and put an empty buffer in it.
3057  * Called from main().
3058  * Return FAIL when something goes wrong (out of memory).
3059  */
3060     int
3061 win_alloc_first()
3062 {
3063     if (win_alloc_firstwin(NULL) == FAIL)
3064 	return FAIL;
3065 
3066 #ifdef FEAT_WINDOWS
3067     first_tabpage = alloc_tabpage();
3068     if (first_tabpage == NULL)
3069 	return FAIL;
3070     first_tabpage->tp_topframe = topframe;
3071     curtab = first_tabpage;
3072 #endif
3073     return OK;
3074 }
3075 
3076 /*
3077  * Allocate the first window or the first window in a new tab page.
3078  * When "oldwin" is NULL create an empty buffer for it.
3079  * When "oldwin" is not NULL copy info from it to the new window (only with
3080  * FEAT_WINDOWS).
3081  * Return FAIL when something goes wrong (out of memory).
3082  */
3083     static int
3084 win_alloc_firstwin(oldwin)
3085     win_T	*oldwin;
3086 {
3087     curwin = win_alloc(NULL);
3088     if (oldwin == NULL)
3089     {
3090 	/* Very first window, need to create an empty buffer for it and
3091 	 * initialize from scratch. */
3092 	curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED);
3093 	if (curwin == NULL || curbuf == NULL)
3094 	    return FAIL;
3095 	curwin->w_buffer = curbuf;
3096 	curbuf->b_nwindows = 1;	/* there is one window */
3097 #ifdef FEAT_WINDOWS
3098 	curwin->w_alist = &global_alist;
3099 #endif
3100 	curwin_init();		/* init current window */
3101     }
3102 #ifdef FEAT_WINDOWS
3103     else
3104     {
3105 	/* First window in new tab page, initialize it from "oldwin". */
3106 	win_init(curwin, oldwin);
3107 
3108 # ifdef FEAT_SCROLLBIND
3109 	/* We don't want scroll-binding in the first window. */
3110 	curwin->w_p_scb = FALSE;
3111 # endif
3112     }
3113 #endif
3114 
3115     topframe = (frame_T *)alloc_clear((unsigned)sizeof(frame_T));
3116     if (topframe == NULL)
3117 	return FAIL;
3118     topframe->fr_layout = FR_LEAF;
3119 #ifdef FEAT_VERTSPLIT
3120     topframe->fr_width = Columns;
3121 #endif
3122     topframe->fr_height = Rows - p_ch;
3123     topframe->fr_win = curwin;
3124     curwin->w_frame = topframe;
3125 
3126     return OK;
3127 }
3128 
3129 /*
3130  * Initialize the window and frame size to the maximum.
3131  */
3132     void
3133 win_init_size()
3134 {
3135     firstwin->w_height = ROWS_AVAIL;
3136     topframe->fr_height = ROWS_AVAIL;
3137 #ifdef FEAT_VERTSPLIT
3138     firstwin->w_width = Columns;
3139     topframe->fr_width = Columns;
3140 #endif
3141 }
3142 
3143 #if defined(FEAT_WINDOWS) || defined(PROTO)
3144 
3145 /*
3146  * Allocate a new tabpage_T and init the values.
3147  * Returns NULL when out of memory.
3148  */
3149     static tabpage_T *
3150 alloc_tabpage()
3151 {
3152     tabpage_T	*tp;
3153 
3154     tp = (tabpage_T *)alloc_clear((unsigned)sizeof(tabpage_T));
3155     if (tp != NULL)
3156     {
3157 # ifdef FEAT_GUI
3158 	int	i;
3159 
3160 	for (i = 0; i < 3; i++)
3161 	    tp->tp_prev_which_scrollbars[i] = -1;
3162 # endif
3163 # ifdef FEAT_DIFF
3164 	tp->tp_diff_invalid = TRUE;
3165 # endif
3166 #ifdef FEAT_EVAL
3167 	/* init t: variables */
3168 	init_var_dict(&tp->tp_vars, &tp->tp_winvar);
3169 #endif
3170 	tp->tp_ch_used = p_ch;
3171     }
3172     return tp;
3173 }
3174 
3175     static void
3176 free_tabpage(tp)
3177     tabpage_T	*tp;
3178 {
3179 # ifdef FEAT_DIFF
3180     diff_clear(tp);
3181 # endif
3182     clear_snapshot(tp);
3183 #ifdef FEAT_EVAL
3184     vars_clear(&tp->tp_vars.dv_hashtab);	/* free all t: variables */
3185 #endif
3186     vim_free(tp);
3187 }
3188 
3189 /*
3190  * Create a new Tab page with one window.
3191  * It will edit the current buffer, like after ":split".
3192  * When "after" is 0 put it just after the current Tab page.
3193  * Otherwise put it just before tab page "after".
3194  * Return FAIL or OK.
3195  */
3196     int
3197 win_new_tabpage(after)
3198     int		after;
3199 {
3200     tabpage_T	*tp = curtab;
3201     tabpage_T	*newtp;
3202     int		n;
3203 
3204     newtp = alloc_tabpage();
3205     if (newtp == NULL)
3206 	return FAIL;
3207 
3208     /* Remember the current windows in this Tab page. */
3209     if (leave_tabpage(curbuf) == FAIL)
3210     {
3211 	vim_free(newtp);
3212 	return FAIL;
3213     }
3214     curtab = newtp;
3215 
3216     /* Create a new empty window. */
3217     if (win_alloc_firstwin(tp->tp_curwin) == OK)
3218     {
3219 	/* Make the new Tab page the new topframe. */
3220 	if (after == 1)
3221 	{
3222 	    /* New tab page becomes the first one. */
3223 	    newtp->tp_next = first_tabpage;
3224 	    first_tabpage = newtp;
3225 	}
3226 	else
3227 	{
3228 	    if (after > 0)
3229 	    {
3230 		/* Put new tab page before tab page "after". */
3231 		n = 2;
3232 		for (tp = first_tabpage; tp->tp_next != NULL
3233 					       && n < after; tp = tp->tp_next)
3234 		    ++n;
3235 	    }
3236 	    newtp->tp_next = tp->tp_next;
3237 	    tp->tp_next = newtp;
3238 	}
3239 	win_init_size();
3240 	firstwin->w_winrow = tabline_height();
3241 	win_comp_scroll(curwin);
3242 
3243 	newtp->tp_topframe = topframe;
3244 	last_status(FALSE);
3245 
3246 #if defined(FEAT_GUI)
3247 	/* When 'guioptions' includes 'L' or 'R' may have to remove or add
3248 	 * scrollbars.  Have to update them anyway. */
3249 	if (gui.in_use && starting == 0)
3250 	{
3251 	    gui_init_which_components(NULL);
3252 	    gui_update_scrollbars(TRUE);
3253 	}
3254 	need_mouse_correct = TRUE;
3255 #endif
3256 
3257 	redraw_all_later(CLEAR);
3258 #ifdef FEAT_AUTOCMD
3259 	apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
3260 	apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
3261 #endif
3262 	return OK;
3263     }
3264 
3265     /* Failed, get back the previous Tab page */
3266     enter_tabpage(curtab, curbuf);
3267     return FAIL;
3268 }
3269 
3270 /*
3271  * Open a new tab page if ":tab cmd" was used.  It will edit the same buffer,
3272  * like with ":split".
3273  * Returns OK if a new tab page was created, FAIL otherwise.
3274  */
3275     int
3276 may_open_tabpage()
3277 {
3278     int		n = cmdmod.tab;
3279 
3280     if (cmdmod.tab != 0)
3281     {
3282 	cmdmod.tab = 0;	    /* reset it to avoid doing it twice */
3283 	return win_new_tabpage(n);
3284     }
3285     return FAIL;
3286 }
3287 
3288 /*
3289  * Create up to "maxcount" tabpages with empty windows.
3290  * Returns the number of resulting tab pages.
3291  */
3292     int
3293 make_tabpages(maxcount)
3294     int		maxcount;
3295 {
3296     int		count = maxcount;
3297     int		todo;
3298 
3299     /* Limit to 'tabpagemax' tabs. */
3300     if (count > p_tpm)
3301 	count = p_tpm;
3302 
3303 #ifdef FEAT_AUTOCMD
3304     /*
3305      * Don't execute autocommands while creating the tab pages.  Must do that
3306      * when putting the buffers in the windows.
3307      */
3308     ++autocmd_block;
3309 #endif
3310 
3311     for (todo = count - 1; todo > 0; --todo)
3312 	if (win_new_tabpage(0) == FAIL)
3313 	    break;
3314 
3315 #ifdef FEAT_AUTOCMD
3316     --autocmd_block;
3317 #endif
3318 
3319     /* return actual number of tab pages */
3320     return (count - todo);
3321 }
3322 
3323 /*
3324  * Return TRUE when "tpc" points to a valid tab page.
3325  */
3326     int
3327 valid_tabpage(tpc)
3328     tabpage_T	*tpc;
3329 {
3330     tabpage_T	*tp;
3331 
3332     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
3333 	if (tp == tpc)
3334 	    return TRUE;
3335     return FALSE;
3336 }
3337 
3338 /*
3339  * Find tab page "n" (first one is 1).  Returns NULL when not found.
3340  */
3341     tabpage_T *
3342 find_tabpage(n)
3343     int		n;
3344 {
3345     tabpage_T	*tp;
3346     int		i = 1;
3347 
3348     for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next)
3349 	++i;
3350     return tp;
3351 }
3352 
3353 /*
3354  * Get index of tab page "tp".  First one has index 1.
3355  * When not found returns number of tab pages plus one.
3356  */
3357     int
3358 tabpage_index(ftp)
3359     tabpage_T	*ftp;
3360 {
3361     int		i = 1;
3362     tabpage_T	*tp;
3363 
3364     for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next)
3365 	++i;
3366     return i;
3367 }
3368 
3369 /*
3370  * Prepare for leaving the current tab page.
3371  * When autocomands change "curtab" we don't leave the tab page and return
3372  * FAIL.
3373  * Careful: When OK is returned need to get a new tab page very very soon!
3374  */
3375 /*ARGSUSED*/
3376     static int
3377 leave_tabpage(new_curbuf)
3378     buf_T	*new_curbuf;	    /* what is going to be the new curbuf,
3379 				       NULL if unknown */
3380 {
3381     tabpage_T	*tp = curtab;
3382 
3383 #ifdef FEAT_VISUAL
3384     reset_VIsual_and_resel();	/* stop Visual mode */
3385 #endif
3386 #ifdef FEAT_AUTOCMD
3387     if (new_curbuf != curbuf)
3388     {
3389 	apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
3390 	if (curtab != tp)
3391 	    return FAIL;
3392     }
3393     apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
3394     if (curtab != tp)
3395 	return FAIL;
3396     apply_autocmds(EVENT_TABLEAVE, NULL, NULL, FALSE, curbuf);
3397     if (curtab != tp)
3398 	return FAIL;
3399 #endif
3400 #if defined(FEAT_GUI)
3401     /* Remove the scrollbars.  They may be added back later. */
3402     if (gui.in_use)
3403 	gui_remove_scrollbars();
3404 #endif
3405     tp->tp_curwin = curwin;
3406     tp->tp_prevwin = prevwin;
3407     tp->tp_firstwin = firstwin;
3408     tp->tp_lastwin = lastwin;
3409     tp->tp_old_Rows = Rows;
3410     tp->tp_old_Columns = Columns;
3411     firstwin = NULL;
3412     lastwin = NULL;
3413     return OK;
3414 }
3415 
3416 /*
3417  * Start using tab page "tp".
3418  * Only to be used after leave_tabpage() or freeing the current tab page.
3419  */
3420 /*ARGSUSED*/
3421     static void
3422 enter_tabpage(tp, old_curbuf)
3423     tabpage_T	*tp;
3424     buf_T	*old_curbuf;
3425 {
3426     int		old_off = tp->tp_firstwin->w_winrow;
3427 
3428     curtab = tp;
3429     firstwin = tp->tp_firstwin;
3430     lastwin = tp->tp_lastwin;
3431     topframe = tp->tp_topframe;
3432 #ifdef FEAT_AUTOCMD
3433     apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
3434 #endif
3435 
3436     win_enter_ext(tp->tp_curwin, FALSE, TRUE);
3437     prevwin = tp->tp_prevwin;
3438 
3439 #ifdef FEAT_AUTOCMD
3440     if (old_curbuf != curbuf)
3441 	apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
3442 #endif
3443 
3444     last_status(FALSE);		/* status line may appear or disappear */
3445     (void)win_comp_pos();	/* recompute w_winrow for all windows */
3446     must_redraw = CLEAR;	/* need to redraw everything */
3447 #ifdef FEAT_DIFF
3448     diff_need_scrollbind = TRUE;
3449 #endif
3450 
3451     /* The tabpage line may have appeared or disappeared, may need to resize
3452      * the frames for that.  When the Vim window was resized need to update
3453      * frame sizes too.  Use the stored value of p_ch, so that it can be
3454      * different for each tab page. */
3455     p_ch = curtab->tp_ch_used;
3456     if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow
3457 #ifdef FEAT_GUI_TABLINE
3458 			    && !gui_use_tabline()
3459 #endif
3460 		))
3461 	shell_new_rows();
3462 #ifdef FEAT_VERTSPLIT
3463     if (curtab->tp_old_Columns != Columns && starting == 0)
3464 	shell_new_columns();	/* update window widths */
3465 #endif
3466 
3467 #if defined(FEAT_GUI)
3468     /* When 'guioptions' includes 'L' or 'R' may have to remove or add
3469      * scrollbars.  Have to update them anyway. */
3470     if (gui.in_use && starting == 0)
3471     {
3472 	gui_init_which_components(NULL);
3473 	gui_update_scrollbars(TRUE);
3474     }
3475     need_mouse_correct = TRUE;
3476 #endif
3477 
3478     redraw_all_later(CLEAR);
3479 }
3480 
3481 /*
3482  * Go to tab page "n".  For ":tab N" and "Ngt".
3483  * When "n" is 9999 go to the last tab page.
3484  */
3485     void
3486 goto_tabpage(n)
3487     int	    n;
3488 {
3489     tabpage_T	*tp;
3490     tabpage_T	*ttp;
3491     int		i;
3492 
3493     /* If there is only one it can't work. */
3494     if (first_tabpage->tp_next == NULL)
3495     {
3496 	if (n > 1)
3497 	    beep_flush();
3498 	return;
3499     }
3500 
3501     if (n == 0)
3502     {
3503 	/* No count, go to next tab page, wrap around end. */
3504 	if (curtab->tp_next == NULL)
3505 	    tp = first_tabpage;
3506 	else
3507 	    tp = curtab->tp_next;
3508     }
3509     else if (n < 0)
3510     {
3511 	/* "gT": go to previous tab page, wrap around end.  "N gT" repeats
3512 	 * this N times. */
3513 	ttp = curtab;
3514 	for (i = n; i < 0; ++i)
3515 	{
3516 	    for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL;
3517 		    tp = tp->tp_next)
3518 		;
3519 	    ttp = tp;
3520 	}
3521     }
3522     else if (n == 9999)
3523     {
3524 	/* Go to last tab page. */
3525 	for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next)
3526 	    ;
3527     }
3528     else
3529     {
3530 	/* Go to tab page "n". */
3531 	tp = find_tabpage(n);
3532 	if (tp == NULL)
3533 	{
3534 	    beep_flush();
3535 	    return;
3536 	}
3537     }
3538 
3539     goto_tabpage_tp(tp);
3540 
3541 #ifdef FEAT_GUI_TABLINE
3542     if (gui_use_tabline())
3543 	gui_mch_set_curtab(tabpage_index(curtab));
3544 #endif
3545 }
3546 
3547 /*
3548  * Go to tabpage "tp".
3549  * Note: doesn't update the GUI tab.
3550  */
3551     void
3552 goto_tabpage_tp(tp)
3553     tabpage_T	*tp;
3554 {
3555     if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer) == OK)
3556     {
3557 	if (valid_tabpage(tp))
3558 	    enter_tabpage(tp, curbuf);
3559 	else
3560 	    enter_tabpage(curtab, curbuf);
3561     }
3562 }
3563 
3564 /*
3565  * Enter window "wp" in tab page "tp".
3566  * Also updates the GUI tab.
3567  */
3568     void
3569 goto_tabpage_win(tp, wp)
3570     tabpage_T	*tp;
3571     win_T	*wp;
3572 {
3573     goto_tabpage_tp(tp);
3574     if (curtab == tp && win_valid(wp))
3575     {
3576 	win_enter(wp, TRUE);
3577 # ifdef FEAT_GUI_TABLINE
3578 	if (gui_use_tabline())
3579 	    gui_mch_set_curtab(tabpage_index(curtab));
3580 # endif
3581     }
3582 }
3583 
3584 /*
3585  * Move the current tab page to before tab page "nr".
3586  */
3587     void
3588 tabpage_move(nr)
3589     int		nr;
3590 {
3591     int		n = nr;
3592     tabpage_T	*tp;
3593 
3594     if (first_tabpage->tp_next == NULL)
3595 	return;
3596 
3597     /* Remove the current tab page from the list of tab pages. */
3598     if (curtab == first_tabpage)
3599 	first_tabpage = curtab->tp_next;
3600     else
3601     {
3602 	for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
3603 	    if (tp->tp_next == curtab)
3604 		break;
3605 	if (tp == NULL)	/* "cannot happen" */
3606 	    return;
3607 	tp->tp_next = curtab->tp_next;
3608     }
3609 
3610     /* Re-insert it at the specified position. */
3611     if (n == 0)
3612     {
3613 	curtab->tp_next = first_tabpage;
3614 	first_tabpage = curtab;
3615     }
3616     else
3617     {
3618 	for (tp = first_tabpage; tp->tp_next != NULL && n > 1; tp = tp->tp_next)
3619 	    --n;
3620 	curtab->tp_next = tp->tp_next;
3621 	tp->tp_next = curtab;
3622     }
3623 
3624     /* Need to redraw the tabline.  Tab page contents doesn't change. */
3625     redraw_tabline = TRUE;
3626 }
3627 
3628 
3629 /*
3630  * Go to another window.
3631  * When jumping to another buffer, stop Visual mode.  Do this before
3632  * changing windows so we can yank the selection into the '*' register.
3633  * When jumping to another window on the same buffer, adjust its cursor
3634  * position to keep the same Visual area.
3635  */
3636     void
3637 win_goto(wp)
3638     win_T	*wp;
3639 {
3640     if (text_locked())
3641     {
3642 	beep_flush();
3643 	text_locked_msg();
3644 	return;
3645     }
3646 #ifdef FEAT_AUTOCMD
3647     if (curbuf_locked())
3648 	return;
3649 #endif
3650 
3651 #ifdef FEAT_VISUAL
3652     if (wp->w_buffer != curbuf)
3653 	reset_VIsual_and_resel();
3654     else if (VIsual_active)
3655 	wp->w_cursor = curwin->w_cursor;
3656 #endif
3657 
3658 #ifdef FEAT_GUI
3659     need_mouse_correct = TRUE;
3660 #endif
3661     win_enter(wp, TRUE);
3662 }
3663 
3664 #if defined(FEAT_PERL) || defined(PROTO)
3665 /*
3666  * Find window number "winnr" (counting top to bottom).
3667  */
3668     win_T *
3669 win_find_nr(winnr)
3670     int		winnr;
3671 {
3672     win_T	*wp;
3673 
3674 # ifdef FEAT_WINDOWS
3675     for (wp = firstwin; wp != NULL; wp = wp->w_next)
3676 	if (--winnr == 0)
3677 	    break;
3678     return wp;
3679 # else
3680     return curwin;
3681 # endif
3682 }
3683 #endif
3684 
3685 #ifdef FEAT_VERTSPLIT
3686 /*
3687  * Move to window above or below "count" times.
3688  */
3689     static void
3690 win_goto_ver(up, count)
3691     int		up;		/* TRUE to go to win above */
3692     long	count;
3693 {
3694     frame_T	*fr;
3695     frame_T	*nfr;
3696     frame_T	*foundfr;
3697 
3698     foundfr = curwin->w_frame;
3699     while (count--)
3700     {
3701 	/*
3702 	 * First go upwards in the tree of frames until we find a upwards or
3703 	 * downwards neighbor.
3704 	 */
3705 	fr = foundfr;
3706 	for (;;)
3707 	{
3708 	    if (fr == topframe)
3709 		goto end;
3710 	    if (up)
3711 		nfr = fr->fr_prev;
3712 	    else
3713 		nfr = fr->fr_next;
3714 	    if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL)
3715 		break;
3716 	    fr = fr->fr_parent;
3717 	}
3718 
3719 	/*
3720 	 * Now go downwards to find the bottom or top frame in it.
3721 	 */
3722 	for (;;)
3723 	{
3724 	    if (nfr->fr_layout == FR_LEAF)
3725 	    {
3726 		foundfr = nfr;
3727 		break;
3728 	    }
3729 	    fr = nfr->fr_child;
3730 	    if (nfr->fr_layout == FR_ROW)
3731 	    {
3732 		/* Find the frame at the cursor row. */
3733 		while (fr->fr_next != NULL
3734 			&& frame2win(fr)->w_wincol + fr->fr_width
3735 					 <= curwin->w_wincol + curwin->w_wcol)
3736 		    fr = fr->fr_next;
3737 	    }
3738 	    if (nfr->fr_layout == FR_COL && up)
3739 		while (fr->fr_next != NULL)
3740 		    fr = fr->fr_next;
3741 	    nfr = fr;
3742 	}
3743     }
3744 end:
3745     if (foundfr != NULL)
3746 	win_goto(foundfr->fr_win);
3747 }
3748 
3749 /*
3750  * Move to left or right window.
3751  */
3752     static void
3753 win_goto_hor(left, count)
3754     int		left;		/* TRUE to go to left win */
3755     long	count;
3756 {
3757     frame_T	*fr;
3758     frame_T	*nfr;
3759     frame_T	*foundfr;
3760 
3761     foundfr = curwin->w_frame;
3762     while (count--)
3763     {
3764 	/*
3765 	 * First go upwards in the tree of frames until we find a left or
3766 	 * right neighbor.
3767 	 */
3768 	fr = foundfr;
3769 	for (;;)
3770 	{
3771 	    if (fr == topframe)
3772 		goto end;
3773 	    if (left)
3774 		nfr = fr->fr_prev;
3775 	    else
3776 		nfr = fr->fr_next;
3777 	    if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
3778 		break;
3779 	    fr = fr->fr_parent;
3780 	}
3781 
3782 	/*
3783 	 * Now go downwards to find the leftmost or rightmost frame in it.
3784 	 */
3785 	for (;;)
3786 	{
3787 	    if (nfr->fr_layout == FR_LEAF)
3788 	    {
3789 		foundfr = nfr;
3790 		break;
3791 	    }
3792 	    fr = nfr->fr_child;
3793 	    if (nfr->fr_layout == FR_COL)
3794 	    {
3795 		/* Find the frame at the cursor row. */
3796 		while (fr->fr_next != NULL
3797 			&& frame2win(fr)->w_winrow + fr->fr_height
3798 					 <= curwin->w_winrow + curwin->w_wrow)
3799 		    fr = fr->fr_next;
3800 	    }
3801 	    if (nfr->fr_layout == FR_ROW && left)
3802 		while (fr->fr_next != NULL)
3803 		    fr = fr->fr_next;
3804 	    nfr = fr;
3805 	}
3806     }
3807 end:
3808     if (foundfr != NULL)
3809 	win_goto(foundfr->fr_win);
3810 }
3811 #endif
3812 
3813 /*
3814  * Make window "wp" the current window.
3815  */
3816     void
3817 win_enter(wp, undo_sync)
3818     win_T	*wp;
3819     int		undo_sync;
3820 {
3821     win_enter_ext(wp, undo_sync, FALSE);
3822 }
3823 
3824 /*
3825  * Make window wp the current window.
3826  * Can be called with "curwin_invalid" TRUE, which means that curwin has just
3827  * been closed and isn't valid.
3828  */
3829     static void
3830 win_enter_ext(wp, undo_sync, curwin_invalid)
3831     win_T	*wp;
3832     int		undo_sync;
3833     int		curwin_invalid;
3834 {
3835 #ifdef FEAT_AUTOCMD
3836     int		other_buffer = FALSE;
3837 #endif
3838 
3839     if (wp == curwin && !curwin_invalid)	/* nothing to do */
3840 	return;
3841 
3842 #ifdef FEAT_AUTOCMD
3843     if (!curwin_invalid)
3844     {
3845 	/*
3846 	 * Be careful: If autocommands delete the window, return now.
3847 	 */
3848 	if (wp->w_buffer != curbuf)
3849 	{
3850 	    apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
3851 	    other_buffer = TRUE;
3852 	    if (!win_valid(wp))
3853 		return;
3854 	}
3855 	apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
3856 	if (!win_valid(wp))
3857 	    return;
3858 # ifdef FEAT_EVAL
3859 	/* autocmds may abort script processing */
3860 	if (aborting())
3861 	    return;
3862 # endif
3863     }
3864 #endif
3865 
3866     /* sync undo before leaving the current buffer */
3867     if (undo_sync && curbuf != wp->w_buffer)
3868 	u_sync(FALSE);
3869     /* may have to copy the buffer options when 'cpo' contains 'S' */
3870     if (wp->w_buffer != curbuf)
3871 	buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP);
3872     if (!curwin_invalid)
3873     {
3874 	prevwin = curwin;	/* remember for CTRL-W p */
3875 	curwin->w_redr_status = TRUE;
3876     }
3877     curwin = wp;
3878     curbuf = wp->w_buffer;
3879     check_cursor();
3880 #ifdef FEAT_VIRTUALEDIT
3881     if (!virtual_active())
3882 	curwin->w_cursor.coladd = 0;
3883 #endif
3884     changed_line_abv_curs();	/* assume cursor position needs updating */
3885 
3886     if (curwin->w_localdir != NULL)
3887     {
3888 	/* Window has a local directory: Save current directory as global
3889 	 * directory (unless that was done already) and change to the local
3890 	 * directory. */
3891 	if (globaldir == NULL)
3892 	{
3893 	    char_u	cwd[MAXPATHL];
3894 
3895 	    if (mch_dirname(cwd, MAXPATHL) == OK)
3896 		globaldir = vim_strsave(cwd);
3897 	}
3898 	mch_chdir((char *)curwin->w_localdir);
3899 	shorten_fnames(TRUE);
3900     }
3901     else if (globaldir != NULL)
3902     {
3903 	/* Window doesn't have a local directory and we are not in the global
3904 	 * directory: Change to the global directory. */
3905 	mch_chdir((char *)globaldir);
3906 	vim_free(globaldir);
3907 	globaldir = NULL;
3908 	shorten_fnames(TRUE);
3909     }
3910 
3911 #ifdef FEAT_AUTOCMD
3912     apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
3913     if (other_buffer)
3914 	apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
3915 #endif
3916 
3917 #ifdef FEAT_TITLE
3918     maketitle();
3919 #endif
3920     curwin->w_redr_status = TRUE;
3921     redraw_tabline = TRUE;
3922     if (restart_edit)
3923 	redraw_later(VALID);	/* causes status line redraw */
3924 
3925     /* set window height to desired minimal value */
3926     if (curwin->w_height < p_wh && !curwin->w_p_wfh)
3927 	win_setheight((int)p_wh);
3928     else if (curwin->w_height == 0)
3929 	win_setheight(1);
3930 
3931 #ifdef FEAT_VERTSPLIT
3932     /* set window width to desired minimal value */
3933     if (curwin->w_width < p_wiw && !curwin->w_p_wfw)
3934 	win_setwidth((int)p_wiw);
3935 #endif
3936 
3937 #ifdef FEAT_MOUSE
3938     setmouse();			/* in case jumped to/from help buffer */
3939 #endif
3940 
3941 #ifdef FEAT_AUTOCHDIR
3942     /* Change directories when the 'acd' option is set on and after
3943      * switching windows. */
3944     if (p_acd && curbuf->b_ffname != NULL
3945 				     && vim_chdirfile(curbuf->b_ffname) == OK)
3946 	shorten_fnames(TRUE);
3947 #endif
3948 }
3949 
3950 #endif /* FEAT_WINDOWS */
3951 
3952 #if defined(FEAT_WINDOWS) || defined(FEAT_SIGNS) || defined(PROTO)
3953 /*
3954  * Jump to the first open window that contains buffer "buf", if one exists.
3955  * Returns a pointer to the window found, otherwise NULL.
3956  */
3957     win_T *
3958 buf_jump_open_win(buf)
3959     buf_T	*buf;
3960 {
3961 # ifdef FEAT_WINDOWS
3962     win_T	*wp;
3963 
3964     for (wp = firstwin; wp != NULL; wp = wp->w_next)
3965 	if (wp->w_buffer == buf)
3966 	    break;
3967     if (wp != NULL)
3968 	win_enter(wp, FALSE);
3969     return wp;
3970 # else
3971     if (curwin->w_buffer == buf)
3972 	return curwin;
3973     return NULL;
3974 # endif
3975 }
3976 
3977 /*
3978  * Jump to the first open window in any tab page that contains buffer "buf",
3979  * if one exists.
3980  * Returns a pointer to the window found, otherwise NULL.
3981  */
3982     win_T *
3983 buf_jump_open_tab(buf)
3984     buf_T	*buf;
3985 {
3986 # ifdef FEAT_WINDOWS
3987     win_T	*wp;
3988     tabpage_T	*tp;
3989 
3990     /* First try the current tab page. */
3991     wp = buf_jump_open_win(buf);
3992     if (wp != NULL)
3993 	return wp;
3994 
3995     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
3996 	if (tp != curtab)
3997 	{
3998 	    for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
3999 		if (wp->w_buffer == buf)
4000 		    break;
4001 	    if (wp != NULL)
4002 	    {
4003 		goto_tabpage_win(tp, wp);
4004 		if (curwin != wp)
4005 		    wp = NULL;	/* something went wrong */
4006 		break;
4007 	    }
4008 	}
4009 
4010     return wp;
4011 # else
4012     if (curwin->w_buffer == buf)
4013 	return curwin;
4014     return NULL;
4015 # endif
4016 }
4017 #endif
4018 
4019 /*
4020  * allocate a window structure and link it in the window list
4021  */
4022 /*ARGSUSED*/
4023     static win_T *
4024 win_alloc(after)
4025     win_T	*after;
4026 {
4027     win_T	*newwin;
4028 
4029     /*
4030      * allocate window structure and linesizes arrays
4031      */
4032     newwin = (win_T *)alloc_clear((unsigned)sizeof(win_T));
4033     if (newwin != NULL && win_alloc_lines(newwin) == FAIL)
4034     {
4035 	vim_free(newwin);
4036 	newwin = NULL;
4037     }
4038 
4039     if (newwin != NULL)
4040     {
4041 	/*
4042 	 * link the window in the window list
4043 	 */
4044 #ifdef FEAT_WINDOWS
4045 	win_append(after, newwin);
4046 #endif
4047 #ifdef FEAT_VERTSPLIT
4048 	newwin->w_wincol = 0;
4049 	newwin->w_width = Columns;
4050 #endif
4051 
4052 	/* position the display and the cursor at the top of the file. */
4053 	newwin->w_topline = 1;
4054 #ifdef FEAT_DIFF
4055 	newwin->w_topfill = 0;
4056 #endif
4057 	newwin->w_botline = 2;
4058 	newwin->w_cursor.lnum = 1;
4059 #ifdef FEAT_SCROLLBIND
4060 	newwin->w_scbind_pos = 1;
4061 #endif
4062 
4063 	/* We won't calculate w_fraction until resizing the window */
4064 	newwin->w_fraction = 0;
4065 	newwin->w_prev_fraction_row = -1;
4066 
4067 #ifdef FEAT_GUI
4068 	if (gui.in_use)
4069 	{
4070 	    out_flush();
4071 	    gui_create_scrollbar(&newwin->w_scrollbars[SBAR_LEFT],
4072 		    SBAR_LEFT, newwin);
4073 	    gui_create_scrollbar(&newwin->w_scrollbars[SBAR_RIGHT],
4074 		    SBAR_RIGHT, newwin);
4075 	}
4076 #endif
4077 #ifdef FEAT_EVAL
4078 	/* init w: variables */
4079 	init_var_dict(&newwin->w_vars, &newwin->w_winvar);
4080 #endif
4081 #ifdef FEAT_FOLDING
4082 	foldInitWin(newwin);
4083 #endif
4084     }
4085     return newwin;
4086 }
4087 
4088 #if defined(FEAT_WINDOWS) || defined(PROTO)
4089 
4090 /*
4091  * remove window 'wp' from the window list and free the structure
4092  */
4093     static void
4094 win_free(wp, tp)
4095     win_T	*wp;
4096     tabpage_T	*tp;		/* tab page "win" is in, NULL for current */
4097 {
4098     int		i;
4099 
4100 #ifdef FEAT_MZSCHEME
4101     mzscheme_window_free(wp);
4102 #endif
4103 
4104 #ifdef FEAT_PERL
4105     perl_win_free(wp);
4106 #endif
4107 
4108 #ifdef FEAT_PYTHON
4109     python_window_free(wp);
4110 #endif
4111 
4112 #ifdef FEAT_TCL
4113     tcl_window_free(wp);
4114 #endif
4115 
4116 #ifdef FEAT_RUBY
4117     ruby_window_free(wp);
4118 #endif
4119 
4120     clear_winopt(&wp->w_onebuf_opt);
4121     clear_winopt(&wp->w_allbuf_opt);
4122 
4123 #ifdef FEAT_EVAL
4124     vars_clear(&wp->w_vars.dv_hashtab);	    /* free all w: variables */
4125 #endif
4126 
4127     if (prevwin == wp)
4128 	prevwin = NULL;
4129     win_free_lsize(wp);
4130 
4131     for (i = 0; i < wp->w_tagstacklen; ++i)
4132 	vim_free(wp->w_tagstack[i].tagname);
4133 
4134     vim_free(wp->w_localdir);
4135 #ifdef FEAT_SEARCH_EXTRA
4136     vim_free(wp->w_match[0].regprog);
4137     vim_free(wp->w_match[1].regprog);
4138     vim_free(wp->w_match[2].regprog);
4139 #endif
4140 #ifdef FEAT_JUMPLIST
4141     free_jumplist(wp);
4142 #endif
4143 
4144 #ifdef FEAT_QUICKFIX
4145     qf_free_all(wp);
4146 #endif
4147 
4148 #ifdef FEAT_GUI
4149     if (gui.in_use)
4150     {
4151 	out_flush();
4152 	gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]);
4153 	gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]);
4154     }
4155 #endif /* FEAT_GUI */
4156 
4157     win_remove(wp, tp);
4158     vim_free(wp);
4159 }
4160 
4161 /*
4162  * Append window "wp" in the window list after window "after".
4163  */
4164     static void
4165 win_append(after, wp)
4166     win_T	*after, *wp;
4167 {
4168     win_T	*before;
4169 
4170     if (after == NULL)	    /* after NULL is in front of the first */
4171 	before = firstwin;
4172     else
4173 	before = after->w_next;
4174 
4175     wp->w_next = before;
4176     wp->w_prev = after;
4177     if (after == NULL)
4178 	firstwin = wp;
4179     else
4180 	after->w_next = wp;
4181     if (before == NULL)
4182 	lastwin = wp;
4183     else
4184 	before->w_prev = wp;
4185 }
4186 
4187 /*
4188  * Remove a window from the window list.
4189  */
4190     static void
4191 win_remove(wp, tp)
4192     win_T	*wp;
4193     tabpage_T	*tp;		/* tab page "win" is in, NULL for current */
4194 {
4195     if (wp->w_prev != NULL)
4196 	wp->w_prev->w_next = wp->w_next;
4197     else if (tp == NULL)
4198 	firstwin = wp->w_next;
4199     else
4200 	tp->tp_firstwin = wp->w_next;
4201     if (wp->w_next != NULL)
4202 	wp->w_next->w_prev = wp->w_prev;
4203     else if (tp == NULL)
4204 	lastwin = wp->w_prev;
4205     else
4206 	tp->tp_lastwin = wp->w_prev;
4207 }
4208 
4209 /*
4210  * Append frame "frp" in a frame list after frame "after".
4211  */
4212     static void
4213 frame_append(after, frp)
4214     frame_T	*after, *frp;
4215 {
4216     frp->fr_next = after->fr_next;
4217     after->fr_next = frp;
4218     if (frp->fr_next != NULL)
4219 	frp->fr_next->fr_prev = frp;
4220     frp->fr_prev = after;
4221 }
4222 
4223 /*
4224  * Insert frame "frp" in a frame list before frame "before".
4225  */
4226     static void
4227 frame_insert(before, frp)
4228     frame_T	*before, *frp;
4229 {
4230     frp->fr_next = before;
4231     frp->fr_prev = before->fr_prev;
4232     before->fr_prev = frp;
4233     if (frp->fr_prev != NULL)
4234 	frp->fr_prev->fr_next = frp;
4235     else
4236 	frp->fr_parent->fr_child = frp;
4237 }
4238 
4239 /*
4240  * Remove a frame from a frame list.
4241  */
4242     static void
4243 frame_remove(frp)
4244     frame_T	*frp;
4245 {
4246     if (frp->fr_prev != NULL)
4247 	frp->fr_prev->fr_next = frp->fr_next;
4248     else
4249 	frp->fr_parent->fr_child = frp->fr_next;
4250     if (frp->fr_next != NULL)
4251 	frp->fr_next->fr_prev = frp->fr_prev;
4252 }
4253 
4254 #endif /* FEAT_WINDOWS */
4255 
4256 /*
4257  * Allocate w_lines[] for window "wp".
4258  * Return FAIL for failure, OK for success.
4259  */
4260     int
4261 win_alloc_lines(wp)
4262     win_T	*wp;
4263 {
4264     wp->w_lines_valid = 0;
4265     wp->w_lines = (wline_T *)alloc((unsigned)(Rows * sizeof(wline_T)));
4266     if (wp->w_lines == NULL)
4267 	return FAIL;
4268     return OK;
4269 }
4270 
4271 /*
4272  * free lsize arrays for a window
4273  */
4274     void
4275 win_free_lsize(wp)
4276     win_T	*wp;
4277 {
4278     vim_free(wp->w_lines);
4279     wp->w_lines = NULL;
4280 }
4281 
4282 /*
4283  * Called from win_new_shellsize() after Rows changed.
4284  * This only does the current tab page, others must be done when made active.
4285  */
4286     void
4287 shell_new_rows()
4288 {
4289     int		h = (int)ROWS_AVAIL;
4290 
4291     if (firstwin == NULL)	/* not initialized yet */
4292 	return;
4293 #ifdef FEAT_WINDOWS
4294     if (h < frame_minheight(topframe, NULL))
4295 	h = frame_minheight(topframe, NULL);
4296 
4297     /* First try setting the heights of windows with 'winfixheight'.  If
4298      * that doesn't result in the right height, forget about that option. */
4299     frame_new_height(topframe, h, FALSE, TRUE);
4300     if (topframe->fr_height != h)
4301 	frame_new_height(topframe, h, FALSE, FALSE);
4302 
4303     (void)win_comp_pos();		/* recompute w_winrow and w_wincol */
4304 #else
4305     if (h < 1)
4306 	h = 1;
4307     win_new_height(firstwin, h);
4308 #endif
4309     compute_cmdrow();
4310 #ifdef FEAT_WINDOWS
4311     curtab->tp_ch_used = p_ch;
4312 #endif
4313 
4314 #if 0
4315     /* Disabled: don't want making the screen smaller make a window larger. */
4316     if (p_ea)
4317 	win_equal(curwin, FALSE, 'v');
4318 #endif
4319 }
4320 
4321 #if defined(FEAT_VERTSPLIT) || defined(PROTO)
4322 /*
4323  * Called from win_new_shellsize() after Columns changed.
4324  */
4325     void
4326 shell_new_columns()
4327 {
4328     if (firstwin == NULL)	/* not initialized yet */
4329 	return;
4330 
4331     /* First try setting the widths of windows with 'winfixwidth'.  If that
4332      * doesn't result in the right width, forget about that option. */
4333     frame_new_width(topframe, (int)Columns, FALSE, TRUE);
4334     if (topframe->fr_width != Columns)
4335 	frame_new_width(topframe, (int)Columns, FALSE, FALSE);
4336 
4337     (void)win_comp_pos();		/* recompute w_winrow and w_wincol */
4338 #if 0
4339     /* Disabled: don't want making the screen smaller make a window larger. */
4340     if (p_ea)
4341 	win_equal(curwin, FALSE, 'h');
4342 #endif
4343 }
4344 #endif
4345 
4346 #if defined(FEAT_CMDWIN) || defined(PROTO)
4347 /*
4348  * Save the size of all windows in "gap".
4349  */
4350     void
4351 win_size_save(gap)
4352     garray_T	*gap;
4353 
4354 {
4355     win_T	*wp;
4356 
4357     ga_init2(gap, (int)sizeof(int), 1);
4358     if (ga_grow(gap, win_count() * 2) == OK)
4359 	for (wp = firstwin; wp != NULL; wp = wp->w_next)
4360 	{
4361 	    ((int *)gap->ga_data)[gap->ga_len++] =
4362 					       wp->w_width + wp->w_vsep_width;
4363 	    ((int *)gap->ga_data)[gap->ga_len++] = wp->w_height;
4364 	}
4365 }
4366 
4367 /*
4368  * Restore window sizes, but only if the number of windows is still the same.
4369  * Does not free the growarray.
4370  */
4371     void
4372 win_size_restore(gap)
4373     garray_T	*gap;
4374 {
4375     win_T	*wp;
4376     int		i;
4377 
4378     if (win_count() * 2 == gap->ga_len)
4379     {
4380 	i = 0;
4381 	for (wp = firstwin; wp != NULL; wp = wp->w_next)
4382 	{
4383 	    frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]);
4384 	    win_setheight_win(((int *)gap->ga_data)[i++], wp);
4385 	}
4386 	/* recompute the window positions */
4387 	(void)win_comp_pos();
4388     }
4389 }
4390 #endif /* FEAT_CMDWIN */
4391 
4392 #if defined(FEAT_WINDOWS) || defined(PROTO)
4393 /*
4394  * Update the position for all windows, using the width and height of the
4395  * frames.
4396  * Returns the row just after the last window.
4397  */
4398     int
4399 win_comp_pos()
4400 {
4401     int		row = tabline_height();
4402     int		col = 0;
4403 
4404     frame_comp_pos(topframe, &row, &col);
4405     return row;
4406 }
4407 
4408 /*
4409  * Update the position of the windows in frame "topfrp", using the width and
4410  * height of the frames.
4411  * "*row" and "*col" are the top-left position of the frame.  They are updated
4412  * to the bottom-right position plus one.
4413  */
4414     static void
4415 frame_comp_pos(topfrp, row, col)
4416     frame_T	*topfrp;
4417     int		*row;
4418     int		*col;
4419 {
4420     win_T	*wp;
4421     frame_T	*frp;
4422 #ifdef FEAT_VERTSPLIT
4423     int		startcol;
4424     int		startrow;
4425 #endif
4426 
4427     wp = topfrp->fr_win;
4428     if (wp != NULL)
4429     {
4430 	if (wp->w_winrow != *row
4431 #ifdef FEAT_VERTSPLIT
4432 		|| wp->w_wincol != *col
4433 #endif
4434 		)
4435 	{
4436 	    /* position changed, redraw */
4437 	    wp->w_winrow = *row;
4438 #ifdef FEAT_VERTSPLIT
4439 	    wp->w_wincol = *col;
4440 #endif
4441 	    redraw_win_later(wp, NOT_VALID);
4442 	    wp->w_redr_status = TRUE;
4443 	}
4444 	*row += wp->w_height + wp->w_status_height;
4445 #ifdef FEAT_VERTSPLIT
4446 	*col += wp->w_width + wp->w_vsep_width;
4447 #endif
4448     }
4449     else
4450     {
4451 #ifdef FEAT_VERTSPLIT
4452 	startrow = *row;
4453 	startcol = *col;
4454 #endif
4455 	for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
4456 	{
4457 #ifdef FEAT_VERTSPLIT
4458 	    if (topfrp->fr_layout == FR_ROW)
4459 		*row = startrow;	/* all frames are at the same row */
4460 	    else
4461 		*col = startcol;	/* all frames are at the same col */
4462 #endif
4463 	    frame_comp_pos(frp, row, col);
4464 	}
4465     }
4466 }
4467 
4468 #endif /* FEAT_WINDOWS */
4469 
4470 /*
4471  * Set current window height and take care of repositioning other windows to
4472  * fit around it.
4473  */
4474     void
4475 win_setheight(height)
4476     int		height;
4477 {
4478     win_setheight_win(height, curwin);
4479 }
4480 
4481 /*
4482  * Set the window height of window "win" and take care of repositioning other
4483  * windows to fit around it.
4484  */
4485     void
4486 win_setheight_win(height, win)
4487     int		height;
4488     win_T	*win;
4489 {
4490     int		row;
4491 
4492     if (win == curwin)
4493     {
4494 	/* Always keep current window at least one line high, even when
4495 	 * 'winminheight' is zero. */
4496 #ifdef FEAT_WINDOWS
4497 	if (height < p_wmh)
4498 	    height = p_wmh;
4499 #endif
4500 	if (height == 0)
4501 	    height = 1;
4502     }
4503 
4504 #ifdef FEAT_WINDOWS
4505     frame_setheight(win->w_frame, height + win->w_status_height);
4506 
4507     /* recompute the window positions */
4508     row = win_comp_pos();
4509 #else
4510     if (height > topframe->fr_height)
4511 	height = topframe->fr_height;
4512     win->w_height = height;
4513     row = height;
4514 #endif
4515 
4516     /*
4517      * If there is extra space created between the last window and the command
4518      * line, clear it.
4519      */
4520     if (full_screen && msg_scrolled == 0 && row < cmdline_row)
4521 	screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
4522     cmdline_row = row;
4523     msg_row = row;
4524     msg_col = 0;
4525 
4526     redraw_all_later(NOT_VALID);
4527 }
4528 
4529 #if defined(FEAT_WINDOWS) || defined(PROTO)
4530 
4531 /*
4532  * Set the height of a frame to "height" and take care that all frames and
4533  * windows inside it are resized.  Also resize frames on the left and right if
4534  * the are in the same FR_ROW frame.
4535  *
4536  * Strategy:
4537  * If the frame is part of a FR_COL frame, try fitting the frame in that
4538  * frame.  If that doesn't work (the FR_COL frame is too small), recursively
4539  * go to containing frames to resize them and make room.
4540  * If the frame is part of a FR_ROW frame, all frames must be resized as well.
4541  * Check for the minimal height of the FR_ROW frame.
4542  * At the top level we can also use change the command line height.
4543  */
4544     static void
4545 frame_setheight(curfrp, height)
4546     frame_T	*curfrp;
4547     int		height;
4548 {
4549     int		room;		/* total number of lines available */
4550     int		take;		/* number of lines taken from other windows */
4551     int		room_cmdline;	/* lines available from cmdline */
4552     int		run;
4553     frame_T	*frp;
4554     int		h;
4555     int		room_reserved;
4556 
4557     /* If the height already is the desired value, nothing to do. */
4558     if (curfrp->fr_height == height)
4559 	return;
4560 
4561     if (curfrp->fr_parent == NULL)
4562     {
4563 	/* topframe: can only change the command line */
4564 	if (height > ROWS_AVAIL)
4565 	    height = ROWS_AVAIL;
4566 	if (height > 0)
4567 	    frame_new_height(curfrp, height, FALSE, FALSE);
4568     }
4569     else if (curfrp->fr_parent->fr_layout == FR_ROW)
4570     {
4571 	/* Row of frames: Also need to resize frames left and right of this
4572 	 * one.  First check for the minimal height of these. */
4573 	h = frame_minheight(curfrp->fr_parent, NULL);
4574 	if (height < h)
4575 	    height = h;
4576 	frame_setheight(curfrp->fr_parent, height);
4577     }
4578     else
4579     {
4580 	/*
4581 	 * Column of frames: try to change only frames in this column.
4582 	 */
4583 #ifdef FEAT_VERTSPLIT
4584 	/*
4585 	 * Do this twice:
4586 	 * 1: compute room available, if it's not enough try resizing the
4587 	 *    containing frame.
4588 	 * 2: compute the room available and adjust the height to it.
4589 	 * Try not to reduce the height of a window with 'winfixheight' set.
4590 	 */
4591 	for (run = 1; run <= 2; ++run)
4592 #else
4593 	for (;;)
4594 #endif
4595 	{
4596 	    room = 0;
4597 	    room_reserved = 0;
4598 	    for (frp = curfrp->fr_parent->fr_child; frp != NULL;
4599 							   frp = frp->fr_next)
4600 	    {
4601 		if (frp != curfrp
4602 			&& frp->fr_win != NULL
4603 			&& frp->fr_win->w_p_wfh)
4604 		    room_reserved += frp->fr_height;
4605 		room += frp->fr_height;
4606 		if (frp != curfrp)
4607 		    room -= frame_minheight(frp, NULL);
4608 	    }
4609 #ifdef FEAT_VERTSPLIT
4610 	    if (curfrp->fr_width != Columns)
4611 		room_cmdline = 0;
4612 	    else
4613 #endif
4614 	    {
4615 		room_cmdline = Rows - p_ch - (lastwin->w_winrow
4616 			       + lastwin->w_height + lastwin->w_status_height);
4617 		if (room_cmdline < 0)
4618 		    room_cmdline = 0;
4619 	    }
4620 
4621 	    if (height <= room + room_cmdline)
4622 		break;
4623 #ifdef FEAT_VERTSPLIT
4624 	    if (run == 2 || curfrp->fr_width == Columns)
4625 #endif
4626 	    {
4627 		if (height > room + room_cmdline)
4628 		    height = room + room_cmdline;
4629 		break;
4630 	    }
4631 #ifdef FEAT_VERTSPLIT
4632 	    frame_setheight(curfrp->fr_parent, height
4633 		+ frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1);
4634 #endif
4635 	    /*NOTREACHED*/
4636 	}
4637 
4638 	/*
4639 	 * Compute the number of lines we will take from others frames (can be
4640 	 * negative!).
4641 	 */
4642 	take = height - curfrp->fr_height;
4643 
4644 	/* If there is not enough room, also reduce the height of a window
4645 	 * with 'winfixheight' set. */
4646 	if (height > room + room_cmdline - room_reserved)
4647 	    room_reserved = room + room_cmdline - height;
4648 	/* If there is only a 'winfixheight' window and making the
4649 	 * window smaller, need to make the other window taller. */
4650 	if (take < 0 && room - curfrp->fr_height < room_reserved)
4651 	    room_reserved = 0;
4652 
4653 	if (take > 0 && room_cmdline > 0)
4654 	{
4655 	    /* use lines from cmdline first */
4656 	    if (take < room_cmdline)
4657 		room_cmdline = take;
4658 	    take -= room_cmdline;
4659 	    topframe->fr_height += room_cmdline;
4660 	}
4661 
4662 	/*
4663 	 * set the current frame to the new height
4664 	 */
4665 	frame_new_height(curfrp, height, FALSE, FALSE);
4666 
4667 	/*
4668 	 * First take lines from the frames after the current frame.  If
4669 	 * that is not enough, takes lines from frames above the current
4670 	 * frame.
4671 	 */
4672 	for (run = 0; run < 2; ++run)
4673 	{
4674 	    if (run == 0)
4675 		frp = curfrp->fr_next;	/* 1st run: start with next window */
4676 	    else
4677 		frp = curfrp->fr_prev;	/* 2nd run: start with prev window */
4678 	    while (frp != NULL && take != 0)
4679 	    {
4680 		h = frame_minheight(frp, NULL);
4681 		if (room_reserved > 0
4682 			&& frp->fr_win != NULL
4683 			&& frp->fr_win->w_p_wfh)
4684 		{
4685 		    if (room_reserved >= frp->fr_height)
4686 			room_reserved -= frp->fr_height;
4687 		    else
4688 		    {
4689 			if (frp->fr_height - room_reserved > take)
4690 			    room_reserved = frp->fr_height - take;
4691 			take -= frp->fr_height - room_reserved;
4692 			frame_new_height(frp, room_reserved, FALSE, FALSE);
4693 			room_reserved = 0;
4694 		    }
4695 		}
4696 		else
4697 		{
4698 		    if (frp->fr_height - take < h)
4699 		    {
4700 			take -= frp->fr_height - h;
4701 			frame_new_height(frp, h, FALSE, FALSE);
4702 		    }
4703 		    else
4704 		    {
4705 			frame_new_height(frp, frp->fr_height - take,
4706 								FALSE, FALSE);
4707 			take = 0;
4708 		    }
4709 		}
4710 		if (run == 0)
4711 		    frp = frp->fr_next;
4712 		else
4713 		    frp = frp->fr_prev;
4714 	    }
4715 	}
4716     }
4717 }
4718 
4719 #if defined(FEAT_VERTSPLIT) || defined(PROTO)
4720 /*
4721  * Set current window width and take care of repositioning other windows to
4722  * fit around it.
4723  */
4724     void
4725 win_setwidth(width)
4726     int		width;
4727 {
4728     win_setwidth_win(width, curwin);
4729 }
4730 
4731     void
4732 win_setwidth_win(width, wp)
4733     int		width;
4734     win_T	*wp;
4735 {
4736     /* Always keep current window at least one column wide, even when
4737      * 'winminwidth' is zero. */
4738     if (wp == curwin)
4739     {
4740 	if (width < p_wmw)
4741 	    width = p_wmw;
4742 	if (width == 0)
4743 	    width = 1;
4744     }
4745 
4746     frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
4747 
4748     /* recompute the window positions */
4749     (void)win_comp_pos();
4750 
4751     redraw_all_later(NOT_VALID);
4752 }
4753 
4754 /*
4755  * Set the width of a frame to "width" and take care that all frames and
4756  * windows inside it are resized.  Also resize frames above and below if the
4757  * are in the same FR_ROW frame.
4758  *
4759  * Strategy is similar to frame_setheight().
4760  */
4761     static void
4762 frame_setwidth(curfrp, width)
4763     frame_T	*curfrp;
4764     int		width;
4765 {
4766     int		room;		/* total number of lines available */
4767     int		take;		/* number of lines taken from other windows */
4768     int		run;
4769     frame_T	*frp;
4770     int		w;
4771     int		room_reserved;
4772 
4773     /* If the width already is the desired value, nothing to do. */
4774     if (curfrp->fr_width == width)
4775 	return;
4776 
4777     if (curfrp->fr_parent == NULL)
4778 	/* topframe: can't change width */
4779 	return;
4780 
4781     if (curfrp->fr_parent->fr_layout == FR_COL)
4782     {
4783 	/* Column of frames: Also need to resize frames above and below of
4784 	 * this one.  First check for the minimal width of these. */
4785 	w = frame_minwidth(curfrp->fr_parent, NULL);
4786 	if (width < w)
4787 	    width = w;
4788 	frame_setwidth(curfrp->fr_parent, width);
4789     }
4790     else
4791     {
4792 	/*
4793 	 * Row of frames: try to change only frames in this row.
4794 	 *
4795 	 * Do this twice:
4796 	 * 1: compute room available, if it's not enough try resizing the
4797 	 *    containing frame.
4798 	 * 2: compute the room available and adjust the width to it.
4799 	 */
4800 	for (run = 1; run <= 2; ++run)
4801 	{
4802 	    room = 0;
4803 	    room_reserved = 0;
4804 	    for (frp = curfrp->fr_parent->fr_child; frp != NULL;
4805 							   frp = frp->fr_next)
4806 	    {
4807 		if (frp != curfrp
4808 			&& frp->fr_win != NULL
4809 			&& frp->fr_win->w_p_wfw)
4810 		    room_reserved += frp->fr_width;
4811 		room += frp->fr_width;
4812 		if (frp != curfrp)
4813 		    room -= frame_minwidth(frp, NULL);
4814 	    }
4815 
4816 	    if (width <= room)
4817 		break;
4818 	    if (run == 2 || curfrp->fr_height >= ROWS_AVAIL)
4819 	    {
4820 		if (width > room)
4821 		    width = room;
4822 		break;
4823 	    }
4824 	    frame_setwidth(curfrp->fr_parent, width
4825 		 + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1);
4826 	}
4827 
4828 	/*
4829 	 * Compute the number of lines we will take from others frames (can be
4830 	 * negative!).
4831 	 */
4832 	take = width - curfrp->fr_width;
4833 
4834 	/* If there is not enough room, also reduce the width of a window
4835 	 * with 'winfixwidth' set. */
4836 	if (width > room - room_reserved)
4837 	    room_reserved = room - width;
4838 	/* If there is only a 'winfixwidth' window and making the
4839 	 * window smaller, need to make the other window narrower. */
4840 	if (take < 0 && room - curfrp->fr_width < room_reserved)
4841 	    room_reserved = 0;
4842 
4843 	/*
4844 	 * set the current frame to the new width
4845 	 */
4846 	frame_new_width(curfrp, width, FALSE, FALSE);
4847 
4848 	/*
4849 	 * First take lines from the frames right of the current frame.  If
4850 	 * that is not enough, takes lines from frames left of the current
4851 	 * frame.
4852 	 */
4853 	for (run = 0; run < 2; ++run)
4854 	{
4855 	    if (run == 0)
4856 		frp = curfrp->fr_next;	/* 1st run: start with next window */
4857 	    else
4858 		frp = curfrp->fr_prev;	/* 2nd run: start with prev window */
4859 	    while (frp != NULL && take != 0)
4860 	    {
4861 		w = frame_minwidth(frp, NULL);
4862 		if (room_reserved > 0
4863 			&& frp->fr_win != NULL
4864 			&& frp->fr_win->w_p_wfw)
4865 		{
4866 		    if (room_reserved >= frp->fr_width)
4867 			room_reserved -= frp->fr_width;
4868 		    else
4869 		    {
4870 			if (frp->fr_width - room_reserved > take)
4871 			    room_reserved = frp->fr_width - take;
4872 			take -= frp->fr_width - room_reserved;
4873 			frame_new_width(frp, room_reserved, FALSE, FALSE);
4874 			room_reserved = 0;
4875 		    }
4876 		}
4877 		else
4878 		{
4879 		    if (frp->fr_width - take < w)
4880 		    {
4881 			take -= frp->fr_width - w;
4882 			frame_new_width(frp, w, FALSE, FALSE);
4883 		    }
4884 		    else
4885 		    {
4886 			frame_new_width(frp, frp->fr_width - take,
4887 								FALSE, FALSE);
4888 			take = 0;
4889 		    }
4890 		}
4891 		if (run == 0)
4892 		    frp = frp->fr_next;
4893 		else
4894 		    frp = frp->fr_prev;
4895 	    }
4896 	}
4897     }
4898 }
4899 #endif /* FEAT_VERTSPLIT */
4900 
4901 /*
4902  * Check 'winminheight' for a valid value.
4903  */
4904     void
4905 win_setminheight()
4906 {
4907     int		room;
4908     int		first = TRUE;
4909     win_T	*wp;
4910 
4911     /* loop until there is a 'winminheight' that is possible */
4912     while (p_wmh > 0)
4913     {
4914 	/* TODO: handle vertical splits */
4915 	room = -p_wh;
4916 	for (wp = firstwin; wp != NULL; wp = wp->w_next)
4917 	    room += wp->w_height - p_wmh;
4918 	if (room >= 0)
4919 	    break;
4920 	--p_wmh;
4921 	if (first)
4922 	{
4923 	    EMSG(_(e_noroom));
4924 	    first = FALSE;
4925 	}
4926     }
4927 }
4928 
4929 #ifdef FEAT_MOUSE
4930 
4931 /*
4932  * Status line of dragwin is dragged "offset" lines down (negative is up).
4933  */
4934     void
4935 win_drag_status_line(dragwin, offset)
4936     win_T	*dragwin;
4937     int		offset;
4938 {
4939     frame_T	*curfr;
4940     frame_T	*fr;
4941     int		room;
4942     int		row;
4943     int		up;	/* if TRUE, drag status line up, otherwise down */
4944     int		n;
4945 
4946     fr = dragwin->w_frame;
4947     curfr = fr;
4948     if (fr != topframe)		/* more than one window */
4949     {
4950 	fr = fr->fr_parent;
4951 	/* When the parent frame is not a column of frames, its parent should
4952 	 * be. */
4953 	if (fr->fr_layout != FR_COL)
4954 	{
4955 	    curfr = fr;
4956 	    if (fr != topframe)	/* only a row of windows, may drag statusline */
4957 		fr = fr->fr_parent;
4958 	}
4959     }
4960 
4961     /* If this is the last frame in a column, may want to resize the parent
4962      * frame instead (go two up to skip a row of frames). */
4963     while (curfr != topframe && curfr->fr_next == NULL)
4964     {
4965 	if (fr != topframe)
4966 	    fr = fr->fr_parent;
4967 	curfr = fr;
4968 	if (fr != topframe)
4969 	    fr = fr->fr_parent;
4970     }
4971 
4972     if (offset < 0) /* drag up */
4973     {
4974 	up = TRUE;
4975 	offset = -offset;
4976 	/* sum up the room of the current frame and above it */
4977 	if (fr == curfr)
4978 	{
4979 	    /* only one window */
4980 	    room = fr->fr_height - frame_minheight(fr, NULL);
4981 	}
4982 	else
4983 	{
4984 	    room = 0;
4985 	    for (fr = fr->fr_child; ; fr = fr->fr_next)
4986 	    {
4987 		room += fr->fr_height - frame_minheight(fr, NULL);
4988 		if (fr == curfr)
4989 		    break;
4990 	    }
4991 	}
4992 	fr = curfr->fr_next;		/* put fr at frame that grows */
4993     }
4994     else    /* drag down */
4995     {
4996 	up = FALSE;
4997 	/*
4998 	 * Only dragging the last status line can reduce p_ch.
4999 	 */
5000 	room = Rows - cmdline_row;
5001 	if (curfr->fr_next == NULL)
5002 	    room -= 1;
5003 	else
5004 	    room -= p_ch;
5005 	if (room < 0)
5006 	    room = 0;
5007 	/* sum up the room of frames below of the current one */
5008 	for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next)
5009 	    room += fr->fr_height - frame_minheight(fr, NULL);
5010 	fr = curfr;			/* put fr at window that grows */
5011     }
5012 
5013     if (room < offset)		/* Not enough room */
5014 	offset = room;		/* Move as far as we can */
5015     if (offset <= 0)
5016 	return;
5017 
5018     /*
5019      * Grow frame fr by "offset" lines.
5020      * Doesn't happen when dragging the last status line up.
5021      */
5022     if (fr != NULL)
5023 	frame_new_height(fr, fr->fr_height + offset, up, FALSE);
5024 
5025     if (up)
5026 	fr = curfr;		/* current frame gets smaller */
5027     else
5028 	fr = curfr->fr_next;	/* next frame gets smaller */
5029 
5030     /*
5031      * Now make the other frames smaller.
5032      */
5033     while (fr != NULL && offset > 0)
5034     {
5035 	n = frame_minheight(fr, NULL);
5036 	if (fr->fr_height - offset <= n)
5037 	{
5038 	    offset -= fr->fr_height - n;
5039 	    frame_new_height(fr, n, !up, FALSE);
5040 	}
5041 	else
5042 	{
5043 	    frame_new_height(fr, fr->fr_height - offset, !up, FALSE);
5044 	    break;
5045 	}
5046 	if (up)
5047 	    fr = fr->fr_prev;
5048 	else
5049 	    fr = fr->fr_next;
5050     }
5051     row = win_comp_pos();
5052     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
5053     cmdline_row = row;
5054     p_ch = Rows - cmdline_row;
5055     if (p_ch < 1)
5056 	p_ch = 1;
5057     curtab->tp_ch_used = p_ch;
5058     redraw_all_later(SOME_VALID);
5059     showmode();
5060 }
5061 
5062 #ifdef FEAT_VERTSPLIT
5063 /*
5064  * Separator line of dragwin is dragged "offset" lines right (negative is left).
5065  */
5066     void
5067 win_drag_vsep_line(dragwin, offset)
5068     win_T	*dragwin;
5069     int		offset;
5070 {
5071     frame_T	*curfr;
5072     frame_T	*fr;
5073     int		room;
5074     int		left;	/* if TRUE, drag separator line left, otherwise right */
5075     int		n;
5076 
5077     fr = dragwin->w_frame;
5078     if (fr == topframe)		/* only one window (cannot happe?) */
5079 	return;
5080     curfr = fr;
5081     fr = fr->fr_parent;
5082     /* When the parent frame is not a row of frames, its parent should be. */
5083     if (fr->fr_layout != FR_ROW)
5084     {
5085 	if (fr == topframe)	/* only a column of windows (cannot happen?) */
5086 	    return;
5087 	curfr = fr;
5088 	fr = fr->fr_parent;
5089     }
5090 
5091     /* If this is the last frame in a row, may want to resize a parent
5092      * frame instead. */
5093     while (curfr->fr_next == NULL)
5094     {
5095 	if (fr == topframe)
5096 	    break;
5097 	curfr = fr;
5098 	fr = fr->fr_parent;
5099 	if (fr != topframe)
5100 	{
5101 	    curfr = fr;
5102 	    fr = fr->fr_parent;
5103 	}
5104     }
5105 
5106     if (offset < 0) /* drag left */
5107     {
5108 	left = TRUE;
5109 	offset = -offset;
5110 	/* sum up the room of the current frame and left of it */
5111 	room = 0;
5112 	for (fr = fr->fr_child; ; fr = fr->fr_next)
5113 	{
5114 	    room += fr->fr_width - frame_minwidth(fr, NULL);
5115 	    if (fr == curfr)
5116 		break;
5117 	}
5118 	fr = curfr->fr_next;		/* put fr at frame that grows */
5119     }
5120     else    /* drag right */
5121     {
5122 	left = FALSE;
5123 	/* sum up the room of frames right of the current one */
5124 	room = 0;
5125 	for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next)
5126 	    room += fr->fr_width - frame_minwidth(fr, NULL);
5127 	fr = curfr;			/* put fr at window that grows */
5128     }
5129 
5130     if (room < offset)		/* Not enough room */
5131 	offset = room;		/* Move as far as we can */
5132     if (offset <= 0)		/* No room at all, quit. */
5133 	return;
5134 
5135     /* grow frame fr by offset lines */
5136     frame_new_width(fr, fr->fr_width + offset, left, FALSE);
5137 
5138     /* shrink other frames: current and at the left or at the right */
5139     if (left)
5140 	fr = curfr;		/* current frame gets smaller */
5141     else
5142 	fr = curfr->fr_next;	/* next frame gets smaller */
5143 
5144     while (fr != NULL && offset > 0)
5145     {
5146 	n = frame_minwidth(fr, NULL);
5147 	if (fr->fr_width - offset <= n)
5148 	{
5149 	    offset -= fr->fr_width - n;
5150 	    frame_new_width(fr, n, !left, FALSE);
5151 	}
5152 	else
5153 	{
5154 	    frame_new_width(fr, fr->fr_width - offset, !left, FALSE);
5155 	    break;
5156 	}
5157 	if (left)
5158 	    fr = fr->fr_prev;
5159 	else
5160 	    fr = fr->fr_next;
5161     }
5162     (void)win_comp_pos();
5163     redraw_all_later(NOT_VALID);
5164 }
5165 #endif /* FEAT_VERTSPLIT */
5166 #endif /* FEAT_MOUSE */
5167 
5168 #endif /* FEAT_WINDOWS */
5169 
5170 /*
5171  * Set the height of a window.
5172  * This takes care of the things inside the window, not what happens to the
5173  * window position, the frame or to other windows.
5174  */
5175     static void
5176 win_new_height(wp, height)
5177     win_T	*wp;
5178     int		height;
5179 {
5180     linenr_T	lnum;
5181     linenr_T	bot;
5182     int		sline, line_size;
5183     int		space;
5184     int		did_below = FALSE;
5185 #define FRACTION_MULT	16384L
5186 
5187     /* Don't want a negative height.  Happens when splitting a tiny window.
5188      * Will equalize heights soon to fix it. */
5189     if (height < 0)
5190 	height = 0;
5191     if (wp->w_height == height)
5192 	return;	    /* nothing to do */
5193 
5194     if (wp->w_wrow != wp->w_prev_fraction_row && wp->w_height > 0)
5195 	wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT
5196 				    + FRACTION_MULT / 2) / (long)wp->w_height;
5197 
5198     wp->w_height = height;
5199     wp->w_skipcol = 0;
5200 
5201     /* Don't change w_topline when height is zero.  Don't set w_topline when
5202      * 'scrollbind' is set and this isn't the current window. */
5203     if (height > 0
5204 #ifdef FEAT_SCROLLBIND
5205 	    && (!wp->w_p_scb || wp == curwin)
5206 #endif
5207        )
5208     {
5209 	/*
5210 	 * Find a value for w_topline that shows the cursor at the same
5211 	 * relative position in the window as before (more or less).
5212 	 */
5213 	lnum = wp->w_cursor.lnum;
5214 	if (lnum < 1)		/* can happen when starting up */
5215 	    lnum = 1;
5216 	wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT;
5217 	line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
5218 	sline = wp->w_wrow - line_size;
5219 	if (sline < 0)
5220 	{
5221 	    /*
5222 	     * Cursor line would go off top of screen if w_wrow was this high.
5223 	     */
5224 	    wp->w_wrow = line_size;
5225 	}
5226 	else
5227 	{
5228 	    space = height;
5229 	    while (lnum > 1)
5230 	    {
5231 		space -= line_size;
5232 		if (space > 0 && sline <= 0 && !did_below)
5233 		{
5234 		    /* Try to use "~" lines below the text to avoid that text
5235 		     * is above the window while there are empty lines.
5236 		     * Subtract the rows below the cursor from "space" and
5237 		     * give the rest to "sline". */
5238 		    did_below = TRUE;
5239 		    bot = wp->w_cursor.lnum;
5240 		    while (space > 0)
5241 		    {
5242 			if (wp->w_buffer->b_ml.ml_line_count - bot >= space)
5243 			    space = 0;
5244 			else
5245 			{
5246 #ifdef FEAT_FOLDING
5247 			    hasFoldingWin(wp, bot, NULL, &bot, TRUE, NULL);
5248 #endif
5249 			    if (bot >= wp->w_buffer->b_ml.ml_line_count)
5250 				break;
5251 			    ++bot;
5252 			    space -= plines_win(wp, bot, TRUE);
5253 			}
5254 		    }
5255 		    if (bot == wp->w_buffer->b_ml.ml_line_count && space > 0)
5256 			sline += space;
5257 		}
5258 		if (sline <= 0)
5259 		    break;
5260 
5261 #ifdef FEAT_FOLDING
5262 		hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
5263 		if (lnum == 1)
5264 		{
5265 		    /* first line in buffer is folded */
5266 		    line_size = 1;
5267 		    --sline;
5268 		    break;
5269 		}
5270 #endif
5271 		--lnum;
5272 #ifdef FEAT_DIFF
5273 		if (lnum == wp->w_topline)
5274 		    line_size = plines_win_nofill(wp, lnum, TRUE)
5275 							      + wp->w_topfill;
5276 		else
5277 #endif
5278 		    line_size = plines_win(wp, lnum, TRUE);
5279 		sline -= line_size;
5280 	    }
5281 
5282 	    if (sline < 0)
5283 	    {
5284 		/*
5285 		 * Line we want at top would go off top of screen.  Use next
5286 		 * line instead.
5287 		 */
5288 #ifdef FEAT_FOLDING
5289 		hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL);
5290 #endif
5291 		lnum++;
5292 		wp->w_wrow -= line_size + sline;
5293 	    }
5294 	    else if (sline > 0)
5295 	    {
5296 		/* First line of file reached, use that as topline. */
5297 		lnum = 1;
5298 		wp->w_wrow -= sline;
5299 	    }
5300 	}
5301 	set_topline(wp, lnum);
5302     }
5303 
5304     if (wp == curwin)
5305     {
5306 	if (p_so)
5307 	    update_topline();
5308 	curs_columns(FALSE);	/* validate w_wrow */
5309     }
5310     wp->w_prev_fraction_row = wp->w_wrow;
5311 
5312     win_comp_scroll(wp);
5313     redraw_win_later(wp, SOME_VALID);
5314 #ifdef FEAT_WINDOWS
5315     wp->w_redr_status = TRUE;
5316 #endif
5317     invalidate_botline_win(wp);
5318 }
5319 
5320 #ifdef FEAT_VERTSPLIT
5321 /*
5322  * Set the width of a window.
5323  */
5324     static void
5325 win_new_width(wp, width)
5326     win_T	*wp;
5327     int		width;
5328 {
5329     wp->w_width = width;
5330     wp->w_lines_valid = 0;
5331     changed_line_abv_curs_win(wp);
5332     invalidate_botline_win(wp);
5333     if (wp == curwin)
5334     {
5335 	update_topline();
5336 	curs_columns(TRUE);	/* validate w_wrow */
5337     }
5338     redraw_win_later(wp, NOT_VALID);
5339     wp->w_redr_status = TRUE;
5340 }
5341 #endif
5342 
5343     void
5344 win_comp_scroll(wp)
5345     win_T	*wp;
5346 {
5347     wp->w_p_scr = ((unsigned)wp->w_height >> 1);
5348     if (wp->w_p_scr == 0)
5349 	wp->w_p_scr = 1;
5350 }
5351 
5352 /*
5353  * command_height: called whenever p_ch has been changed
5354  */
5355     void
5356 command_height()
5357 {
5358 #ifdef FEAT_WINDOWS
5359     int		h;
5360     frame_T	*frp;
5361     int		old_p_ch = curtab->tp_ch_used;
5362 
5363     /* Use the value of p_ch that we remembered.  This is needed for when the
5364      * GUI starts up, we can't be sure in what order things happen.  And when
5365      * p_ch was changed in another tab page. */
5366     curtab->tp_ch_used = p_ch;
5367 
5368     /* Find bottom frame with width of screen. */
5369     frp = lastwin->w_frame;
5370 # ifdef FEAT_VERTSPLIT
5371     while (frp->fr_width != Columns && frp->fr_parent != NULL)
5372 	frp = frp->fr_parent;
5373 # endif
5374 
5375     /* Avoid changing the height of a window with 'winfixheight' set. */
5376     while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF
5377 						      && frp->fr_win->w_p_wfh)
5378 	frp = frp->fr_prev;
5379 
5380     if (starting != NO_SCREEN)
5381     {
5382 	cmdline_row = Rows - p_ch;
5383 
5384 	if (p_ch > old_p_ch)		    /* p_ch got bigger */
5385 	{
5386 	    while (p_ch > old_p_ch)
5387 	    {
5388 		if (frp == NULL)
5389 		{
5390 		    EMSG(_(e_noroom));
5391 		    p_ch = old_p_ch;
5392 		    cmdline_row = Rows - p_ch;
5393 		    break;
5394 		}
5395 		h = frp->fr_height - frame_minheight(frp, NULL);
5396 		if (h > p_ch - old_p_ch)
5397 		    h = p_ch - old_p_ch;
5398 		old_p_ch += h;
5399 		frame_add_height(frp, -h);
5400 		frp = frp->fr_prev;
5401 	    }
5402 
5403 	    /* Recompute window positions. */
5404 	    (void)win_comp_pos();
5405 
5406 	    /* clear the lines added to cmdline */
5407 	    if (full_screen)
5408 		screen_fill((int)(cmdline_row), (int)Rows, 0,
5409 						   (int)Columns, ' ', ' ', 0);
5410 	    msg_row = cmdline_row;
5411 	    redraw_cmdline = TRUE;
5412 	    return;
5413 	}
5414 
5415 	if (msg_row < cmdline_row)
5416 	    msg_row = cmdline_row;
5417 	redraw_cmdline = TRUE;
5418     }
5419     frame_add_height(frp, (int)(old_p_ch - p_ch));
5420 
5421     /* Recompute window positions. */
5422     if (frp != lastwin->w_frame)
5423 	(void)win_comp_pos();
5424 #else
5425     cmdline_row = Rows - p_ch;
5426     win_setheight(cmdline_row);
5427 #endif
5428 }
5429 
5430 #if defined(FEAT_WINDOWS) || defined(PROTO)
5431 /*
5432  * Resize frame "frp" to be "n" lines higher (negative for less high).
5433  * Also resize the frames it is contained in.
5434  */
5435     static void
5436 frame_add_height(frp, n)
5437     frame_T	*frp;
5438     int		n;
5439 {
5440     frame_new_height(frp, frp->fr_height + n, FALSE, FALSE);
5441     for (;;)
5442     {
5443 	frp = frp->fr_parent;
5444 	if (frp == NULL)
5445 	    break;
5446 	frp->fr_height += n;
5447     }
5448 }
5449 
5450 /*
5451  * Add or remove a status line for the bottom window(s), according to the
5452  * value of 'laststatus'.
5453  */
5454     void
5455 last_status(morewin)
5456     int		morewin;	/* pretend there are two or more windows */
5457 {
5458     /* Don't make a difference between horizontal or vertical split. */
5459     last_status_rec(topframe, (p_ls == 2
5460 			  || (p_ls == 1 && (morewin || lastwin != firstwin))));
5461 }
5462 
5463     static void
5464 last_status_rec(fr, statusline)
5465     frame_T	*fr;
5466     int		statusline;
5467 {
5468     frame_T	*fp;
5469     win_T	*wp;
5470 
5471     if (fr->fr_layout == FR_LEAF)
5472     {
5473 	wp = fr->fr_win;
5474 	if (wp->w_status_height != 0 && !statusline)
5475 	{
5476 	    /* remove status line */
5477 	    win_new_height(wp, wp->w_height + 1);
5478 	    wp->w_status_height = 0;
5479 	    comp_col();
5480 	}
5481 	else if (wp->w_status_height == 0 && statusline)
5482 	{
5483 	    /* Find a frame to take a line from. */
5484 	    fp = fr;
5485 	    while (fp->fr_height <= frame_minheight(fp, NULL))
5486 	    {
5487 		if (fp == topframe)
5488 		{
5489 		    EMSG(_(e_noroom));
5490 		    return;
5491 		}
5492 		/* In a column of frames: go to frame above.  If already at
5493 		 * the top or in a row of frames: go to parent. */
5494 		if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL)
5495 		    fp = fp->fr_prev;
5496 		else
5497 		    fp = fp->fr_parent;
5498 	    }
5499 	    wp->w_status_height = 1;
5500 	    if (fp != fr)
5501 	    {
5502 		frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE);
5503 		frame_fix_height(wp);
5504 		(void)win_comp_pos();
5505 	    }
5506 	    else
5507 		win_new_height(wp, wp->w_height - 1);
5508 	    comp_col();
5509 	    redraw_all_later(SOME_VALID);
5510 	}
5511     }
5512 #ifdef FEAT_VERTSPLIT
5513     else if (fr->fr_layout == FR_ROW)
5514     {
5515 	/* vertically split windows, set status line for each one */
5516 	for (fp = fr->fr_child; fp != NULL; fp = fp->fr_next)
5517 	    last_status_rec(fp, statusline);
5518     }
5519 #endif
5520     else
5521     {
5522 	/* horizontally split window, set status line for last one */
5523 	for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
5524 	    ;
5525 	last_status_rec(fp, statusline);
5526     }
5527 }
5528 
5529 /*
5530  * Return the number of lines used by the tab page line.
5531  */
5532     int
5533 tabline_height()
5534 {
5535 #ifdef FEAT_GUI_TABLINE
5536     /* When the GUI has the tabline then this always returns zero. */
5537     if (gui_use_tabline())
5538 	return 0;
5539 #endif
5540     switch (p_stal)
5541     {
5542 	case 0: return 0;
5543 	case 1: return (first_tabpage->tp_next == NULL) ? 0 : 1;
5544     }
5545     return 1;
5546 }
5547 
5548 #endif /* FEAT_WINDOWS */
5549 
5550 #if defined(FEAT_SEARCHPATH) || defined(PROTO)
5551 /*
5552  * Get the file name at the cursor.
5553  * If Visual mode is active, use the selected text if it's in one line.
5554  * Returns the name in allocated memory, NULL for failure.
5555  */
5556     char_u *
5557 grab_file_name(count, file_lnum)
5558     long	count;
5559     linenr_T	*file_lnum;
5560 {
5561 # ifdef FEAT_VISUAL
5562     if (VIsual_active)
5563     {
5564 	int	len;
5565 	char_u	*ptr;
5566 
5567 	if (get_visual_text(NULL, &ptr, &len) == FAIL)
5568 	    return NULL;
5569 	return find_file_name_in_path(ptr, len,
5570 		     FNAME_MESS|FNAME_EXP|FNAME_REL, count, curbuf->b_ffname);
5571     }
5572 # endif
5573     return file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP|FNAME_REL, count,
5574 			       file_lnum);
5575 
5576 }
5577 
5578 /*
5579  * Return the file name under or after the cursor.
5580  *
5581  * The 'path' option is searched if the file name is not absolute.
5582  * The string returned has been alloc'ed and should be freed by the caller.
5583  * NULL is returned if the file name or file is not found.
5584  *
5585  * options:
5586  * FNAME_MESS	    give error messages
5587  * FNAME_EXP	    expand to path
5588  * FNAME_HYP	    check for hypertext link
5589  * FNAME_INCL	    apply "includeexpr"
5590  */
5591     char_u *
5592 file_name_at_cursor(options, count, file_lnum)
5593     int		options;
5594     long	count;
5595     linenr_T	*file_lnum;
5596 {
5597     return file_name_in_line(ml_get_curline(),
5598 		      curwin->w_cursor.col, options, count, curbuf->b_ffname,
5599 		      file_lnum);
5600 }
5601 
5602 /*
5603  * Return the name of the file under or after ptr[col].
5604  * Otherwise like file_name_at_cursor().
5605  */
5606     char_u *
5607 file_name_in_line(line, col, options, count, rel_fname, file_lnum)
5608     char_u	*line;
5609     int		col;
5610     int		options;
5611     long	count;
5612     char_u	*rel_fname;	/* file we are searching relative to */
5613     linenr_T	*file_lnum;	/* line number after the file name */
5614 {
5615     char_u	*ptr;
5616     int		len;
5617 
5618     /*
5619      * search forward for what could be the start of a file name
5620      */
5621     ptr = line + col;
5622     while (*ptr != NUL && !vim_isfilec(*ptr))
5623 	mb_ptr_adv(ptr);
5624     if (*ptr == NUL)		/* nothing found */
5625     {
5626 	if (options & FNAME_MESS)
5627 	    EMSG(_("E446: No file name under cursor"));
5628 	return NULL;
5629     }
5630 
5631     /*
5632      * Search backward for first char of the file name.
5633      * Go one char back to ":" before "//" even when ':' is not in 'isfname'.
5634      */
5635     while (ptr > line)
5636     {
5637 #ifdef FEAT_MBYTE
5638 	if (has_mbyte && (len = (*mb_head_off)(line, ptr - 1)) > 0)
5639 	    ptr -= len + 1;
5640 	else
5641 #endif
5642 	if (vim_isfilec(ptr[-1])
5643 		|| ((options & FNAME_HYP) && path_is_url(ptr - 1)))
5644 	    --ptr;
5645 	else
5646 	    break;
5647     }
5648 
5649     /*
5650      * Search forward for the last char of the file name.
5651      * Also allow "://" when ':' is not in 'isfname'.
5652      */
5653     len = 0;
5654     while (vim_isfilec(ptr[len])
5655 			 || ((options & FNAME_HYP) && path_is_url(ptr + len)))
5656 #ifdef FEAT_MBYTE
5657 	if (has_mbyte)
5658 	    len += (*mb_ptr2len)(ptr + len);
5659 	else
5660 #endif
5661 	    ++len;
5662 
5663     /*
5664      * If there is trailing punctuation, remove it.
5665      * But don't remove "..", could be a directory name.
5666      */
5667     if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL
5668 						       && ptr[len - 2] != '.')
5669 	--len;
5670 
5671     if (file_lnum != NULL)
5672     {
5673 	char_u *p;
5674 
5675 	/* Get the number after the file name and a separator character */
5676 	p = ptr + len;
5677 	p = skipwhite(p);
5678 	if (*p != NUL)
5679 	{
5680 	    if (!isdigit(*p))
5681 		++p;		    /* skip the separator */
5682 	    p = skipwhite(p);
5683 	    if (isdigit(*p))
5684 		*file_lnum = (int)getdigits(&p);
5685 	}
5686     }
5687 
5688     return find_file_name_in_path(ptr, len, options, count, rel_fname);
5689 }
5690 
5691 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
5692 static char_u *eval_includeexpr __ARGS((char_u *ptr, int len));
5693 
5694     static char_u *
5695 eval_includeexpr(ptr, len)
5696     char_u	*ptr;
5697     int		len;
5698 {
5699     char_u	*res;
5700 
5701     set_vim_var_string(VV_FNAME, ptr, len);
5702     res = eval_to_string_safe(curbuf->b_p_inex, NULL,
5703 		      was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL));
5704     set_vim_var_string(VV_FNAME, NULL, 0);
5705     return res;
5706 }
5707 #endif
5708 
5709 /*
5710  * Return the name of the file ptr[len] in 'path'.
5711  * Otherwise like file_name_at_cursor().
5712  */
5713     char_u *
5714 find_file_name_in_path(ptr, len, options, count, rel_fname)
5715     char_u	*ptr;
5716     int		len;
5717     int		options;
5718     long	count;
5719     char_u	*rel_fname;	/* file we are searching relative to */
5720 {
5721     char_u	*file_name;
5722     int		c;
5723 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
5724     char_u	*tofree = NULL;
5725 
5726     if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
5727     {
5728 	tofree = eval_includeexpr(ptr, len);
5729 	if (tofree != NULL)
5730 	{
5731 	    ptr = tofree;
5732 	    len = (int)STRLEN(ptr);
5733 	}
5734     }
5735 # endif
5736 
5737     if (options & FNAME_EXP)
5738     {
5739 	file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
5740 							     TRUE, rel_fname);
5741 
5742 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
5743 	/*
5744 	 * If the file could not be found in a normal way, try applying
5745 	 * 'includeexpr' (unless done already).
5746 	 */
5747 	if (file_name == NULL
5748 		&& !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
5749 	{
5750 	    tofree = eval_includeexpr(ptr, len);
5751 	    if (tofree != NULL)
5752 	    {
5753 		ptr = tofree;
5754 		len = (int)STRLEN(ptr);
5755 		file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
5756 							     TRUE, rel_fname);
5757 	    }
5758 	}
5759 # endif
5760 	if (file_name == NULL && (options & FNAME_MESS))
5761 	{
5762 	    c = ptr[len];
5763 	    ptr[len] = NUL;
5764 	    EMSG2(_("E447: Can't find file \"%s\" in path"), ptr);
5765 	    ptr[len] = c;
5766 	}
5767 
5768 	/* Repeat finding the file "count" times.  This matters when it
5769 	 * appears several times in the path. */
5770 	while (file_name != NULL && --count > 0)
5771 	{
5772 	    vim_free(file_name);
5773 	    file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname);
5774 	}
5775     }
5776     else
5777 	file_name = vim_strnsave(ptr, len);
5778 
5779 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
5780     vim_free(tofree);
5781 # endif
5782 
5783     return file_name;
5784 }
5785 #endif /* FEAT_SEARCHPATH */
5786 
5787 /*
5788  * Check if the "://" of a URL is at the pointer, return URL_SLASH.
5789  * Also check for ":\\", which MS Internet Explorer accepts, return
5790  * URL_BACKSLASH.
5791  */
5792     static int
5793 path_is_url(p)
5794     char_u  *p;
5795 {
5796     if (STRNCMP(p, "://", (size_t)3) == 0)
5797 	return URL_SLASH;
5798     else if (STRNCMP(p, ":\\\\", (size_t)3) == 0)
5799 	return URL_BACKSLASH;
5800     return 0;
5801 }
5802 
5803 /*
5804  * Check if "fname" starts with "name://".  Return URL_SLASH if it does.
5805  * Return URL_BACKSLASH for "name:\\".
5806  * Return zero otherwise.
5807  */
5808     int
5809 path_with_url(fname)
5810     char_u *fname;
5811 {
5812     char_u *p;
5813 
5814     for (p = fname; isalpha(*p); ++p)
5815 	;
5816     return path_is_url(p);
5817 }
5818 
5819 /*
5820  * Return TRUE if "name" is a full (absolute) path name or URL.
5821  */
5822     int
5823 vim_isAbsName(name)
5824     char_u	*name;
5825 {
5826     return (path_with_url(name) != 0 || mch_isFullName(name));
5827 }
5828 
5829 /*
5830  * Get absolute file name into buffer "buf[len]".
5831  *
5832  * return FAIL for failure, OK otherwise
5833  */
5834     int
5835 vim_FullName(fname, buf, len, force)
5836     char_u	*fname, *buf;
5837     int		len;
5838     int		force;	    /* force expansion even when already absolute */
5839 {
5840     int		retval = OK;
5841     int		url;
5842 
5843     *buf = NUL;
5844     if (fname == NULL)
5845 	return FAIL;
5846 
5847     url = path_with_url(fname);
5848     if (!url)
5849 	retval = mch_FullName(fname, buf, len, force);
5850     if (url || retval == FAIL)
5851     {
5852 	/* something failed; use the file name (truncate when too long) */
5853 	vim_strncpy(buf, fname, len - 1);
5854     }
5855 #if defined(MACOS_CLASSIC) || defined(OS2) || defined(MSDOS) || defined(MSWIN)
5856     slash_adjust(buf);
5857 #endif
5858     return retval;
5859 }
5860 
5861 /*
5862  * Return the minimal number of rows that is needed on the screen to display
5863  * the current number of windows.
5864  */
5865     int
5866 min_rows()
5867 {
5868     int		total;
5869 #ifdef FEAT_WINDOWS
5870     tabpage_T	*tp;
5871     int		n;
5872 #endif
5873 
5874     if (firstwin == NULL)	/* not initialized yet */
5875 	return MIN_LINES;
5876 
5877 #ifdef FEAT_WINDOWS
5878     total = 0;
5879     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
5880     {
5881 	n = frame_minheight(tp->tp_topframe, NULL);
5882 	if (total < n)
5883 	    total = n;
5884     }
5885     total += tabline_height();
5886 #else
5887     total = 1;		/* at least one window should have a line! */
5888 #endif
5889     total += 1;		/* count the room for the command line */
5890     return total;
5891 }
5892 
5893 /*
5894  * Return TRUE if there is only one window (in the current tab page), not
5895  * counting a help or preview window, unless it is the current window.
5896  */
5897     int
5898 only_one_window()
5899 {
5900 #ifdef FEAT_WINDOWS
5901     int		count = 0;
5902     win_T	*wp;
5903 
5904     /* If there is another tab page there always is another window. */
5905     if (first_tabpage->tp_next != NULL)
5906 	return FALSE;
5907 
5908     for (wp = firstwin; wp != NULL; wp = wp->w_next)
5909 	if (!((wp->w_buffer->b_help && !curbuf->b_help)
5910 # ifdef FEAT_QUICKFIX
5911 		    || wp->w_p_pvw
5912 # endif
5913 	     ) || wp == curwin)
5914 	    ++count;
5915     return (count <= 1);
5916 #else
5917     return TRUE;
5918 #endif
5919 }
5920 
5921 #if defined(FEAT_WINDOWS) || defined(FEAT_AUTOCMD) || defined(PROTO)
5922 /*
5923  * Correct the cursor line number in other windows.  Used after changing the
5924  * current buffer, and before applying autocommands.
5925  * When "do_curwin" is TRUE, also check current window.
5926  */
5927     void
5928 check_lnums(do_curwin)
5929     int		do_curwin;
5930 {
5931     win_T	*wp;
5932 
5933 #ifdef FEAT_WINDOWS
5934     tabpage_T	*tp;
5935 
5936     FOR_ALL_TAB_WINDOWS(tp, wp)
5937 	if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf)
5938 #else
5939     wp = curwin;
5940     if (do_curwin)
5941 #endif
5942 	{
5943 	    if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count)
5944 		wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5945 	    if (wp->w_topline > curbuf->b_ml.ml_line_count)
5946 		wp->w_topline = curbuf->b_ml.ml_line_count;
5947 	}
5948 }
5949 #endif
5950 
5951 #if defined(FEAT_WINDOWS) || defined(PROTO)
5952 
5953 /*
5954  * A snapshot of the window sizes, to restore them after closing the help
5955  * window.
5956  * Only these fields are used:
5957  * fr_layout
5958  * fr_width
5959  * fr_height
5960  * fr_next
5961  * fr_child
5962  * fr_win (only valid for the old curwin, NULL otherwise)
5963  */
5964 
5965 /*
5966  * Create a snapshot of the current frame sizes.
5967  */
5968     static void
5969 make_snapshot()
5970 {
5971     clear_snapshot(curtab);
5972     make_snapshot_rec(topframe, &curtab->tp_snapshot);
5973 }
5974 
5975     static void
5976 make_snapshot_rec(fr, frp)
5977     frame_T	*fr;
5978     frame_T	**frp;
5979 {
5980     *frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T));
5981     if (*frp == NULL)
5982 	return;
5983     (*frp)->fr_layout = fr->fr_layout;
5984 # ifdef FEAT_VERTSPLIT
5985     (*frp)->fr_width = fr->fr_width;
5986 # endif
5987     (*frp)->fr_height = fr->fr_height;
5988     if (fr->fr_next != NULL)
5989 	make_snapshot_rec(fr->fr_next, &((*frp)->fr_next));
5990     if (fr->fr_child != NULL)
5991 	make_snapshot_rec(fr->fr_child, &((*frp)->fr_child));
5992     if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin)
5993 	(*frp)->fr_win = curwin;
5994 }
5995 
5996 /*
5997  * Remove any existing snapshot.
5998  */
5999     static void
6000 clear_snapshot(tp)
6001     tabpage_T	*tp;
6002 {
6003     clear_snapshot_rec(tp->tp_snapshot);
6004     tp->tp_snapshot = NULL;
6005 }
6006 
6007     static void
6008 clear_snapshot_rec(fr)
6009     frame_T	*fr;
6010 {
6011     if (fr != NULL)
6012     {
6013 	clear_snapshot_rec(fr->fr_next);
6014 	clear_snapshot_rec(fr->fr_child);
6015 	vim_free(fr);
6016     }
6017 }
6018 
6019 /*
6020  * Restore a previously created snapshot, if there is any.
6021  * This is only done if the screen size didn't change and the window layout is
6022  * still the same.
6023  */
6024     static void
6025 restore_snapshot(close_curwin)
6026     int		close_curwin;	    /* closing current window */
6027 {
6028     win_T	*wp;
6029 
6030     if (curtab->tp_snapshot != NULL
6031 # ifdef FEAT_VERTSPLIT
6032 	    && curtab->tp_snapshot->fr_width == topframe->fr_width
6033 # endif
6034 	    && curtab->tp_snapshot->fr_height == topframe->fr_height
6035 	    && check_snapshot_rec(curtab->tp_snapshot, topframe) == OK)
6036     {
6037 	wp = restore_snapshot_rec(curtab->tp_snapshot, topframe);
6038 	win_comp_pos();
6039 	if (wp != NULL && close_curwin)
6040 	    win_goto(wp);
6041 	redraw_all_later(CLEAR);
6042     }
6043     clear_snapshot(curtab);
6044 }
6045 
6046 /*
6047  * Check if frames "sn" and "fr" have the same layout, same following frames
6048  * and same children.
6049  */
6050     static int
6051 check_snapshot_rec(sn, fr)
6052     frame_T	*sn;
6053     frame_T	*fr;
6054 {
6055     if (sn->fr_layout != fr->fr_layout
6056 	    || (sn->fr_next == NULL) != (fr->fr_next == NULL)
6057 	    || (sn->fr_child == NULL) != (fr->fr_child == NULL)
6058 	    || (sn->fr_next != NULL
6059 		&& check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL)
6060 	    || (sn->fr_child != NULL
6061 		&& check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL))
6062 	return FAIL;
6063     return OK;
6064 }
6065 
6066 /*
6067  * Copy the size of snapshot frame "sn" to frame "fr".  Do the same for all
6068  * following frames and children.
6069  * Returns a pointer to the old current window, or NULL.
6070  */
6071     static win_T *
6072 restore_snapshot_rec(sn, fr)
6073     frame_T	*sn;
6074     frame_T	*fr;
6075 {
6076     win_T	*wp = NULL;
6077     win_T	*wp2;
6078 
6079     fr->fr_height = sn->fr_height;
6080 # ifdef FEAT_VERTSPLIT
6081     fr->fr_width = sn->fr_width;
6082 # endif
6083     if (fr->fr_layout == FR_LEAF)
6084     {
6085 	frame_new_height(fr, fr->fr_height, FALSE, FALSE);
6086 # ifdef FEAT_VERTSPLIT
6087 	frame_new_width(fr, fr->fr_width, FALSE, FALSE);
6088 # endif
6089 	wp = sn->fr_win;
6090     }
6091     if (sn->fr_next != NULL)
6092     {
6093 	wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next);
6094 	if (wp2 != NULL)
6095 	    wp = wp2;
6096     }
6097     if (sn->fr_child != NULL)
6098     {
6099 	wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child);
6100 	if (wp2 != NULL)
6101 	    wp = wp2;
6102     }
6103     return wp;
6104 }
6105 
6106 #endif
6107 
6108 #if (defined(FEAT_GUI) && defined(FEAT_VERTSPLIT)) || defined(PROTO)
6109 /*
6110  * Return TRUE if there is any vertically split window.
6111  */
6112     int
6113 win_hasvertsplit()
6114 {
6115     frame_T	*fr;
6116 
6117     if (topframe->fr_layout == FR_ROW)
6118 	return TRUE;
6119 
6120     if (topframe->fr_layout == FR_COL)
6121 	for (fr = topframe->fr_child; fr != NULL; fr = fr->fr_next)
6122 	    if (fr->fr_layout == FR_ROW)
6123 		return TRUE;
6124 
6125     return FALSE;
6126 }
6127 #endif
6128