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