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