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