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