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