blob: 18f07b17a4cba34fdc6ecb1d0bc7d5f0090f3d37 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010012static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, long Prenum);
13static void win_init(win_T *newp, win_T *oldp, int flags);
14static void win_init_some(win_T *newp, win_T *oldp);
15static void frame_comp_pos(frame_T *topfrp, int *row, int *col);
16static void frame_setheight(frame_T *curfrp, int height);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010017static void frame_setwidth(frame_T *curfrp, int width);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010018static void win_exchange(long);
19static void win_rotate(int, int);
20static void win_totop(int size, int flags);
21static void win_equal_rec(win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height);
naohiro ono23beefe2021-11-13 12:38:49 +000022static void trigger_winclosed(win_T *win);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010023static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp);
24static frame_T *win_altframe(win_T *win, tabpage_T *tp);
25static tabpage_T *alt_tabpage(void);
26static win_T *frame2win(frame_T *frp);
27static int frame_has_win(frame_T *frp, win_T *wp);
Luuk van Baal29ab5242022-09-11 16:59:53 +010028static void win_fix_scroll(int resize);
29static void win_fix_cursor(int normal);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010030static void frame_new_height(frame_T *topfrp, int height, int topfirst, int wfh);
31static int frame_fixed_height(frame_T *frp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010032static int frame_fixed_width(frame_T *frp);
33static void frame_add_statusline(frame_T *frp);
34static void frame_new_width(frame_T *topfrp, int width, int leftfirst, int wfw);
35static void frame_add_vsep(frame_T *frp);
36static int frame_minwidth(frame_T *topfrp, win_T *next_curwin);
37static void frame_fix_width(win_T *wp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010038static int win_alloc_firstwin(win_T *oldwin);
39static void new_frame(win_T *wp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010040static tabpage_T *alloc_tabpage(void);
41static int leave_tabpage(buf_T *new_curbuf, int trigger_leave_autocmds);
42static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_autocmds, int trigger_leave_autocmds);
43static void frame_fix_height(win_T *wp);
44static int frame_minheight(frame_T *topfrp, win_T *next_curwin);
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020045static int may_open_tabpage(void);
Bram Moolenaar57942232021-08-04 20:54:55 +020046static int win_enter_ext(win_T *wp, int flags);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010047static void win_free(win_T *wp, tabpage_T *tp);
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020048static void win_append(win_T *after, win_T *wp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010049static void frame_append(frame_T *after, frame_T *frp);
50static void frame_insert(frame_T *before, frame_T *frp);
51static void frame_remove(frame_T *frp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010052static void win_goto_ver(int up, long count);
53static void win_goto_hor(int left, long count);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010054static void frame_add_height(frame_T *frp, int n);
55static void last_status_rec(frame_T *fr, int statusline);
Bram Moolenaar071d4272004-06-13 20:20:40 +000056
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010057static void make_snapshot_rec(frame_T *fr, frame_T **frp);
58static void clear_snapshot(tabpage_T *tp, int idx);
59static void clear_snapshot_rec(frame_T *fr);
60static int check_snapshot_rec(frame_T *sn, frame_T *fr);
61static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr);
LemonBoy2a2707d2022-05-04 22:13:47 +010062static win_T *get_snapshot_curwin(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +000063
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010064static int frame_check_height(frame_T *topfrp, int height);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010065static int frame_check_width(frame_T *topfrp, int width);
Bram Moolenaarb893ac22013-06-26 14:04:47 +020066
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010067static win_T *win_alloc(win_T *after, int hidden);
Bram Moolenaar071d4272004-06-13 20:20:40 +000068
kylo252ae6f1d82022-02-16 19:24:07 +000069#define NOWIN ((win_T *)-1) // non-existing window
Bram Moolenaar071d4272004-06-13 20:20:40 +000070
Bram Moolenaar4033c552017-09-16 20:54:51 +020071#define ROWS_AVAIL (Rows - p_ch - tabline_height())
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000072
Bram Moolenaard61f2f72021-08-04 20:26:19 +020073// flags for win_enter_ext()
74#define WEE_UNDO_SYNC 0x01
75#define WEE_CURWIN_INVALID 0x02
76#define WEE_TRIGGER_NEW_AUTOCMDS 0x04
77#define WEE_TRIGGER_ENTER_AUTOCMDS 0x08
78#define WEE_TRIGGER_LEAVE_AUTOCMDS 0x10
Bram Moolenaar57942232021-08-04 20:54:55 +020079#define WEE_ALLOW_PARSE_MESSAGES 0x20
Bram Moolenaard61f2f72021-08-04 20:26:19 +020080
Bram Moolenaar4c3f5362006-04-11 21:38:50 +000081static char *m_onlyone = N_("Already only one window");
82
Bram Moolenaar1417c762019-07-27 17:31:36 +020083// When non-zero splitting a window is forbidden. Used to avoid that nasty
84// autocommands mess up the window structure.
85static int split_disallowed = 0;
86
Bram Moolenaard63a8552022-11-19 11:41:30 +000087// When non-zero closing a window is forbidden. Used to avoid that nasty
88// autocommands mess up the window structure.
89static int close_disallowed = 0;
90
91/*
92 * Disallow changing the window layout (split window, close window, move
93 * window). Resizing is still allowed.
94 * Used for autocommands that temporarily use another window and need to
95 * make sure the previously selected window is still there.
96 * Must be matched with exactly one call to window_layout_unlock()!
97 */
98 static void
99window_layout_lock(void)
100{
101 ++split_disallowed;
102 ++close_disallowed;
103}
104
105 static void
106window_layout_unlock(void)
107{
108 --split_disallowed;
109 --close_disallowed;
110}
111
112/*
113 * When the window layout cannot be changed give an error and return TRUE.
Bram Moolenaar9fda8152022-11-19 13:14:10 +0000114 * "cmd" indicates the action being performed and is used to pick the relevant
115 * error message.
Bram Moolenaard63a8552022-11-19 11:41:30 +0000116 */
117 int
Bram Moolenaar9fda8152022-11-19 13:14:10 +0000118window_layout_locked(enum CMD_index cmd)
Bram Moolenaard63a8552022-11-19 11:41:30 +0000119{
120 if (split_disallowed > 0 || close_disallowed > 0)
121 {
Bram Moolenaar9fda8152022-11-19 13:14:10 +0000122 if (close_disallowed == 0 && cmd == CMD_tabnew)
Bram Moolenaard63a8552022-11-19 11:41:30 +0000123 emsg(_(e_cannot_split_window_when_closing_buffer));
124 else
125 emsg(_(e_not_allowed_to_change_window_layout_in_this_autocmd));
126 return TRUE;
127 }
128 return FALSE;
129}
130
Bram Moolenaar1417c762019-07-27 17:31:36 +0200131// #define WIN_DEBUG
132#ifdef WIN_DEBUG
133/*
134 * Call this method to log the current window layout.
135 */
136 static void
137log_frame_layout(frame_T *frame)
138{
139 ch_log(NULL, "layout %s, wi: %d, he: %d, wwi: %d, whe: %d, id: %d",
140 frame->fr_layout == FR_LEAF ? "LEAF"
141 : frame->fr_layout == FR_ROW ? "ROW" : "COL",
142 frame->fr_width,
143 frame->fr_height,
144 frame->fr_win == NULL ? -1 : frame->fr_win->w_width,
145 frame->fr_win == NULL ? -1 : frame->fr_win->w_height,
146 frame->fr_win == NULL ? -1 : frame->fr_win->w_id);
147 if (frame->fr_child != NULL)
148 {
149 ch_log(NULL, "children");
150 log_frame_layout(frame->fr_child);
151 if (frame->fr_next != NULL)
152 ch_log(NULL, "END of children");
153 }
154 if (frame->fr_next != NULL)
155 log_frame_layout(frame->fr_next);
156}
157#endif
158
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159/*
Bram Moolenaar0f6e28f2022-02-20 20:49:35 +0000160 * Return the current window, unless in the cmdline window and "prevwin" is
161 * set, then return "prevwin".
162 */
163 win_T *
164prevwin_curwin(void)
165{
Martin Tournoij7904fa42022-10-04 16:28:45 +0100166 // In cmdwin, the alternative buffer should be used.
167 return is_in_cmdwin() && prevwin != NULL ? prevwin : curwin;
Bram Moolenaar0f6e28f2022-02-20 20:49:35 +0000168}
169
170/*
Yegappan Lakshmanane42c27d2023-05-14 17:24:22 +0100171 * If the 'switchbuf' option contains "useopen" or "usetab", then try to jump
172 * to a window containing "buf".
173 * Returns the pointer to the window that was jumped to or NULL.
174 */
175 win_T *
176swbuf_goto_win_with_buf(buf_T *buf)
177{
178 win_T *wp = NULL;
179
180 if (buf == NULL)
181 return wp;
182
183 // If 'switchbuf' contains "useopen": jump to first window in the current
184 // tab page containing "buf" if one exists.
185 if (swb_flags & SWB_USEOPEN)
186 wp = buf_jump_open_win(buf);
187
188 // If 'switchbuf' contains "usetab": jump to first window in any tab page
189 // containing "buf" if one exists.
190 if (wp == NULL && (swb_flags & SWB_USETAB))
191 wp = buf_jump_open_tab(buf);
192
193 return wp;
194}
195
196/*
Bram Moolenaar72e83c12019-02-22 16:09:52 +0100197 * All CTRL-W window commands are handled here, called from normal_cmd().
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198 */
199 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +0100200do_window(
201 int nchar,
202 long Prenum,
Bram Moolenaare38eab22019-12-05 21:50:01 +0100203 int xchar) // extra char from ":wincmd gx" or NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204{
205 long Prenum1;
206 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207 char_u *ptr;
Bram Moolenaard1f56e62006-02-22 21:25:37 +0000208 linenr_T lnum = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209#ifdef FEAT_FIND_ID
210 int type = FIND_DEFINE;
211 int len;
212#endif
213 char_u cbuf[40];
214
Bram Moolenaar3c01c4a2020-02-01 23:04:24 +0100215 if (ERROR_IF_ANY_POPUP_WINDOW)
Bram Moolenaar815b76b2019-06-01 14:15:52 +0200216 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217
Martin Tournoij7904fa42022-10-04 16:28:45 +0100218#define CHECK_CMDWIN \
Bram Moolenaar6f470022018-04-10 18:47:20 +0200219 do { \
220 if (cmdwin_type != 0) \
221 { \
Bram Moolenaar108010a2021-06-27 22:03:33 +0200222 emsg(_(e_invalid_in_cmdline_window)); \
Bram Moolenaar6f470022018-04-10 18:47:20 +0200223 return; \
224 } \
225 } while (0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226
Bram Moolenaar815b76b2019-06-01 14:15:52 +0200227 Prenum1 = Prenum == 0 ? 1 : Prenum;
228
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 switch (nchar)
230 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100231// split current window in two parts, horizontally
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 case 'S':
233 case Ctrl_S:
234 case 's':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200235 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100236 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaare38eab22019-12-05 21:50:01 +0100237 // When splitting the quickfix window open a new buffer in it,
238 // don't replicate the quickfix buffer.
Bram Moolenaarb1b715d2006-01-21 22:09:43 +0000239 if (bt_quickfix(curbuf))
240 goto newwindow;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000241#ifdef FEAT_GUI
242 need_mouse_correct = TRUE;
243#endif
Bram Moolenaarcde88542015-08-11 19:14:00 +0200244 (void)win_split((int)Prenum, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245 break;
246
Bram Moolenaare38eab22019-12-05 21:50:01 +0100247// split current window in two parts, vertically
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248 case Ctrl_V:
249 case 'v':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200250 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100251 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaare38eab22019-12-05 21:50:01 +0100252 // When splitting the quickfix window open a new buffer in it,
253 // don't replicate the quickfix buffer.
Bram Moolenaar990d95c2008-07-07 19:23:37 +0000254 if (bt_quickfix(curbuf))
255 goto newwindow;
Bram Moolenaar44a2f922016-03-19 22:11:51 +0100256#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +0000257 need_mouse_correct = TRUE;
Bram Moolenaar44a2f922016-03-19 22:11:51 +0100258#endif
Bram Moolenaarcde88542015-08-11 19:14:00 +0200259 (void)win_split((int)Prenum, WSP_VERT);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000260 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261
Bram Moolenaare38eab22019-12-05 21:50:01 +0100262// split current window and edit alternate file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000263 case Ctrl_HAT:
264 case '^':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200265 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100266 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaar1bbb6192018-11-10 16:02:01 +0100267
268 if (buflist_findnr(Prenum == 0
269 ? curwin->w_alt_fnum : Prenum) == NULL)
270 {
271 if (Prenum == 0)
Bram Moolenaar108010a2021-06-27 22:03:33 +0200272 emsg(_(e_no_alternate_file));
Bram Moolenaar1bbb6192018-11-10 16:02:01 +0100273 else
Bram Moolenaare1242042021-12-16 20:56:57 +0000274 semsg(_(e_buffer_nr_not_found), Prenum);
Bram Moolenaar1bbb6192018-11-10 16:02:01 +0100275 break;
276 }
277
278 if (!curbuf_locked() && win_split(0, 0) == OK)
279 (void)buflist_getfile(
280 Prenum == 0 ? curwin->w_alt_fnum : Prenum,
281 (linenr_T)0, GETF_ALT, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 break;
283
Bram Moolenaare38eab22019-12-05 21:50:01 +0100284// open new window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000285 case Ctrl_N:
286 case 'n':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200287 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100288 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaarb1b715d2006-01-21 22:09:43 +0000289newwindow:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290 if (Prenum)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100291 // window height
Bram Moolenaar990d95c2008-07-07 19:23:37 +0000292 vim_snprintf((char *)cbuf, sizeof(cbuf) - 5, "%ld", Prenum);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 else
294 cbuf[0] = NUL;
Bram Moolenaar44a2f922016-03-19 22:11:51 +0100295#if defined(FEAT_QUICKFIX)
Bram Moolenaar990d95c2008-07-07 19:23:37 +0000296 if (nchar == 'v' || nchar == Ctrl_V)
297 STRCAT(cbuf, "v");
298#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000299 STRCAT(cbuf, "new");
300 do_cmdline_cmd(cbuf);
301 break;
302
Bram Moolenaare38eab22019-12-05 21:50:01 +0100303// quit current window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000304 case Ctrl_Q:
305 case 'q':
Bram Moolenaare38eab22019-12-05 21:50:01 +0100306 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaar2f1e51a2014-12-13 03:58:09 +0100307 cmd_with_count("quit", cbuf, sizeof(cbuf), Prenum);
Bram Moolenaarb96a7f32014-11-27 16:22:48 +0100308 do_cmdline_cmd(cbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309 break;
310
Bram Moolenaare38eab22019-12-05 21:50:01 +0100311// close current window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000312 case Ctrl_C:
313 case 'c':
Bram Moolenaare38eab22019-12-05 21:50:01 +0100314 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaar2f1e51a2014-12-13 03:58:09 +0100315 cmd_with_count("close", cbuf, sizeof(cbuf), Prenum);
Bram Moolenaarb96a7f32014-11-27 16:22:48 +0100316 do_cmdline_cmd(cbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317 break;
318
Bram Moolenaar4033c552017-09-16 20:54:51 +0200319#if defined(FEAT_QUICKFIX)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100320// close preview window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321 case Ctrl_Z:
322 case 'z':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200323 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100324 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +0000325 do_cmdline_cmd((char_u *)"pclose");
326 break;
327
Bram Moolenaare38eab22019-12-05 21:50:01 +0100328// cursor to preview window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329 case 'P':
Bram Moolenaar29323592016-07-24 22:04:11 +0200330 FOR_ALL_WINDOWS(wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 if (wp->w_p_pvw)
332 break;
333 if (wp == NULL)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000334 emsg(_(e_there_is_no_preview_window));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000335 else
336 win_goto(wp);
337 break;
338#endif
339
Bram Moolenaare38eab22019-12-05 21:50:01 +0100340// close all but current window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 case Ctrl_O:
342 case 'o':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200343 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100344 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaar2f1e51a2014-12-13 03:58:09 +0100345 cmd_with_count("only", cbuf, sizeof(cbuf), Prenum);
Bram Moolenaarb96a7f32014-11-27 16:22:48 +0100346 do_cmdline_cmd(cbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347 break;
348
Bram Moolenaare38eab22019-12-05 21:50:01 +0100349// cursor to next window with wrap around
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350 case Ctrl_W:
351 case 'w':
Bram Moolenaare38eab22019-12-05 21:50:01 +0100352// cursor to previous window with wrap around
Bram Moolenaar071d4272004-06-13 20:20:40 +0000353 case 'W':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200354 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100355 if (ONE_WINDOW && Prenum != 1) // just one window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000356 beep_flush();
357 else
358 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100359 if (Prenum) // go to specified window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 {
361 for (wp = firstwin; --Prenum > 0; )
362 {
363 if (wp->w_next == NULL)
364 break;
365 else
366 wp = wp->w_next;
367 }
368 }
369 else
370 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100371 if (nchar == 'W') // go to previous window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000372 {
373 wp = curwin->w_prev;
374 if (wp == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100375 wp = lastwin; // wrap around
Bram Moolenaar071d4272004-06-13 20:20:40 +0000376 }
Bram Moolenaare38eab22019-12-05 21:50:01 +0100377 else // go to next window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000378 {
379 wp = curwin->w_next;
380 if (wp == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100381 wp = firstwin; // wrap around
Bram Moolenaar071d4272004-06-13 20:20:40 +0000382 }
383 }
384 win_goto(wp);
385 }
386 break;
387
Bram Moolenaare38eab22019-12-05 21:50:01 +0100388// cursor to window below
Bram Moolenaar071d4272004-06-13 20:20:40 +0000389 case 'j':
390 case K_DOWN:
391 case Ctrl_J:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200392 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000393 win_goto_ver(FALSE, Prenum1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394 break;
395
Bram Moolenaare38eab22019-12-05 21:50:01 +0100396// cursor to window above
Bram Moolenaar071d4272004-06-13 20:20:40 +0000397 case 'k':
398 case K_UP:
399 case Ctrl_K:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200400 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401 win_goto_ver(TRUE, Prenum1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402 break;
403
Bram Moolenaare38eab22019-12-05 21:50:01 +0100404// cursor to left window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000405 case 'h':
406 case K_LEFT:
407 case Ctrl_H:
408 case K_BS:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200409 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000410 win_goto_hor(TRUE, Prenum1);
411 break;
412
Bram Moolenaare38eab22019-12-05 21:50:01 +0100413// cursor to right window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414 case 'l':
415 case K_RIGHT:
416 case Ctrl_L:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200417 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000418 win_goto_hor(FALSE, Prenum1);
419 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420
Bram Moolenaare38eab22019-12-05 21:50:01 +0100421// move window to new tab page
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000422 case 'T':
Bram Moolenaar4fdb8bd2020-06-07 17:03:21 +0200423 CHECK_CMDWIN;
Bram Moolenaar746ebd32009-06-16 14:01:43 +0000424 if (one_window())
Bram Moolenaar32526b32019-01-19 17:43:09 +0100425 msg(_(m_onlyone));
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000426 else
427 {
428 tabpage_T *oldtab = curtab;
429 tabpage_T *newtab;
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000430
Bram Moolenaare38eab22019-12-05 21:50:01 +0100431 // First create a new tab with the window, then go back to
432 // the old tab and close the window there.
Bram Moolenaar89d40322006-08-29 15:30:07 +0000433 wp = curwin;
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000434 if (win_new_tabpage((int)Prenum) == OK
435 && valid_tabpage(oldtab))
436 {
437 newtab = curtab;
Bram Moolenaar49e649f2013-05-06 04:50:35 +0200438 goto_tabpage_tp(oldtab, TRUE, TRUE);
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000439 if (curwin == wp)
440 win_close(curwin, FALSE);
441 if (valid_tabpage(newtab))
Bram Moolenaar49e649f2013-05-06 04:50:35 +0200442 goto_tabpage_tp(newtab, TRUE, TRUE);
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000443 }
444 }
445 break;
446
Bram Moolenaare38eab22019-12-05 21:50:01 +0100447// cursor to top-left window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000448 case 't':
449 case Ctrl_T:
450 win_goto(firstwin);
451 break;
452
Bram Moolenaare38eab22019-12-05 21:50:01 +0100453// cursor to bottom-right window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000454 case 'b':
455 case Ctrl_B:
456 win_goto(lastwin);
457 break;
458
Bram Moolenaare38eab22019-12-05 21:50:01 +0100459// cursor to last accessed (previous) window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000460 case 'p':
461 case Ctrl_P:
Bram Moolenaar3dda7db2016-04-03 21:22:58 +0200462 if (!win_valid(prevwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000463 beep_flush();
464 else
465 win_goto(prevwin);
466 break;
467
Bram Moolenaare38eab22019-12-05 21:50:01 +0100468// exchange current and next window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469 case 'x':
470 case Ctrl_X:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200471 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472 win_exchange(Prenum);
473 break;
474
Bram Moolenaare38eab22019-12-05 21:50:01 +0100475// rotate windows downwards
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476 case Ctrl_R:
477 case 'r':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200478 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100479 reset_VIsual_and_resel(); // stop Visual mode
480 win_rotate(FALSE, (int)Prenum1); // downwards
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 break;
482
Bram Moolenaare38eab22019-12-05 21:50:01 +0100483// rotate windows upwards
Bram Moolenaar071d4272004-06-13 20:20:40 +0000484 case 'R':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200485 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100486 reset_VIsual_and_resel(); // stop Visual mode
487 win_rotate(TRUE, (int)Prenum1); // upwards
Bram Moolenaar071d4272004-06-13 20:20:40 +0000488 break;
489
Bram Moolenaare38eab22019-12-05 21:50:01 +0100490// move window to the very top/bottom/left/right
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 case 'K':
492 case 'J':
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 case 'H':
494 case 'L':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200495 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000496 win_totop((int)Prenum,
497 ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
498 | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT));
499 break;
500
Bram Moolenaar21c3a802022-08-31 17:49:14 +0100501// make all windows the same width and/or height
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 case '=':
Bram Moolenaar21c3a802022-08-31 17:49:14 +0100503 {
504 int mod = cmdmod.cmod_split & (WSP_VERT | WSP_HOR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505#ifdef FEAT_GUI
Bram Moolenaar21c3a802022-08-31 17:49:14 +0100506 need_mouse_correct = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507#endif
Bram Moolenaar21c3a802022-08-31 17:49:14 +0100508 win_equal(NULL, FALSE,
509 mod == WSP_VERT ? 'v' : mod == WSP_HOR ? 'h' : 'b');
510 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511 break;
512
Bram Moolenaare38eab22019-12-05 21:50:01 +0100513// increase current window height
Bram Moolenaar071d4272004-06-13 20:20:40 +0000514 case '+':
515#ifdef FEAT_GUI
516 need_mouse_correct = TRUE;
517#endif
518 win_setheight(curwin->w_height + (int)Prenum1);
519 break;
520
Bram Moolenaare38eab22019-12-05 21:50:01 +0100521// decrease current window height
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522 case '-':
523#ifdef FEAT_GUI
524 need_mouse_correct = TRUE;
525#endif
526 win_setheight(curwin->w_height - (int)Prenum1);
527 break;
528
Bram Moolenaare38eab22019-12-05 21:50:01 +0100529// set current window height
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 case Ctrl__:
531 case '_':
532#ifdef FEAT_GUI
533 need_mouse_correct = TRUE;
534#endif
535 win_setheight(Prenum ? (int)Prenum : 9999);
536 break;
537
Bram Moolenaare38eab22019-12-05 21:50:01 +0100538// increase current window width
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539 case '>':
540#ifdef FEAT_GUI
541 need_mouse_correct = TRUE;
542#endif
543 win_setwidth(curwin->w_width + (int)Prenum1);
544 break;
545
Bram Moolenaare38eab22019-12-05 21:50:01 +0100546// decrease current window width
Bram Moolenaar071d4272004-06-13 20:20:40 +0000547 case '<':
548#ifdef FEAT_GUI
549 need_mouse_correct = TRUE;
550#endif
551 win_setwidth(curwin->w_width - (int)Prenum1);
552 break;
553
Bram Moolenaare38eab22019-12-05 21:50:01 +0100554// set current window width
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555 case '|':
556#ifdef FEAT_GUI
557 need_mouse_correct = TRUE;
558#endif
559 win_setwidth(Prenum != 0 ? (int)Prenum : 9999);
560 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561
Bram Moolenaare38eab22019-12-05 21:50:01 +0100562// jump to tag and split window if tag exists (in preview window)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000563#if defined(FEAT_QUICKFIX)
564 case '}':
Bram Moolenaar6f470022018-04-10 18:47:20 +0200565 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 if (Prenum)
567 g_do_tagpreview = Prenum;
568 else
569 g_do_tagpreview = p_pvh;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570#endif
Bram Moolenaare38eab22019-12-05 21:50:01 +0100571 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572 case ']':
573 case Ctrl_RSB:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200574 CHECK_CMDWIN;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100575 // keep Visual mode, can select words to use as a tag
Bram Moolenaar071d4272004-06-13 20:20:40 +0000576 if (Prenum)
577 postponed_split = Prenum;
578 else
579 postponed_split = -1;
Bram Moolenaarda014b92014-09-24 13:26:44 +0200580#ifdef FEAT_QUICKFIX
Bram Moolenaar56095e12014-10-09 10:44:37 +0200581 if (nchar != '}')
582 g_do_tagpreview = 0;
Bram Moolenaarda014b92014-09-24 13:26:44 +0200583#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000584
Bram Moolenaare38eab22019-12-05 21:50:01 +0100585 // Execute the command right here, required when "wincmd ]"
586 // was used in a function.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587 do_nv_ident(Ctrl_RSB, NUL);
Rob Pillingcb94c912022-12-13 12:26:09 +0000588 postponed_split = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589 break;
590
Bram Moolenaare38eab22019-12-05 21:50:01 +0100591// edit file name under cursor in a new window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000592 case 'f':
Bram Moolenaard1f56e62006-02-22 21:25:37 +0000593 case 'F':
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594 case Ctrl_F:
Bram Moolenaar8dff8182006-04-06 20:18:50 +0000595wingotofile:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200596 CHECK_CMDWIN;
Bram Moolenaarcc762a42022-11-25 13:03:31 +0000597 if (check_text_or_curbuf_locked(NULL))
598 break;
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000599
Bram Moolenaard1f56e62006-02-22 21:25:37 +0000600 ptr = grab_file_name(Prenum1, &lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000601 if (ptr != NULL)
602 {
Bram Moolenaar5d2ca042016-06-26 17:11:21 +0200603 tabpage_T *oldtab = curtab;
604 win_T *oldwin = curwin;
Bram Moolenaarf80f40a2022-08-25 16:02:23 +0100605#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +0000606 need_mouse_correct = TRUE;
Bram Moolenaarf80f40a2022-08-25 16:02:23 +0100607#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000608 setpcmark();
Yegappan Lakshmanan54be5fb2023-05-12 17:49:13 +0100609
610 // If 'switchbuf' is set to 'useopen' or 'usetab' and the
611 // file is already opened in a window, then jump to it.
612 wp = NULL;
613 if ((swb_flags & (SWB_USEOPEN | SWB_USETAB))
614 && cmdmod.cmod_tab == 0)
Yegappan Lakshmanane42c27d2023-05-14 17:24:22 +0100615 wp = swbuf_goto_win_with_buf(buflist_findname_exp(ptr));
Yegappan Lakshmanan54be5fb2023-05-12 17:49:13 +0100616
617 if (wp == NULL && win_split(0, 0) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 {
Bram Moolenaar3368ea22010-09-21 16:56:35 +0200619 RESET_BINDING(curwin);
Bram Moolenaar5d2ca042016-06-26 17:11:21 +0200620 if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL,
621 ECMD_HIDE, NULL) == FAIL)
622 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100623 // Failed to open the file, close the window
624 // opened for it.
Bram Moolenaar5d2ca042016-06-26 17:11:21 +0200625 win_close(curwin, FALSE);
626 goto_tabpage_win(oldtab, oldwin);
627 }
Yegappan Lakshmanan54be5fb2023-05-12 17:49:13 +0100628 else
629 wp = curwin;
630 }
631
632 if (wp != NULL && nchar == 'F' && lnum >= 0)
633 {
634 curwin->w_cursor.lnum = lnum;
635 check_cursor_lnum();
636 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 }
638 vim_free(ptr);
639 }
640 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641
642#ifdef FEAT_FIND_ID
Bram Moolenaare38eab22019-12-05 21:50:01 +0100643// Go to the first occurrence of the identifier under cursor along path in a
644// new window -- webb
645 case 'i': // Go to any match
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 case Ctrl_I:
647 type = FIND_ANY;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100648 // FALLTHROUGH
649 case 'd': // Go to definition, using 'define'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650 case Ctrl_D:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200651 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
653 break;
Bram Moolenaar28d032c2022-05-18 16:29:08 +0100654
655 // Make a copy, if the line was changed it will be freed.
656 ptr = vim_strnsave(ptr, len);
657 if (ptr == NULL)
658 break;
659
Bram Moolenaar071d4272004-06-13 20:20:40 +0000660 find_pattern_in_path(ptr, 0, len, TRUE,
661 Prenum == 0 ? TRUE : FALSE, type,
662 Prenum1, ACTION_SPLIT, (linenr_T)1, (linenr_T)MAXLNUM);
Bram Moolenaar28d032c2022-05-18 16:29:08 +0100663 vim_free(ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000664 curwin->w_set_curswant = TRUE;
665 break;
666#endif
667
Bram Moolenaare38eab22019-12-05 21:50:01 +0100668// Quickfix window only: view the result under the cursor in a new split.
Bram Moolenaar0a08c632018-07-25 22:36:52 +0200669#if defined(FEAT_QUICKFIX)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000670 case K_KENTER:
671 case CAR:
Bram Moolenaar05159a02005-02-26 23:04:13 +0000672 if (bt_quickfix(curbuf))
Bram Moolenaar0a08c632018-07-25 22:36:52 +0200673 qf_view_result(TRUE);
Bram Moolenaar05159a02005-02-26 23:04:13 +0000674 break;
Bram Moolenaar0a08c632018-07-25 22:36:52 +0200675#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000676
Bram Moolenaare38eab22019-12-05 21:50:01 +0100677// CTRL-W g extended commands
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678 case 'g':
679 case Ctrl_G:
Bram Moolenaar6f470022018-04-10 18:47:20 +0200680 CHECK_CMDWIN;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000681#ifdef USE_ON_FLY_SCROLL
Bram Moolenaare38eab22019-12-05 21:50:01 +0100682 dont_scroll = TRUE; // disallow scrolling here
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683#endif
684 ++no_mapping;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100685 ++allow_keys; // no mapping for xchar, but allow key codes
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686 if (xchar == NUL)
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000687 xchar = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000688 LANGMAP_ADJUST(xchar, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 --no_mapping;
690 --allow_keys;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691 (void)add_to_showcmd(xchar);
Martin Tournoijba43e762022-10-13 22:12:15 +0100692
Bram Moolenaar071d4272004-06-13 20:20:40 +0000693 switch (xchar)
694 {
695#if defined(FEAT_QUICKFIX)
696 case '}':
697 xchar = Ctrl_RSB;
698 if (Prenum)
699 g_do_tagpreview = Prenum;
700 else
701 g_do_tagpreview = p_pvh;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702#endif
Bram Moolenaare38eab22019-12-05 21:50:01 +0100703 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +0000704 case ']':
705 case Ctrl_RSB:
Bram Moolenaare38eab22019-12-05 21:50:01 +0100706 // keep Visual mode, can select words to use as a tag
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707 if (Prenum)
708 postponed_split = Prenum;
709 else
710 postponed_split = -1;
711
Bram Moolenaare38eab22019-12-05 21:50:01 +0100712 // Execute the command right here, required when
713 // "wincmd g}" was used in a function.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000714 do_nv_ident('g', xchar);
Rob Pillingcb94c912022-12-13 12:26:09 +0000715 postponed_split = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716 break;
717
Bram Moolenaare38eab22019-12-05 21:50:01 +0100718 case 'f': // CTRL-W gf: "gf" in a new tab page
719 case 'F': // CTRL-W gF: "gF" in a new tab page
Bram Moolenaare1004402020-10-24 20:49:43 +0200720 cmdmod.cmod_tab = tabpage_index(curtab) + 1;
Bram Moolenaar57657d82006-04-21 22:12:41 +0000721 nchar = xchar;
Bram Moolenaar8dff8182006-04-06 20:18:50 +0000722 goto wingotofile;
Bram Moolenaarf80f40a2022-08-25 16:02:23 +0100723
Bram Moolenaar72e83c12019-02-22 16:09:52 +0100724 case 't': // CTRL-W gt: go to next tab page
725 goto_tabpage((int)Prenum);
726 break;
727
Bram Moolenaar882d02e2019-02-22 17:56:43 +0100728 case 'T': // CTRL-W gT: go to previous tab page
729 goto_tabpage(-(int)Prenum1);
730 break;
731
Bram Moolenaar62a23252020-08-09 14:04:42 +0200732 case TAB: // CTRL-W g<Tab>: go to last used tab page
733 if (goto_tabpage_lastused() == FAIL)
734 beep_flush();
735 break;
736
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737 default:
738 beep_flush();
739 break;
740 }
741 break;
742
743 default: beep_flush();
744 break;
745 }
746}
747
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100748/*
Bram Moolenaarb7316892019-05-01 18:08:42 +0200749 * Figure out the address type for ":wincmd".
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100750 */
751 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +0100752get_wincmd_addr_type(char_u *arg, exarg_T *eap)
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100753{
754 switch (*arg)
755 {
756 case 'S':
757 case Ctrl_S:
758 case 's':
759 case Ctrl_N:
760 case 'n':
761 case 'j':
762 case Ctrl_J:
763 case 'k':
764 case Ctrl_K:
765 case 'T':
766 case Ctrl_R:
767 case 'r':
768 case 'R':
769 case 'K':
770 case 'J':
771 case '+':
772 case '-':
773 case Ctrl__:
774 case '_':
775 case '|':
776 case ']':
777 case Ctrl_RSB:
778 case 'g':
779 case Ctrl_G:
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100780 case Ctrl_V:
781 case 'v':
782 case 'h':
783 case Ctrl_H:
784 case 'l':
785 case Ctrl_L:
786 case 'H':
787 case 'L':
788 case '>':
789 case '<':
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100790#if defined(FEAT_QUICKFIX)
791 case '}':
792#endif
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100793 case 'f':
794 case 'F':
795 case Ctrl_F:
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100796#ifdef FEAT_FIND_ID
797 case 'i':
798 case Ctrl_I:
799 case 'd':
800 case Ctrl_D:
801#endif
Bram Moolenaarb7316892019-05-01 18:08:42 +0200802 // window size or any count
803 eap->addr_type = ADDR_OTHER;
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100804 break;
805
806 case Ctrl_HAT:
807 case '^':
Bram Moolenaarb7316892019-05-01 18:08:42 +0200808 // buffer number
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100809 eap->addr_type = ADDR_BUFFERS;
810 break;
811
812 case Ctrl_Q:
813 case 'q':
814 case Ctrl_C:
815 case 'c':
816 case Ctrl_O:
817 case 'o':
818 case Ctrl_W:
819 case 'w':
820 case 'W':
821 case 'x':
822 case Ctrl_X:
Bram Moolenaarb7316892019-05-01 18:08:42 +0200823 // window number
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100824 eap->addr_type = ADDR_WINDOWS;
825 break;
826
827#if defined(FEAT_QUICKFIX)
828 case Ctrl_Z:
829 case 'z':
830 case 'P':
831#endif
832 case 't':
833 case Ctrl_T:
834 case 'b':
835 case Ctrl_B:
836 case 'p':
837 case Ctrl_P:
838 case '=':
839 case CAR:
Bram Moolenaarb7316892019-05-01 18:08:42 +0200840 // no count
841 eap->addr_type = ADDR_NONE;
Bram Moolenaar84c8e5a2015-01-14 15:47:36 +0100842 break;
843 }
844}
845
Bram Moolenaar2f1e51a2014-12-13 03:58:09 +0100846 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +0100847cmd_with_count(
848 char *cmd,
849 char_u *bufp,
850 size_t bufsize,
851 long Prenum)
Bram Moolenaar2f1e51a2014-12-13 03:58:09 +0100852{
Bram Moolenaar2f1e51a2014-12-13 03:58:09 +0100853 if (Prenum > 0)
Bram Moolenaar1ff89de2021-03-24 20:08:12 +0100854 vim_snprintf((char *)bufp, bufsize, "%s %ld", cmd, Prenum);
855 else
856 STRCPY(bufp, cmd);
Bram Moolenaar2f1e51a2014-12-13 03:58:09 +0100857}
858
Bram Moolenaar071d4272004-06-13 20:20:40 +0000859/*
Bram Moolenaar54969f42022-02-07 13:56:44 +0000860 * If "split_disallowed" is set give an error and return FAIL.
Bram Moolenaar1417c762019-07-27 17:31:36 +0200861 * Otherwise return OK.
862 */
863 static int
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000864check_split_disallowed(void)
Bram Moolenaar1417c762019-07-27 17:31:36 +0200865{
866 if (split_disallowed > 0)
867 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +0000868 emsg(_(e_cant_split_window_while_closing_another));
Bram Moolenaar1417c762019-07-27 17:31:36 +0200869 return FAIL;
870 }
Bram Moolenaar983d83f2021-02-07 12:12:43 +0100871 if (curwin->w_buffer->b_locked_split)
872 {
873 emsg(_(e_cannot_split_window_when_closing_buffer));
874 return FAIL;
875 }
Bram Moolenaar1417c762019-07-27 17:31:36 +0200876 return OK;
877}
878
879/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880 * split the current window, implements CTRL-W s and :split
881 *
882 * "size" is the height or width for the new window, 0 to use half of current
883 * height or width.
884 *
885 * "flags":
886 * WSP_ROOM: require enough room for new window
887 * WSP_VERT: vertical split.
888 * WSP_TOP: open window at the top-left of the shell (help window).
889 * WSP_BOT: open window at the bottom-right of the shell (quickfix window).
890 * WSP_HELP: creating the help window, keep layout snapshot
891 *
892 * return FAIL for failure, OK otherwise
893 */
894 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +0100895win_split(int size, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896{
Bram Moolenaar3c01c4a2020-02-01 23:04:24 +0100897 if (ERROR_IF_ANY_POPUP_WINDOW)
Bram Moolenaar815b76b2019-06-01 14:15:52 +0200898 return FAIL;
899
Bram Moolenaar983d83f2021-02-07 12:12:43 +0100900 if (check_split_disallowed() == FAIL)
901 return FAIL;
902
Bram Moolenaare38eab22019-12-05 21:50:01 +0100903 // When the ":tab" modifier was used open a new tab page instead.
Bram Moolenaar80a94a52006-02-23 21:26:58 +0000904 if (may_open_tabpage() == OK)
905 return OK;
906
Bram Moolenaare38eab22019-12-05 21:50:01 +0100907 // Add flags from ":vertical", ":topleft" and ":botright".
Bram Moolenaare1004402020-10-24 20:49:43 +0200908 flags |= cmdmod.cmod_split;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909 if ((flags & WSP_TOP) && (flags & WSP_BOT))
910 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000911 emsg(_(e_cant_split_topleft_and_botright_at_the_same_time));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000912 return FAIL;
913 }
914
Bram Moolenaare38eab22019-12-05 21:50:01 +0100915 // When creating the help window make a snapshot of the window layout.
916 // Otherwise clear the snapshot, it's now invalid.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000917 if (flags & WSP_HELP)
Bram Moolenaar746ebd32009-06-16 14:01:43 +0000918 make_snapshot(SNAP_HELP_IDX);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919 else
Bram Moolenaar746ebd32009-06-16 14:01:43 +0000920 clear_snapshot(curtab, SNAP_HELP_IDX);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921
922 return win_split_ins(size, flags, NULL, 0);
923}
924
925/*
Bram Moolenaar70b2a562012-01-10 22:26:17 +0100926 * When "new_wp" is NULL: split the current window in two.
927 * When "new_wp" is not NULL: insert this window at the far
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 * top/left/right/bottom.
Bram Moolenaarcc762a42022-11-25 13:03:31 +0000929 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 */
Bram Moolenaar746ebd32009-06-16 14:01:43 +0000931 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +0100932win_split_ins(
933 int size,
934 int flags,
935 win_T *new_wp,
936 int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937{
Bram Moolenaar70b2a562012-01-10 22:26:17 +0100938 win_T *wp = new_wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000939 win_T *oldwin;
940 int new_size = size;
941 int i;
942 int need_status = 0;
943 int do_equal = FALSE;
944 int needed;
945 int available;
946 int oldwin_height = 0;
947 int layout;
Bram Moolenaar54368f22014-07-23 15:21:20 +0200948 frame_T *frp, *curfrp, *frp2, *prevfrp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000949 int before;
Bram Moolenaarb4d21352014-07-16 14:16:46 +0200950 int minheight;
Bram Moolenaar1f538352014-07-16 18:19:27 +0200951 int wmh1;
Bram Moolenaar98da6ec2018-04-13 22:15:46 +0200952 int did_set_fraction = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000953
954 if (flags & WSP_TOP)
955 oldwin = firstwin;
956 else if (flags & WSP_BOT)
957 oldwin = lastwin;
958 else
959 oldwin = curwin;
960
Bram Moolenaare38eab22019-12-05 21:50:01 +0100961 // add a status line when p_ls == 1 and splitting the first window
Bram Moolenaar459ca562016-11-10 18:16:33 +0100962 if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000963 {
Bram Moolenaar415a6932017-12-05 20:31:07 +0100964 if (VISIBLE_HEIGHT(oldwin) <= p_wmh && new_wp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000965 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200966 emsg(_(e_not_enough_room));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967 return FAIL;
968 }
969 need_status = STATUS_HEIGHT;
970 }
971
Bram Moolenaaree79cbc2007-05-02 19:50:14 +0000972#ifdef FEAT_GUI
Bram Moolenaare38eab22019-12-05 21:50:01 +0100973 // May be needed for the scrollbars that are going to change.
Bram Moolenaaree79cbc2007-05-02 19:50:14 +0000974 if (gui.in_use)
975 out_flush();
976#endif
977
Bram Moolenaar071d4272004-06-13 20:20:40 +0000978 if (flags & WSP_VERT)
979 {
Bram Moolenaara0485492014-07-16 23:39:54 +0200980 int wmw1;
981 int minwidth;
982
Bram Moolenaar071d4272004-06-13 20:20:40 +0000983 layout = FR_ROW;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000984
985 /*
986 * Check if we are able to split the current window and compute its
987 * width.
988 */
Bram Moolenaare38eab22019-12-05 21:50:01 +0100989 // Current window requires at least 1 space.
Bram Moolenaar1f538352014-07-16 18:19:27 +0200990 wmw1 = (p_wmw == 0 ? 1 : p_wmw);
991 needed = wmw1 + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000992 if (flags & WSP_ROOM)
Bram Moolenaar1f538352014-07-16 18:19:27 +0200993 needed += p_wiw - wmw1;
Bram Moolenaar54368f22014-07-23 15:21:20 +0200994 if (flags & (WSP_BOT | WSP_TOP))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000995 {
Bram Moolenaar1f538352014-07-16 18:19:27 +0200996 minwidth = frame_minwidth(topframe, NOWIN);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000997 available = topframe->fr_width;
Bram Moolenaarb4d21352014-07-16 14:16:46 +0200998 needed += minwidth;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000999 }
Bram Moolenaar54368f22014-07-23 15:21:20 +02001000 else if (p_ea)
1001 {
1002 minwidth = frame_minwidth(oldwin->w_frame, NOWIN);
1003 prevfrp = oldwin->w_frame;
1004 for (frp = oldwin->w_frame->fr_parent; frp != NULL;
1005 frp = frp->fr_parent)
1006 {
1007 if (frp->fr_layout == FR_ROW)
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01001008 FOR_ALL_FRAMES(frp2, frp->fr_child)
Bram Moolenaar54368f22014-07-23 15:21:20 +02001009 if (frp2 != prevfrp)
1010 minwidth += frame_minwidth(frp2, NOWIN);
1011 prevfrp = frp;
1012 }
1013 available = topframe->fr_width;
1014 needed += minwidth;
1015 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 else
Bram Moolenaarb4d21352014-07-16 14:16:46 +02001017 {
Bram Moolenaar1f538352014-07-16 18:19:27 +02001018 minwidth = frame_minwidth(oldwin->w_frame, NOWIN);
1019 available = oldwin->w_frame->fr_width;
1020 needed += minwidth;
Bram Moolenaarb4d21352014-07-16 14:16:46 +02001021 }
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001022 if (available < needed && new_wp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02001024 emsg(_(e_not_enough_room));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001025 return FAIL;
1026 }
1027 if (new_size == 0)
1028 new_size = oldwin->w_width / 2;
Bram Moolenaarb4d21352014-07-16 14:16:46 +02001029 if (new_size > available - minwidth - 1)
1030 new_size = available - minwidth - 1;
Bram Moolenaar1f538352014-07-16 18:19:27 +02001031 if (new_size < wmw1)
1032 new_size = wmw1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001033
Bram Moolenaare38eab22019-12-05 21:50:01 +01001034 // if it doesn't fit in the current window, need win_equal()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035 if (oldwin->w_width - new_size - 1 < p_wmw)
1036 do_equal = TRUE;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00001037
Bram Moolenaare38eab22019-12-05 21:50:01 +01001038 // We don't like to take lines for the new window from a
1039 // 'winfixwidth' window. Take them from a window to the left or right
1040 // instead, if possible. Add one for the separator.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00001041 if (oldwin->w_p_wfw)
Bram Moolenaar38e34832017-03-19 20:22:36 +01001042 win_setwidth_win(oldwin->w_width + new_size + 1, oldwin);
Bram Moolenaar67f71312007-08-12 14:55:56 +00001043
Bram Moolenaare38eab22019-12-05 21:50:01 +01001044 // Only make all windows the same width if one of them (except oldwin)
1045 // is wider than one of the split windows.
Bram Moolenaar67f71312007-08-12 14:55:56 +00001046 if (!do_equal && p_ea && size == 0 && *p_ead != 'v'
Bram Moolenaar1417c762019-07-27 17:31:36 +02001047 && oldwin->w_frame->fr_parent != NULL)
Bram Moolenaar67f71312007-08-12 14:55:56 +00001048 {
1049 frp = oldwin->w_frame->fr_parent->fr_child;
1050 while (frp != NULL)
1051 {
1052 if (frp->fr_win != oldwin && frp->fr_win != NULL
1053 && (frp->fr_win->w_width > new_size
1054 || frp->fr_win->w_width > oldwin->w_width
Bram Moolenaardf46f6f2014-11-19 13:40:08 +01001055 - new_size - 1))
Bram Moolenaar67f71312007-08-12 14:55:56 +00001056 {
1057 do_equal = TRUE;
1058 break;
1059 }
1060 frp = frp->fr_next;
1061 }
1062 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001063 }
1064 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001065 {
1066 layout = FR_COL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067
1068 /*
1069 * Check if we are able to split the current window and compute its
1070 * height.
1071 */
Bram Moolenaare38eab22019-12-05 21:50:01 +01001072 // Current window requires at least 1 space.
Bram Moolenaar415a6932017-12-05 20:31:07 +01001073 wmh1 = (p_wmh == 0 ? 1 : p_wmh) + WINBAR_HEIGHT(curwin);
Bram Moolenaar1f538352014-07-16 18:19:27 +02001074 needed = wmh1 + STATUS_HEIGHT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075 if (flags & WSP_ROOM)
Bram Moolenaar1f538352014-07-16 18:19:27 +02001076 needed += p_wh - wmh1;
Bram Moolenaar54368f22014-07-23 15:21:20 +02001077 if (flags & (WSP_BOT | WSP_TOP))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001078 {
Bram Moolenaar1f538352014-07-16 18:19:27 +02001079 minheight = frame_minheight(topframe, NOWIN) + need_status;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080 available = topframe->fr_height;
Bram Moolenaarb4d21352014-07-16 14:16:46 +02001081 needed += minheight;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001082 }
Bram Moolenaar54368f22014-07-23 15:21:20 +02001083 else if (p_ea)
1084 {
1085 minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
1086 prevfrp = oldwin->w_frame;
1087 for (frp = oldwin->w_frame->fr_parent; frp != NULL;
1088 frp = frp->fr_parent)
1089 {
1090 if (frp->fr_layout == FR_COL)
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01001091 FOR_ALL_FRAMES(frp2, frp->fr_child)
Bram Moolenaar54368f22014-07-23 15:21:20 +02001092 if (frp2 != prevfrp)
1093 minheight += frame_minheight(frp2, NOWIN);
1094 prevfrp = frp;
1095 }
1096 available = topframe->fr_height;
1097 needed += minheight;
1098 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 else
1100 {
Bram Moolenaar1f538352014-07-16 18:19:27 +02001101 minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
1102 available = oldwin->w_frame->fr_height;
1103 needed += minheight;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001104 }
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001105 if (available < needed && new_wp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02001107 emsg(_(e_not_enough_room));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 return FAIL;
1109 }
1110 oldwin_height = oldwin->w_height;
1111 if (need_status)
1112 {
1113 oldwin->w_status_height = STATUS_HEIGHT;
1114 oldwin_height -= STATUS_HEIGHT;
1115 }
1116 if (new_size == 0)
1117 new_size = oldwin_height / 2;
Bram Moolenaarb4d21352014-07-16 14:16:46 +02001118 if (new_size > available - minheight - STATUS_HEIGHT)
1119 new_size = available - minheight - STATUS_HEIGHT;
Bram Moolenaar1f538352014-07-16 18:19:27 +02001120 if (new_size < wmh1)
1121 new_size = wmh1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001122
Bram Moolenaare38eab22019-12-05 21:50:01 +01001123 // if it doesn't fit in the current window, need win_equal()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh)
1125 do_equal = TRUE;
1126
Bram Moolenaare38eab22019-12-05 21:50:01 +01001127 // We don't like to take lines for the new window from a
1128 // 'winfixheight' window. Take them from a window above or below
1129 // instead, if possible.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130 if (oldwin->w_p_wfh)
1131 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001132 // Set w_fraction now so that the cursor keeps the same relative
1133 // vertical position using the old height.
Bram Moolenaar98da6ec2018-04-13 22:15:46 +02001134 set_fraction(oldwin);
1135 did_set_fraction = TRUE;
1136
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT,
1138 oldwin);
1139 oldwin_height = oldwin->w_height;
1140 if (need_status)
1141 oldwin_height -= STATUS_HEIGHT;
1142 }
Bram Moolenaar67f71312007-08-12 14:55:56 +00001143
Bram Moolenaare38eab22019-12-05 21:50:01 +01001144 // Only make all windows the same height if one of them (except oldwin)
1145 // is higher than one of the split windows.
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001146 if (!do_equal && p_ea && size == 0 && *p_ead != 'h'
Bram Moolenaar67f71312007-08-12 14:55:56 +00001147 && oldwin->w_frame->fr_parent != NULL)
1148 {
1149 frp = oldwin->w_frame->fr_parent->fr_child;
1150 while (frp != NULL)
1151 {
1152 if (frp->fr_win != oldwin && frp->fr_win != NULL
1153 && (frp->fr_win->w_height > new_size
1154 || frp->fr_win->w_height > oldwin_height - new_size
1155 - STATUS_HEIGHT))
1156 {
1157 do_equal = TRUE;
1158 break;
1159 }
1160 frp = frp->fr_next;
1161 }
1162 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001163 }
1164
1165 /*
1166 * allocate new window structure and link it in the window list
1167 */
1168 if ((flags & WSP_TOP) == 0
1169 && ((flags & WSP_BOT)
1170 || (flags & WSP_BELOW)
1171 || (!(flags & WSP_ABOVE)
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001172 && ( (flags & WSP_VERT) ? p_spr : p_sb))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001173 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001174 // new window below/right of current one
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001175 if (new_wp == NULL)
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001176 wp = win_alloc(oldwin, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001177 else
1178 win_append(oldwin, wp);
1179 }
1180 else
1181 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001182 if (new_wp == NULL)
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001183 wp = win_alloc(oldwin->w_prev, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 else
1185 win_append(oldwin->w_prev, wp);
1186 }
1187
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001188 if (new_wp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 {
1190 if (wp == NULL)
1191 return FAIL;
1192
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001193 new_frame(wp);
1194 if (wp->w_frame == NULL)
1195 {
1196 win_free(wp, NULL);
1197 return FAIL;
1198 }
1199
Bram Moolenaare38eab22019-12-05 21:50:01 +01001200 // make the contents of the new window the same as the current one
Bram Moolenaar884ae642009-02-22 01:37:59 +00001201 win_init(wp, curwin, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202 }
1203
1204 /*
1205 * Reorganise the tree of frames to insert the new window.
1206 */
1207 if (flags & (WSP_TOP | WSP_BOT))
1208 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209 if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0)
1210 || (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211 {
1212 curfrp = topframe->fr_child;
1213 if (flags & WSP_BOT)
1214 while (curfrp->fr_next != NULL)
1215 curfrp = curfrp->fr_next;
1216 }
1217 else
1218 curfrp = topframe;
1219 before = (flags & WSP_TOP);
1220 }
1221 else
1222 {
1223 curfrp = oldwin->w_frame;
1224 if (flags & WSP_BELOW)
1225 before = FALSE;
1226 else if (flags & WSP_ABOVE)
1227 before = TRUE;
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001228 else if (flags & WSP_VERT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229 before = !p_spr;
1230 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 before = !p_sb;
1232 }
1233 if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout)
1234 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001235 // Need to create a new frame in the tree to make a branch.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001236 frp = ALLOC_CLEAR_ONE(frame_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 *frp = *curfrp;
1238 curfrp->fr_layout = layout;
1239 frp->fr_parent = curfrp;
1240 frp->fr_next = NULL;
1241 frp->fr_prev = NULL;
1242 curfrp->fr_child = frp;
1243 curfrp->fr_win = NULL;
1244 curfrp = frp;
1245 if (frp->fr_win != NULL)
1246 oldwin->w_frame = frp;
1247 else
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01001248 FOR_ALL_FRAMES(frp, frp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 frp->fr_parent = curfrp;
1250 }
1251
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001252 if (new_wp == NULL)
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001253 frp = wp->w_frame;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 else
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001255 frp = new_wp->w_frame;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 frp->fr_parent = curfrp->fr_parent;
1257
Bram Moolenaare38eab22019-12-05 21:50:01 +01001258 // Insert the new frame at the right place in the frame list.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 if (before)
1260 frame_insert(curfrp, frp);
1261 else
1262 frame_append(curfrp, frp);
1263
Bram Moolenaare38eab22019-12-05 21:50:01 +01001264 // Set w_fraction now so that the cursor keeps the same relative
1265 // vertical position.
Bram Moolenaar98da6ec2018-04-13 22:15:46 +02001266 if (!did_set_fraction)
1267 set_fraction(oldwin);
Bram Moolenaar0215e8e2010-12-17 17:35:10 +01001268 wp->w_fraction = oldwin->w_fraction;
1269
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 if (flags & WSP_VERT)
1271 {
1272 wp->w_p_scr = curwin->w_p_scr;
Bram Moolenaar0215e8e2010-12-17 17:35:10 +01001273
Bram Moolenaar071d4272004-06-13 20:20:40 +00001274 if (need_status)
1275 {
Bram Moolenaar9b73a782010-03-17 16:54:57 +01001276 win_new_height(oldwin, oldwin->w_height - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277 oldwin->w_status_height = need_status;
1278 }
1279 if (flags & (WSP_TOP | WSP_BOT))
1280 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001281 // set height and row of new window to full height
Bram Moolenaar32466aa2006-02-24 23:53:04 +00001282 wp->w_winrow = tabline_height();
Bram Moolenaard326ad62017-09-18 20:31:41 +02001283 win_new_height(wp, curfrp->fr_height - (p_ls > 0)
Bram Moolenaar3167c3e2017-11-25 14:19:43 +01001284 - WINBAR_HEIGHT(wp));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285 wp->w_status_height = (p_ls > 0);
1286 }
1287 else
1288 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001289 // height and row of new window is same as current window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290 wp->w_winrow = oldwin->w_winrow;
Bram Moolenaar415a6932017-12-05 20:31:07 +01001291 win_new_height(wp, VISIBLE_HEIGHT(oldwin));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292 wp->w_status_height = oldwin->w_status_height;
1293 }
1294 frp->fr_height = curfrp->fr_height;
1295
Bram Moolenaare38eab22019-12-05 21:50:01 +01001296 // "new_size" of the current window goes to the new window, use
1297 // one column for the vertical separator
Bram Moolenaar9b73a782010-03-17 16:54:57 +01001298 win_new_width(wp, new_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299 if (before)
1300 wp->w_vsep_width = 1;
1301 else
1302 {
1303 wp->w_vsep_width = oldwin->w_vsep_width;
1304 oldwin->w_vsep_width = 1;
1305 }
1306 if (flags & (WSP_TOP | WSP_BOT))
1307 {
1308 if (flags & WSP_BOT)
1309 frame_add_vsep(curfrp);
Bram Moolenaare38eab22019-12-05 21:50:01 +01001310 // Set width of neighbor frame
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 frame_new_width(curfrp, curfrp->fr_width
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00001312 - (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP,
1313 FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 }
1315 else
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00001316 win_new_width(oldwin, oldwin->w_width - (new_size + 1));
Bram Moolenaare38eab22019-12-05 21:50:01 +01001317 if (before) // new window left of current one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 {
1319 wp->w_wincol = oldwin->w_wincol;
1320 oldwin->w_wincol += new_size + 1;
1321 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01001322 else // new window right of current one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 wp->w_wincol = oldwin->w_wincol + oldwin->w_width + 1;
1324 frame_fix_width(oldwin);
1325 frame_fix_width(wp);
1326 }
1327 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001328 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001329 // width and column of new window is same as current window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 if (flags & (WSP_TOP | WSP_BOT))
1331 {
1332 wp->w_wincol = 0;
Bram Moolenaar9b73a782010-03-17 16:54:57 +01001333 win_new_width(wp, Columns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001334 wp->w_vsep_width = 0;
1335 }
1336 else
1337 {
1338 wp->w_wincol = oldwin->w_wincol;
Bram Moolenaar9b73a782010-03-17 16:54:57 +01001339 win_new_width(wp, oldwin->w_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340 wp->w_vsep_width = oldwin->w_vsep_width;
1341 }
1342 frp->fr_width = curfrp->fr_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343
Bram Moolenaare38eab22019-12-05 21:50:01 +01001344 // "new_size" of the current window goes to the new window, use
1345 // one row for the status line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 win_new_height(wp, new_size);
zeertzjqfbf20712023-04-26 19:01:44 +01001347 int old_status_height = oldwin->w_status_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001348 if (flags & (WSP_TOP | WSP_BOT))
Bram Moolenaar991dea32016-05-24 11:31:32 +02001349 {
Bram Moolenaard326ad62017-09-18 20:31:41 +02001350 int new_fr_height = curfrp->fr_height - new_size
Bram Moolenaar3167c3e2017-11-25 14:19:43 +01001351 + WINBAR_HEIGHT(wp) ;
Bram Moolenaar991dea32016-05-24 11:31:32 +02001352
1353 if (!((flags & WSP_BOT) && p_ls == 0))
1354 new_fr_height -= STATUS_HEIGHT;
zeertzjqfbf20712023-04-26 19:01:44 +01001355 if (flags & WSP_BOT)
1356 frame_add_statusline(curfrp);
Bram Moolenaar991dea32016-05-24 11:31:32 +02001357 frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, FALSE);
1358 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359 else
1360 win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
Bram Moolenaare38eab22019-12-05 21:50:01 +01001361 if (before) // new window above current one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362 {
1363 wp->w_winrow = oldwin->w_winrow;
1364 wp->w_status_height = STATUS_HEIGHT;
1365 oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
1366 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01001367 else // new window below current one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001368 {
Bram Moolenaar415a6932017-12-05 20:31:07 +01001369 wp->w_winrow = oldwin->w_winrow + VISIBLE_HEIGHT(oldwin)
1370 + STATUS_HEIGHT;
zeertzjqfbf20712023-04-26 19:01:44 +01001371 wp->w_status_height = old_status_height;
Bram Moolenaar991dea32016-05-24 11:31:32 +02001372 if (!(flags & WSP_BOT))
1373 oldwin->w_status_height = STATUS_HEIGHT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375 frame_fix_height(wp);
1376 frame_fix_height(oldwin);
1377 }
1378
1379 if (flags & (WSP_TOP | WSP_BOT))
1380 (void)win_comp_pos();
1381
Bram Moolenaar668008b2020-10-01 19:06:35 +02001382 // Both windows need redrawing. Update all status lines, in case they
1383 // show something related to the window count or position.
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001384 redraw_win_later(wp, UPD_NOT_VALID);
1385 redraw_win_later(oldwin, UPD_NOT_VALID);
Bram Moolenaar668008b2020-10-01 19:06:35 +02001386 status_redraw_all();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387
1388 if (need_status)
1389 {
1390 msg_row = Rows - 1;
1391 msg_col = sc_col;
Bram Moolenaare38eab22019-12-05 21:50:01 +01001392 msg_clr_eos_force(); // Old command/ruler may still be there
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 comp_col();
1394 msg_row = Rows - 1;
Bram Moolenaare38eab22019-12-05 21:50:01 +01001395 msg_col = 0; // put position back at start of line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 }
1397
1398 /*
Bram Moolenaar9b73a782010-03-17 16:54:57 +01001399 * equalize the window sizes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400 */
1401 if (do_equal || dir != 0)
1402 win_equal(wp, TRUE,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001404 : dir == 'h' ? 'b' : 'v');
Bram Moolenaare76062c2022-11-28 18:51:43 +00001405 else if (*p_spk != 'c' && !is_aucmd_win(wp))
Luuk van Baal29ab5242022-09-11 16:59:53 +01001406 win_fix_scroll(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001407
Bram Moolenaare38eab22019-12-05 21:50:01 +01001408 // Don't change the window height/width to 'winheight' / 'winwidth' if a
1409 // size was given.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001410 if (flags & WSP_VERT)
1411 {
1412 i = p_wiw;
1413 if (size != 0)
1414 p_wiw = size;
1415
1416# ifdef FEAT_GUI
Bram Moolenaare38eab22019-12-05 21:50:01 +01001417 // When 'guioptions' includes 'L' or 'R' may have to add scrollbars.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 if (gui.in_use)
1419 gui_init_which_components(NULL);
1420# endif
1421 }
1422 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001423 {
1424 i = p_wh;
1425 if (size != 0)
1426 p_wh = size;
1427 }
Bram Moolenaar9b73a782010-03-17 16:54:57 +01001428
1429 /*
1430 * make the new window the current window
1431 */
Bram Moolenaar57942232021-08-04 20:54:55 +02001432 (void)win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS
1433 | WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 if (flags & WSP_VERT)
1435 p_wiw = i;
1436 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 p_wh = i;
1438
1439 return OK;
1440}
1441
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001442
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001443/*
1444 * Initialize window "newp" from window "oldp".
1445 * Used when splitting a window and when creating a new tab page.
1446 * The windows will both edit the same buffer.
Bram Moolenaar884ae642009-02-22 01:37:59 +00001447 * WSP_NEWLOC may be specified in flags to prevent the location list from
1448 * being copied.
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001449 */
1450 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001451win_init(win_T *newp, win_T *oldp, int flags UNUSED)
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001452{
1453 int i;
1454
1455 newp->w_buffer = oldp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001456#ifdef FEAT_SYN_HL
Bram Moolenaarfd29f462010-06-06 16:11:09 +02001457 newp->w_s = &(oldp->w_buffer->b_s);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001458#endif
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001459 oldp->w_buffer->b_nwindows++;
1460 newp->w_cursor = oldp->w_cursor;
1461 newp->w_valid = 0;
1462 newp->w_curswant = oldp->w_curswant;
1463 newp->w_set_curswant = oldp->w_set_curswant;
1464 newp->w_topline = oldp->w_topline;
1465#ifdef FEAT_DIFF
1466 newp->w_topfill = oldp->w_topfill;
1467#endif
1468 newp->w_leftcol = oldp->w_leftcol;
1469 newp->w_pcmark = oldp->w_pcmark;
1470 newp->w_prev_pcmark = oldp->w_prev_pcmark;
1471 newp->w_alt_fnum = oldp->w_alt_fnum;
Bram Moolenaar4c3f5362006-04-11 21:38:50 +00001472 newp->w_wrow = oldp->w_wrow;
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001473 newp->w_fraction = oldp->w_fraction;
1474 newp->w_prev_fraction_row = oldp->w_prev_fraction_row;
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001475 copy_jumplist(oldp, newp);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001476#ifdef FEAT_QUICKFIX
Bram Moolenaar884ae642009-02-22 01:37:59 +00001477 if (flags & WSP_NEWLOC)
1478 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001479 // Don't copy the location list.
Bram Moolenaar884ae642009-02-22 01:37:59 +00001480 newp->w_llist = NULL;
1481 newp->w_llist_ref = NULL;
1482 }
1483 else
Bram Moolenaar09037502018-09-25 22:08:14 +02001484 copy_loclist_stack(oldp, newp);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001485#endif
Bram Moolenaarbd2dc342014-01-10 15:53:13 +01001486 newp->w_localdir = (oldp->w_localdir == NULL)
1487 ? NULL : vim_strsave(oldp->w_localdir);
Bram Moolenaara9a47d12020-08-09 21:45:52 +02001488 newp->w_prevdir = (oldp->w_prevdir == NULL)
1489 ? NULL : vim_strsave(oldp->w_prevdir);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001490
Luuk van Baal13ece2a2022-10-03 15:28:08 +01001491 if (*p_spk != 'c')
Luuk van Baal29ab5242022-09-11 16:59:53 +01001492 {
Luuk van Baalb926bf42023-05-06 12:53:50 +01001493 if (*p_spk == 't')
1494 newp->w_skipcol = oldp->w_skipcol;
Luuk van Baal29ab5242022-09-11 16:59:53 +01001495 newp->w_botline = oldp->w_botline;
1496 newp->w_prev_height = oldp->w_height - WINBAR_HEIGHT(oldp);
1497 newp->w_prev_winrow = oldp->w_winrow + 2 * WINBAR_HEIGHT(oldp);
1498 }
1499
Bram Moolenaare38eab22019-12-05 21:50:01 +01001500 // copy tagstack and folds
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001501 for (i = 0; i < oldp->w_tagstacklen; i++)
1502 {
Bram Moolenaar45e18cb2019-04-28 18:05:35 +02001503 taggy_T *tag = &newp->w_tagstack[i];
1504 *tag = oldp->w_tagstack[i];
1505 if (tag->tagname != NULL)
1506 tag->tagname = vim_strsave(tag->tagname);
1507 if (tag->user_data != NULL)
1508 tag->user_data = vim_strsave(tag->user_data);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001509 }
1510 newp->w_tagstackidx = oldp->w_tagstackidx;
1511 newp->w_tagstacklen = oldp->w_tagstacklen;
zeertzjqe6f13b42022-05-28 10:49:44 +01001512
1513 // Keep same changelist position in new window.
1514 newp->w_changelistidx = oldp->w_changelistidx;
1515
Bram Moolenaara971b822011-09-14 14:43:25 +02001516#ifdef FEAT_FOLDING
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001517 copyFoldingState(oldp, newp);
Bram Moolenaara971b822011-09-14 14:43:25 +02001518#endif
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001519
1520 win_init_some(newp, oldp);
Bram Moolenaar87fd0922021-11-20 13:47:45 +00001521#ifdef FEAT_TERMINAL
1522 term_update_wincolor(newp);
1523#endif
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001524}
1525
1526/*
Bram Moolenaar5d2bae82014-09-19 14:26:36 +02001527 * Initialize window "newp" from window "old".
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001528 * Only the essential things are copied.
1529 */
1530 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001531win_init_some(win_T *newp, win_T *oldp)
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001532{
Bram Moolenaare38eab22019-12-05 21:50:01 +01001533 // Use the same argument list.
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001534 newp->w_alist = oldp->w_alist;
1535 ++newp->w_alist->al_refcount;
1536 newp->w_arg_idx = oldp->w_arg_idx;
1537
Bram Moolenaare38eab22019-12-05 21:50:01 +01001538 // copy options from existing window
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001539 win_copy_options(oldp, newp);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00001540}
1541
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +02001542/*
1543 * Return TRUE if "win" is a global popup or a popup in the current tab page.
1544 */
Bram Moolenaarb53fb312019-06-13 23:59:52 +02001545 int
Bram Moolenaar682725c2019-05-25 20:10:37 +02001546win_valid_popup(win_T *win UNUSED)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001547{
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001548#ifdef FEAT_PROP_POPUP
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001549 win_T *wp;
1550
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001551 FOR_ALL_POPUPWINS(wp)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001552 if (wp == win)
1553 return TRUE;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001554 FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001555 if (wp == win)
1556 return TRUE;
1557#endif
1558 return FALSE;
1559}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001560
Bram Moolenaar071d4272004-06-13 20:20:40 +00001561/*
Bram Moolenaare59215c2016-08-14 19:08:45 +02001562 * Check if "win" is a pointer to an existing window in the current tab page.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001563 */
1564 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001565win_valid(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566{
1567 win_T *wp;
1568
1569 if (win == NULL)
1570 return FALSE;
Bram Moolenaar29323592016-07-24 22:04:11 +02001571 FOR_ALL_WINDOWS(wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001572 if (wp == win)
1573 return TRUE;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001574 return win_valid_popup(win);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575}
1576
1577/*
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001578 * Find window "id" in the current tab page.
Bram Moolenaar8adc8d92020-11-16 20:47:31 +01001579 * Also find popup windows.
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001580 * Return NULL if not found.
1581 */
1582 win_T *
1583win_find_by_id(int id)
1584{
1585 win_T *wp;
1586
1587 FOR_ALL_WINDOWS(wp)
1588 if (wp->w_id == id)
1589 return wp;
Bram Moolenaar8adc8d92020-11-16 20:47:31 +01001590#ifdef FEAT_PROP_POPUP
1591 FOR_ALL_POPUPWINS(wp)
1592 if (wp->w_id == id)
1593 return wp;
1594 FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
1595 if (wp->w_id == id)
1596 return wp;
1597#endif
Bram Moolenaarcbcd9cb2020-11-07 16:58:59 +01001598 return NULL;
1599}
1600
1601/*
Bram Moolenaare59215c2016-08-14 19:08:45 +02001602 * Check if "win" is a pointer to an existing window in any tab page.
1603 */
1604 int
1605win_valid_any_tab(win_T *win)
1606{
1607 win_T *wp;
1608 tabpage_T *tp;
1609
1610 if (win == NULL)
1611 return FALSE;
1612 FOR_ALL_TABPAGES(tp)
1613 {
1614 FOR_ALL_WINDOWS_IN_TAB(tp, wp)
1615 {
1616 if (wp == win)
1617 return TRUE;
1618 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001619#ifdef FEAT_PROP_POPUP
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001620 FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +02001621 if (wp == win)
1622 return TRUE;
1623#endif
Bram Moolenaare59215c2016-08-14 19:08:45 +02001624 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001625 return win_valid_popup(win);
Bram Moolenaare59215c2016-08-14 19:08:45 +02001626}
1627
1628/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 * Return the number of windows.
1630 */
1631 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001632win_count(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633{
1634 win_T *wp;
1635 int count = 0;
1636
Bram Moolenaar29323592016-07-24 22:04:11 +02001637 FOR_ALL_WINDOWS(wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001638 ++count;
1639 return count;
1640}
1641
1642/*
1643 * Make "count" windows on the screen.
1644 * Return actual number of windows on the screen.
1645 * Must be called when there is just one window, filling the whole screen
1646 * (excluding the command line).
1647 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001649make_windows(
1650 int count,
Bram Moolenaare38eab22019-12-05 21:50:01 +01001651 int vertical UNUSED) // split windows vertically if TRUE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001652{
1653 int maxcount;
1654 int todo;
1655
Bram Moolenaar071d4272004-06-13 20:20:40 +00001656 if (vertical)
1657 {
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001658 // Each window needs at least 'winminwidth' lines and a separator
Bram Moolenaare38eab22019-12-05 21:50:01 +01001659 // column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001660 maxcount = (curwin->w_width + curwin->w_vsep_width
1661 - (p_wiw - p_wmw)) / (p_wmw + 1);
1662 }
1663 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001665 // Each window needs at least 'winminheight' lines and a status line.
Bram Moolenaar415a6932017-12-05 20:31:07 +01001666 maxcount = (VISIBLE_HEIGHT(curwin) + curwin->w_status_height
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667 - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
1668 }
1669
1670 if (maxcount < 2)
1671 maxcount = 2;
1672 if (count > maxcount)
1673 count = maxcount;
1674
1675 /*
1676 * add status line now, otherwise first window will be too big
1677 */
1678 if (count > 1)
1679 last_status(TRUE);
1680
Bram Moolenaar071d4272004-06-13 20:20:40 +00001681 /*
1682 * Don't execute autocommands while creating the windows. Must do that
1683 * when putting the buffers in the windows.
1684 */
Bram Moolenaar78ab3312007-09-29 12:16:41 +00001685 block_autocmds();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001686
Bram Moolenaare38eab22019-12-05 21:50:01 +01001687 // todo is number of windows left to create
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688 for (todo = count - 1; todo > 0; --todo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001689 if (vertical)
1690 {
1691 if (win_split(curwin->w_width - (curwin->w_width - todo)
1692 / (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL)
1693 break;
1694 }
1695 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001696 {
1697 if (win_split(curwin->w_height - (curwin->w_height - todo
1698 * STATUS_HEIGHT) / (todo + 1)
1699 - STATUS_HEIGHT, WSP_ABOVE) == FAIL)
1700 break;
1701 }
1702
Bram Moolenaar78ab3312007-09-29 12:16:41 +00001703 unblock_autocmds();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704
Bram Moolenaare38eab22019-12-05 21:50:01 +01001705 // return actual number of windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00001706 return (count - todo);
1707}
1708
1709/*
1710 * Exchange current and next window
1711 */
1712 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001713win_exchange(long Prenum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714{
1715 frame_T *frp;
1716 frame_T *frp2;
1717 win_T *wp;
1718 win_T *wp2;
1719 int temp;
1720
Bram Moolenaar3c01c4a2020-02-01 23:04:24 +01001721 if (ERROR_IF_ANY_POPUP_WINDOW)
Bram Moolenaar815b76b2019-06-01 14:15:52 +02001722 return;
1723 if (ONE_WINDOW) // just one window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724 {
1725 beep_flush();
1726 return;
1727 }
1728
1729#ifdef FEAT_GUI
1730 need_mouse_correct = TRUE;
1731#endif
1732
1733 /*
1734 * find window to exchange with
1735 */
1736 if (Prenum)
1737 {
1738 frp = curwin->w_frame->fr_parent->fr_child;
1739 while (frp != NULL && --Prenum > 0)
1740 frp = frp->fr_next;
1741 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01001742 else if (curwin->w_frame->fr_next != NULL) // Swap with next
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743 frp = curwin->w_frame->fr_next;
Bram Moolenaare38eab22019-12-05 21:50:01 +01001744 else // Swap last window in row/col with previous
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 frp = curwin->w_frame->fr_prev;
1746
Bram Moolenaare38eab22019-12-05 21:50:01 +01001747 // We can only exchange a window with another window, not with a frame
1748 // containing windows.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001749 if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin)
1750 return;
1751 wp = frp->fr_win;
1752
1753/*
1754 * 1. remove curwin from the list. Remember after which window it was in wp2
1755 * 2. insert curwin before wp in the list
1756 * if wp != wp2
1757 * 3. remove wp from the list
1758 * 4. insert wp after wp2
1759 * 5. exchange the status line height and vsep width.
1760 */
1761 wp2 = curwin->w_prev;
1762 frp2 = curwin->w_frame->fr_prev;
1763 if (wp->w_prev != curwin)
1764 {
Bram Moolenaarf740b292006-02-16 22:11:02 +00001765 win_remove(curwin, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 frame_remove(curwin->w_frame);
1767 win_append(wp->w_prev, curwin);
1768 frame_insert(frp, curwin->w_frame);
1769 }
1770 if (wp != wp2)
1771 {
Bram Moolenaarf740b292006-02-16 22:11:02 +00001772 win_remove(wp, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 frame_remove(wp->w_frame);
1774 win_append(wp2, wp);
1775 if (frp2 == NULL)
1776 frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame);
1777 else
1778 frame_append(frp2, wp->w_frame);
1779 }
1780 temp = curwin->w_status_height;
1781 curwin->w_status_height = wp->w_status_height;
1782 wp->w_status_height = temp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783 temp = curwin->w_vsep_width;
1784 curwin->w_vsep_width = wp->w_vsep_width;
1785 wp->w_vsep_width = temp;
1786
gmntroll9e2fa4b2021-08-07 22:35:52 +02001787 frame_fix_height(curwin);
1788 frame_fix_height(wp);
1789 frame_fix_width(curwin);
1790 frame_fix_width(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791
Bram Moolenaare38eab22019-12-05 21:50:01 +01001792 (void)win_comp_pos(); // recompute window positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793
Bram Moolenaar05b27612022-01-20 13:32:50 +00001794 if (wp->w_buffer != curbuf)
1795 reset_VIsual_and_resel();
1796 else if (VIsual_active)
1797 wp->w_cursor = curwin->w_cursor;
1798
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 win_enter(wp, TRUE);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001800 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801}
1802
1803/*
1804 * rotate windows: if upwards TRUE the second window becomes the first one
1805 * if upwards FALSE the first window becomes the second one
1806 */
1807 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001808win_rotate(int upwards, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809{
1810 win_T *wp1;
1811 win_T *wp2;
1812 frame_T *frp;
1813 int n;
1814
Bram Moolenaare38eab22019-12-05 21:50:01 +01001815 if (ONE_WINDOW) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816 {
1817 beep_flush();
1818 return;
1819 }
1820
1821#ifdef FEAT_GUI
1822 need_mouse_correct = TRUE;
1823#endif
1824
Bram Moolenaare38eab22019-12-05 21:50:01 +01001825 // Check if all frames in this row/col have one window.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01001826 FOR_ALL_FRAMES(frp, curwin->w_frame->fr_parent->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 if (frp->fr_win == NULL)
1828 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001829 emsg(_(e_cannot_rotate_when_another_window_is_split));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 return;
1831 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832
1833 while (count--)
1834 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001835 if (upwards) // first window becomes last window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001836 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001837 // remove first window/frame from the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838 frp = curwin->w_frame->fr_parent->fr_child;
1839 wp1 = frp->fr_win;
Bram Moolenaarf740b292006-02-16 22:11:02 +00001840 win_remove(wp1, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 frame_remove(frp);
1842
Bram Moolenaare38eab22019-12-05 21:50:01 +01001843 // find last frame and append removed window/frame after it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001844 for ( ; frp->fr_next != NULL; frp = frp->fr_next)
1845 ;
1846 win_append(frp->fr_win, wp1);
1847 frame_append(frp, wp1->w_frame);
1848
Bram Moolenaare38eab22019-12-05 21:50:01 +01001849 wp2 = frp->fr_win; // previously last window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01001851 else // last window becomes first window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001853 // find last window/frame in the list and remove it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 for (frp = curwin->w_frame; frp->fr_next != NULL;
1855 frp = frp->fr_next)
1856 ;
1857 wp1 = frp->fr_win;
Bram Moolenaare38eab22019-12-05 21:50:01 +01001858 wp2 = wp1->w_prev; // will become last window
Bram Moolenaarf740b292006-02-16 22:11:02 +00001859 win_remove(wp1, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001860 frame_remove(frp);
1861
Bram Moolenaare38eab22019-12-05 21:50:01 +01001862 // append the removed window/frame before the first in the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1);
1864 frame_insert(frp->fr_parent->fr_child, frp);
1865 }
1866
Bram Moolenaare38eab22019-12-05 21:50:01 +01001867 // exchange status height and vsep width of old and new last window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868 n = wp2->w_status_height;
1869 wp2->w_status_height = wp1->w_status_height;
1870 wp1->w_status_height = n;
1871 frame_fix_height(wp1);
1872 frame_fix_height(wp2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001873 n = wp2->w_vsep_width;
1874 wp2->w_vsep_width = wp1->w_vsep_width;
1875 wp1->w_vsep_width = n;
1876 frame_fix_width(wp1);
1877 frame_fix_width(wp2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878
Bram Moolenaare38eab22019-12-05 21:50:01 +01001879 // recompute w_winrow and w_wincol for all windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880 (void)win_comp_pos();
1881 }
1882
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001883 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001884}
1885
1886/*
1887 * Move the current window to the very top/bottom/left/right of the screen.
1888 */
1889 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001890win_totop(int size, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891{
1892 int dir;
1893 int height = curwin->w_height;
1894
Bram Moolenaar459ca562016-11-10 18:16:33 +01001895 if (ONE_WINDOW)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896 {
1897 beep_flush();
1898 return;
1899 }
Bram Moolenaar1417c762019-07-27 17:31:36 +02001900 if (check_split_disallowed() == FAIL)
1901 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902
Bram Moolenaare38eab22019-12-05 21:50:01 +01001903 // Remove the window and frame from the tree of frames.
Bram Moolenaarf740b292006-02-16 22:11:02 +00001904 (void)winframe_remove(curwin, &dir, NULL);
1905 win_remove(curwin, NULL);
Bram Moolenaare38eab22019-12-05 21:50:01 +01001906 last_status(FALSE); // may need to remove last status line
1907 (void)win_comp_pos(); // recompute window positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001908
Bram Moolenaare38eab22019-12-05 21:50:01 +01001909 // Split a window on the desired side and put the window there.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001910 (void)win_split_ins(size, flags, curwin, dir);
1911 if (!(flags & WSP_VERT))
1912 {
1913 win_setheight(height);
1914 if (p_ea)
1915 win_equal(curwin, TRUE, 'v');
1916 }
1917
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001918#if defined(FEAT_GUI)
Bram Moolenaare38eab22019-12-05 21:50:01 +01001919 // When 'guioptions' includes 'L' or 'R' may have to remove or add
1920 // scrollbars. Have to update them anyway.
Bram Moolenaar746ebd32009-06-16 14:01:43 +00001921 gui_may_update_scrollbars();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001922#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001923}
1924
1925/*
1926 * Move window "win1" to below/right of "win2" and make "win1" the current
1927 * window. Only works within the same frame!
1928 */
1929 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001930win_move_after(win_T *win1, win_T *win2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001931{
1932 int height;
1933
Bram Moolenaare38eab22019-12-05 21:50:01 +01001934 // check if the arguments are reasonable
Bram Moolenaar071d4272004-06-13 20:20:40 +00001935 if (win1 == win2)
1936 return;
1937
Bram Moolenaare38eab22019-12-05 21:50:01 +01001938 // check if there is something to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 if (win2->w_next != win1)
1940 {
Bram Moolenaar1417c762019-07-27 17:31:36 +02001941 if (win1->w_frame->fr_parent != win2->w_frame->fr_parent)
1942 {
1943 iemsg("INTERNAL: trying to move a window into another frame");
1944 return;
1945 }
1946
Bram Moolenaar5d3c9f82020-06-26 20:41:39 +02001947 // may need to move the status line/vertical separator of the last
1948 // window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001949 if (win1 == lastwin)
1950 {
1951 height = win1->w_prev->w_status_height;
1952 win1->w_prev->w_status_height = win1->w_status_height;
1953 win1->w_status_height = height;
Bram Moolenaar0396ab02007-02-19 23:14:18 +00001954 if (win1->w_prev->w_vsep_width == 1)
1955 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001956 // Remove the vertical separator from the last-but-one window,
1957 // add it to the last window. Adjust the frame widths.
Bram Moolenaar0396ab02007-02-19 23:14:18 +00001958 win1->w_prev->w_vsep_width = 0;
1959 win1->w_prev->w_frame->fr_width -= 1;
1960 win1->w_vsep_width = 1;
1961 win1->w_frame->fr_width += 1;
1962 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001963 }
1964 else if (win2 == lastwin)
1965 {
1966 height = win1->w_status_height;
1967 win1->w_status_height = win2->w_status_height;
1968 win2->w_status_height = height;
Bram Moolenaar0396ab02007-02-19 23:14:18 +00001969 if (win1->w_vsep_width == 1)
1970 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001971 // Remove the vertical separator from win1, add it to the last
1972 // window, win2. Adjust the frame widths.
Bram Moolenaar0396ab02007-02-19 23:14:18 +00001973 win2->w_vsep_width = 1;
1974 win2->w_frame->fr_width += 1;
1975 win1->w_vsep_width = 0;
1976 win1->w_frame->fr_width -= 1;
1977 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001978 }
Bram Moolenaarf740b292006-02-16 22:11:02 +00001979 win_remove(win1, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001980 frame_remove(win1->w_frame);
1981 win_append(win2, win1);
1982 frame_append(win2->w_frame, win1->w_frame);
1983
Bram Moolenaare38eab22019-12-05 21:50:01 +01001984 (void)win_comp_pos(); // recompute w_winrow for all windows
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001985 redraw_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001986 }
1987 win_enter(win1, FALSE);
1988}
1989
1990/*
1991 * Make all windows the same height.
1992 * 'next_curwin' will soon be the current window, make sure it has enough
1993 * rows.
1994 */
1995 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01001996win_equal(
Bram Moolenaare38eab22019-12-05 21:50:01 +01001997 win_T *next_curwin, // pointer to current window to be or NULL
1998 int current, // do only frame with current window
1999 int dir) // 'v' for vertically, 'h' for horizontally,
2000 // 'b' for both, 0 for using p_ead
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001{
2002 if (dir == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 dir = *p_ead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002004 win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002005 topframe, dir, 0, tabline_height(),
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00002006 (int)Columns, topframe->fr_height);
Bram Moolenaare76062c2022-11-28 18:51:43 +00002007 if (*p_spk != 'c' && !is_aucmd_win(next_curwin))
Luuk van Baal29ab5242022-09-11 16:59:53 +01002008 win_fix_scroll(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009}
2010
2011/*
2012 * Set a frame to a new position and height, spreading the available room
2013 * equally over contained frames.
2014 * The window "next_curwin" (if not NULL) should at least get the size from
2015 * 'winheight' and 'winwidth' if possible.
2016 */
2017 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01002018win_equal_rec(
Bram Moolenaare38eab22019-12-05 21:50:01 +01002019 win_T *next_curwin, // pointer to current window to be or NULL
2020 int current, // do only frame with current window
2021 frame_T *topfr, // frame to set size off
2022 int dir, // 'v', 'h' or 'b', see win_equal()
2023 int col, // horizontal position for frame
2024 int row, // vertical position for frame
2025 int width, // new width of frame
2026 int height) // new height of frame
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027{
2028 int n, m;
2029 int extra_sep = 0;
2030 int wincount, totwincount = 0;
2031 frame_T *fr;
2032 int next_curwin_size = 0;
2033 int room = 0;
2034 int new_size;
2035 int has_next_curwin = 0;
2036 int hnc;
2037
2038 if (topfr->fr_layout == FR_LEAF)
2039 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002040 // Set the width/height of this frame.
2041 // Redraw when size or position changes
Bram Moolenaar071d4272004-06-13 20:20:40 +00002042 if (topfr->fr_height != height || topfr->fr_win->w_winrow != row
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 || topfr->fr_width != width || topfr->fr_win->w_wincol != col
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044 )
2045 {
2046 topfr->fr_win->w_winrow = row;
2047 frame_new_height(topfr, height, FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002048 topfr->fr_win->w_wincol = col;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002049 frame_new_width(topfr, width, FALSE, FALSE);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002050 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002051 }
2052 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053 else if (topfr->fr_layout == FR_ROW)
2054 {
2055 topfr->fr_width = width;
2056 topfr->fr_height = height;
2057
Bram Moolenaare38eab22019-12-05 21:50:01 +01002058 if (dir != 'v') // equalize frame widths
Bram Moolenaar071d4272004-06-13 20:20:40 +00002059 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002060 // Compute the maximum number of windows horizontally in this
2061 // frame.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002062 n = frame_minwidth(topfr, NOWIN);
Bram Moolenaare38eab22019-12-05 21:50:01 +01002063 // add one for the rightmost window, it doesn't have a separator
Bram Moolenaar071d4272004-06-13 20:20:40 +00002064 if (col + width == Columns)
2065 extra_sep = 1;
2066 else
2067 extra_sep = 0;
2068 totwincount = (n + extra_sep) / (p_wmw + 1);
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002069 has_next_curwin = frame_has_win(topfr, next_curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002071 /*
2072 * Compute width for "next_curwin" window and room available for
2073 * other windows.
2074 * "m" is the minimal width when counting p_wiw for "next_curwin".
2075 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 m = frame_minwidth(topfr, next_curwin);
2077 room = width - m;
2078 if (room < 0)
2079 {
2080 next_curwin_size = p_wiw + room;
2081 room = 0;
2082 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083 else
2084 {
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002085 next_curwin_size = -1;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01002086 FOR_ALL_FRAMES(fr, topfr->fr_child)
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002087 {
zeertzjq101d57b2022-07-31 18:34:32 +01002088 if (!frame_fixed_width(fr))
2089 continue;
2090 // If 'winfixwidth' set keep the window width if possible.
Bram Moolenaare38eab22019-12-05 21:50:01 +01002091 // Watch out for this window being the next_curwin.
zeertzjq101d57b2022-07-31 18:34:32 +01002092 n = frame_minwidth(fr, NOWIN);
2093 new_size = fr->fr_width;
2094 if (frame_has_win(fr, next_curwin))
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002095 {
zeertzjq101d57b2022-07-31 18:34:32 +01002096 room += p_wiw - p_wmw;
2097 next_curwin_size = 0;
2098 if (new_size < p_wiw)
2099 new_size = p_wiw;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002100 }
zeertzjq101d57b2022-07-31 18:34:32 +01002101 else
2102 // These windows don't use up room.
2103 totwincount -= (n + (fr->fr_next == NULL
2104 ? extra_sep : 0)) / (p_wmw + 1);
2105 room -= new_size - n;
2106 if (room < 0)
2107 {
2108 new_size += room;
2109 room = 0;
2110 }
2111 fr->fr_newwidth = new_size;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002112 }
2113 if (next_curwin_size == -1)
2114 {
2115 if (!has_next_curwin)
2116 next_curwin_size = 0;
2117 else if (totwincount > 1
2118 && (room + (totwincount - 2))
2119 / (totwincount - 1) > p_wiw)
2120 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002121 // Can make all windows wider than 'winwidth', spread
2122 // the room equally.
Bram Moolenaarb21e5842006-04-16 18:30:08 +00002123 next_curwin_size = (room + p_wiw
2124 + (totwincount - 1) * p_wmw
2125 + (totwincount - 1)) / totwincount;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002126 room -= next_curwin_size - p_wiw;
2127 }
2128 else
2129 next_curwin_size = p_wiw;
2130 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 }
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002132
2133 if (has_next_curwin)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002134 --totwincount; // don't count curwin
Bram Moolenaar071d4272004-06-13 20:20:40 +00002135 }
2136
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01002137 FOR_ALL_FRAMES(fr, topfr->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002138 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 wincount = 1;
2140 if (fr->fr_next == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002141 // last frame gets all that remains (avoid roundoff error)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002142 new_size = width;
2143 else if (dir == 'v')
2144 new_size = fr->fr_width;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002145 else if (frame_fixed_width(fr))
2146 {
2147 new_size = fr->fr_newwidth;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002148 wincount = 0; // doesn't count as a sizeable window
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002149 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002150 else
2151 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002152 // Compute the maximum number of windows horiz. in "fr".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153 n = frame_minwidth(fr, NOWIN);
2154 wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
2155 / (p_wmw + 1);
2156 m = frame_minwidth(fr, next_curwin);
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002157 if (has_next_curwin)
2158 hnc = frame_has_win(fr, next_curwin);
2159 else
2160 hnc = FALSE;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002161 if (hnc) // don't count next_curwin
Bram Moolenaar071d4272004-06-13 20:20:40 +00002162 --wincount;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002163 if (totwincount == 0)
2164 new_size = room;
2165 else
2166 new_size = (wincount * room + ((unsigned)totwincount >> 1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 / totwincount;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002168 if (hnc) // add next_curwin size
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169 {
2170 next_curwin_size -= p_wiw - (m - n);
Bram Moolenaar8279af52022-09-26 23:08:22 +01002171 if (next_curwin_size < 0)
2172 next_curwin_size = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002173 new_size += next_curwin_size;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002174 room -= new_size - next_curwin_size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175 }
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002176 else
2177 room -= new_size;
2178 new_size += n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002179 }
2180
Bram Moolenaare38eab22019-12-05 21:50:01 +01002181 // Skip frame that is full width when splitting or closing a
2182 // window, unless equalizing all frames.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183 if (!current || dir != 'v' || topfr->fr_parent != NULL
2184 || (new_size != fr->fr_width)
2185 || frame_has_win(fr, next_curwin))
2186 win_equal_rec(next_curwin, current, fr, dir, col, row,
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00002187 new_size, height);
2188 col += new_size;
2189 width -= new_size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002190 totwincount -= wincount;
2191 }
2192 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01002193 else // topfr->fr_layout == FR_COL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195 topfr->fr_width = width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196 topfr->fr_height = height;
2197
Bram Moolenaare38eab22019-12-05 21:50:01 +01002198 if (dir != 'h') // equalize frame heights
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002200 // Compute maximum number of windows vertically in this frame.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201 n = frame_minheight(topfr, NOWIN);
Bram Moolenaare38eab22019-12-05 21:50:01 +01002202 // add one for the bottom window if it doesn't have a statusline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 if (row + height == cmdline_row && p_ls == 0)
2204 extra_sep = 1;
2205 else
2206 extra_sep = 0;
2207 totwincount = (n + extra_sep) / (p_wmh + 1);
2208 has_next_curwin = frame_has_win(topfr, next_curwin);
2209
2210 /*
2211 * Compute height for "next_curwin" window and room available for
2212 * other windows.
2213 * "m" is the minimal height when counting p_wh for "next_curwin".
2214 */
2215 m = frame_minheight(topfr, next_curwin);
2216 room = height - m;
2217 if (room < 0)
2218 {
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00002219 // The room is less than 'winheight', use all space for the
Bram Moolenaare38eab22019-12-05 21:50:01 +01002220 // current window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 next_curwin_size = p_wh + room;
2222 room = 0;
2223 }
2224 else
2225 {
2226 next_curwin_size = -1;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01002227 FOR_ALL_FRAMES(fr, topfr->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002228 {
zeertzjq101d57b2022-07-31 18:34:32 +01002229 if (!frame_fixed_height(fr))
2230 continue;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002231 // If 'winfixheight' set keep the window height if
2232 // possible.
2233 // Watch out for this window being the next_curwin.
zeertzjq101d57b2022-07-31 18:34:32 +01002234 n = frame_minheight(fr, NOWIN);
2235 new_size = fr->fr_height;
2236 if (frame_has_win(fr, next_curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002237 {
zeertzjq101d57b2022-07-31 18:34:32 +01002238 room += p_wh - p_wmh;
2239 next_curwin_size = 0;
2240 if (new_size < p_wh)
2241 new_size = p_wh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002242 }
zeertzjq101d57b2022-07-31 18:34:32 +01002243 else
2244 // These windows don't use up room.
2245 totwincount -= (n + (fr->fr_next == NULL
2246 ? extra_sep : 0)) / (p_wmh + 1);
2247 room -= new_size - n;
2248 if (room < 0)
2249 {
2250 new_size += room;
2251 room = 0;
2252 }
2253 fr->fr_newheight = new_size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002254 }
2255 if (next_curwin_size == -1)
2256 {
2257 if (!has_next_curwin)
2258 next_curwin_size = 0;
2259 else if (totwincount > 1
2260 && (room + (totwincount - 2))
2261 / (totwincount - 1) > p_wh)
2262 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002263 // can make all windows higher than 'winheight',
2264 // spread the room equally.
Bram Moolenaarb21e5842006-04-16 18:30:08 +00002265 next_curwin_size = (room + p_wh
2266 + (totwincount - 1) * p_wmh
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267 + (totwincount - 1)) / totwincount;
2268 room -= next_curwin_size - p_wh;
2269 }
2270 else
2271 next_curwin_size = p_wh;
2272 }
2273 }
2274
2275 if (has_next_curwin)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002276 --totwincount; // don't count curwin
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277 }
2278
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01002279 FOR_ALL_FRAMES(fr, topfr->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 wincount = 1;
2282 if (fr->fr_next == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002283 // last frame gets all that remains (avoid roundoff error)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284 new_size = height;
2285 else if (dir == 'h')
2286 new_size = fr->fr_height;
2287 else if (frame_fixed_height(fr))
2288 {
2289 new_size = fr->fr_newheight;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002290 wincount = 0; // doesn't count as a sizeable window
Bram Moolenaar071d4272004-06-13 20:20:40 +00002291 }
2292 else
2293 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002294 // Compute the maximum number of windows vert. in "fr".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002295 n = frame_minheight(fr, NOWIN);
2296 wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
2297 / (p_wmh + 1);
2298 m = frame_minheight(fr, next_curwin);
2299 if (has_next_curwin)
2300 hnc = frame_has_win(fr, next_curwin);
2301 else
2302 hnc = FALSE;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002303 if (hnc) // don't count next_curwin
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 --wincount;
2305 if (totwincount == 0)
2306 new_size = room;
2307 else
2308 new_size = (wincount * room + ((unsigned)totwincount >> 1))
2309 / totwincount;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002310 if (hnc) // add next_curwin size
Bram Moolenaar071d4272004-06-13 20:20:40 +00002311 {
2312 next_curwin_size -= p_wh - (m - n);
2313 new_size += next_curwin_size;
2314 room -= new_size - next_curwin_size;
2315 }
2316 else
2317 room -= new_size;
2318 new_size += n;
2319 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01002320 // Skip frame that is full width when splitting or closing a
2321 // window, unless equalizing all frames.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322 if (!current || dir != 'h' || topfr->fr_parent != NULL
2323 || (new_size != fr->fr_height)
2324 || frame_has_win(fr, next_curwin))
2325 win_equal_rec(next_curwin, current, fr, dir, col, row,
2326 width, new_size);
2327 row += new_size;
2328 height -= new_size;
2329 totwincount -= wincount;
2330 }
2331 }
2332}
2333
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002334#ifdef FEAT_JOB_CHANNEL
orbitalcde8de02023-04-02 22:05:13 +01002335 void
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002336leaving_window(win_T *win)
2337{
Bram Moolenaarf98b8452018-06-10 14:39:52 +02002338 // Only matters for a prompt window.
2339 if (!bt_prompt(win->w_buffer))
2340 return;
2341
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002342 // When leaving a prompt window stop Insert mode and perhaps restart
2343 // it when entering that window again.
2344 win->w_buffer->b_prompt_insert = restart_edit;
Bram Moolenaar942b4542018-06-17 16:23:34 +02002345 if (restart_edit != 0 && mode_displayed)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002346 clear_cmdline = TRUE; // unshow mode later
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002347 restart_edit = NUL;
2348
2349 // When leaving the window (or closing the window) was done from a
Bram Moolenaarf98b8452018-06-10 14:39:52 +02002350 // callback we need to break out of the Insert mode loop and restart Insert
2351 // mode when entering the window again.
Bram Moolenaar24959102022-05-07 20:01:16 +01002352 if (State & MODE_INSERT)
Bram Moolenaar891e1fd2018-06-06 18:02:39 +02002353 {
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002354 stop_insert_mode = TRUE;
Bram Moolenaarf98b8452018-06-10 14:39:52 +02002355 if (win->w_buffer->b_prompt_insert == NUL)
Bram Moolenaar891e1fd2018-06-06 18:02:39 +02002356 win->w_buffer->b_prompt_insert = 'A';
2357 }
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002358}
2359
Bram Moolenaarbdf931c2020-10-01 22:37:40 +02002360 void
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002361entering_window(win_T *win)
2362{
Bram Moolenaarf98b8452018-06-10 14:39:52 +02002363 // Only matters for a prompt window.
2364 if (!bt_prompt(win->w_buffer))
2365 return;
2366
Bram Moolenaar891e1fd2018-06-06 18:02:39 +02002367 // When switching to a prompt buffer that was in Insert mode, don't stop
2368 // Insert mode, it may have been set in leaving_window().
Bram Moolenaarf98b8452018-06-10 14:39:52 +02002369 if (win->w_buffer->b_prompt_insert != NUL)
Bram Moolenaar891e1fd2018-06-06 18:02:39 +02002370 stop_insert_mode = FALSE;
2371
Bram Moolenaarf98b8452018-06-10 14:39:52 +02002372 // When entering the prompt window restart Insert mode if we were in Insert
Bram Moolenaar34c20ff2021-11-25 13:04:48 +00002373 // mode when we left it and not already in Insert mode.
Bram Moolenaar24959102022-05-07 20:01:16 +01002374 if ((State & MODE_INSERT) == 0)
Bram Moolenaar34c20ff2021-11-25 13:04:48 +00002375 restart_edit = win->w_buffer->b_prompt_insert;
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002376}
2377#endif
2378
Bram Moolenaar347538f2022-03-26 16:28:06 +00002379 static void
2380win_init_empty(win_T *wp)
2381{
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002382 redraw_win_later(wp, UPD_NOT_VALID);
Bram Moolenaar347538f2022-03-26 16:28:06 +00002383 wp->w_lines_valid = 0;
2384 wp->w_cursor.lnum = 1;
2385 wp->w_curswant = wp->w_cursor.col = 0;
2386 wp->w_cursor.coladd = 0;
2387 wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1
2388 wp->w_pcmark.col = 0;
2389 wp->w_prev_pcmark.lnum = 0;
2390 wp->w_prev_pcmark.col = 0;
2391 wp->w_topline = 1;
2392#ifdef FEAT_DIFF
2393 wp->w_topfill = 0;
2394#endif
2395 wp->w_botline = 2;
2396#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
2397 wp->w_s = &wp->w_buffer->b_s;
2398#endif
2399#ifdef FEAT_TERMINAL
2400 term_reset_wincolor(wp);
2401#endif
2402}
2403
2404/*
2405 * Init the current window "curwin".
2406 * Called when a new file is being edited.
2407 */
2408 void
2409curwin_init(void)
2410{
2411 win_init_empty(curwin);
2412}
2413
Bram Moolenaar071d4272004-06-13 20:20:40 +00002414/*
Bram Moolenaar8c752bd2017-03-19 17:09:56 +01002415 * Close all windows for buffer "buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416 */
2417 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01002418close_windows(
2419 buf_T *buf,
Bram Moolenaare38eab22019-12-05 21:50:01 +01002420 int keep_curwin) // don't close "curwin"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002421{
Bram Moolenaarf740b292006-02-16 22:11:02 +00002422 win_T *wp;
2423 tabpage_T *tp, *nexttp;
Bram Moolenaar12c11d52016-07-19 23:13:03 +02002424 int count = tabpage_index(NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425
2426 ++RedrawingDisabled;
Bram Moolenaarf740b292006-02-16 22:11:02 +00002427
Bram Moolenaar459ca562016-11-10 18:16:33 +01002428 for (wp = firstwin; wp != NULL && !ONE_WINDOW; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429 {
Bram Moolenaar362ce482012-06-06 19:02:45 +02002430 if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002431 && !(wp->w_closing || wp->w_buffer->b_locked > 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432 {
Bram Moolenaar8c752bd2017-03-19 17:09:56 +01002433 if (win_close(wp, FALSE) == FAIL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002434 // If closing the window fails give up, to avoid looping
2435 // forever.
Bram Moolenaar8c752bd2017-03-19 17:09:56 +01002436 break;
Bram Moolenaarf740b292006-02-16 22:11:02 +00002437
Bram Moolenaare38eab22019-12-05 21:50:01 +01002438 // Start all over, autocommands may change the window layout.
Bram Moolenaarf740b292006-02-16 22:11:02 +00002439 wp = firstwin;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002440 }
2441 else
Bram Moolenaarf740b292006-02-16 22:11:02 +00002442 wp = wp->w_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 }
Bram Moolenaarf740b292006-02-16 22:11:02 +00002444
Bram Moolenaare38eab22019-12-05 21:50:01 +01002445 // Also check windows in other tab pages.
Bram Moolenaarf740b292006-02-16 22:11:02 +00002446 for (tp = first_tabpage; tp != NULL; tp = nexttp)
2447 {
2448 nexttp = tp->tp_next;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002449 if (tp != curtab)
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002450 FOR_ALL_WINDOWS_IN_TAB(tp, wp)
Bram Moolenaar362ce482012-06-06 19:02:45 +02002451 if (wp->w_buffer == buf
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002452 && !(wp->w_closing || wp->w_buffer->b_locked > 0))
Bram Moolenaarf740b292006-02-16 22:11:02 +00002453 {
2454 win_close_othertab(wp, FALSE, tp);
2455
Bram Moolenaare38eab22019-12-05 21:50:01 +01002456 // Start all over, the tab page may be closed and
2457 // autocommands may change the window layout.
Bram Moolenaarf740b292006-02-16 22:11:02 +00002458 nexttp = first_tabpage;
2459 break;
2460 }
2461 }
2462
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463 --RedrawingDisabled;
Bram Moolenaarf740b292006-02-16 22:11:02 +00002464
Bram Moolenaar12c11d52016-07-19 23:13:03 +02002465 if (count != tabpage_index(NULL))
2466 apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002467}
2468
2469/*
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002470 * Return TRUE if the current window is the only window that exists (ignoring
Bram Moolenaare76062c2022-11-28 18:51:43 +00002471 * "aucmd_win[]").
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002472 * Returns FALSE if there is a window, possibly in another tab page.
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00002473 */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002474 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01002475last_window(void)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00002476{
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002477 return (one_window() && first_tabpage->tp_next == NULL);
2478}
2479
2480/*
Bram Moolenaare76062c2022-11-28 18:51:43 +00002481 * Return TRUE if there is only one window other than "aucmd_win[]" in the
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002482 * current tab page.
2483 */
Bram Moolenaar42ec6562012-02-22 14:58:37 +01002484 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01002485one_window(void)
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002486{
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002487 win_T *wp;
2488 int seen_one = FALSE;
2489
2490 FOR_ALL_WINDOWS(wp)
2491 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00002492 if (!is_aucmd_win(wp))
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002493 {
2494 if (seen_one)
2495 return FALSE;
2496 seen_one = TRUE;
2497 }
2498 }
2499 return TRUE;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00002500}
2501
2502/*
Bram Moolenaarbef1c362012-05-25 12:39:00 +02002503 * Close the possibly last window in a tab page.
2504 * Returns TRUE when the window was closed already.
2505 */
2506 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01002507close_last_window_tabpage(
2508 win_T *win,
2509 int free_buf,
2510 tabpage_T *prev_curtab)
Bram Moolenaarbef1c362012-05-25 12:39:00 +02002511{
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00002512 if (!ONE_WINDOW)
2513 return FALSE;
Bram Moolenaar49e649f2013-05-06 04:50:35 +02002514
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00002515 buf_T *old_curbuf = curbuf;
Bram Moolenaarbef1c362012-05-25 12:39:00 +02002516
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00002517 /*
2518 * Closing the last window in a tab page. First go to another tab
2519 * page and then close the window and the tab page. This avoids that
2520 * curwin and curtab are invalid while we are freeing memory, they may
2521 * be used in GUI events.
2522 * Don't trigger autocommands yet, they may use wrong values, so do
2523 * that below.
2524 */
2525 goto_tabpage_tp(alt_tabpage(), FALSE, TRUE);
2526
2527 // Safety check: Autocommands may have closed the window when jumping
2528 // to the other tab page.
2529 if (valid_tabpage(prev_curtab) && prev_curtab->tp_firstwin == win)
2530 win_close_othertab(win, free_buf, prev_curtab);
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002531#ifdef FEAT_JOB_CHANNEL
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00002532 entering_window(curwin);
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002533#endif
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00002534 // Since goto_tabpage_tp above did not trigger *Enter autocommands, do
2535 // that now.
2536 apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
2537 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
2538 apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
2539 if (old_curbuf != curbuf)
2540 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
2541 return TRUE;
Bram Moolenaarbef1c362012-05-25 12:39:00 +02002542}
2543
2544/*
Bram Moolenaar7c7f01e2019-06-12 21:06:32 +02002545 * Close the buffer of "win" and unload it if "action" is DOBUF_UNLOAD.
2546 * "action" can also be zero (do nothing) or DOBUF_WIPE.
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002547 * "abort_if_last" is passed to close_buffer(): abort closing if all other
2548 * windows are closed.
2549 */
2550 static void
Bram Moolenaar7c7f01e2019-06-12 21:06:32 +02002551win_close_buffer(win_T *win, int action, int abort_if_last)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002552{
2553#ifdef FEAT_SYN_HL
2554 // Free independent synblock before the buffer is freed.
2555 if (win->w_buffer != NULL)
2556 reset_synblock(win);
2557#endif
2558
2559#ifdef FEAT_QUICKFIX
Yegappan Lakshmanan78a61062021-12-08 20:03:31 +00002560 // When a quickfix/location list window is closed and the buffer is
2561 // displayed in only one window, then unlist the buffer.
2562 if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)
2563 && win->w_buffer->b_nwindows == 1)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002564 win->w_buffer->b_p_bl = FALSE;
2565#endif
2566
2567 // Close the link to the buffer.
2568 if (win->w_buffer != NULL)
2569 {
2570 bufref_T bufref;
2571
2572 set_bufref(&bufref, curbuf);
2573 win->w_closing = TRUE;
Bram Moolenaarc947b9a2022-04-06 17:59:21 +01002574 close_buffer(win, win->w_buffer, action, abort_if_last, TRUE);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002575 if (win_valid_any_tab(win))
2576 win->w_closing = FALSE;
2577 // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
2578 // "wipe".
2579 if (!bufref_valid(&bufref))
2580 curbuf = firstbuf;
2581 }
2582}
2583
2584/*
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002585 * Close window "win". Only works for the current tab page.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002586 * If "free_buf" is TRUE related buffer may be unloaded.
2587 *
Bram Moolenaar42ec6562012-02-22 14:58:37 +01002588 * Called by :quit, :close, :xit, :wq and findtag().
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002589 * Returns FAIL when the window was not closed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002590 */
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002591 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01002592win_close(win_T *win, int free_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593{
2594 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595 int other_buffer = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596 int close_curwin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002597 int dir;
2598 int help_window = FALSE;
Bram Moolenaarc1b52862006-04-28 22:32:28 +00002599 tabpage_T *prev_curtab = curtab;
Bram Moolenaar41cc0382017-06-26 09:59:35 +02002600 frame_T *win_frame = win->w_frame->fr_parent;
Bram Moolenaarc8234772019-11-10 21:00:27 +01002601#ifdef FEAT_DIFF
2602 int had_diffmode = win->w_p_diff;
2603#endif
Bram Moolenaarf18e8a92021-08-04 21:16:50 +02002604#ifdef MESSAGE_QUEUE
Bram Moolenaar57942232021-08-04 20:54:55 +02002605 int did_decrement = FALSE;
Bram Moolenaarf18e8a92021-08-04 21:16:50 +02002606#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002607
Bram Moolenaard98c0b62020-02-02 15:25:16 +01002608#if defined(FEAT_TERMINAL) && defined(FEAT_PROP_POPUP)
2609 // Can close a popup window with a terminal if the job has finished.
2610 if (may_close_term_popup() == OK)
2611 return OK;
2612#endif
Bram Moolenaar3c01c4a2020-02-01 23:04:24 +01002613 if (ERROR_IF_ANY_POPUP_WINDOW)
Bram Moolenaar815b76b2019-06-01 14:15:52 +02002614 return FAIL;
2615
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00002616 if (last_window())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002617 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002618 emsg(_(e_cannot_close_last_window));
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002619 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002620 }
Bram Moolenaar9fda8152022-11-19 13:14:10 +00002621 if (window_layout_locked(CMD_close))
Bram Moolenaard63a8552022-11-19 11:41:30 +00002622 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623
Bram Moolenaare0ab94e2016-09-04 19:50:54 +02002624 if (win->w_closing || (win->w_buffer != NULL
2625 && win->w_buffer->b_locked > 0))
Bram Moolenaare38eab22019-12-05 21:50:01 +01002626 return FAIL; // window is already being closed
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002627 if (win_unlisted(win))
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002628 {
Bram Moolenaar74409f62022-01-01 15:58:22 +00002629 emsg(_(e_cannot_close_autocmd_or_popup_window));
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002630 return FAIL;
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002631 }
Bram Moolenaare76062c2022-11-28 18:51:43 +00002632 if ((is_aucmd_win(firstwin) || is_aucmd_win(lastwin)) && one_window())
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002633 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00002634 emsg(_(e_cannot_close_window_only_autocmd_window_would_remain));
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002635 return FAIL;
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002636 }
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002637
Bram Moolenaare38eab22019-12-05 21:50:01 +01002638 // When closing the last window in a tab page first go to another tab page
2639 // and then close the window and the tab page to avoid that curwin and
2640 // curtab are invalid while we are freeing memory.
Bram Moolenaarbef1c362012-05-25 12:39:00 +02002641 if (close_last_window_tabpage(win, free_buf, prev_curtab))
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002642 return FAIL;
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00002643
Bram Moolenaare38eab22019-12-05 21:50:01 +01002644 // When closing the help window, try restoring a snapshot after closing
2645 // the window. Otherwise clear the snapshot, it's now invalid.
Bram Moolenaard28cc3f2017-07-27 22:03:50 +02002646 if (bt_help(win->w_buffer))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 help_window = TRUE;
2648 else
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002649 clear_snapshot(curtab, SNAP_HELP_IDX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651 if (win == curwin)
2652 {
Bram Moolenaar6d41c782018-06-06 09:11:12 +02002653#ifdef FEAT_JOB_CHANNEL
2654 leaving_window(curwin);
2655#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656 /*
2657 * Guess which window is going to be the new current window.
2658 * This may change because of the autocommands (sigh).
2659 */
Bram Moolenaarf740b292006-02-16 22:11:02 +00002660 wp = frame2win(win_altframe(win, NULL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002661
2662 /*
Bram Moolenaar362ce482012-06-06 19:02:45 +02002663 * Be careful: If autocommands delete the window or cause this window
2664 * to be the last one left, return now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 */
2666 if (wp->w_buffer != curbuf)
2667 {
Bram Moolenaar3d51ce12022-07-01 15:26:15 +01002668 reset_VIsual_and_resel(); // stop Visual mode
2669
Bram Moolenaar071d4272004-06-13 20:20:40 +00002670 other_buffer = TRUE;
Bram Moolenaar362ce482012-06-06 19:02:45 +02002671 win->w_closing = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
Bram Moolenaar362ce482012-06-06 19:02:45 +02002673 if (!win_valid(win))
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002674 return FAIL;
Bram Moolenaar362ce482012-06-06 19:02:45 +02002675 win->w_closing = FALSE;
2676 if (last_window())
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002677 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002678 }
Bram Moolenaar362ce482012-06-06 19:02:45 +02002679 win->w_closing = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
Bram Moolenaar362ce482012-06-06 19:02:45 +02002681 if (!win_valid(win))
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002682 return FAIL;
Bram Moolenaar362ce482012-06-06 19:02:45 +02002683 win->w_closing = FALSE;
2684 if (last_window())
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002685 return FAIL;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002686#ifdef FEAT_EVAL
Bram Moolenaare38eab22019-12-05 21:50:01 +01002687 // autocmds may abort script processing
Bram Moolenaar071d4272004-06-13 20:20:40 +00002688 if (aborting())
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002689 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002690#endif
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002691 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692
Bram Moolenaar053b9fa2007-04-26 14:09:42 +00002693#ifdef FEAT_GUI
Bram Moolenaar647e24b2019-03-17 16:39:46 +01002694 // Avoid trouble with scrollbars that are going to be deleted in
2695 // win_free().
Bram Moolenaar053b9fa2007-04-26 14:09:42 +00002696 if (gui.in_use)
2697 out_flush();
2698#endif
2699
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002700#ifdef FEAT_PROP_POPUP
Bram Moolenaar12034e22019-08-25 22:25:02 +02002701 if (popup_win_closed(win) && !win_valid(win))
2702 return FAIL;
2703#endif
naohiro ono23beefe2021-11-13 12:38:49 +00002704
2705 // Trigger WinClosed just before starting to free window-related resources.
2706 trigger_winclosed(win);
2707 // autocmd may have freed the window already.
2708 if (!win_valid_any_tab(win))
2709 return OK;
2710
Bram Moolenaar7c7f01e2019-06-12 21:06:32 +02002711 win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, TRUE);
Bram Moolenaarc1b52862006-04-28 22:32:28 +00002712
Bram Moolenaar802418d2013-01-17 14:00:11 +01002713 if (only_one_window() && win_valid(win) && win->w_buffer == NULL
2714 && (last_window() || curtab != prev_curtab
2715 || close_last_window_tabpage(win, free_buf, prev_curtab)))
Bram Moolenaar2b90ed22013-07-24 16:02:36 +02002716 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002717 // Autocommands have closed all windows, quit now. Restore
2718 // curwin->w_buffer, otherwise writing viminfo may fail.
Bram Moolenaar2b90ed22013-07-24 16:02:36 +02002719 if (curwin->w_buffer == NULL)
2720 curwin->w_buffer = curbuf;
Bram Moolenaar802418d2013-01-17 14:00:11 +01002721 getout(0);
Bram Moolenaar2b90ed22013-07-24 16:02:36 +02002722 }
Bram Moolenaar802418d2013-01-17 14:00:11 +01002723
Bram Moolenaare38eab22019-12-05 21:50:01 +01002724 // Autocommands may have moved to another tab page.
Bram Moolenaar11fbc282016-09-02 21:48:32 +02002725 if (curtab != prev_curtab && win_valid_any_tab(win)
2726 && win->w_buffer == NULL)
2727 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002728 // Need to close the window anyway, since the buffer is NULL.
zeertzjq62de54b2022-09-22 18:08:37 +01002729 // Don't trigger autocmds with a NULL buffer.
2730 block_autocmds();
Bram Moolenaar11fbc282016-09-02 21:48:32 +02002731 win_close_othertab(win, FALSE, prev_curtab);
zeertzjq62de54b2022-09-22 18:08:37 +01002732 unblock_autocmds();
Bram Moolenaar11fbc282016-09-02 21:48:32 +02002733 return FAIL;
2734 }
2735
Bram Moolenaare38eab22019-12-05 21:50:01 +01002736 // Autocommands may have closed the window already or closed the only
2737 // other window.
Bram Moolenaar11fbc282016-09-02 21:48:32 +02002738 if (!win_valid(win) || last_window()
Bram Moolenaarbef1c362012-05-25 12:39:00 +02002739 || close_last_window_tabpage(win, free_buf, prev_curtab))
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002740 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741
Bram Moolenaar1417c762019-07-27 17:31:36 +02002742 // Now we are really going to close the window. Disallow any autocommand
2743 // to split a window to avoid trouble.
Bram Moolenaar4778b4d2020-11-04 11:03:12 +01002744 // Also bail out of parse_queued_messages() to avoid it tries to update the
2745 // screen.
Bram Moolenaar1417c762019-07-27 17:31:36 +02002746 ++split_disallowed;
Bram Moolenaar4778b4d2020-11-04 11:03:12 +01002747#ifdef MESSAGE_QUEUE
2748 ++dont_parse_messages;
2749#endif
Bram Moolenaar1417c762019-07-27 17:31:36 +02002750
Bram Moolenaare38eab22019-12-05 21:50:01 +01002751 // Free the memory used for the window and get the window that received
2752 // the screen space.
Bram Moolenaarc1b52862006-04-28 22:32:28 +00002753 wp = win_free_mem(win, &dir, NULL);
2754
LemonBoy2a2707d2022-05-04 22:13:47 +01002755 if (help_window)
2756 {
2757 // Closing the help window moves the cursor back to the current window
2758 // of the snapshot.
2759 win_T *prev_win = get_snapshot_curwin(SNAP_HELP_IDX);
2760
2761 if (win_valid(prev_win))
2762 wp = prev_win;
2763 }
2764
Bram Moolenaare38eab22019-12-05 21:50:01 +01002765 // Make sure curwin isn't invalid. It can cause severe trouble when
2766 // printing an error message. For win_equal() curbuf needs to be valid
2767 // too.
Bram Moolenaarc1b52862006-04-28 22:32:28 +00002768 if (win == curwin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002769 {
2770 curwin = wp;
2771#ifdef FEAT_QUICKFIX
2772 if (wp->w_p_pvw || bt_quickfix(wp->w_buffer))
2773 {
2774 /*
Bram Moolenaar48cc5fe2007-08-11 11:39:45 +00002775 * If the cursor goes to the preview or the quickfix window, try
Bram Moolenaar071d4272004-06-13 20:20:40 +00002776 * finding another window to go to.
2777 */
2778 for (;;)
2779 {
2780 if (wp->w_next == NULL)
2781 wp = firstwin;
2782 else
2783 wp = wp->w_next;
2784 if (wp == curwin)
2785 break;
2786 if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer))
2787 {
2788 curwin = wp;
2789 break;
2790 }
2791 }
2792 }
2793#endif
2794 curbuf = curwin->w_buffer;
2795 close_curwin = TRUE;
Bram Moolenaarf79225e2017-03-18 23:11:04 +01002796
Bram Moolenaare38eab22019-12-05 21:50:01 +01002797 // The cursor position may be invalid if the buffer changed after last
2798 // using the window.
Bram Moolenaarf79225e2017-03-18 23:11:04 +01002799 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 }
Luuk van Baalfd7e60a2022-09-07 14:42:49 +01002801
2802 /*
2803 * If last window has a status line now and we don't want one, remove the
2804 * status line. Do this before win_equal(), because it may change the
2805 * height of a window
2806 */
2807 last_status(FALSE);
2808
Bram Moolenaar44a2f922016-03-19 22:11:51 +01002809 if (p_ea && (*p_ead == 'b' || *p_ead == dir))
Bram Moolenaare38eab22019-12-05 21:50:01 +01002810 // If the frame of the closed window contains the new current window,
2811 // only resize that frame. Otherwise resize all windows.
Bram Moolenaar41cc0382017-06-26 09:59:35 +02002812 win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 else
Luuk van Baal29ab5242022-09-11 16:59:53 +01002814 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815 win_comp_pos();
Luuk van Baal13ece2a2022-10-03 15:28:08 +01002816 if (*p_spk != 'c')
Luuk van Baal29ab5242022-09-11 16:59:53 +01002817 win_fix_scroll(FALSE);
2818 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819 if (close_curwin)
2820 {
Bram Moolenaar57942232021-08-04 20:54:55 +02002821 // Pass WEE_ALLOW_PARSE_MESSAGES to decrement dont_parse_messages
2822 // before autocommands.
Bram Moolenaarf18e8a92021-08-04 21:16:50 +02002823#ifdef MESSAGE_QUEUE
2824 did_decrement =
2825#else
2826 (void)
2827#endif
2828 win_enter_ext(wp,
Bram Moolenaar57942232021-08-04 20:54:55 +02002829 WEE_CURWIN_INVALID | WEE_TRIGGER_ENTER_AUTOCMDS
2830 | WEE_TRIGGER_LEAVE_AUTOCMDS | WEE_ALLOW_PARSE_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831 if (other_buffer)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002832 // careful: after this wp and win may be invalid!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002833 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 }
2835
Bram Moolenaar1417c762019-07-27 17:31:36 +02002836 --split_disallowed;
Bram Moolenaar4778b4d2020-11-04 11:03:12 +01002837#ifdef MESSAGE_QUEUE
Bram Moolenaar57942232021-08-04 20:54:55 +02002838 if (!did_decrement)
2839 --dont_parse_messages;
Bram Moolenaar4778b4d2020-11-04 11:03:12 +01002840#endif
Bram Moolenaar1417c762019-07-27 17:31:36 +02002841
Bram Moolenaare38eab22019-12-05 21:50:01 +01002842 // After closing the help window, try restoring the window layout from
2843 // before it was opened.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 if (help_window)
Bram Moolenaar746ebd32009-06-16 14:01:43 +00002845 restore_snapshot(SNAP_HELP_IDX, close_curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002846
Bram Moolenaarc8234772019-11-10 21:00:27 +01002847#ifdef FEAT_DIFF
2848 // If the window had 'diff' set and now there is only one window left in
2849 // the tab page with 'diff' set, and "closeoff" is in 'diffopt', then
2850 // execute ":diffoff!".
2851 if (diffopt_closeoff() && had_diffmode && curtab == prev_curtab)
2852 {
2853 int diffcount = 0;
2854 win_T *dwin;
2855
2856 FOR_ALL_WINDOWS(dwin)
2857 if (dwin->w_p_diff)
2858 ++diffcount;
2859 if (diffcount == 1)
2860 do_cmdline_cmd((char_u *)"diffoff!");
2861 }
2862#endif
2863
Bram Moolenaar44a2f922016-03-19 22:11:51 +01002864#if defined(FEAT_GUI)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002865 // When 'guioptions' includes 'L' or 'R' may have to remove scrollbars.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002866 if (gui.in_use && !win_hasvertsplit())
2867 gui_init_which_components(NULL);
2868#endif
2869
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002870 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarc93df6b2013-08-14 17:11:20 +02002871 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002872}
2873
naohiro ono23beefe2021-11-13 12:38:49 +00002874 static void
2875trigger_winclosed(win_T *win)
2876{
2877 static int recursive = FALSE;
2878 char_u winid[NUMBUFLEN];
2879
2880 if (recursive)
2881 return;
2882 recursive = TRUE;
LemonBoy09371822022-04-08 15:18:45 +01002883 vim_snprintf((char *)winid, sizeof(winid), "%d", win->w_id);
naohiro ono23beefe2021-11-13 12:38:49 +00002884 apply_autocmds(EVENT_WINCLOSED, winid, winid, FALSE, win->w_buffer);
2885 recursive = FALSE;
2886}
2887
zeertzjqd58862d2022-04-12 11:32:48 +01002888/*
Bram Moolenaar0a60f792022-11-19 21:18:11 +00002889 * Make a snapshot of all the window scroll positions and sizes of the current
2890 * tab page.
2891 */
Bram Moolenaar29967732022-11-20 12:11:45 +00002892 void
Bram Moolenaar0a60f792022-11-19 21:18:11 +00002893snapshot_windows_scroll_size(void)
2894{
2895 win_T *wp;
2896 FOR_ALL_WINDOWS(wp)
2897 {
2898 wp->w_last_topline = wp->w_topline;
zeertzjq3fc84dc2022-12-07 09:17:59 +00002899#ifdef FEAT_DIFF
2900 wp->w_last_topfill = wp->w_topfill;
2901#endif
Bram Moolenaar0a60f792022-11-19 21:18:11 +00002902 wp->w_last_leftcol = wp->w_leftcol;
2903 wp->w_last_skipcol = wp->w_skipcol;
2904 wp->w_last_width = wp->w_width;
2905 wp->w_last_height = wp->w_height;
2906 }
2907}
2908
2909static int did_initial_scroll_size_snapshot = FALSE;
2910
2911 void
2912may_make_initial_scroll_size_snapshot(void)
2913{
2914 if (!did_initial_scroll_size_snapshot)
2915 {
2916 did_initial_scroll_size_snapshot = TRUE;
2917 snapshot_windows_scroll_size();
2918 }
2919}
2920
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00002921#ifdef FEAT_EVAL
Bram Moolenaar0a60f792022-11-19 21:18:11 +00002922/*
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002923 * Create a dictionary with information about size and scroll changes in a
2924 * window.
2925 * Returns the dictionary with refcount set to one.
2926 * Returns NULL when out of memory.
zeertzjqd58862d2022-04-12 11:32:48 +01002927 */
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002928 static dict_T *
2929make_win_info_dict(
2930 int width,
2931 int height,
2932 int topline,
zeertzjq3fc84dc2022-12-07 09:17:59 +00002933# ifdef FEAT_DIFF
2934 int topfill,
2935# endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002936 int leftcol,
2937 int skipcol)
LemonBoy09371822022-04-08 15:18:45 +01002938{
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002939 dict_T *d = dict_alloc();
2940 if (d == NULL)
2941 return NULL;
2942 d->dv_refcount = 1;
LemonBoy09371822022-04-08 15:18:45 +01002943
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002944 // not actually looping, for breaking out on error
2945 while (1)
2946 {
2947 typval_T tv;
2948 tv.v_lock = 0;
2949 tv.v_type = VAR_NUMBER;
2950
2951 tv.vval.v_number = width;
2952 if (dict_add_tv(d, "width", &tv) == FAIL)
2953 break;
2954 tv.vval.v_number = height;
2955 if (dict_add_tv(d, "height", &tv) == FAIL)
2956 break;
2957 tv.vval.v_number = topline;
2958 if (dict_add_tv(d, "topline", &tv) == FAIL)
2959 break;
zeertzjq3fc84dc2022-12-07 09:17:59 +00002960#ifdef FEAT_DIFF
2961 tv.vval.v_number = topfill;
2962#else
2963 tv.vval.v_number = 0;
2964#endif
2965 if (dict_add_tv(d, "topfill", &tv) == FAIL)
2966 break;
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002967 tv.vval.v_number = leftcol;
2968 if (dict_add_tv(d, "leftcol", &tv) == FAIL)
2969 break;
2970 tv.vval.v_number = skipcol;
2971 if (dict_add_tv(d, "skipcol", &tv) == FAIL)
2972 break;
2973 return d;
2974 }
2975 dict_unref(d);
2976 return NULL;
2977}
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00002978#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00002979
2980// Return values of check_window_scroll_resize():
2981#define CWSR_SCROLLED 1 // at least one window scrolled
2982#define CWSR_RESIZED 2 // at least one window size changed
2983
2984/*
2985 * This function is used for three purposes:
2986 * 1. Goes over all windows in the current tab page and returns:
2987 * 0 no scrolling and no size changes found
2988 * CWSR_SCROLLED at least one window scrolled
2989 * CWSR_RESIZED at least one window changed size
2990 * CWSR_SCROLLED + CWSR_RESIZED both
2991 * "size_count" is set to the nr of windows with size changes.
2992 * "first_scroll_win" is set to the first window with any relevant changes.
2993 * "first_size_win" is set to the first window with size changes.
2994 *
2995 * 2. When the first three arguments are NULL and "winlist" is not NULL,
2996 * "winlist" is set to the list of window IDs with size changes.
2997 *
2998 * 3. When the first three arguments are NULL and "v_event" is not NULL,
2999 * information about changed windows is added to "v_event".
3000 */
3001 static int
3002check_window_scroll_resize(
3003 int *size_count,
3004 win_T **first_scroll_win,
3005 win_T **first_size_win,
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003006 list_T *winlist UNUSED,
3007 dict_T *v_event UNUSED)
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003008{
3009 int result = 0;
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003010#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003011 int listidx = 0;
3012 int tot_width = 0;
3013 int tot_height = 0;
3014 int tot_topline = 0;
zeertzjq3fc84dc2022-12-07 09:17:59 +00003015# ifdef FEAT_DIFF
3016 int tot_topfill = 0;
3017# endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003018 int tot_leftcol = 0;
3019 int tot_skipcol = 0;
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003020#endif
LemonBoy09371822022-04-08 15:18:45 +01003021
Bram Moolenaar0a60f792022-11-19 21:18:11 +00003022 win_T *wp;
3023 FOR_ALL_WINDOWS(wp)
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003024 {
3025 int size_changed = wp->w_last_width != wp->w_width
3026 || wp->w_last_height != wp->w_height;
3027 if (size_changed)
zeertzjqd58862d2022-04-12 11:32:48 +01003028 {
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003029 result |= CWSR_RESIZED;
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003030#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003031 if (winlist != NULL)
3032 {
3033 // Add this window to the list of changed windows.
3034 typval_T tv;
3035 tv.v_lock = 0;
3036 tv.v_type = VAR_NUMBER;
3037 tv.vval.v_number = wp->w_id;
3038 list_set_item(winlist, listidx++, &tv);
3039 }
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003040 else
3041#endif
3042 if (size_count != NULL)
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003043 {
3044 ++*size_count;
3045 if (*first_size_win == NULL)
3046 *first_size_win = wp;
3047 // For WinScrolled the first window with a size change is used
3048 // even when it didn't scroll.
3049 if (*first_scroll_win == NULL)
3050 *first_scroll_win = wp;
3051 }
zeertzjqd58862d2022-04-12 11:32:48 +01003052 }
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003053
3054 int scroll_changed = wp->w_last_topline != wp->w_topline
zeertzjq3fc84dc2022-12-07 09:17:59 +00003055#ifdef FEAT_DIFF
3056 || wp->w_last_topfill != wp->w_topfill
3057#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003058 || wp->w_last_leftcol != wp->w_leftcol
3059 || wp->w_last_skipcol != wp->w_skipcol;
3060 if (scroll_changed)
3061 {
3062 result |= CWSR_SCROLLED;
3063 if (first_scroll_win != NULL && *first_scroll_win == NULL)
3064 *first_scroll_win = wp;
3065 }
3066
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003067#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003068 if ((size_changed || scroll_changed) && v_event != NULL)
3069 {
3070 // Add info about this window to the v:event dictionary.
3071 int width = wp->w_width - wp->w_last_width;
3072 int height = wp->w_height - wp->w_last_height;
3073 int topline = wp->w_topline - wp->w_last_topline;
zeertzjq3fc84dc2022-12-07 09:17:59 +00003074#ifdef FEAT_DIFF
3075 int topfill = wp->w_topfill - wp->w_last_topfill;
3076#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003077 int leftcol = wp->w_leftcol - wp->w_last_leftcol;
3078 int skipcol = wp->w_skipcol - wp->w_last_skipcol;
zeertzjq3fc84dc2022-12-07 09:17:59 +00003079 dict_T *d = make_win_info_dict(width, height, topline,
3080#ifdef FEAT_DIFF
3081 topfill,
3082#endif
3083 leftcol, skipcol);
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003084 if (d == NULL)
3085 break;
3086 char winid[NUMBUFLEN];
3087 vim_snprintf(winid, sizeof(winid), "%d", wp->w_id);
3088 if (dict_add_dict(v_event, winid, d) == FAIL)
3089 {
3090 dict_unref(d);
3091 break;
3092 }
3093 --d->dv_refcount;
3094
3095 tot_width += abs(width);
3096 tot_height += abs(height);
3097 tot_topline += abs(topline);
zeertzjq3fc84dc2022-12-07 09:17:59 +00003098#ifdef FEAT_DIFF
3099 tot_topfill += abs(topfill);
3100#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003101 tot_leftcol += abs(leftcol);
3102 tot_skipcol += abs(skipcol);
3103 }
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003104#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003105 }
3106
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003107#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003108 if (v_event != NULL)
3109 {
zeertzjq3fc84dc2022-12-07 09:17:59 +00003110 dict_T *alldict = make_win_info_dict(tot_width, tot_height, tot_topline,
3111# ifdef FEAT_DIFF
3112 tot_topfill,
3113# endif
3114 tot_leftcol, tot_skipcol);
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003115 if (alldict != NULL)
3116 {
3117 if (dict_add_dict(v_event, "all", alldict) == FAIL)
3118 dict_unref(alldict);
3119 else
3120 --alldict->dv_refcount;
3121 }
3122 }
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003123#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003124
3125 return result;
3126}
3127
3128/*
3129 * Trigger WinScrolled and/or WinResized if any window in the current tab page
3130 * scrolled or changed size.
3131 */
3132 void
3133may_trigger_win_scrolled_resized(void)
3134{
3135 static int recursive = FALSE;
3136 int do_resize = has_winresized();
3137 int do_scroll = has_winscrolled();
3138
3139 // Do not trigger WinScrolled or WinResized recursively. Do not trigger
3140 // before the initial snapshot of the w_last_ values was made.
3141 if (recursive
3142 || !(do_scroll || do_resize)
3143 || !did_initial_scroll_size_snapshot)
3144 return;
3145
3146 int size_count = 0;
3147 win_T *first_scroll_win = NULL, *first_size_win = NULL;
3148 int cwsr = check_window_scroll_resize(&size_count,
3149 &first_scroll_win, &first_size_win,
3150 NULL, NULL);
3151 int trigger_resize = do_resize && size_count > 0;
3152 int trigger_scroll = do_scroll && cwsr != 0;
3153 if (!trigger_resize && !trigger_scroll)
3154 return; // no relevant changes
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003155#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003156 list_T *windows_list = NULL;
3157 if (trigger_resize)
3158 {
3159 // Create the list for v:event.windows before making the snapshot.
3160 windows_list = list_alloc_with_items(size_count);
3161 (void)check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL);
3162 }
3163
3164 dict_T *scroll_dict = NULL;
3165 if (trigger_scroll)
3166 {
3167 // Create the dict with entries for v:event before making the snapshot.
3168 scroll_dict = dict_alloc();
3169 if (scroll_dict != NULL)
3170 {
3171 scroll_dict->dv_refcount = 1;
3172 (void)check_window_scroll_resize(NULL, NULL, NULL, NULL,
3173 scroll_dict);
3174 }
3175 }
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003176#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003177
3178 // WinScrolled/WinResized are triggered only once, even when multiple
3179 // windows scrolled or changed size. Store the current values before
3180 // triggering the event, if a scroll or resize happens as a side effect
3181 // then WinScrolled/WinResized is triggered for that later.
3182 snapshot_windows_scroll_size();
3183
3184 // "curwin" may be different from the actual current window, make
3185 // sure it can be restored.
3186 window_layout_lock();
3187 recursive = TRUE;
3188
3189 // If both are to be triggered do WinResized first.
3190 if (trigger_resize)
3191 {
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003192#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003193 save_v_event_T save_v_event;
3194 dict_T *v_event = get_v_event(&save_v_event);
3195
3196 if (dict_add_list(v_event, "windows", windows_list) == OK)
3197 {
3198 dict_set_items_ro(v_event);
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003199#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003200 char_u winid[NUMBUFLEN];
3201 vim_snprintf((char *)winid, sizeof(winid), "%d",
3202 first_size_win->w_id);
3203 apply_autocmds(EVENT_WINRESIZED, winid, winid, FALSE,
3204 first_size_win->w_buffer);
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003205#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003206 }
3207 restore_v_event(v_event, &save_v_event);
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003208#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003209 }
3210
Bram Moolenaar0b6d6a12022-11-23 14:33:01 +00003211 if (trigger_scroll
3212#ifdef FEAT_EVAL
3213 && scroll_dict != NULL
3214#endif
3215 )
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003216 {
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003217#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003218 save_v_event_T save_v_event;
3219 dict_T *v_event = get_v_event(&save_v_event);
3220
3221 // Move the entries from scroll_dict to v_event.
3222 dict_extend(v_event, scroll_dict, (char_u *)"move", NULL);
3223 dict_set_items_ro(v_event);
3224 dict_unref(scroll_dict);
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003225#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003226 char_u winid[NUMBUFLEN];
3227 vim_snprintf((char *)winid, sizeof(winid), "%d",
3228 first_scroll_win->w_id);
3229 apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE,
3230 first_scroll_win->w_buffer);
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003231#ifdef FEAT_EVAL
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003232 restore_v_event(v_event, &save_v_event);
Bram Moolenaar9c5b7cb2022-11-22 13:29:20 +00003233#endif
Bram Moolenaar35fc61c2022-11-22 12:40:50 +00003234 }
3235
3236 recursive = FALSE;
3237 window_layout_unlock();
LemonBoy09371822022-04-08 15:18:45 +01003238}
3239
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240/*
Bram Moolenaarf740b292006-02-16 22:11:02 +00003241 * Close window "win" in tab page "tp", which is not the current tab page.
Bram Moolenaarbef1c362012-05-25 12:39:00 +02003242 * This may be the last window in that tab page and result in closing the tab,
Bram Moolenaarf740b292006-02-16 22:11:02 +00003243 * thus "tp" may become invalid!
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00003244 * Caller must check if buffer is hidden and whether the tabline needs to be
3245 * updated.
Bram Moolenaarf740b292006-02-16 22:11:02 +00003246 */
3247 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003248win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
Bram Moolenaarf740b292006-02-16 22:11:02 +00003249{
3250 win_T *wp;
3251 int dir;
3252 tabpage_T *ptp = NULL;
Bram Moolenaar4d770fb2010-07-12 21:38:19 +02003253 int free_tp = FALSE;
Bram Moolenaarf740b292006-02-16 22:11:02 +00003254
Bram Moolenaare38eab22019-12-05 21:50:01 +01003255 // Get here with win->w_buffer == NULL when win_close() detects the tab
3256 // page changed.
Bram Moolenaare0ab94e2016-09-04 19:50:54 +02003257 if (win->w_closing || (win->w_buffer != NULL
3258 && win->w_buffer->b_locked > 0))
Bram Moolenaare38eab22019-12-05 21:50:01 +01003259 return; // window is already being closed
Bram Moolenaar362ce482012-06-06 19:02:45 +02003260
naohiro ono23beefe2021-11-13 12:38:49 +00003261 // Trigger WinClosed just before starting to free window-related resources.
3262 trigger_winclosed(win);
3263 // autocmd may have freed the window already.
3264 if (!win_valid_any_tab(win))
3265 return;
3266
Bram Moolenaar11fbc282016-09-02 21:48:32 +02003267 if (win->w_buffer != NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003268 // Close the link to the buffer.
Bram Moolenaara6e8f882019-12-14 16:18:15 +01003269 close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0,
zeertzjq6a069402022-04-07 14:08:29 +01003270 FALSE, TRUE);
Bram Moolenaarf740b292006-02-16 22:11:02 +00003271
Bram Moolenaare38eab22019-12-05 21:50:01 +01003272 // Careful: Autocommands may have closed the tab page or made it the
3273 // current tab page.
Bram Moolenaarf740b292006-02-16 22:11:02 +00003274 for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next)
3275 ;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00003276 if (ptp == NULL || tp == curtab)
Bram Moolenaar347538f2022-03-26 16:28:06 +00003277 {
3278 // If the buffer was removed from the window we have to give it any
3279 // buffer.
3280 if (win_valid_any_tab(win) && win->w_buffer == NULL)
3281 {
3282 win->w_buffer = firstbuf;
3283 ++firstbuf->b_nwindows;
3284 win_init_empty(win);
3285 }
Bram Moolenaarf740b292006-02-16 22:11:02 +00003286 return;
Bram Moolenaar347538f2022-03-26 16:28:06 +00003287 }
Bram Moolenaarf740b292006-02-16 22:11:02 +00003288
Bram Moolenaare38eab22019-12-05 21:50:01 +01003289 // Autocommands may have closed the window already.
Bram Moolenaarf740b292006-02-16 22:11:02 +00003290 for (wp = tp->tp_firstwin; wp != NULL && wp != win; wp = wp->w_next)
3291 ;
3292 if (wp == NULL)
3293 return;
3294
Bram Moolenaare38eab22019-12-05 21:50:01 +01003295 // When closing the last window in a tab page remove the tab page.
Bram Moolenaarcde88542015-08-11 19:14:00 +02003296 if (tp->tp_firstwin == tp->tp_lastwin)
Bram Moolenaarf740b292006-02-16 22:11:02 +00003297 {
zeertzjq62de54b2022-09-22 18:08:37 +01003298 int h = tabline_height();
3299
Bram Moolenaarf740b292006-02-16 22:11:02 +00003300 if (tp == first_tabpage)
3301 first_tabpage = tp->tp_next;
3302 else
3303 {
3304 for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp;
3305 ptp = ptp->tp_next)
3306 ;
3307 if (ptp == NULL)
3308 {
Bram Moolenaar95f09602016-11-10 20:01:45 +01003309 internal_error("win_close_othertab()");
Bram Moolenaarf740b292006-02-16 22:11:02 +00003310 return;
3311 }
3312 ptp->tp_next = tp->tp_next;
3313 }
Bram Moolenaar4d770fb2010-07-12 21:38:19 +02003314 free_tp = TRUE;
zeertzjq62de54b2022-09-22 18:08:37 +01003315 redraw_tabline = TRUE;
3316 if (h != tabline_height())
3317 shell_new_rows();
Bram Moolenaarf740b292006-02-16 22:11:02 +00003318 }
Bram Moolenaar4d770fb2010-07-12 21:38:19 +02003319
Bram Moolenaare38eab22019-12-05 21:50:01 +01003320 // Free the memory used for the window.
Bram Moolenaar4d770fb2010-07-12 21:38:19 +02003321 win_free_mem(win, &dir, tp);
3322
3323 if (free_tp)
3324 free_tabpage(tp);
Bram Moolenaarf740b292006-02-16 22:11:02 +00003325}
3326
3327/*
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003328 * Free the memory used for a window.
3329 * Returns a pointer to the window that got the freed up space.
3330 */
3331 static win_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003332win_free_mem(
3333 win_T *win,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003334 int *dirp, // set to 'v' or 'h' for direction if 'ea'
3335 tabpage_T *tp) // tab page "win" is in, NULL for current
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003336{
3337 frame_T *frp;
3338 win_T *wp;
Bram Moolenaarf3c51bb2020-09-26 19:11:39 +02003339 tabpage_T *win_tp = tp == NULL ? curtab : tp;
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003340
Bram Moolenaare38eab22019-12-05 21:50:01 +01003341 // Remove the window and its frame from the tree of frames.
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003342 frp = win->w_frame;
Bram Moolenaarf740b292006-02-16 22:11:02 +00003343 wp = winframe_remove(win, dirp, tp);
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003344 vim_free(frp);
Bram Moolenaarf740b292006-02-16 22:11:02 +00003345 win_free(win, tp);
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003346
Bram Moolenaarf3c51bb2020-09-26 19:11:39 +02003347 // When deleting the current window in the tab, select a new current
3348 // window.
3349 if (win == win_tp->tp_curwin)
3350 win_tp->tp_curwin = wp;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003351
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003352 return wp;
3353}
3354
3355#if defined(EXITFREE) || defined(PROTO)
3356 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003357win_free_all(void)
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003358{
3359 int dummy;
3360
zeertzjq6a8b1362022-02-23 12:23:08 +00003361 // avoid an error for switching tabpage with the cmdline window open
3362 cmdwin_type = 0;
Martin Tournoij7904fa42022-10-04 16:28:45 +01003363
Bram Moolenaarf740b292006-02-16 22:11:02 +00003364 while (first_tabpage->tp_next != NULL)
3365 tabpage_close(TRUE);
Bram Moolenaarf740b292006-02-16 22:11:02 +00003366
Bram Moolenaare76062c2022-11-28 18:51:43 +00003367 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
Bram Moolenaar84497cd2022-11-28 20:34:52 +00003368 if (aucmd_win[i].auc_win != NULL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00003369 {
3370 (void)win_free_mem(aucmd_win[i].auc_win, &dummy, NULL);
Bram Moolenaar84497cd2022-11-28 20:34:52 +00003371 aucmd_win[i].auc_win = NULL;
Bram Moolenaare76062c2022-11-28 18:51:43 +00003372 }
Bram Moolenaarf061e0b2009-06-24 15:32:01 +00003373
3374 while (firstwin != NULL)
3375 (void)win_free_mem(firstwin, &dummy, NULL);
Bram Moolenaar4e036c92014-07-16 16:30:28 +02003376
Bram Moolenaare38eab22019-12-05 21:50:01 +01003377 // No window should be used after this. Set curwin to NULL to crash
3378 // instead of using freed memory.
Bram Moolenaar4e036c92014-07-16 16:30:28 +02003379 curwin = NULL;
Bram Moolenaar0a5fe212005-06-24 23:01:23 +00003380}
3381#endif
3382
3383/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384 * Remove a window and its frame from the tree of frames.
3385 * Returns a pointer to the window that got the freed up space.
3386 */
Bram Moolenaar746ebd32009-06-16 14:01:43 +00003387 win_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003388winframe_remove(
3389 win_T *win,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003390 int *dirp UNUSED, // set to 'v' or 'h' for direction if 'ea'
3391 tabpage_T *tp) // tab page "win" is in, NULL for current
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392{
3393 frame_T *frp, *frp2, *frp3;
3394 frame_T *frp_close = win->w_frame;
3395 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396
3397 /*
Bram Moolenaarf740b292006-02-16 22:11:02 +00003398 * If there is only one window there is nothing to remove.
3399 */
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01003400 if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
Bram Moolenaarf740b292006-02-16 22:11:02 +00003401 return NULL;
3402
3403 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404 * Remove the window from its frame.
3405 */
Bram Moolenaarf740b292006-02-16 22:11:02 +00003406 frp2 = win_altframe(win, tp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003407 wp = frame2win(frp2);
3408
Bram Moolenaare38eab22019-12-05 21:50:01 +01003409 // Remove this frame from the list of frames.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003410 frame_remove(frp_close);
3411
Bram Moolenaar071d4272004-06-13 20:20:40 +00003412 if (frp_close->fr_parent->fr_layout == FR_COL)
3413 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003414 // When 'winfixheight' is set, try to find another frame in the column
3415 // (as close to the closed frame as possible) to distribute the height
3416 // to.
Bram Moolenaar48cc5fe2007-08-11 11:39:45 +00003417 if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfh)
3418 {
3419 frp = frp_close->fr_prev;
3420 frp3 = frp_close->fr_next;
3421 while (frp != NULL || frp3 != NULL)
3422 {
3423 if (frp != NULL)
3424 {
Bram Moolenaar9e1e3582019-03-30 19:49:06 +01003425 if (!frame_fixed_height(frp))
Bram Moolenaar48cc5fe2007-08-11 11:39:45 +00003426 {
3427 frp2 = frp;
Bram Moolenaar9e1e3582019-03-30 19:49:06 +01003428 wp = frame2win(frp2);
Bram Moolenaar48cc5fe2007-08-11 11:39:45 +00003429 break;
3430 }
3431 frp = frp->fr_prev;
3432 }
3433 if (frp3 != NULL)
3434 {
3435 if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfh)
3436 {
3437 frp2 = frp3;
3438 wp = frp3->fr_win;
3439 break;
3440 }
3441 frp3 = frp3->fr_next;
3442 }
3443 }
3444 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445 frame_new_height(frp2, frp2->fr_height + frp_close->fr_height,
3446 frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447 *dirp = 'v';
3448 }
3449 else
3450 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003451 // When 'winfixwidth' is set, try to find another frame in the column
3452 // (as close to the closed frame as possible) to distribute the width
3453 // to.
Bram Moolenaar48cc5fe2007-08-11 11:39:45 +00003454 if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfw)
3455 {
3456 frp = frp_close->fr_prev;
3457 frp3 = frp_close->fr_next;
3458 while (frp != NULL || frp3 != NULL)
3459 {
3460 if (frp != NULL)
3461 {
Bram Moolenaar9e1e3582019-03-30 19:49:06 +01003462 if (!frame_fixed_width(frp))
Bram Moolenaar48cc5fe2007-08-11 11:39:45 +00003463 {
3464 frp2 = frp;
Bram Moolenaar9e1e3582019-03-30 19:49:06 +01003465 wp = frame2win(frp2);
Bram Moolenaar48cc5fe2007-08-11 11:39:45 +00003466 break;
3467 }
3468 frp = frp->fr_prev;
3469 }
3470 if (frp3 != NULL)
3471 {
3472 if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfw)
3473 {
3474 frp2 = frp3;
3475 wp = frp3->fr_win;
3476 break;
3477 }
3478 frp3 = frp3->fr_next;
3479 }
3480 }
3481 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482 frame_new_width(frp2, frp2->fr_width + frp_close->fr_width,
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003483 frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003484 *dirp = 'h';
3485 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486
Bram Moolenaare38eab22019-12-05 21:50:01 +01003487 // If rows/columns go to a window below/right its positions need to be
3488 // updated. Can only be done after the sizes have been updated.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489 if (frp2 == frp_close->fr_next)
3490 {
3491 int row = win->w_winrow;
Bram Moolenaar53f81742017-09-22 14:35:51 +02003492 int col = win->w_wincol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003493
3494 frame_comp_pos(frp2, &row, &col);
3495 }
3496
3497 if (frp2->fr_next == NULL && frp2->fr_prev == NULL)
3498 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003499 // There is no other frame in this list, move its info to the parent
3500 // and remove it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 frp2->fr_parent->fr_layout = frp2->fr_layout;
3502 frp2->fr_parent->fr_child = frp2->fr_child;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003503 FOR_ALL_FRAMES(frp, frp2->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504 frp->fr_parent = frp2->fr_parent;
3505 frp2->fr_parent->fr_win = frp2->fr_win;
3506 if (frp2->fr_win != NULL)
3507 frp2->fr_win->w_frame = frp2->fr_parent;
3508 frp = frp2->fr_parent;
Bram Moolenaar6f361c92018-01-31 19:06:50 +01003509 if (topframe->fr_child == frp2)
3510 topframe->fr_child = frp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511 vim_free(frp2);
3512
3513 frp2 = frp->fr_parent;
3514 if (frp2 != NULL && frp2->fr_layout == frp->fr_layout)
3515 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003516 // The frame above the parent has the same layout, have to merge
3517 // the frames into this list.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518 if (frp2->fr_child == frp)
3519 frp2->fr_child = frp->fr_child;
3520 frp->fr_child->fr_prev = frp->fr_prev;
3521 if (frp->fr_prev != NULL)
3522 frp->fr_prev->fr_next = frp->fr_child;
3523 for (frp3 = frp->fr_child; ; frp3 = frp3->fr_next)
3524 {
3525 frp3->fr_parent = frp2;
3526 if (frp3->fr_next == NULL)
3527 {
3528 frp3->fr_next = frp->fr_next;
3529 if (frp->fr_next != NULL)
3530 frp->fr_next->fr_prev = frp3;
3531 break;
3532 }
3533 }
Bram Moolenaar6f361c92018-01-31 19:06:50 +01003534 if (topframe->fr_child == frp)
3535 topframe->fr_child = frp2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003536 vim_free(frp);
3537 }
3538 }
3539
3540 return wp;
3541}
3542
3543/*
Bram Moolenaarc136af22018-05-04 20:15:38 +02003544 * Return a pointer to the frame that will receive the empty screen space that
3545 * is left over after "win" is closed.
3546 *
3547 * If 'splitbelow' or 'splitright' is set, the space goes above or to the left
3548 * by default. Otherwise, the free space goes below or to the right. The
3549 * result is that opening a window and then immediately closing it will
3550 * preserve the initial window layout. The 'wfh' and 'wfw' settings are
3551 * respected when possible.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552 */
3553 static frame_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003554win_altframe(
3555 win_T *win,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003556 tabpage_T *tp) // tab page "win" is in, NULL for current
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557{
3558 frame_T *frp;
Bram Moolenaarc136af22018-05-04 20:15:38 +02003559 frame_T *other_fr, *target_fr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003560
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01003561 if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00003562 return alt_tabpage()->tp_curwin->w_frame;
3563
Bram Moolenaar071d4272004-06-13 20:20:40 +00003564 frp = win->w_frame;
Bram Moolenaarc136af22018-05-04 20:15:38 +02003565
3566 if (frp->fr_prev == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003567 return frp->fr_next;
Bram Moolenaarc136af22018-05-04 20:15:38 +02003568 if (frp->fr_next == NULL)
3569 return frp->fr_prev;
3570
Bram Moolenaaredd327c2020-04-15 20:05:47 +02003571 // By default the next window will get the space that was abandoned by this
3572 // window
Bram Moolenaarc136af22018-05-04 20:15:38 +02003573 target_fr = frp->fr_next;
3574 other_fr = frp->fr_prev;
Bram Moolenaaredd327c2020-04-15 20:05:47 +02003575
3576 // If this is part of a column of windows and 'splitbelow' is true then the
3577 // previous window will get the space.
3578 if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_COL && p_sb)
3579 {
3580 target_fr = frp->fr_prev;
3581 other_fr = frp->fr_next;
3582 }
3583
3584 // If this is part of a row of windows, and 'splitright' is true then the
3585 // previous window will get the space.
3586 if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW && p_spr)
Bram Moolenaarc136af22018-05-04 20:15:38 +02003587 {
3588 target_fr = frp->fr_prev;
3589 other_fr = frp->fr_next;
3590 }
3591
Bram Moolenaare38eab22019-12-05 21:50:01 +01003592 // If 'wfh' or 'wfw' is set for the target and not for the alternate
3593 // window, reverse the selection.
Bram Moolenaarc136af22018-05-04 20:15:38 +02003594 if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW)
3595 {
3596 if (frame_fixed_width(target_fr) && !frame_fixed_width(other_fr))
3597 target_fr = other_fr;
3598 }
3599 else
3600 {
3601 if (frame_fixed_height(target_fr) && !frame_fixed_height(other_fr))
3602 target_fr = other_fr;
3603 }
3604
3605 return target_fr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606}
3607
3608/*
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00003609 * Return the tabpage that will be used if the current one is closed.
3610 */
3611 static tabpage_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003612alt_tabpage(void)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00003613{
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00003614 tabpage_T *tp;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00003615
Bram Moolenaare38eab22019-12-05 21:50:01 +01003616 // Use the next tab page if possible.
Bram Moolenaar80a94a52006-02-23 21:26:58 +00003617 if (curtab->tp_next != NULL)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00003618 return curtab->tp_next;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00003619
Bram Moolenaare38eab22019-12-05 21:50:01 +01003620 // Find the last but one tab page.
Bram Moolenaar80a94a52006-02-23 21:26:58 +00003621 for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next)
3622 ;
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00003623 return tp;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00003624}
3625
3626/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627 * Find the left-upper window in frame "frp".
3628 */
3629 static win_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003630frame2win(frame_T *frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631{
3632 while (frp->fr_win == NULL)
3633 frp = frp->fr_child;
3634 return frp->fr_win;
3635}
3636
3637/*
3638 * Return TRUE if frame "frp" contains window "wp".
3639 */
3640 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003641frame_has_win(frame_T *frp, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642{
3643 frame_T *p;
3644
3645 if (frp->fr_layout == FR_LEAF)
3646 return frp->fr_win == wp;
3647
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003648 FOR_ALL_FRAMES(p, frp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649 if (frame_has_win(p, wp))
3650 return TRUE;
3651 return FALSE;
3652}
3653
3654/*
3655 * Set a new height for a frame. Recursively sets the height for contained
3656 * frames and windows. Caller must take care of positions.
3657 */
3658 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003659frame_new_height(
3660 frame_T *topfrp,
3661 int height,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003662 int topfirst, // resize topmost contained frame first
3663 int wfh) // obey 'winfixheight' when there is a choice;
3664 // may cause the height not to be set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665{
3666 frame_T *frp;
3667 int extra_lines;
3668 int h;
3669
3670 if (topfrp->fr_win != NULL)
3671 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003672 // Simple case: just one window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003673 win_new_height(topfrp->fr_win,
Bram Moolenaard326ad62017-09-18 20:31:41 +02003674 height - topfrp->fr_win->w_status_height
Bram Moolenaar3167c3e2017-11-25 14:19:43 +01003675 - WINBAR_HEIGHT(topfrp->fr_win));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677 else if (topfrp->fr_layout == FR_ROW)
3678 {
3679 do
3680 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003681 // All frames in this row get the same new height.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003682 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683 {
3684 frame_new_height(frp, height, topfirst, wfh);
3685 if (frp->fr_height > height)
3686 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003687 // Could not fit the windows, make the whole row higher.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688 height = frp->fr_height;
3689 break;
3690 }
3691 }
3692 }
3693 while (frp != NULL);
3694 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01003695 else // fr_layout == FR_COL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003697 // Complicated case: Resize a column of frames. Resize the bottom
3698 // frame first, frames above that when needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003699
3700 frp = topfrp->fr_child;
3701 if (wfh)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003702 // Advance past frames with one window with 'wfh' set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703 while (frame_fixed_height(frp))
3704 {
3705 frp = frp->fr_next;
3706 if (frp == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003707 return; // no frame without 'wfh', give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708 }
3709 if (!topfirst)
3710 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003711 // Find the bottom frame of this column
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 while (frp->fr_next != NULL)
3713 frp = frp->fr_next;
3714 if (wfh)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003715 // Advance back for frames with one window with 'wfh' set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 while (frame_fixed_height(frp))
3717 frp = frp->fr_prev;
3718 }
3719
3720 extra_lines = height - topfrp->fr_height;
3721 if (extra_lines < 0)
3722 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003723 // reduce height of contained frames, bottom or top frame first
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 while (frp != NULL)
3725 {
3726 h = frame_minheight(frp, NULL);
3727 if (frp->fr_height + extra_lines < h)
3728 {
3729 extra_lines += frp->fr_height - h;
3730 frame_new_height(frp, h, topfirst, wfh);
3731 }
3732 else
3733 {
3734 frame_new_height(frp, frp->fr_height + extra_lines,
3735 topfirst, wfh);
3736 break;
3737 }
3738 if (topfirst)
3739 {
3740 do
3741 frp = frp->fr_next;
3742 while (wfh && frp != NULL && frame_fixed_height(frp));
3743 }
3744 else
3745 {
3746 do
3747 frp = frp->fr_prev;
3748 while (wfh && frp != NULL && frame_fixed_height(frp));
3749 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01003750 // Increase "height" if we could not reduce enough frames.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 if (frp == NULL)
3752 height -= extra_lines;
3753 }
3754 }
3755 else if (extra_lines > 0)
3756 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003757 // increase height of bottom or top frame
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh);
3759 }
3760 }
3761 topfrp->fr_height = height;
3762}
3763
3764/*
3765 * Return TRUE if height of frame "frp" should not be changed because of
3766 * the 'winfixheight' option.
3767 */
3768 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003769frame_fixed_height(frame_T *frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003770{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003771 // frame with one window: fixed height if 'winfixheight' set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772 if (frp->fr_win != NULL)
3773 return frp->fr_win->w_p_wfh;
3774
3775 if (frp->fr_layout == FR_ROW)
3776 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003777 // The frame is fixed height if one of the frames in the row is fixed
3778 // height.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003779 FOR_ALL_FRAMES(frp, frp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780 if (frame_fixed_height(frp))
3781 return TRUE;
3782 return FALSE;
3783 }
3784
Bram Moolenaare38eab22019-12-05 21:50:01 +01003785 // frp->fr_layout == FR_COL: The frame is fixed height if all of the
3786 // frames in the row are fixed height.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003787 FOR_ALL_FRAMES(frp, frp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788 if (!frame_fixed_height(frp))
3789 return FALSE;
3790 return TRUE;
3791}
3792
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793/*
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003794 * Return TRUE if width of frame "frp" should not be changed because of
3795 * the 'winfixwidth' option.
3796 */
3797 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003798frame_fixed_width(frame_T *frp)
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003799{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003800 // frame with one window: fixed width if 'winfixwidth' set.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003801 if (frp->fr_win != NULL)
3802 return frp->fr_win->w_p_wfw;
3803
3804 if (frp->fr_layout == FR_COL)
3805 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003806 // The frame is fixed width if one of the frames in the row is fixed
3807 // width.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003808 FOR_ALL_FRAMES(frp, frp->fr_child)
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003809 if (frame_fixed_width(frp))
3810 return TRUE;
3811 return FALSE;
3812 }
3813
Bram Moolenaare38eab22019-12-05 21:50:01 +01003814 // frp->fr_layout == FR_ROW: The frame is fixed width if all of the
3815 // frames in the row are fixed width.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003816 FOR_ALL_FRAMES(frp, frp->fr_child)
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003817 if (!frame_fixed_width(frp))
3818 return FALSE;
3819 return TRUE;
3820}
3821
3822/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 * Add a status line to windows at the bottom of "frp".
3824 * Note: Does not check if there is room!
3825 */
3826 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003827frame_add_statusline(frame_T *frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003828{
3829 win_T *wp;
3830
3831 if (frp->fr_layout == FR_LEAF)
3832 {
3833 wp = frp->fr_win;
zeertzjqfbf20712023-04-26 19:01:44 +01003834 wp->w_status_height = STATUS_HEIGHT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835 }
3836 else if (frp->fr_layout == FR_ROW)
3837 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003838 // Handle all the frames in the row.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003839 FOR_ALL_FRAMES(frp, frp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 frame_add_statusline(frp);
3841 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01003842 else // frp->fr_layout == FR_COL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003843 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003844 // Only need to handle the last frame in the column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next)
3846 ;
3847 frame_add_statusline(frp);
3848 }
3849}
3850
3851/*
3852 * Set width of a frame. Handles recursively going through contained frames.
3853 * May remove separator line for windows at the right side (for win_close()).
3854 */
3855 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003856frame_new_width(
3857 frame_T *topfrp,
3858 int width,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003859 int leftfirst, // resize leftmost contained frame first
3860 int wfw) // obey 'winfixwidth' when there is a choice;
3861 // may cause the width not to be set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862{
3863 frame_T *frp;
3864 int extra_cols;
3865 int w;
3866 win_T *wp;
3867
3868 if (topfrp->fr_layout == FR_LEAF)
3869 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003870 // Simple case: just one window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871 wp = topfrp->fr_win;
Bram Moolenaare38eab22019-12-05 21:50:01 +01003872 // Find out if there are any windows right of this one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent)
3874 if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL)
3875 break;
3876 if (frp->fr_parent == NULL)
3877 wp->w_vsep_width = 0;
3878 win_new_width(wp, width - wp->w_vsep_width);
3879 }
3880 else if (topfrp->fr_layout == FR_COL)
3881 {
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003882 do
3883 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003884 // All frames in this column get the same new width.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003885 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003886 {
3887 frame_new_width(frp, width, leftfirst, wfw);
3888 if (frp->fr_width > width)
3889 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003890 // Could not fit the windows, make whole column wider.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003891 width = frp->fr_width;
3892 break;
3893 }
3894 }
3895 } while (frp != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01003897 else // fr_layout == FR_ROW
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003899 // Complicated case: Resize a row of frames. Resize the rightmost
3900 // frame first, frames left of it when needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902 frp = topfrp->fr_child;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003903 if (wfw)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003904 // Advance past frames with one window with 'wfw' set.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003905 while (frame_fixed_width(frp))
3906 {
3907 frp = frp->fr_next;
3908 if (frp == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003909 return; // no frame without 'wfw', give up
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003910 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 if (!leftfirst)
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003912 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003913 // Find the rightmost frame of this row
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 while (frp->fr_next != NULL)
3915 frp = frp->fr_next;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003916 if (wfw)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003917 // Advance back for frames with one window with 'wfw' set.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003918 while (frame_fixed_width(frp))
3919 frp = frp->fr_prev;
3920 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921
3922 extra_cols = width - topfrp->fr_width;
3923 if (extra_cols < 0)
3924 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003925 // reduce frame width, rightmost frame first
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 while (frp != NULL)
3927 {
3928 w = frame_minwidth(frp, NULL);
3929 if (frp->fr_width + extra_cols < w)
3930 {
3931 extra_cols += frp->fr_width - w;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003932 frame_new_width(frp, w, leftfirst, wfw);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 }
3934 else
3935 {
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003936 frame_new_width(frp, frp->fr_width + extra_cols,
3937 leftfirst, wfw);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938 break;
3939 }
3940 if (leftfirst)
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003941 {
3942 do
3943 frp = frp->fr_next;
3944 while (wfw && frp != NULL && frame_fixed_width(frp));
3945 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003946 else
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003947 {
3948 do
3949 frp = frp->fr_prev;
3950 while (wfw && frp != NULL && frame_fixed_width(frp));
3951 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01003952 // Increase "width" if we could not reduce enough frames.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003953 if (frp == NULL)
3954 width -= extra_cols;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955 }
3956 }
3957 else if (extra_cols > 0)
3958 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003959 // increase width of rightmost frame
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00003960 frame_new_width(frp, frp->fr_width + extra_cols, leftfirst, wfw);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003961 }
3962 }
3963 topfrp->fr_width = width;
3964}
3965
3966/*
3967 * Add the vertical separator to windows at the right side of "frp".
3968 * Note: Does not check if there is room!
3969 */
3970 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01003971frame_add_vsep(frame_T *frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003972{
3973 win_T *wp;
3974
3975 if (frp->fr_layout == FR_LEAF)
3976 {
3977 wp = frp->fr_win;
3978 if (wp->w_vsep_width == 0)
3979 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003980 if (wp->w_width > 0) // don't make it negative
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 --wp->w_width;
3982 wp->w_vsep_width = 1;
3983 }
3984 }
3985 else if (frp->fr_layout == FR_COL)
3986 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003987 // Handle all the frames in the column.
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01003988 FOR_ALL_FRAMES(frp, frp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003989 frame_add_vsep(frp);
3990 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01003991 else // frp->fr_layout == FR_ROW
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003993 // Only need to handle the last frame in the row.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994 frp = frp->fr_child;
3995 while (frp->fr_next != NULL)
3996 frp = frp->fr_next;
3997 frame_add_vsep(frp);
3998 }
3999}
4000
4001/*
4002 * Set frame width from the window it contains.
4003 */
4004 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004005frame_fix_width(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004006{
4007 wp->w_frame->fr_width = wp->w_width + wp->w_vsep_width;
4008}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004009
4010/*
4011 * Set frame height from the window it contains.
4012 */
4013 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004014frame_fix_height(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004015{
Bram Moolenaar415a6932017-12-05 20:31:07 +01004016 wp->w_frame->fr_height = VISIBLE_HEIGHT(wp) + wp->w_status_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017}
4018
4019/*
4020 * Compute the minimal height for frame "topfrp".
4021 * Uses the 'winminheight' option.
4022 * When "next_curwin" isn't NULL, use p_wh for this window.
4023 * When "next_curwin" is NOWIN, don't use at least one line for the current
4024 * window.
4025 */
4026 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004027frame_minheight(frame_T *topfrp, win_T *next_curwin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028{
4029 frame_T *frp;
4030 int m;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032
4033 if (topfrp->fr_win != NULL)
4034 {
4035 if (topfrp->fr_win == next_curwin)
4036 m = p_wh + topfrp->fr_win->w_status_height;
4037 else
4038 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004039 // window: minimal height of the window plus status line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 m = p_wmh + topfrp->fr_win->w_status_height;
Bram Moolenaar415a6932017-12-05 20:31:07 +01004041 if (topfrp->fr_win == curwin && next_curwin == NULL)
4042 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004043 // Current window is minimal one line high and WinBar is
4044 // visible.
Bram Moolenaar415a6932017-12-05 20:31:07 +01004045 if (p_wmh == 0)
4046 ++m;
4047 m += WINBAR_HEIGHT(curwin);
4048 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004049 }
4050 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 else if (topfrp->fr_layout == FR_ROW)
4052 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004053 // get the minimal height from each frame in this row
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054 m = 0;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01004055 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 {
4057 n = frame_minheight(frp, next_curwin);
4058 if (n > m)
4059 m = n;
4060 }
4061 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 else
4063 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004064 // Add up the minimal heights for all frames in this column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004065 m = 0;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01004066 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067 m += frame_minheight(frp, next_curwin);
4068 }
4069
4070 return m;
4071}
4072
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073/*
4074 * Compute the minimal width for frame "topfrp".
4075 * When "next_curwin" isn't NULL, use p_wiw for this window.
4076 * When "next_curwin" is NOWIN, don't use at least one column for the current
4077 * window.
4078 */
4079 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004080frame_minwidth(
4081 frame_T *topfrp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01004082 win_T *next_curwin) // use p_wh and p_wiw for next_curwin
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083{
4084 frame_T *frp;
4085 int m, n;
4086
4087 if (topfrp->fr_win != NULL)
4088 {
4089 if (topfrp->fr_win == next_curwin)
4090 m = p_wiw + topfrp->fr_win->w_vsep_width;
4091 else
4092 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004093 // window: minimal width of the window plus separator column
Bram Moolenaar071d4272004-06-13 20:20:40 +00004094 m = p_wmw + topfrp->fr_win->w_vsep_width;
Bram Moolenaare38eab22019-12-05 21:50:01 +01004095 // Current window is minimal one column wide
Bram Moolenaar071d4272004-06-13 20:20:40 +00004096 if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL)
4097 ++m;
4098 }
4099 }
4100 else if (topfrp->fr_layout == FR_COL)
4101 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004102 // get the minimal width from each frame in this column
Bram Moolenaar071d4272004-06-13 20:20:40 +00004103 m = 0;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01004104 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105 {
4106 n = frame_minwidth(frp, next_curwin);
4107 if (n > m)
4108 m = n;
4109 }
4110 }
4111 else
4112 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004113 // Add up the minimal widths for all frames in this row.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004114 m = 0;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01004115 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 m += frame_minwidth(frp, next_curwin);
4117 }
4118
4119 return m;
4120}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004121
4122
4123/*
4124 * Try to close all windows except current one.
4125 * Buffers in the other windows become hidden if 'hidden' is set, or '!' is
4126 * used and the buffer was modified.
4127 *
4128 * Used by ":bdel" and ":only".
4129 */
4130 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004131close_others(
4132 int message,
Bram Moolenaare38eab22019-12-05 21:50:01 +01004133 int forceit) // always hide all other windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00004134{
4135 win_T *wp;
4136 win_T *nextwp;
4137 int r;
4138
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004139 if (one_window())
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004141 if (message && !autocmd_busy)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004142 msg(_(m_onlyone));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004143 return;
4144 }
4145
Bram Moolenaare38eab22019-12-05 21:50:01 +01004146 // Be very careful here: autocommands may change the window layout.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004147 for (wp = firstwin; win_valid(wp); wp = nextwp)
4148 {
4149 nextwp = wp->w_next;
zeertzjq101d57b2022-07-31 18:34:32 +01004150 if (wp == curwin) // don't close current window
4151 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152
zeertzjq101d57b2022-07-31 18:34:32 +01004153 // Check if it's allowed to abandon this window
4154 r = can_abandon(wp->w_buffer, forceit);
4155 if (!win_valid(wp)) // autocommands messed wp up
4156 {
4157 nextwp = firstwin;
4158 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 }
zeertzjq101d57b2022-07-31 18:34:32 +01004160 if (!r)
4161 {
4162#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
4163 if (message && (p_confirm
4164 || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
4165 {
4166 dialog_changed(wp->w_buffer, FALSE);
4167 if (!win_valid(wp)) // autocommands messed wp up
4168 {
4169 nextwp = firstwin;
4170 continue;
4171 }
4172 }
4173 if (bufIsChanged(wp->w_buffer))
4174#endif
4175 continue;
4176 }
4177 win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 }
4179
Bram Moolenaar459ca562016-11-10 18:16:33 +01004180 if (message && !ONE_WINDOW)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004181 emsg(_(e_other_window_contains_changes));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182}
4183
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02004184/*
Bram Moolenaar29967732022-11-20 12:11:45 +00004185 * Store the relevant window pointers for tab page "tp". To be used before
4186 * use_tabpage().
4187 */
4188 void
4189unuse_tabpage(tabpage_T *tp)
4190{
4191 tp->tp_topframe = topframe;
4192 tp->tp_firstwin = firstwin;
4193 tp->tp_lastwin = lastwin;
4194 tp->tp_curwin = curwin;
4195}
4196
4197/*
4198 * Set the relevant pointers to use tab page "tp". May want to call
4199 * unuse_tabpage() first.
4200 */
4201 void
4202use_tabpage(tabpage_T *tp)
4203{
4204 curtab = tp;
4205 topframe = curtab->tp_topframe;
4206 firstwin = curtab->tp_firstwin;
4207 lastwin = curtab->tp_lastwin;
4208 curwin = curtab->tp_curwin;
4209}
4210
4211/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004212 * Allocate the first window and put an empty buffer in it.
4213 * Called from main().
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004214 * Return FAIL when something goes wrong (out of memory).
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 */
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004216 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004217win_alloc_first(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218{
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004219 if (win_alloc_firstwin(NULL) == FAIL)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004220 return FAIL;
4221
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004222 first_tabpage = alloc_tabpage();
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004223 if (first_tabpage == NULL)
4224 return FAIL;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004225 curtab = first_tabpage;
Bram Moolenaar29967732022-11-20 12:11:45 +00004226 unuse_tabpage(first_tabpage);
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004227
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004228 return OK;
4229}
4230
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004231/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +02004232 * Allocate and init a window that is not a regular window.
4233 * This can only be done after the first window is fully initialized, thus it
4234 * can't be in win_alloc_first().
4235 */
4236 win_T *
4237win_alloc_popup_win(void)
4238{
4239 win_T *wp;
4240
4241 wp = win_alloc(NULL, TRUE);
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00004242 if (wp == NULL)
4243 return NULL;
4244 // We need to initialize options with something, using the current
4245 // window makes most sense.
4246 win_init_some(wp, curwin);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02004247
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00004248 RESET_BINDING(wp);
4249 new_frame(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02004250 return wp;
4251}
4252
4253/*
4254 * Initialize window "wp" to display buffer "buf".
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004255 */
4256 void
Bram Moolenaar4d784b22019-05-25 19:51:39 +02004257win_init_popup_win(win_T *wp, buf_T *buf)
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004258{
Bram Moolenaar4d784b22019-05-25 19:51:39 +02004259 wp->w_buffer = buf;
4260 ++buf->b_nwindows;
4261 win_init_empty(wp); // set cursor and topline to safe values
4262
4263 // Make sure w_localdir and globaldir are NULL to avoid a chdir() in
4264 // win_enter_ext().
4265 VIM_CLEAR(wp->w_localdir);
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004266}
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004267
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004268/*
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004269 * Allocate the first window or the first window in a new tab page.
4270 * When "oldwin" is NULL create an empty buffer for it.
Bram Moolenaar4033c552017-09-16 20:54:51 +02004271 * When "oldwin" is not NULL copy info from it to the new window.
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004272 * Return FAIL when something goes wrong (out of memory).
4273 */
4274 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004275win_alloc_firstwin(win_T *oldwin)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004276{
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004277 curwin = win_alloc(NULL, FALSE);
Yegappan Lakshmanan72bb47e2022-04-03 11:22:38 +01004278 if (curwin == NULL)
4279 return FAIL;
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004280 if (oldwin == NULL)
4281 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004282 // Very first window, need to create an empty buffer for it and
4283 // initialize from scratch.
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004284 curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED);
4285 if (curwin == NULL || curbuf == NULL)
4286 return FAIL;
4287 curwin->w_buffer = curbuf;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004288#ifdef FEAT_SYN_HL
4289 curwin->w_s = &(curbuf->b_s);
4290#endif
Bram Moolenaare38eab22019-12-05 21:50:01 +01004291 curbuf->b_nwindows = 1; // there is one window
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004292 curwin->w_alist = &global_alist;
Bram Moolenaare38eab22019-12-05 21:50:01 +01004293 curwin_init(); // init current window
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004294 }
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004295 else
4296 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004297 // First window in new tab page, initialize it from "oldwin".
Bram Moolenaar884ae642009-02-22 01:37:59 +00004298 win_init(curwin, oldwin, 0);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004299
Bram Moolenaare38eab22019-12-05 21:50:01 +01004300 // We don't want cursor- and scroll-binding in the first window.
Bram Moolenaar3368ea22010-09-21 16:56:35 +02004301 RESET_BINDING(curwin);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004302 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004303
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004304 new_frame(curwin);
4305 if (curwin->w_frame == NULL)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004306 return FAIL;
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004307 topframe = curwin->w_frame;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 topframe->fr_width = Columns;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 topframe->fr_height = Rows - p_ch;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004310
4311 return OK;
4312}
4313
4314/*
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004315 * Create a frame for window "wp".
4316 */
4317 static void
4318new_frame(win_T *wp)
4319{
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004320 frame_T *frp = ALLOC_CLEAR_ONE(frame_T);
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004321
4322 wp->w_frame = frp;
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00004323 if (frp == NULL)
4324 return;
4325 frp->fr_layout = FR_LEAF;
4326 frp->fr_win = wp;
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004327}
4328
4329/*
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004330 * Initialize the window and frame size to the maximum.
4331 */
4332 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004333win_init_size(void)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004334{
4335 firstwin->w_height = ROWS_AVAIL;
Luuk van Baal5ed39172022-09-13 11:55:10 +01004336 firstwin->w_prev_height = ROWS_AVAIL;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004337 topframe->fr_height = ROWS_AVAIL;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004338 firstwin->w_width = Columns;
4339 topframe->fr_width = Columns;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340}
4341
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004342/*
4343 * Allocate a new tabpage_T and init the values.
4344 * Returns NULL when out of memory.
4345 */
4346 static tabpage_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004347alloc_tabpage(void)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004348{
4349 tabpage_T *tp;
Bram Moolenaar429fa852013-04-15 12:27:36 +02004350# ifdef FEAT_GUI
4351 int i;
4352# endif
4353
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004354
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004355 tp = ALLOC_CLEAR_ONE(tabpage_T);
Bram Moolenaar429fa852013-04-15 12:27:36 +02004356 if (tp == NULL)
4357 return NULL;
Bram Moolenaar371d5402006-03-20 21:47:49 +00004358
Bram Moolenaar429fa852013-04-15 12:27:36 +02004359# ifdef FEAT_EVAL
Bram Moolenaare38eab22019-12-05 21:50:01 +01004360 // init t: variables
Yegappan Lakshmanan72bb47e2022-04-03 11:22:38 +01004361 tp->tp_vars = dict_alloc_id(aid_newtabpage_tvars);
Bram Moolenaar429fa852013-04-15 12:27:36 +02004362 if (tp->tp_vars == NULL)
4363 {
4364 vim_free(tp);
4365 return NULL;
4366 }
4367 init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE);
4368# endif
4369
4370# ifdef FEAT_GUI
4371 for (i = 0; i < 3; i++)
4372 tp->tp_prev_which_scrollbars[i] = -1;
Bram Moolenaar371d5402006-03-20 21:47:49 +00004373# endif
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004374# ifdef FEAT_DIFF
Bram Moolenaar429fa852013-04-15 12:27:36 +02004375 tp->tp_diff_invalid = TRUE;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004376# endif
Bram Moolenaar429fa852013-04-15 12:27:36 +02004377 tp->tp_ch_used = p_ch;
4378
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004379 return tp;
4380}
4381
Bram Moolenaard8fc5c02006-04-29 21:55:22 +00004382 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004383free_tabpage(tabpage_T *tp)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004384{
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004385 int idx;
4386
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004387# ifdef FEAT_DIFF
4388 diff_clear(tp);
4389# endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01004390# ifdef FEAT_PROP_POPUP
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02004391 while (tp->tp_first_popupwin != NULL)
Bram Moolenaar03a9f842020-05-13 13:40:16 +02004392 popup_close_tabpage(tp, tp->tp_first_popupwin->w_id, TRUE);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02004393#endif
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004394 for (idx = 0; idx < SNAP_COUNT; ++idx)
4395 clear_snapshot(tp, idx);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004396#ifdef FEAT_EVAL
Bram Moolenaare38eab22019-12-05 21:50:01 +01004397 vars_clear(&tp->tp_vars->dv_hashtab); // free all t: variables
Bram Moolenaar429fa852013-04-15 12:27:36 +02004398 hash_init(&tp->tp_vars->dv_hashtab);
4399 unref_var_dict(tp->tp_vars);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004400#endif
Bram Moolenaar5e538ec2013-05-15 15:12:29 +02004401
Bram Moolenaar62a23252020-08-09 14:04:42 +02004402 if (tp == lastused_tabpage)
4403 lastused_tabpage = NULL;
4404
Bram Moolenaar00aa0692019-04-27 20:37:57 +02004405 vim_free(tp->tp_localdir);
Bram Moolenaar002bc792020-06-05 22:33:42 +02004406 vim_free(tp->tp_prevdir);
Bram Moolenaar00aa0692019-04-27 20:37:57 +02004407
Bram Moolenaar5e538ec2013-05-15 15:12:29 +02004408#ifdef FEAT_PYTHON
4409 python_tabpage_free(tp);
4410#endif
4411
4412#ifdef FEAT_PYTHON3
4413 python3_tabpage_free(tp);
4414#endif
4415
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004416 vim_free(tp);
4417}
4418
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004419/*
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004420 * Create a new Tab page with one window.
4421 * It will edit the current buffer, like after ":split".
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004422 * When "after" is 0 put it just after the current Tab page.
4423 * Otherwise put it just before tab page "after".
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004424 * Return FAIL or OK.
4425 */
4426 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004427win_new_tabpage(int after)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004428{
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004429 tabpage_T *tp = curtab;
Bram Moolenaar94f4ffa2020-08-10 19:21:15 +02004430 tabpage_T *prev_tp = curtab;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004431 tabpage_T *newtp;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004432 int n;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004433
Bram Moolenaar0f6e28f2022-02-20 20:49:35 +00004434 if (cmdwin_type != 0)
4435 {
4436 emsg(_(e_invalid_in_cmdline_window));
4437 return FAIL;
4438 }
Bram Moolenaar9fda8152022-11-19 13:14:10 +00004439 if (window_layout_locked(CMD_tabnew))
Bram Moolenaard63a8552022-11-19 11:41:30 +00004440 return FAIL;
Bram Moolenaar0f6e28f2022-02-20 20:49:35 +00004441
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004442 newtp = alloc_tabpage();
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004443 if (newtp == NULL)
4444 return FAIL;
4445
Bram Moolenaare38eab22019-12-05 21:50:01 +01004446 // Remember the current windows in this Tab page.
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004447 if (leave_tabpage(curbuf, TRUE) == FAIL)
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004448 {
4449 vim_free(newtp);
4450 return FAIL;
4451 }
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004452 curtab = newtp;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004453
Bram Moolenaar00aa0692019-04-27 20:37:57 +02004454 newtp->tp_localdir = (tp->tp_localdir == NULL)
4455 ? NULL : vim_strsave(tp->tp_localdir);
Bram Moolenaare38eab22019-12-05 21:50:01 +01004456 // Create a new empty window.
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004457 if (win_alloc_firstwin(tp->tp_curwin) == OK)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004458 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004459 // Make the new Tab page the new topframe.
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004460 if (after == 1)
4461 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004462 // New tab page becomes the first one.
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004463 newtp->tp_next = first_tabpage;
4464 first_tabpage = newtp;
4465 }
4466 else
4467 {
4468 if (after > 0)
4469 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004470 // Put new tab page before tab page "after".
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004471 n = 2;
4472 for (tp = first_tabpage; tp->tp_next != NULL
4473 && n < after; tp = tp->tp_next)
4474 ++n;
4475 }
4476 newtp->tp_next = tp->tp_next;
4477 tp->tp_next = newtp;
4478 }
Bram Moolenaara44b3ee2020-01-20 21:44:31 +01004479 newtp->tp_firstwin = newtp->tp_lastwin = newtp->tp_curwin = curwin;
4480
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004481 win_init_size();
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004482 firstwin->w_winrow = tabline_height();
Luuk van Baal5ed39172022-09-13 11:55:10 +01004483 firstwin->w_prev_winrow = firstwin->w_winrow;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004484 win_comp_scroll(curwin);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004485
4486 newtp->tp_topframe = topframe;
Bram Moolenaarf740b292006-02-16 22:11:02 +00004487 last_status(FALSE);
Bram Moolenaar607a95ed2006-03-28 20:57:42 +00004488
Bram Moolenaar94f4ffa2020-08-10 19:21:15 +02004489 lastused_tabpage = prev_tp;
Bram Moolenaar62a23252020-08-09 14:04:42 +02004490
Bram Moolenaar607a95ed2006-03-28 20:57:42 +00004491#if defined(FEAT_GUI)
Bram Moolenaare38eab22019-12-05 21:50:01 +01004492 // When 'guioptions' includes 'L' or 'R' may have to remove or add
4493 // scrollbars. Have to update them anyway.
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004494 gui_may_update_scrollbars();
Bram Moolenaar607a95ed2006-03-28 20:57:42 +00004495#endif
Bram Moolenaar6d41c782018-06-06 09:11:12 +02004496#ifdef FEAT_JOB_CHANNEL
4497 entering_window(curwin);
4498#endif
Bram Moolenaar607a95ed2006-03-28 20:57:42 +00004499
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004500 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarc917da42016-07-19 22:31:36 +02004501 apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf);
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004502 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
Bram Moolenaarc917da42016-07-19 22:31:36 +02004503 apply_autocmds(EVENT_TABNEW, NULL, NULL, FALSE, curbuf);
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004504 apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004505 return OK;
4506 }
4507
Bram Moolenaare38eab22019-12-05 21:50:01 +01004508 // Failed, get back the previous Tab page
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004509 enter_tabpage(curtab, curbuf, TRUE, TRUE);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004510 return FAIL;
4511}
4512
4513/*
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004514 * Open a new tab page if ":tab cmd" was used. It will edit the same buffer,
4515 * like with ":split".
4516 * Returns OK if a new tab page was created, FAIL otherwise.
4517 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02004518 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004519may_open_tabpage(void)
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004520{
Bram Moolenaare1004402020-10-24 20:49:43 +02004521 int n = (cmdmod.cmod_tab == 0)
4522 ? postponed_split_tab : cmdmod.cmod_tab;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004523
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00004524 if (n == 0)
4525 return FAIL;
4526
4527 cmdmod.cmod_tab = 0; // reset it to avoid doing it twice
4528 postponed_split_tab = 0;
4529 return win_new_tabpage(n);
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004530}
4531
4532/*
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004533 * Create up to "maxcount" tabpages with empty windows.
4534 * Returns the number of resulting tab pages.
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004535 */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004536 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004537make_tabpages(int maxcount)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004538{
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004539 int count = maxcount;
4540 int todo;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004541
Bram Moolenaare38eab22019-12-05 21:50:01 +01004542 // Limit to 'tabpagemax' tabs.
Bram Moolenaare1438bb2006-03-01 22:01:55 +00004543 if (count > p_tpm)
4544 count = p_tpm;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004545
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004546 /*
4547 * Don't execute autocommands while creating the tab pages. Must do that
4548 * when putting the buffers in the windows.
4549 */
Bram Moolenaar78ab3312007-09-29 12:16:41 +00004550 block_autocmds();
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004551
4552 for (todo = count - 1; todo > 0; --todo)
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004553 if (win_new_tabpage(0) == FAIL)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004554 break;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004555
Bram Moolenaar78ab3312007-09-29 12:16:41 +00004556 unblock_autocmds();
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004557
Bram Moolenaare38eab22019-12-05 21:50:01 +01004558 // return actual number of tab pages
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004559 return (count - todo);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004560}
4561
4562/*
Bram Moolenaarf740b292006-02-16 22:11:02 +00004563 * Return TRUE when "tpc" points to a valid tab page.
4564 */
4565 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004566valid_tabpage(tabpage_T *tpc)
Bram Moolenaarf740b292006-02-16 22:11:02 +00004567{
4568 tabpage_T *tp;
4569
Bram Moolenaar29323592016-07-24 22:04:11 +02004570 FOR_ALL_TABPAGES(tp)
Bram Moolenaarf740b292006-02-16 22:11:02 +00004571 if (tp == tpc)
4572 return TRUE;
4573 return FALSE;
4574}
4575
4576/*
Bram Moolenaar8c752bd2017-03-19 17:09:56 +01004577 * Return TRUE when "tpc" points to a valid tab page and at least one window is
4578 * valid.
4579 */
4580 int
4581valid_tabpage_win(tabpage_T *tpc)
4582{
4583 tabpage_T *tp;
4584 win_T *wp;
4585
4586 FOR_ALL_TABPAGES(tp)
4587 {
4588 if (tp == tpc)
4589 {
4590 FOR_ALL_WINDOWS_IN_TAB(tp, wp)
4591 {
4592 if (win_valid_any_tab(wp))
4593 return TRUE;
4594 }
4595 return FALSE;
4596 }
4597 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01004598 // shouldn't happen
Bram Moolenaar8c752bd2017-03-19 17:09:56 +01004599 return FALSE;
4600}
4601
4602/*
4603 * Close tabpage "tab", assuming it has no windows in it.
4604 * There must be another tabpage or this will crash.
4605 */
4606 void
4607close_tabpage(tabpage_T *tab)
4608{
4609 tabpage_T *ptp;
4610
4611 if (tab == first_tabpage)
4612 {
4613 first_tabpage = tab->tp_next;
4614 ptp = first_tabpage;
4615 }
4616 else
4617 {
4618 for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab;
4619 ptp = ptp->tp_next)
4620 ;
Bram Moolenaara37ffaa2017-03-21 21:58:00 +01004621 assert(ptp != NULL);
Bram Moolenaar8c752bd2017-03-19 17:09:56 +01004622 ptp->tp_next = tab->tp_next;
4623 }
4624
4625 goto_tabpage_tp(ptp, FALSE, FALSE);
4626 free_tabpage(tab);
4627}
4628
4629/*
Bram Moolenaarf740b292006-02-16 22:11:02 +00004630 * Find tab page "n" (first one is 1). Returns NULL when not found.
4631 */
4632 tabpage_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004633find_tabpage(int n)
Bram Moolenaarf740b292006-02-16 22:11:02 +00004634{
4635 tabpage_T *tp;
4636 int i = 1;
4637
Bram Moolenaar00aa0692019-04-27 20:37:57 +02004638 if (n == 0)
4639 return curtab;
4640
Bram Moolenaarf740b292006-02-16 22:11:02 +00004641 for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next)
4642 ++i;
4643 return tp;
4644}
4645
4646/*
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004647 * Get index of tab page "tp". First one has index 1.
Bram Moolenaarba6c0522006-02-25 21:45:02 +00004648 * When not found returns number of tab pages plus one.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004649 */
4650 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004651tabpage_index(tabpage_T *ftp)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004652{
4653 int i = 1;
4654 tabpage_T *tp;
4655
4656 for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next)
4657 ++i;
4658 return i;
4659}
4660
4661/*
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004662 * Prepare for leaving the current tab page.
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02004663 * When autocommands change "curtab" we don't leave the tab page and return
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004664 * FAIL.
4665 * Careful: When OK is returned need to get a new tab page very very soon!
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004666 */
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004667 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004668leave_tabpage(
Bram Moolenaare38eab22019-12-05 21:50:01 +01004669 buf_T *new_curbuf UNUSED, // what is going to be the new curbuf,
4670 // NULL if unknown
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01004671 int trigger_leave_autocmds)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004672{
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004673 tabpage_T *tp = curtab;
4674
Bram Moolenaar6d41c782018-06-06 09:11:12 +02004675#ifdef FEAT_JOB_CHANNEL
4676 leaving_window(curwin);
4677#endif
Bram Moolenaare38eab22019-12-05 21:50:01 +01004678 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004679 if (trigger_leave_autocmds)
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004680 {
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004681 if (new_curbuf != curbuf)
4682 {
4683 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
4684 if (curtab != tp)
4685 return FAIL;
4686 }
4687 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
4688 if (curtab != tp)
4689 return FAIL;
4690 apply_autocmds(EVENT_TABLEAVE, NULL, NULL, FALSE, curbuf);
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004691 if (curtab != tp)
4692 return FAIL;
4693 }
Bram Moolenaar7a7db042022-10-31 23:07:11 +00004694
zeertzjq8e0ccb62022-11-01 18:35:27 +00004695 reset_dragwin();
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004696#if defined(FEAT_GUI)
Bram Moolenaare38eab22019-12-05 21:50:01 +01004697 // Remove the scrollbars. They may be added back later.
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004698 if (gui.in_use)
4699 gui_remove_scrollbars();
4700#endif
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004701 tp->tp_curwin = curwin;
Bram Moolenaarf740b292006-02-16 22:11:02 +00004702 tp->tp_prevwin = prevwin;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004703 tp->tp_firstwin = firstwin;
4704 tp->tp_lastwin = lastwin;
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004705 tp->tp_old_Rows = Rows;
Bram Moolenaarb7118142021-12-10 13:40:08 +00004706 if (tp->tp_old_Columns != -1)
4707 tp->tp_old_Columns = Columns;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004708 firstwin = NULL;
4709 lastwin = NULL;
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004710 return OK;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004711}
4712
4713/*
4714 * Start using tab page "tp".
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004715 * Only to be used after leave_tabpage() or freeing the current tab page.
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004716 * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE.
4717 * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE.
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004718 */
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004719 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004720enter_tabpage(
4721 tabpage_T *tp,
4722 buf_T *old_curbuf UNUSED,
Bram Moolenaarf1d25012016-03-03 12:22:53 +01004723 int trigger_enter_autocmds,
4724 int trigger_leave_autocmds)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004725{
Bram Moolenaar479950f2020-01-19 15:45:17 +01004726 int row;
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004727 int old_off = tp->tp_firstwin->w_winrow;
Bram Moolenaar773560b2006-05-06 21:38:18 +00004728 win_T *next_prevwin = tp->tp_prevwin;
Bram Moolenaar62a23252020-08-09 14:04:42 +02004729 tabpage_T *last_tab = curtab;
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004730
Bram Moolenaar29967732022-11-20 12:11:45 +00004731 use_tabpage(tp);
Bram Moolenaar773560b2006-05-06 21:38:18 +00004732
Bram Moolenaare38eab22019-12-05 21:50:01 +01004733 // We would like doing the TabEnter event first, but we don't have a
4734 // valid current window yet, which may break some commands.
4735 // This triggers autocommands, thus may make "tp" invalid.
Bram Moolenaar57942232021-08-04 20:54:55 +02004736 (void)win_enter_ext(tp->tp_curwin, WEE_CURWIN_INVALID
Bram Moolenaard61f2f72021-08-04 20:26:19 +02004737 | (trigger_enter_autocmds ? WEE_TRIGGER_ENTER_AUTOCMDS : 0)
4738 | (trigger_leave_autocmds ? WEE_TRIGGER_LEAVE_AUTOCMDS : 0));
Bram Moolenaar773560b2006-05-06 21:38:18 +00004739 prevwin = next_prevwin;
4740
Bram Moolenaare38eab22019-12-05 21:50:01 +01004741 last_status(FALSE); // status line may appear or disappear
Bram Moolenaar479950f2020-01-19 15:45:17 +01004742 row = win_comp_pos(); // recompute w_winrow for all windows
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00004743#ifdef FEAT_DIFF
4744 diff_need_scrollbind = TRUE;
4745#endif
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004746
zeertzjq0b0ccbb2022-08-02 12:15:51 +01004747 // Use the stored value of p_ch, so that it can be different for each tab
4748 // page.
Bram Moolenaar0fef0ae2019-05-01 20:30:40 +02004749 if (p_ch != curtab->tp_ch_used)
4750 clear_cmdline = TRUE;
Bram Moolenaarc6fe9192006-04-09 21:54:49 +00004751 p_ch = curtab->tp_ch_used;
Bram Moolenaar479950f2020-01-19 15:45:17 +01004752
4753 // When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is
4754 // changed but p_ch and tp_ch_used are not changed. Thus we also need to
4755 // check cmdline_row.
Bram Moolenaarc9f5f732022-10-06 11:39:06 +01004756 if (row < cmdline_row && cmdline_row <= Rows - p_ch)
Bram Moolenaar479950f2020-01-19 15:45:17 +01004757 clear_cmdline = TRUE;
4758
Bram Moolenaar7a7db042022-10-31 23:07:11 +00004759 // If there was a click in a window, it won't be usable for a following
4760 // drag.
zeertzjq8e0ccb62022-11-01 18:35:27 +00004761 reset_dragwin();
Bram Moolenaar7a7db042022-10-31 23:07:11 +00004762
zeertzjq0b0ccbb2022-08-02 12:15:51 +01004763 // The tabpage line may have appeared or disappeared, may need to resize
4764 // the frames for that. When the Vim window was resized need to update
4765 // frame sizes too.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004766 if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow
4767#ifdef FEAT_GUI_TABLINE
4768 && !gui_use_tabline()
4769#endif
4770 ))
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004771 shell_new_rows();
Bram Moolenaarb7118142021-12-10 13:40:08 +00004772 if (curtab->tp_old_Columns != Columns)
4773 {
4774 if (starting == 0)
4775 {
4776 shell_new_columns(); // update window widths
4777 curtab->tp_old_Columns = Columns;
4778 }
4779 else
4780 curtab->tp_old_Columns = -1; // update window widths later
4781 }
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004782
Bram Moolenaar62a23252020-08-09 14:04:42 +02004783 lastused_tabpage = last_tab;
4784
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00004785#if defined(FEAT_GUI)
Bram Moolenaare38eab22019-12-05 21:50:01 +01004786 // When 'guioptions' includes 'L' or 'R' may have to remove or add
4787 // scrollbars. Have to update them anyway.
Bram Moolenaar746ebd32009-06-16 14:01:43 +00004788 gui_may_update_scrollbars();
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004789#endif
4790
Bram Moolenaare38eab22019-12-05 21:50:01 +01004791 // Apply autocommands after updating the display, when 'rows' and
4792 // 'columns' have been set correctly.
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004793 if (trigger_enter_autocmds)
Bram Moolenaara8596c42012-06-13 14:28:20 +02004794 {
4795 apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
4796 if (old_curbuf != curbuf)
4797 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
4798 }
Bram Moolenaar5ad15df2012-03-16 19:07:58 +01004799
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004800 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004801}
4802
4803/*
4804 * Go to tab page "n". For ":tab N" and "Ngt".
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004805 * When "n" is 9999 go to the last tab page.
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004806 */
4807 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004808goto_tabpage(int n)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004809{
Bram Moolenaar1f3601e2019-04-26 20:33:00 +02004810 tabpage_T *tp = NULL; // shut up compiler
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004811 tabpage_T *ttp;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004812 int i;
4813
Bram Moolenaard68071d2006-05-02 22:08:30 +00004814 if (text_locked())
4815 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004816 // Not allowed when editing the command line.
Bram Moolenaar5a497892016-09-03 16:29:04 +02004817 text_locked_msg();
Bram Moolenaard68071d2006-05-02 22:08:30 +00004818 return;
4819 }
4820
Bram Moolenaare38eab22019-12-05 21:50:01 +01004821 // If there is only one it can't work.
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00004822 if (first_tabpage->tp_next == NULL)
4823 {
4824 if (n > 1)
4825 beep_flush();
4826 return;
4827 }
4828
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004829 if (n == 0)
4830 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004831 // No count, go to next tab page, wrap around end.
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004832 if (curtab->tp_next == NULL)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004833 tp = first_tabpage;
4834 else
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004835 tp = curtab->tp_next;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004836 }
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004837 else if (n < 0)
4838 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004839 // "gT": go to previous tab page, wrap around end. "N gT" repeats
4840 // this N times.
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004841 ttp = curtab;
4842 for (i = n; i < 0; ++i)
4843 {
4844 for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL;
4845 tp = tp->tp_next)
4846 ;
4847 ttp = tp;
4848 }
4849 }
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004850 else if (n == 9999)
4851 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004852 // Go to last tab page.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004853 for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next)
4854 ;
4855 }
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004856 else
4857 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01004858 // Go to tab page "n".
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004859 tp = find_tabpage(n);
Bram Moolenaarf740b292006-02-16 22:11:02 +00004860 if (tp == NULL)
4861 {
4862 beep_flush();
4863 return;
4864 }
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004865 }
4866
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004867 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004868
4869#ifdef FEAT_GUI_TABLINE
4870 if (gui_use_tabline())
Bram Moolenaara226a6d2006-02-26 23:59:20 +00004871 gui_mch_set_curtab(tabpage_index(curtab));
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004872#endif
4873}
4874
4875/*
4876 * Go to tabpage "tp".
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004877 * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE.
4878 * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004879 * Note: doesn't update the GUI tab.
4880 */
4881 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004882goto_tabpage_tp(
4883 tabpage_T *tp,
4884 int trigger_enter_autocmds,
4885 int trigger_leave_autocmds)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00004886{
Bram Moolenaar592f6252022-02-21 16:13:49 +00004887 if (trigger_enter_autocmds || trigger_leave_autocmds)
4888 CHECK_CMDWIN;
Bram Moolenaar0f6e28f2022-02-20 20:49:35 +00004889
Bram Moolenaare38eab22019-12-05 21:50:01 +01004890 // Don't repeat a message in another tab page.
Bram Moolenaarc6af8122010-05-21 12:04:55 +02004891 set_keep_msg(NULL, 0);
4892
Luuk van Baalfaf1d412022-09-19 16:45:29 +01004893 skip_win_fix_scroll = TRUE;
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004894 if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer,
4895 trigger_leave_autocmds) == OK)
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004896 {
4897 if (valid_tabpage(tp))
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004898 enter_tabpage(tp, curbuf, trigger_enter_autocmds,
4899 trigger_leave_autocmds);
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004900 else
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004901 enter_tabpage(curtab, curbuf, trigger_enter_autocmds,
4902 trigger_leave_autocmds);
Bram Moolenaar7e8fd632006-02-18 22:14:51 +00004903 }
Luuk van Baalfaf1d412022-09-19 16:45:29 +01004904 skip_win_fix_scroll = FALSE;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00004905}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906
4907/*
Bram Moolenaar62a23252020-08-09 14:04:42 +02004908 * Go to the last accessed tab page, if there is one.
4909 * Return OK or FAIL
4910 */
4911 int
4912goto_tabpage_lastused(void)
4913{
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00004914 if (!valid_tabpage(lastused_tabpage))
4915 return FAIL;
4916
4917 goto_tabpage_tp(lastused_tabpage, TRUE, TRUE);
4918 return OK;
Bram Moolenaar62a23252020-08-09 14:04:42 +02004919}
4920
4921/*
Bram Moolenaar779b74b2006-04-10 14:55:34 +00004922 * Enter window "wp" in tab page "tp".
4923 * Also updates the GUI tab.
4924 */
4925 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004926goto_tabpage_win(tabpage_T *tp, win_T *wp)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00004927{
Bram Moolenaar49e649f2013-05-06 04:50:35 +02004928 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaar779b74b2006-04-10 14:55:34 +00004929 if (curtab == tp && win_valid(wp))
4930 {
4931 win_enter(wp, TRUE);
4932# ifdef FEAT_GUI_TABLINE
4933 if (gui_use_tabline())
4934 gui_mch_set_curtab(tabpage_index(curtab));
4935# endif
4936 }
4937}
4938
4939/*
Bram Moolenaar40ce3a42015-04-21 18:08:39 +02004940 * Move the current tab page to after tab page "nr".
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004941 */
4942 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004943tabpage_move(int nr)
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004944{
Bram Moolenaar40ce3a42015-04-21 18:08:39 +02004945 int n = 1;
4946 tabpage_T *tp, *tp_dst;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004947
4948 if (first_tabpage->tp_next == NULL)
4949 return;
4950
Bram Moolenaar40ce3a42015-04-21 18:08:39 +02004951 for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next)
4952 ++n;
4953
4954 if (tp == curtab || (nr > 0 && tp->tp_next != NULL
4955 && tp->tp_next == curtab))
4956 return;
4957
4958 tp_dst = tp;
4959
Bram Moolenaare38eab22019-12-05 21:50:01 +01004960 // Remove the current tab page from the list of tab pages.
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004961 if (curtab == first_tabpage)
4962 first_tabpage = curtab->tp_next;
4963 else
4964 {
Bram Moolenaar29323592016-07-24 22:04:11 +02004965 FOR_ALL_TABPAGES(tp)
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004966 if (tp->tp_next == curtab)
4967 break;
Bram Moolenaare38eab22019-12-05 21:50:01 +01004968 if (tp == NULL) // "cannot happen"
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004969 return;
4970 tp->tp_next = curtab->tp_next;
4971 }
4972
Bram Moolenaare38eab22019-12-05 21:50:01 +01004973 // Re-insert it at the specified position.
Bram Moolenaar40ce3a42015-04-21 18:08:39 +02004974 if (nr <= 0)
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004975 {
4976 curtab->tp_next = first_tabpage;
4977 first_tabpage = curtab;
4978 }
4979 else
4980 {
Bram Moolenaar40ce3a42015-04-21 18:08:39 +02004981 curtab->tp_next = tp_dst->tp_next;
4982 tp_dst->tp_next = curtab;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004983 }
4984
Bram Moolenaare38eab22019-12-05 21:50:01 +01004985 // Need to redraw the tabline. Tab page contents doesn't change.
Bram Moolenaar80a94a52006-02-23 21:26:58 +00004986 redraw_tabline = TRUE;
4987}
4988
4989
4990/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004991 * Go to another window.
4992 * When jumping to another buffer, stop Visual mode. Do this before
4993 * changing windows so we can yank the selection into the '*' register.
4994 * When jumping to another window on the same buffer, adjust its cursor
4995 * position to keep the same Visual area.
4996 */
4997 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01004998win_goto(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999{
Bram Moolenaar23c347c2010-07-14 20:57:00 +02005000#ifdef FEAT_CONCEAL
5001 win_T *owp = curwin;
5002#endif
5003
Bram Moolenaar8bf716c2020-01-23 15:33:54 +01005004#ifdef FEAT_PROP_POPUP
Bram Moolenaar3c01c4a2020-02-01 23:04:24 +01005005 if (ERROR_IF_ANY_POPUP_WINDOW)
Bram Moolenaar815b76b2019-06-01 14:15:52 +02005006 return;
Bram Moolenaar8bf716c2020-01-23 15:33:54 +01005007 if (popup_is_popup(wp))
5008 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005009 emsg(_(e_not_allowed_to_enter_popup_window));
Bram Moolenaar8bf716c2020-01-23 15:33:54 +01005010 return;
5011 }
5012#endif
Bram Moolenaar71223e22022-05-30 15:23:09 +01005013 if (text_or_buf_locked())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005014 {
5015 beep_flush();
5016 return;
5017 }
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00005018
Bram Moolenaar071d4272004-06-13 20:20:40 +00005019 if (wp->w_buffer != curbuf)
5020 reset_VIsual_and_resel();
5021 else if (VIsual_active)
5022 wp->w_cursor = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005023
5024#ifdef FEAT_GUI
5025 need_mouse_correct = TRUE;
5026#endif
5027 win_enter(wp, TRUE);
Bram Moolenaar23c347c2010-07-14 20:57:00 +02005028
5029#ifdef FEAT_CONCEAL
Bram Moolenaar535d5b62019-01-11 20:45:36 +01005030 // Conceal cursor line in previous window, unconceal in current window.
Bram Moolenaar530e7df2013-02-06 13:38:02 +01005031 if (win_valid(owp) && owp->w_p_cole > 0 && !msg_scrolled)
Bram Moolenaar535d5b62019-01-11 20:45:36 +01005032 redrawWinline(owp, owp->w_cursor.lnum);
Bram Moolenaar530e7df2013-02-06 13:38:02 +01005033 if (curwin->w_p_cole > 0 && !msg_scrolled)
5034 need_cursor_line_redraw = TRUE;
Bram Moolenaar23c347c2010-07-14 20:57:00 +02005035#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036}
5037
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02005038#if defined(FEAT_PERL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005039/*
5040 * Find window number "winnr" (counting top to bottom).
5041 */
5042 win_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005043win_find_nr(int winnr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005044{
5045 win_T *wp;
5046
Bram Moolenaar29323592016-07-24 22:04:11 +02005047 FOR_ALL_WINDOWS(wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005048 if (--winnr == 0)
5049 break;
5050 return wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005051}
5052#endif
5053
Bram Moolenaar4033c552017-09-16 20:54:51 +02005054#if ((defined(FEAT_PYTHON) || defined(FEAT_PYTHON3))) || defined(PROTO)
Bram Moolenaar105bc352013-05-17 16:03:57 +02005055/*
5056 * Find the tabpage for window "win".
5057 */
5058 tabpage_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005059win_find_tabpage(win_T *win)
Bram Moolenaar105bc352013-05-17 16:03:57 +02005060{
5061 win_T *wp;
5062 tabpage_T *tp;
5063
Bram Moolenaar29323592016-07-24 22:04:11 +02005064 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar105bc352013-05-17 16:03:57 +02005065 if (wp == win)
5066 return tp;
5067 return NULL;
5068}
5069#endif
5070
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071/*
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005072 * Get the above or below neighbor window of the specified window.
5073 * up - TRUE for the above neighbor
5074 * count - nth neighbor window
5075 * Returns the specified window if the neighbor is not found.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005076 */
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005077 win_T *
5078win_vert_neighbor(tabpage_T *tp, win_T *wp, int up, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005079{
5080 frame_T *fr;
5081 frame_T *nfr;
5082 frame_T *foundfr;
5083
Bram Moolenaar631ebc42020-02-03 22:15:26 +01005084#ifdef FEAT_PROP_POPUP
5085 if (popup_is_popup(wp))
5086 // popups don't have neighbors.
5087 return NULL;
5088#endif
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005089 foundfr = wp->w_frame;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005090 while (count--)
5091 {
5092 /*
5093 * First go upwards in the tree of frames until we find a upwards or
5094 * downwards neighbor.
5095 */
5096 fr = foundfr;
5097 for (;;)
5098 {
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005099 if (fr == tp->tp_topframe)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005100 goto end;
5101 if (up)
5102 nfr = fr->fr_prev;
5103 else
5104 nfr = fr->fr_next;
5105 if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL)
5106 break;
5107 fr = fr->fr_parent;
5108 }
5109
5110 /*
5111 * Now go downwards to find the bottom or top frame in it.
5112 */
5113 for (;;)
5114 {
5115 if (nfr->fr_layout == FR_LEAF)
5116 {
5117 foundfr = nfr;
5118 break;
5119 }
5120 fr = nfr->fr_child;
5121 if (nfr->fr_layout == FR_ROW)
5122 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01005123 // Find the frame at the cursor row.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005124 while (fr->fr_next != NULL
5125 && frame2win(fr)->w_wincol + fr->fr_width
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005126 <= wp->w_wincol + wp->w_wcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127 fr = fr->fr_next;
5128 }
5129 if (nfr->fr_layout == FR_COL && up)
5130 while (fr->fr_next != NULL)
5131 fr = fr->fr_next;
5132 nfr = fr;
5133 }
5134 }
5135end:
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005136 return foundfr != NULL ? foundfr->fr_win : NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137}
5138
5139/*
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005140 * Move to window above or below "count" times.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005141 */
5142 static void
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005143win_goto_ver(
5144 int up, // TRUE to go to win above
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005145 long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146{
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005147 win_T *win;
5148
Bram Moolenaar219c7d02020-02-01 21:57:29 +01005149#ifdef FEAT_PROP_POPUP
5150 if (ERROR_IF_TERM_POPUP_WINDOW)
5151 return;
5152#endif
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005153 win = win_vert_neighbor(curtab, curwin, up, count);
5154 if (win != NULL)
5155 win_goto(win);
5156}
5157
5158/*
5159 * Get the left or right neighbor window of the specified window.
5160 * left - TRUE for the left neighbor
5161 * count - nth neighbor window
5162 * Returns the specified window if the neighbor is not found.
5163 */
5164 win_T *
Bram Moolenaarb9cdb372019-04-17 18:24:35 +02005165win_horz_neighbor(tabpage_T *tp, win_T *wp, int left, long count)
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005166{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005167 frame_T *fr;
5168 frame_T *nfr;
5169 frame_T *foundfr;
5170
Bram Moolenaar631ebc42020-02-03 22:15:26 +01005171#ifdef FEAT_PROP_POPUP
5172 if (popup_is_popup(wp))
5173 // popups don't have neighbors.
5174 return NULL;
5175#endif
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005176 foundfr = wp->w_frame;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005177 while (count--)
5178 {
5179 /*
5180 * First go upwards in the tree of frames until we find a left or
5181 * right neighbor.
5182 */
5183 fr = foundfr;
5184 for (;;)
5185 {
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005186 if (fr == tp->tp_topframe)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005187 goto end;
5188 if (left)
5189 nfr = fr->fr_prev;
5190 else
5191 nfr = fr->fr_next;
5192 if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
5193 break;
5194 fr = fr->fr_parent;
5195 }
5196
5197 /*
5198 * Now go downwards to find the leftmost or rightmost frame in it.
5199 */
5200 for (;;)
5201 {
5202 if (nfr->fr_layout == FR_LEAF)
5203 {
5204 foundfr = nfr;
5205 break;
5206 }
5207 fr = nfr->fr_child;
5208 if (nfr->fr_layout == FR_COL)
5209 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01005210 // Find the frame at the cursor row.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005211 while (fr->fr_next != NULL
5212 && frame2win(fr)->w_winrow + fr->fr_height
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005213 <= wp->w_winrow + wp->w_wrow)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 fr = fr->fr_next;
5215 }
5216 if (nfr->fr_layout == FR_ROW && left)
5217 while (fr->fr_next != NULL)
5218 fr = fr->fr_next;
5219 nfr = fr;
5220 }
5221 }
5222end:
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005223 return foundfr != NULL ? foundfr->fr_win : NULL;
5224}
5225
5226/*
5227 * Move to left or right window.
5228 */
5229 static void
5230win_goto_hor(
5231 int left, // TRUE to go to left win
5232 long count)
5233{
5234 win_T *win;
5235
Bram Moolenaar219c7d02020-02-01 21:57:29 +01005236#ifdef FEAT_PROP_POPUP
5237 if (ERROR_IF_TERM_POPUP_WINDOW)
5238 return;
5239#endif
Bram Moolenaar46ad2882019-04-08 20:01:47 +02005240 win = win_horz_neighbor(curtab, curwin, left, count);
5241 if (win != NULL)
5242 win_goto(win);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005244
5245/*
5246 * Make window "wp" the current window.
5247 */
5248 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005249win_enter(win_T *wp, int undo_sync)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005250{
Bram Moolenaar57942232021-08-04 20:54:55 +02005251 (void)win_enter_ext(wp, (undo_sync ? WEE_UNDO_SYNC : 0)
Bram Moolenaard61f2f72021-08-04 20:26:19 +02005252 | WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005253}
5254
5255/*
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00005256 * Used after making another window the current one: change directory if
5257 * needed.
5258 */
5259 static void
5260fix_current_dir(void)
5261{
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00005262 if (curwin->w_localdir != NULL || curtab->tp_localdir != NULL)
5263 {
5264 char_u *dirname;
5265
5266 // Window or tab has a local directory: Save current directory as
5267 // global directory (unless that was done already) and change to the
5268 // local directory.
5269 if (globaldir == NULL)
5270 {
5271 char_u cwd[MAXPATHL];
5272
5273 if (mch_dirname(cwd, MAXPATHL) == OK)
5274 globaldir = vim_strsave(cwd);
5275 }
5276 if (curwin->w_localdir != NULL)
5277 dirname = curwin->w_localdir;
5278 else
5279 dirname = curtab->tp_localdir;
5280
5281 if (mch_chdir((char *)dirname) == 0)
5282 {
5283 last_chdir_reason = NULL;
5284 shorten_fnames(TRUE);
5285 }
5286 }
5287 else if (globaldir != NULL)
5288 {
5289 // Window doesn't have a local directory and we are not in the global
5290 // directory: Change to the global directory.
5291 vim_ignored = mch_chdir((char *)globaldir);
5292 VIM_CLEAR(globaldir);
5293 last_chdir_reason = NULL;
5294 shorten_fnames(TRUE);
5295 }
5296}
5297
5298/*
Bram Moolenaard61f2f72021-08-04 20:26:19 +02005299 * Make window "wp" the current window.
5300 * Can be called with "flags" containing WEE_CURWIN_INVALID, which means that
5301 * curwin has just been closed and isn't valid.
Bram Moolenaar57942232021-08-04 20:54:55 +02005302 * Returns TRUE when dont_parse_messages was decremented.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303 */
Bram Moolenaar57942232021-08-04 20:54:55 +02005304 static int
Bram Moolenaard61f2f72021-08-04 20:26:19 +02005305win_enter_ext(win_T *wp, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005306{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005307 int other_buffer = FALSE;
Bram Moolenaard61f2f72021-08-04 20:26:19 +02005308 int curwin_invalid = (flags & WEE_CURWIN_INVALID);
Bram Moolenaar57942232021-08-04 20:54:55 +02005309 int did_decrement = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005310
Bram Moolenaar99ad3a82023-02-27 17:18:01 +00005311 if (wp == curwin && curwin_invalid == 0) // nothing to do
Bram Moolenaar57942232021-08-04 20:54:55 +02005312 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005313
Bram Moolenaar6d41c782018-06-06 09:11:12 +02005314#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ad3a82023-02-27 17:18:01 +00005315 if (curwin_invalid == 0)
Bram Moolenaar6d41c782018-06-06 09:11:12 +02005316 leaving_window(curwin);
5317#endif
5318
Bram Moolenaar99ad3a82023-02-27 17:18:01 +00005319 if (curwin_invalid == 0 && (flags & WEE_TRIGGER_LEAVE_AUTOCMDS))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005320 {
5321 /*
5322 * Be careful: If autocommands delete the window, return now.
5323 */
5324 if (wp->w_buffer != curbuf)
5325 {
5326 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
5327 other_buffer = TRUE;
5328 if (!win_valid(wp))
Bram Moolenaar57942232021-08-04 20:54:55 +02005329 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005330 }
5331 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
5332 if (!win_valid(wp))
Bram Moolenaar57942232021-08-04 20:54:55 +02005333 return FALSE;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01005334#ifdef FEAT_EVAL
Bram Moolenaare38eab22019-12-05 21:50:01 +01005335 // autocmds may abort script processing
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336 if (aborting())
Bram Moolenaar57942232021-08-04 20:54:55 +02005337 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005338#endif
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01005339 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005340
Bram Moolenaare38eab22019-12-05 21:50:01 +01005341 // sync undo before leaving the current buffer
Bram Moolenaard61f2f72021-08-04 20:26:19 +02005342 if ((flags & WEE_UNDO_SYNC) && curbuf != wp->w_buffer)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005343 u_sync(FALSE);
Bram Moolenaarec1561c2014-06-17 13:52:40 +02005344
Bram Moolenaare38eab22019-12-05 21:50:01 +01005345 // Might need to scroll the old window before switching, e.g., when the
5346 // cursor was moved.
Bram Moolenaar99ad3a82023-02-27 17:18:01 +00005347 if (*p_spk == 'c' && curwin_invalid == 0)
Luuk van Baal29ab5242022-09-11 16:59:53 +01005348 update_topline();
Bram Moolenaarec1561c2014-06-17 13:52:40 +02005349
Bram Moolenaare38eab22019-12-05 21:50:01 +01005350 // may have to copy the buffer options when 'cpo' contains 'S'
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 if (wp->w_buffer != curbuf)
5352 buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP);
Bram Moolenaar99ad3a82023-02-27 17:18:01 +00005353 if (curwin_invalid == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005354 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01005355 prevwin = curwin; // remember for CTRL-W p
Bram Moolenaar071d4272004-06-13 20:20:40 +00005356 curwin->w_redr_status = TRUE;
5357 }
5358 curwin = wp;
5359 curbuf = wp->w_buffer;
5360 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005361 if (!virtual_active())
5362 curwin->w_cursor.coladd = 0;
Luuk van Baal13ece2a2022-10-03 15:28:08 +01005363 if (*p_spk == 'c') // assume cursor position needs updating
Luuk van Baal29ab5242022-09-11 16:59:53 +01005364 changed_line_abv_curs();
5365 else
Luuk van Baalbc3dc292023-02-15 16:45:27 +00005366 // Make sure the cursor position is valid, either by moving the cursor
5367 // or by scrolling the text.
5368 win_fix_cursor(
5369 get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005370
Bram Moolenaar57942232021-08-04 20:54:55 +02005371 // Now it is OK to parse messages again, which may be needed in
5372 // autocommands.
5373#ifdef MESSAGE_QUEUE
5374 if (flags & WEE_ALLOW_PARSE_MESSAGES)
5375 {
5376 --dont_parse_messages;
5377 did_decrement = TRUE;
5378 }
5379#endif
5380
Bram Moolenaar7f13b242021-11-14 11:41:31 +00005381 fix_current_dir();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005382
Bram Moolenaar6d41c782018-06-06 09:11:12 +02005383#ifdef FEAT_JOB_CHANNEL
5384 entering_window(curwin);
5385#endif
Bram Moolenaarec66c412019-10-11 21:19:13 +02005386 // Careful: autocommands may close the window and make "wp" invalid
Bram Moolenaard61f2f72021-08-04 20:26:19 +02005387 if (flags & WEE_TRIGGER_NEW_AUTOCMDS)
Bram Moolenaarc917da42016-07-19 22:31:36 +02005388 apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf);
Bram Moolenaard61f2f72021-08-04 20:26:19 +02005389 if (flags & WEE_TRIGGER_ENTER_AUTOCMDS)
Bram Moolenaar49e649f2013-05-06 04:50:35 +02005390 {
5391 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
5392 if (other_buffer)
5393 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
5394 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005395
Bram Moolenaar071d4272004-06-13 20:20:40 +00005396 maketitle();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005397 curwin->w_redr_status = TRUE;
Bram Moolenaarfbbd1022019-10-07 22:38:58 +02005398#ifdef FEAT_TERMINAL
Bram Moolenaarec66c412019-10-11 21:19:13 +02005399 if (bt_terminal(curwin->w_buffer))
Bram Moolenaara27e1dc2019-10-07 22:27:36 +02005400 // terminal is likely in another mode
5401 redraw_mode = TRUE;
Bram Moolenaarfbbd1022019-10-07 22:38:58 +02005402#endif
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00005403 redraw_tabline = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005404 if (restart_edit)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005405 redraw_later(UPD_VALID); // causes status line redraw
Bram Moolenaar071d4272004-06-13 20:20:40 +00005406
Bram Moolenaare38eab22019-12-05 21:50:01 +01005407 // set window height to desired minimal value
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02005408 if (curwin->w_height < p_wh && !curwin->w_p_wfh
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005409#ifdef FEAT_PROP_POPUP
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02005410 && !popup_is_popup(curwin)
5411#endif
5412 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00005413 win_setheight((int)p_wh);
5414 else if (curwin->w_height == 0)
5415 win_setheight(1);
5416
Bram Moolenaare38eab22019-12-05 21:50:01 +01005417 // set window width to desired minimal value
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00005418 if (curwin->w_width < p_wiw && !curwin->w_p_wfw)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005419 win_setwidth((int)p_wiw);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005420
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02005421 setmouse(); // in case jumped to/from help buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00005422
Bram Moolenaare38eab22019-12-05 21:50:01 +01005423 // Change directories when the 'acd' option is set.
Bram Moolenaar6f470022018-04-10 18:47:20 +02005424 DO_AUTOCHDIR;
Bram Moolenaar57942232021-08-04 20:54:55 +02005425
5426 return did_decrement;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005427}
5428
Bram Moolenaar7f13b242021-11-14 11:41:31 +00005429/*
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005430 * Jump to the first open window that contains buffer "buf", if one exists.
5431 * Returns a pointer to the window found, otherwise NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005432 */
5433 win_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005434buf_jump_open_win(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005435{
Bram Moolenaar482a2b52014-10-21 20:57:15 +02005436 win_T *wp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005437
Bram Moolenaar482a2b52014-10-21 20:57:15 +02005438 if (curwin->w_buffer == buf)
5439 wp = curwin;
Bram Moolenaar482a2b52014-10-21 20:57:15 +02005440 else
Bram Moolenaar29323592016-07-24 22:04:11 +02005441 FOR_ALL_WINDOWS(wp)
Bram Moolenaar482a2b52014-10-21 20:57:15 +02005442 if (wp->w_buffer == buf)
5443 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005444 if (wp != NULL)
5445 win_enter(wp, FALSE);
Bram Moolenaar482a2b52014-10-21 20:57:15 +02005446 return wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005447}
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005448
5449/*
5450 * Jump to the first open window in any tab page that contains buffer "buf",
Yegappan Lakshmanan9c9be052022-02-24 12:33:17 +00005451 * if one exists. First search in the windows present in the current tab page.
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005452 * Returns a pointer to the window found, otherwise NULL.
5453 */
5454 win_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005455buf_jump_open_tab(buf_T *buf)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005456{
Bram Moolenaar482a2b52014-10-21 20:57:15 +02005457 win_T *wp = buf_jump_open_win(buf);
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005458 tabpage_T *tp;
5459
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005460 if (wp != NULL)
5461 return wp;
5462
Bram Moolenaar29323592016-07-24 22:04:11 +02005463 FOR_ALL_TABPAGES(tp)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005464 if (tp != curtab)
5465 {
Bram Moolenaaraeea7212020-04-02 18:50:46 +02005466 FOR_ALL_WINDOWS_IN_TAB(tp, wp)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005467 if (wp->w_buffer == buf)
5468 break;
5469 if (wp != NULL)
5470 {
5471 goto_tabpage_win(tp, wp);
5472 if (curwin != wp)
Bram Moolenaare38eab22019-12-05 21:50:01 +01005473 wp = NULL; // something went wrong
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005474 break;
5475 }
5476 }
Bram Moolenaar482a2b52014-10-21 20:57:15 +02005477 return wp;
Bram Moolenaar779b74b2006-04-10 14:55:34 +00005478}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005479
Bram Moolenaar888ccac2016-06-04 18:49:36 +02005480static int last_win_id = LOWEST_WIN_ID - 1;
Bram Moolenaar86edef62016-03-13 18:07:30 +01005481
Bram Moolenaar071d4272004-06-13 20:20:40 +00005482/*
Bram Moolenaar746ebd32009-06-16 14:01:43 +00005483 * Allocate a window structure and link it in the window list when "hidden" is
5484 * FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005485 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005486 static win_T *
Luuk van Baal13ece2a2022-10-03 15:28:08 +01005487win_alloc(win_T *after, int hidden)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005488{
Bram Moolenaar70b2a562012-01-10 22:26:17 +01005489 win_T *new_wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005490
5491 /*
5492 * allocate window structure and linesizes arrays
5493 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005494 new_wp = ALLOC_CLEAR_ONE(win_T);
Bram Moolenaar429fa852013-04-15 12:27:36 +02005495 if (new_wp == NULL)
5496 return NULL;
5497
5498 if (win_alloc_lines(new_wp) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01005500 vim_free(new_wp);
Bram Moolenaar429fa852013-04-15 12:27:36 +02005501 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502 }
5503
Bram Moolenaar86edef62016-03-13 18:07:30 +01005504 new_wp->w_id = ++last_win_id;
5505
Bram Moolenaar429fa852013-04-15 12:27:36 +02005506#ifdef FEAT_EVAL
Bram Moolenaare38eab22019-12-05 21:50:01 +01005507 // init w: variables
Yegappan Lakshmanan72bb47e2022-04-03 11:22:38 +01005508 new_wp->w_vars = dict_alloc_id(aid_newwin_wvars);
Bram Moolenaar429fa852013-04-15 12:27:36 +02005509 if (new_wp->w_vars == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005510 {
Bram Moolenaar429fa852013-04-15 12:27:36 +02005511 win_free_lsize(new_wp);
5512 vim_free(new_wp);
5513 return NULL;
5514 }
5515 init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE);
Bram Moolenaaree79cbc2007-05-02 19:50:14 +00005516#endif
Bram Moolenaar429fa852013-04-15 12:27:36 +02005517
Bram Moolenaare38eab22019-12-05 21:50:01 +01005518 // Don't execute autocommands while the window is not properly
5519 // initialized yet. gui_create_scrollbar() may trigger a FocusGained
5520 // event.
Bram Moolenaar429fa852013-04-15 12:27:36 +02005521 block_autocmds();
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01005522
Bram Moolenaar429fa852013-04-15 12:27:36 +02005523 /*
5524 * link the window in the window list
5525 */
Bram Moolenaar429fa852013-04-15 12:27:36 +02005526 if (!hidden)
5527 win_append(after, new_wp);
Bram Moolenaar429fa852013-04-15 12:27:36 +02005528 new_wp->w_wincol = 0;
5529 new_wp->w_width = Columns;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005530
Bram Moolenaare38eab22019-12-05 21:50:01 +01005531 // position the display and the cursor at the top of the file.
Bram Moolenaar429fa852013-04-15 12:27:36 +02005532 new_wp->w_topline = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005533#ifdef FEAT_DIFF
Bram Moolenaar429fa852013-04-15 12:27:36 +02005534 new_wp->w_topfill = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005535#endif
Bram Moolenaar429fa852013-04-15 12:27:36 +02005536 new_wp->w_botline = 2;
5537 new_wp->w_cursor.lnum = 1;
Bram Moolenaar429fa852013-04-15 12:27:36 +02005538 new_wp->w_scbind_pos = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539
Bram Moolenaar375e3392019-01-31 18:26:10 +01005540 // use global option value for global-local options
5541 new_wp->w_p_so = -1;
5542 new_wp->w_p_siso = -1;
5543
Bram Moolenaare38eab22019-12-05 21:50:01 +01005544 // We won't calculate w_fraction until resizing the window
Bram Moolenaar429fa852013-04-15 12:27:36 +02005545 new_wp->w_fraction = 0;
5546 new_wp->w_prev_fraction_row = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547
5548#ifdef FEAT_GUI
Bram Moolenaar429fa852013-04-15 12:27:36 +02005549 if (gui.in_use)
5550 {
5551 gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_LEFT],
5552 SBAR_LEFT, new_wp);
5553 gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_RIGHT],
5554 SBAR_RIGHT, new_wp);
5555 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005556#endif
5557#ifdef FEAT_FOLDING
Bram Moolenaar429fa852013-04-15 12:27:36 +02005558 foldInitWin(new_wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005559#endif
Bram Moolenaar429fa852013-04-15 12:27:36 +02005560 unblock_autocmds();
Bram Moolenaar6ee10162007-07-26 20:58:42 +00005561#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar9f573a82022-09-29 13:50:08 +01005562 new_wp->w_next_match_id = 1000; // up to 1000 can be picked by the user
Bram Moolenaar6ee10162007-07-26 20:58:42 +00005563#endif
Bram Moolenaar70b2a562012-01-10 22:26:17 +01005564 return new_wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565}
5566
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567/*
Bram Moolenaarff18df02013-07-24 17:51:57 +02005568 * Remove window 'wp' from the window list and free the structure.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569 */
5570 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005571win_free(
5572 win_T *wp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01005573 tabpage_T *tp) // tab page "win" is in, NULL for current
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574{
5575 int i;
Bram Moolenaarff18df02013-07-24 17:51:57 +02005576 buf_T *buf;
5577 wininfo_T *wip;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578
Bram Moolenaarf061e0b2009-06-24 15:32:01 +00005579#ifdef FEAT_FOLDING
5580 clearFolding(wp);
5581#endif
5582
Bram Moolenaare38eab22019-12-05 21:50:01 +01005583 // reduce the reference count to the argument list.
Bram Moolenaarf061e0b2009-06-24 15:32:01 +00005584 alist_unlink(wp->w_alist);
5585
Bram Moolenaare38eab22019-12-05 21:50:01 +01005586 // Don't execute autocommands while the window is halfway being deleted.
5587 // gui_mch_destroy_scrollbar() may trigger a FocusGained event.
Bram Moolenaar78ab3312007-09-29 12:16:41 +00005588 block_autocmds();
Bram Moolenaaree79cbc2007-05-02 19:50:14 +00005589
Bram Moolenaar0ba04292010-07-14 23:23:17 +02005590#ifdef FEAT_LUA
5591 lua_window_free(wp);
5592#endif
5593
Bram Moolenaar325b7a22004-07-05 15:58:32 +00005594#ifdef FEAT_MZSCHEME
5595 mzscheme_window_free(wp);
5596#endif
5597
Bram Moolenaar071d4272004-06-13 20:20:40 +00005598#ifdef FEAT_PERL
5599 perl_win_free(wp);
5600#endif
5601
5602#ifdef FEAT_PYTHON
5603 python_window_free(wp);
5604#endif
5605
Bram Moolenaarbd5e15f2010-07-17 21:19:38 +02005606#ifdef FEAT_PYTHON3
5607 python3_window_free(wp);
5608#endif
5609
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610#ifdef FEAT_TCL
5611 tcl_window_free(wp);
5612#endif
5613
5614#ifdef FEAT_RUBY
5615 ruby_window_free(wp);
5616#endif
5617
5618 clear_winopt(&wp->w_onebuf_opt);
5619 clear_winopt(&wp->w_allbuf_opt);
5620
zeertzjq7a33ebf2021-11-02 20:56:07 +00005621 vim_free(wp->w_lcs_chars.multispace);
Bram Moolenaaraca12fd2022-06-07 10:16:15 +01005622 vim_free(wp->w_lcs_chars.leadmultispace);
zeertzjq7a33ebf2021-11-02 20:56:07 +00005623
Bram Moolenaar071d4272004-06-13 20:20:40 +00005624#ifdef FEAT_EVAL
Bram Moolenaare38eab22019-12-05 21:50:01 +01005625 vars_clear(&wp->w_vars->dv_hashtab); // free all w: variables
Bram Moolenaar429fa852013-04-15 12:27:36 +02005626 hash_init(&wp->w_vars->dv_hashtab);
5627 unref_var_dict(wp->w_vars);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628#endif
5629
Bram Moolenaar3dda7db2016-04-03 21:22:58 +02005630 {
5631 tabpage_T *ttp;
5632
5633 if (prevwin == wp)
5634 prevwin = NULL;
Bram Moolenaar29323592016-07-24 22:04:11 +02005635 FOR_ALL_TABPAGES(ttp)
Bram Moolenaar3dda7db2016-04-03 21:22:58 +02005636 if (ttp->tp_prevwin == wp)
5637 ttp->tp_prevwin = NULL;
5638 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 win_free_lsize(wp);
5640
5641 for (i = 0; i < wp->w_tagstacklen; ++i)
Bram Moolenaar55008aa2019-09-01 20:21:56 +02005642 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005643 vim_free(wp->w_tagstack[i].tagname);
Bram Moolenaar55008aa2019-09-01 20:21:56 +02005644 vim_free(wp->w_tagstack[i].user_data);
5645 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005646 vim_free(wp->w_localdir);
Bram Moolenaar002bc792020-06-05 22:33:42 +02005647 vim_free(wp->w_prevdir);
Bram Moolenaar6ee10162007-07-26 20:58:42 +00005648
Bram Moolenaare38eab22019-12-05 21:50:01 +01005649 // Remove the window from the b_wininfo lists, it may happen that the
5650 // freed memory is re-used for another window.
Bram Moolenaar29323592016-07-24 22:04:11 +02005651 FOR_ALL_BUFFERS(buf)
Bram Moolenaaraeea7212020-04-02 18:50:46 +02005652 FOR_ALL_BUF_WININFO(buf, wip)
Bram Moolenaarff18df02013-07-24 17:51:57 +02005653 if (wip->wi_win == wp)
Bram Moolenaar4882d982020-10-25 17:55:09 +01005654 {
5655 wininfo_T *wip2;
5656
5657 // If there already is an entry with "wi_win" set to NULL it
5658 // must be removed, it would never be used.
Bram Moolenaarb5098062021-07-07 19:26:19 +02005659 // Skip "wip" itself, otherwise Coverity complains.
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00005660 FOR_ALL_BUF_WININFO(buf, wip2)
Bram Moolenaarb5098062021-07-07 19:26:19 +02005661 if (wip2 != wip && wip2->wi_win == NULL)
Bram Moolenaar4882d982020-10-25 17:55:09 +01005662 {
5663 if (wip2->wi_next != NULL)
5664 wip2->wi_next->wi_prev = wip2->wi_prev;
5665 if (wip2->wi_prev == NULL)
5666 buf->b_wininfo = wip2->wi_next;
5667 else
5668 wip2->wi_prev->wi_next = wip2->wi_next;
5669 free_wininfo(wip2);
5670 break;
5671 }
5672
Bram Moolenaarff18df02013-07-24 17:51:57 +02005673 wip->wi_win = NULL;
Bram Moolenaar4882d982020-10-25 17:55:09 +01005674 }
Bram Moolenaarff18df02013-07-24 17:51:57 +02005675
Bram Moolenaar071d4272004-06-13 20:20:40 +00005676#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar6ee10162007-07-26 20:58:42 +00005677 clear_matches(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678#endif
Bram Moolenaar6ee10162007-07-26 20:58:42 +00005679
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 free_jumplist(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681
Bram Moolenaar28c258f2006-01-25 22:02:51 +00005682#ifdef FEAT_QUICKFIX
5683 qf_free_all(wp);
5684#endif
5685
Bram Moolenaar071d4272004-06-13 20:20:40 +00005686#ifdef FEAT_GUI
5687 if (gui.in_use)
5688 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005689 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]);
5690 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]);
5691 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01005692#endif // FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02005694#ifdef FEAT_MENU
5695 remove_winbar(wp);
5696#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005697#ifdef FEAT_PROP_POPUP
Bram Moolenaar9eaac892019-06-01 22:49:29 +02005698 free_callback(&wp->w_close_cb);
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02005699 free_callback(&wp->w_filter_cb);
Bram Moolenaar790498b2019-06-01 22:15:29 +02005700 for (i = 0; i < 4; ++i)
5701 VIM_CLEAR(wp->w_border_highlight[i]);
Bram Moolenaar4cd583c2019-06-26 05:13:57 +02005702 vim_free(wp->w_scrollbar_highlight);
5703 vim_free(wp->w_thumb_highlight);
Bram Moolenaareb2310d2019-06-16 20:09:10 +02005704 vim_free(wp->w_popup_title);
Bram Moolenaarc662ec92019-06-23 00:15:57 +02005705 list_unref(wp->w_popup_mask);
Bram Moolenaar0aca2932019-07-26 22:22:38 +02005706 vim_free(wp->w_popup_mask_cells);
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02005707#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02005708
Bram Moolenaar860cae12010-06-05 23:22:07 +02005709#ifdef FEAT_SYN_HL
Bram Moolenaar1a384422010-07-14 19:53:30 +02005710 vim_free(wp->w_p_cc_cols);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005711#endif
5712
Bram Moolenaar4d784b22019-05-25 19:51:39 +02005713 if (win_valid_any_tab(wp))
Bram Moolenaarfd29f462010-06-06 16:11:09 +02005714 win_remove(wp, tp);
Bram Moolenaar3be85852014-06-12 14:01:31 +02005715 if (autocmd_busy)
5716 {
5717 wp->w_next = au_pending_free_win;
5718 au_pending_free_win = wp;
5719 }
5720 else
5721 vim_free(wp);
Bram Moolenaaree79cbc2007-05-02 19:50:14 +00005722
Bram Moolenaar78ab3312007-09-29 12:16:41 +00005723 unblock_autocmds();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005724}
5725
5726/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +02005727 * Return TRUE if "wp" is not in the list of windows: the autocmd window or a
5728 * popup window.
5729 */
Bram Moolenaar6f2465d2022-03-22 18:13:01 +00005730 int
Bram Moolenaar4d784b22019-05-25 19:51:39 +02005731win_unlisted(win_T *wp)
5732{
Bram Moolenaare76062c2022-11-28 18:51:43 +00005733 return is_aucmd_win(wp) || WIN_IS_POPUP(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02005734}
5735
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005736#if defined(FEAT_PROP_POPUP) || defined(PROTO)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02005737/*
5738 * Free a popup window. This does not take the window out of the window list
5739 * and assumes there is only one toplevel frame, no split.
5740 */
5741 void
5742win_free_popup(win_T *win)
5743{
Yegappan Lakshmanan0dac1ab2022-04-02 21:46:19 +01005744 if (win->w_buffer != NULL)
5745 {
5746 if (bt_popup(win->w_buffer))
5747 win_close_buffer(win, DOBUF_WIPE_REUSE, FALSE);
5748 else
5749 close_buffer(win, win->w_buffer, 0, FALSE, FALSE);
5750 }
Bram Moolenaar35d5af62019-05-26 20:44:10 +02005751# if defined(FEAT_TIMERS)
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +01005752 // the timer may have been cleared, making the pointer invalid
5753 if (timer_valid(win->w_popup_timer))
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02005754 stop_timer(win->w_popup_timer);
Bram Moolenaar35d5af62019-05-26 20:44:10 +02005755# endif
Bram Moolenaar4d784b22019-05-25 19:51:39 +02005756 vim_free(win->w_frame);
5757 win_free(win, NULL);
5758}
Bram Moolenaar35d5af62019-05-26 20:44:10 +02005759#endif
Bram Moolenaar4d784b22019-05-25 19:51:39 +02005760
5761/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762 * Append window "wp" in the window list after window "after".
5763 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02005764 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005765win_append(win_T *after, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005766{
5767 win_T *before;
5768
Bram Moolenaare38eab22019-12-05 21:50:01 +01005769 if (after == NULL) // after NULL is in front of the first
Bram Moolenaar071d4272004-06-13 20:20:40 +00005770 before = firstwin;
5771 else
5772 before = after->w_next;
5773
5774 wp->w_next = before;
5775 wp->w_prev = after;
5776 if (after == NULL)
5777 firstwin = wp;
5778 else
5779 after->w_next = wp;
5780 if (before == NULL)
5781 lastwin = wp;
5782 else
5783 before->w_prev = wp;
5784}
5785
5786/*
5787 * Remove a window from the window list.
5788 */
Bram Moolenaar746ebd32009-06-16 14:01:43 +00005789 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005790win_remove(
5791 win_T *wp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01005792 tabpage_T *tp) // tab page "win" is in, NULL for current
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793{
5794 if (wp->w_prev != NULL)
5795 wp->w_prev->w_next = wp->w_next;
Bram Moolenaarf740b292006-02-16 22:11:02 +00005796 else if (tp == NULL)
Bram Moolenaar816968d2017-09-29 21:29:18 +02005797 firstwin = curtab->tp_firstwin = wp->w_next;
Bram Moolenaarf740b292006-02-16 22:11:02 +00005798 else
5799 tp->tp_firstwin = wp->w_next;
Bram Moolenaar816968d2017-09-29 21:29:18 +02005800
Bram Moolenaar071d4272004-06-13 20:20:40 +00005801 if (wp->w_next != NULL)
5802 wp->w_next->w_prev = wp->w_prev;
Bram Moolenaarf740b292006-02-16 22:11:02 +00005803 else if (tp == NULL)
Bram Moolenaar816968d2017-09-29 21:29:18 +02005804 lastwin = curtab->tp_lastwin = wp->w_prev;
Bram Moolenaarf740b292006-02-16 22:11:02 +00005805 else
5806 tp->tp_lastwin = wp->w_prev;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005807}
5808
5809/*
5810 * Append frame "frp" in a frame list after frame "after".
5811 */
5812 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005813frame_append(frame_T *after, frame_T *frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005814{
5815 frp->fr_next = after->fr_next;
5816 after->fr_next = frp;
5817 if (frp->fr_next != NULL)
5818 frp->fr_next->fr_prev = frp;
5819 frp->fr_prev = after;
5820}
5821
5822/*
5823 * Insert frame "frp" in a frame list before frame "before".
5824 */
5825 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005826frame_insert(frame_T *before, frame_T *frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005827{
5828 frp->fr_next = before;
5829 frp->fr_prev = before->fr_prev;
5830 before->fr_prev = frp;
5831 if (frp->fr_prev != NULL)
5832 frp->fr_prev->fr_next = frp;
5833 else
5834 frp->fr_parent->fr_child = frp;
5835}
5836
5837/*
5838 * Remove a frame from a frame list.
5839 */
5840 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005841frame_remove(frame_T *frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842{
5843 if (frp->fr_prev != NULL)
5844 frp->fr_prev->fr_next = frp->fr_next;
5845 else
5846 frp->fr_parent->fr_child = frp->fr_next;
5847 if (frp->fr_next != NULL)
5848 frp->fr_next->fr_prev = frp->fr_prev;
5849}
5850
Bram Moolenaar071d4272004-06-13 20:20:40 +00005851/*
5852 * Allocate w_lines[] for window "wp".
5853 * Return FAIL for failure, OK for success.
5854 */
5855 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005856win_alloc_lines(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005857{
5858 wp->w_lines_valid = 0;
Bram Moolenaar05b27612022-01-20 13:32:50 +00005859 wp->w_lines = ALLOC_CLEAR_MULT(wline_T, Rows);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005860 if (wp->w_lines == NULL)
5861 return FAIL;
5862 return OK;
5863}
5864
5865/*
5866 * free lsize arrays for a window
5867 */
5868 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005869win_free_lsize(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005870{
Bram Moolenaare38eab22019-12-05 21:50:01 +01005871 // TODO: why would wp be NULL here?
Bram Moolenaar06e4a6d2014-06-12 11:49:46 +02005872 if (wp != NULL)
Bram Moolenaard23a8232018-02-10 18:45:26 +01005873 VIM_CLEAR(wp->w_lines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005874}
5875
5876/*
5877 * Called from win_new_shellsize() after Rows changed.
Bram Moolenaarf740b292006-02-16 22:11:02 +00005878 * This only does the current tab page, others must be done when made active.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005879 */
5880 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005881shell_new_rows(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005882{
Bram Moolenaarb7118142021-12-10 13:40:08 +00005883 int h = (int)ROWS_AVAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005884
Bram Moolenaare38eab22019-12-05 21:50:01 +01005885 if (firstwin == NULL) // not initialized yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886 return;
Bram Moolenaarb7118142021-12-10 13:40:08 +00005887 if (h < frame_minheight(topframe, NULL))
5888 h = frame_minheight(topframe, NULL);
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00005889
Bram Moolenaarb7118142021-12-10 13:40:08 +00005890 // First try setting the heights of windows with 'winfixheight'. If
5891 // that doesn't result in the right height, forget about that option.
5892 frame_new_height(topframe, h, FALSE, TRUE);
5893 if (!frame_check_height(topframe, h))
5894 frame_new_height(topframe, h, FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005895
Bram Moolenaare38eab22019-12-05 21:50:01 +01005896 (void)win_comp_pos(); // recompute w_winrow and w_wincol
Bram Moolenaar071d4272004-06-13 20:20:40 +00005897 compute_cmdrow();
Bram Moolenaarc6fe9192006-04-09 21:54:49 +00005898 curtab->tp_ch_used = p_ch;
Bram Moolenaar05159a02005-02-26 23:04:13 +00005899
Luuk van Baal13ece2a2022-10-03 15:28:08 +01005900 if (*p_spk != 'c' && !skip_win_fix_scroll)
Luuk van Baal29ab5242022-09-11 16:59:53 +01005901 win_fix_scroll(TRUE);
5902
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903#if 0
Bram Moolenaare38eab22019-12-05 21:50:01 +01005904 // Disabled: don't want making the screen smaller make a window larger.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005905 if (p_ea)
5906 win_equal(curwin, FALSE, 'v');
5907#endif
5908}
5909
Bram Moolenaar071d4272004-06-13 20:20:40 +00005910/*
5911 * Called from win_new_shellsize() after Columns changed.
5912 */
5913 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005914shell_new_columns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005915{
Bram Moolenaare38eab22019-12-05 21:50:01 +01005916 if (firstwin == NULL) // not initialized yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917 return;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00005918
Bram Moolenaarb7118142021-12-10 13:40:08 +00005919 // First try setting the widths of windows with 'winfixwidth'. If that
5920 // doesn't result in the right width, forget about that option.
5921 frame_new_width(topframe, (int)Columns, FALSE, TRUE);
5922 if (!frame_check_width(topframe, Columns))
5923 frame_new_width(topframe, (int)Columns, FALSE, FALSE);
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00005924
Bram Moolenaare38eab22019-12-05 21:50:01 +01005925 (void)win_comp_pos(); // recompute w_winrow and w_wincol
Bram Moolenaar071d4272004-06-13 20:20:40 +00005926#if 0
Bram Moolenaare38eab22019-12-05 21:50:01 +01005927 // Disabled: don't want making the screen smaller make a window larger.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005928 if (p_ea)
5929 win_equal(curwin, FALSE, 'h');
5930#endif
5931}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005932
Bram Moolenaar071d4272004-06-13 20:20:40 +00005933/*
5934 * Save the size of all windows in "gap".
5935 */
5936 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005937win_size_save(garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005938
5939{
5940 win_T *wp;
5941
Bram Moolenaar04935fb2022-01-08 16:19:22 +00005942 ga_init2(gap, sizeof(int), 1);
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00005943 if (ga_grow(gap, win_count() * 2 + 1) == FAIL)
5944 return;
Bram Moolenaar1c329c02019-10-27 20:37:35 +01005945
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00005946 // first entry is value of 'lines'
5947 ((int *)gap->ga_data)[gap->ga_len++] = Rows;
5948
5949 FOR_ALL_WINDOWS(wp)
5950 {
5951 ((int *)gap->ga_data)[gap->ga_len++] =
5952 wp->w_width + wp->w_vsep_width;
5953 ((int *)gap->ga_data)[gap->ga_len++] = wp->w_height;
Bram Moolenaar1c329c02019-10-27 20:37:35 +01005954 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005955}
5956
5957/*
Bram Moolenaar1c329c02019-10-27 20:37:35 +01005958 * Restore window sizes, but only if the number of windows is still the same
5959 * and 'lines' didn't change.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960 * Does not free the growarray.
5961 */
5962 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005963win_size_restore(garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005964{
5965 win_T *wp;
Bram Moolenaarb643e772014-07-16 15:18:26 +02005966 int i, j;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005967
Bram Moolenaar1c329c02019-10-27 20:37:35 +01005968 if (win_count() * 2 + 1 == gap->ga_len
5969 && ((int *)gap->ga_data)[0] == Rows)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005970 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01005971 // The order matters, because frames contain other frames, but it's
5972 // difficult to get right. The easy way out is to do it twice.
Bram Moolenaarb643e772014-07-16 15:18:26 +02005973 for (j = 0; j < 2; ++j)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005974 {
Bram Moolenaar1c329c02019-10-27 20:37:35 +01005975 i = 1;
Bram Moolenaar29323592016-07-24 22:04:11 +02005976 FOR_ALL_WINDOWS(wp)
Bram Moolenaarb643e772014-07-16 15:18:26 +02005977 {
5978 frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]);
5979 win_setheight_win(((int *)gap->ga_data)[i++], wp);
5980 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005981 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01005982 // recompute the window positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00005983 (void)win_comp_pos();
5984 }
5985}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005986
Bram Moolenaar071d4272004-06-13 20:20:40 +00005987/*
5988 * Update the position for all windows, using the width and height of the
5989 * frames.
5990 * Returns the row just after the last window.
5991 */
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00005992 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01005993win_comp_pos(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005994{
Bram Moolenaar32466aa2006-02-24 23:53:04 +00005995 int row = tabline_height();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005996 int col = 0;
5997
5998 frame_comp_pos(topframe, &row, &col);
5999 return row;
6000}
6001
6002/*
6003 * Update the position of the windows in frame "topfrp", using the width and
6004 * height of the frames.
6005 * "*row" and "*col" are the top-left position of the frame. They are updated
6006 * to the bottom-right position plus one.
6007 */
6008 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006009frame_comp_pos(frame_T *topfrp, int *row, int *col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006010{
6011 win_T *wp;
6012 frame_T *frp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006013 int startcol;
6014 int startrow;
Bram Moolenaar415a6932017-12-05 20:31:07 +01006015 int h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006016
6017 wp = topfrp->fr_win;
6018 if (wp != NULL)
6019 {
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006020 if (wp->w_winrow != *row || wp->w_wincol != *col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006021 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006022 // position changed, redraw
Bram Moolenaar071d4272004-06-13 20:20:40 +00006023 wp->w_winrow = *row;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006024 wp->w_wincol = *col;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01006025 redraw_win_later(wp, UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006026 wp->w_redr_status = TRUE;
6027 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01006028 // WinBar will not show if the window height is zero
Bram Moolenaar415a6932017-12-05 20:31:07 +01006029 h = VISIBLE_HEIGHT(wp) + wp->w_status_height;
6030 *row += h > topfrp->fr_height ? topfrp->fr_height : h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006031 *col += wp->w_width + wp->w_vsep_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006032 }
6033 else
6034 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006035 startrow = *row;
6036 startcol = *col;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01006037 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006038 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006039 if (topfrp->fr_layout == FR_ROW)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006040 *row = startrow; // all frames are at the same row
Bram Moolenaar071d4272004-06-13 20:20:40 +00006041 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01006042 *col = startcol; // all frames are at the same col
Bram Moolenaar071d4272004-06-13 20:20:40 +00006043 frame_comp_pos(frp, row, col);
6044 }
6045 }
6046}
6047
Bram Moolenaar071d4272004-06-13 20:20:40 +00006048/*
Bram Moolenaard0fb9072021-12-09 11:57:22 +00006049 * Make the current window show at least one line and one column.
6050 */
6051 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00006052win_ensure_size(void)
Bram Moolenaard0fb9072021-12-09 11:57:22 +00006053{
6054 if (curwin->w_height == 0)
6055 win_setheight(1);
6056 if (curwin->w_width == 0)
6057 win_setwidth(1);
6058}
6059
6060/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006061 * Set current window height and take care of repositioning other windows to
6062 * fit around it.
6063 */
6064 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006065win_setheight(int height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006066{
6067 win_setheight_win(height, curwin);
6068}
6069
6070/*
6071 * Set the window height of window "win" and take care of repositioning other
6072 * windows to fit around it.
6073 */
6074 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006075win_setheight_win(int height, win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006076{
6077 int row;
6078
6079 if (win == curwin)
6080 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006081 // Always keep current window at least one line high, even when
6082 // 'winminheight' is zero.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006083 if (height < p_wmh)
6084 height = p_wmh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006085 if (height == 0)
6086 height = 1;
Bram Moolenaar415a6932017-12-05 20:31:07 +01006087 height += WINBAR_HEIGHT(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006088 }
6089
Bram Moolenaar071d4272004-06-13 20:20:40 +00006090 frame_setheight(win->w_frame, height + win->w_status_height);
6091
Bram Moolenaare38eab22019-12-05 21:50:01 +01006092 // recompute the window positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00006093 row = win_comp_pos();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006094
6095 /*
6096 * If there is extra space created between the last window and the command
6097 * line, clear it.
6098 */
6099 if (full_screen && msg_scrolled == 0 && row < cmdline_row)
6100 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
6101 cmdline_row = row;
6102 msg_row = row;
6103 msg_col = 0;
6104
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006105 if (*p_spk != 'c')
Luuk van Baal29ab5242022-09-11 16:59:53 +01006106 win_fix_scroll(TRUE);
6107
Bram Moolenaara4d158b2022-08-14 14:17:45 +01006108 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006109}
6110
Bram Moolenaar071d4272004-06-13 20:20:40 +00006111/*
6112 * Set the height of a frame to "height" and take care that all frames and
6113 * windows inside it are resized. Also resize frames on the left and right if
6114 * the are in the same FR_ROW frame.
6115 *
6116 * Strategy:
6117 * If the frame is part of a FR_COL frame, try fitting the frame in that
6118 * frame. If that doesn't work (the FR_COL frame is too small), recursively
6119 * go to containing frames to resize them and make room.
6120 * If the frame is part of a FR_ROW frame, all frames must be resized as well.
6121 * Check for the minimal height of the FR_ROW frame.
6122 * At the top level we can also use change the command line height.
6123 */
6124 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006125frame_setheight(frame_T *curfrp, int height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006126{
Bram Moolenaare38eab22019-12-05 21:50:01 +01006127 int room; // total number of lines available
6128 int take; // number of lines taken from other windows
6129 int room_cmdline; // lines available from cmdline
Bram Moolenaar071d4272004-06-13 20:20:40 +00006130 int run;
6131 frame_T *frp;
6132 int h;
6133 int room_reserved;
6134
Bram Moolenaare38eab22019-12-05 21:50:01 +01006135 // If the height already is the desired value, nothing to do.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006136 if (curfrp->fr_height == height)
6137 return;
6138
6139 if (curfrp->fr_parent == NULL)
6140 {
Bram Moolenaarf797e302022-08-11 13:17:30 +01006141 // topframe: can only change the command line height
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006142 if (height > ROWS_AVAIL)
Shougo Matsushita0e412be2022-08-30 11:54:21 +01006143 height = ROWS_AVAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144 if (height > 0)
6145 frame_new_height(curfrp, height, FALSE, FALSE);
6146 }
6147 else if (curfrp->fr_parent->fr_layout == FR_ROW)
6148 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006149 // Row of frames: Also need to resize frames left and right of this
6150 // one. First check for the minimal height of these.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006151 h = frame_minheight(curfrp->fr_parent, NULL);
6152 if (height < h)
6153 height = h;
6154 frame_setheight(curfrp->fr_parent, height);
6155 }
6156 else
6157 {
6158 /*
6159 * Column of frames: try to change only frames in this column.
6160 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006161 /*
6162 * Do this twice:
6163 * 1: compute room available, if it's not enough try resizing the
6164 * containing frame.
6165 * 2: compute the room available and adjust the height to it.
6166 * Try not to reduce the height of a window with 'winfixheight' set.
6167 */
6168 for (run = 1; run <= 2; ++run)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006169 {
6170 room = 0;
6171 room_reserved = 0;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01006172 FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006173 {
6174 if (frp != curfrp
6175 && frp->fr_win != NULL
6176 && frp->fr_win->w_p_wfh)
6177 room_reserved += frp->fr_height;
6178 room += frp->fr_height;
6179 if (frp != curfrp)
6180 room -= frame_minheight(frp, NULL);
6181 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182 if (curfrp->fr_width != Columns)
6183 room_cmdline = 0;
6184 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006185 {
6186 room_cmdline = Rows - p_ch - (lastwin->w_winrow
Bram Moolenaar415a6932017-12-05 20:31:07 +01006187 + VISIBLE_HEIGHT(lastwin)
6188 + lastwin->w_status_height);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006189 if (room_cmdline < 0)
6190 room_cmdline = 0;
6191 }
6192
6193 if (height <= room + room_cmdline)
6194 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006195 if (run == 2 || curfrp->fr_width == Columns)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006196 {
Bram Moolenaarfe154992022-03-22 20:42:12 +00006197 height = room + room_cmdline;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006198 break;
6199 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006200 frame_setheight(curfrp->fr_parent, height
6201 + frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006202 }
6203
6204 /*
6205 * Compute the number of lines we will take from others frames (can be
6206 * negative!).
6207 */
6208 take = height - curfrp->fr_height;
6209
Bram Moolenaare38eab22019-12-05 21:50:01 +01006210 // If there is not enough room, also reduce the height of a window
6211 // with 'winfixheight' set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212 if (height > room + room_cmdline - room_reserved)
6213 room_reserved = room + room_cmdline - height;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006214 // If there is only a 'winfixheight' window and making the
6215 // window smaller, need to make the other window taller.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216 if (take < 0 && room - curfrp->fr_height < room_reserved)
6217 room_reserved = 0;
6218
6219 if (take > 0 && room_cmdline > 0)
6220 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006221 // use lines from cmdline first
Bram Moolenaar071d4272004-06-13 20:20:40 +00006222 if (take < room_cmdline)
6223 room_cmdline = take;
6224 take -= room_cmdline;
6225 topframe->fr_height += room_cmdline;
6226 }
6227
6228 /*
6229 * set the current frame to the new height
6230 */
6231 frame_new_height(curfrp, height, FALSE, FALSE);
6232
6233 /*
6234 * First take lines from the frames after the current frame. If
6235 * that is not enough, takes lines from frames above the current
6236 * frame.
6237 */
6238 for (run = 0; run < 2; ++run)
6239 {
6240 if (run == 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006241 frp = curfrp->fr_next; // 1st run: start with next window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006242 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01006243 frp = curfrp->fr_prev; // 2nd run: start with prev window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006244 while (frp != NULL && take != 0)
6245 {
6246 h = frame_minheight(frp, NULL);
6247 if (room_reserved > 0
6248 && frp->fr_win != NULL
6249 && frp->fr_win->w_p_wfh)
6250 {
6251 if (room_reserved >= frp->fr_height)
6252 room_reserved -= frp->fr_height;
6253 else
6254 {
6255 if (frp->fr_height - room_reserved > take)
6256 room_reserved = frp->fr_height - take;
6257 take -= frp->fr_height - room_reserved;
6258 frame_new_height(frp, room_reserved, FALSE, FALSE);
6259 room_reserved = 0;
6260 }
6261 }
6262 else
6263 {
6264 if (frp->fr_height - take < h)
6265 {
6266 take -= frp->fr_height - h;
6267 frame_new_height(frp, h, FALSE, FALSE);
6268 }
6269 else
6270 {
6271 frame_new_height(frp, frp->fr_height - take,
6272 FALSE, FALSE);
6273 take = 0;
6274 }
6275 }
6276 if (run == 0)
6277 frp = frp->fr_next;
6278 else
6279 frp = frp->fr_prev;
6280 }
6281 }
6282 }
6283}
6284
Bram Moolenaar071d4272004-06-13 20:20:40 +00006285/*
6286 * Set current window width and take care of repositioning other windows to
6287 * fit around it.
6288 */
6289 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006290win_setwidth(int width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291{
6292 win_setwidth_win(width, curwin);
6293}
6294
6295 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006296win_setwidth_win(int width, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006297{
Bram Moolenaare38eab22019-12-05 21:50:01 +01006298 // Always keep current window at least one column wide, even when
6299 // 'winminwidth' is zero.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006300 if (wp == curwin)
6301 {
6302 if (width < p_wmw)
6303 width = p_wmw;
6304 if (width == 0)
6305 width = 1;
6306 }
Bram Moolenaar89015a62020-12-29 12:46:51 +01006307 else if (width < 0)
6308 width = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006309
6310 frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
6311
Bram Moolenaare38eab22019-12-05 21:50:01 +01006312 // recompute the window positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00006313 (void)win_comp_pos();
6314
Bram Moolenaara4d158b2022-08-14 14:17:45 +01006315 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006316}
6317
6318/*
6319 * Set the width of a frame to "width" and take care that all frames and
6320 * windows inside it are resized. Also resize frames above and below if the
6321 * are in the same FR_ROW frame.
6322 *
6323 * Strategy is similar to frame_setheight().
6324 */
6325 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006326frame_setwidth(frame_T *curfrp, int width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327{
Bram Moolenaare38eab22019-12-05 21:50:01 +01006328 int room; // total number of lines available
6329 int take; // number of lines taken from other windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330 int run;
6331 frame_T *frp;
6332 int w;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006333 int room_reserved;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006334
Bram Moolenaare38eab22019-12-05 21:50:01 +01006335 // If the width already is the desired value, nothing to do.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 if (curfrp->fr_width == width)
6337 return;
6338
6339 if (curfrp->fr_parent == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006340 // topframe: can't change width
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341 return;
6342
6343 if (curfrp->fr_parent->fr_layout == FR_COL)
6344 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006345 // Column of frames: Also need to resize frames above and below of
6346 // this one. First check for the minimal width of these.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347 w = frame_minwidth(curfrp->fr_parent, NULL);
6348 if (width < w)
6349 width = w;
6350 frame_setwidth(curfrp->fr_parent, width);
6351 }
6352 else
6353 {
6354 /*
6355 * Row of frames: try to change only frames in this row.
6356 *
6357 * Do this twice:
6358 * 1: compute room available, if it's not enough try resizing the
6359 * containing frame.
6360 * 2: compute the room available and adjust the width to it.
6361 */
6362 for (run = 1; run <= 2; ++run)
6363 {
6364 room = 0;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006365 room_reserved = 0;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01006366 FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367 {
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006368 if (frp != curfrp
6369 && frp->fr_win != NULL
6370 && frp->fr_win->w_p_wfw)
6371 room_reserved += frp->fr_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006372 room += frp->fr_width;
6373 if (frp != curfrp)
6374 room -= frame_minwidth(frp, NULL);
6375 }
6376
6377 if (width <= room)
6378 break;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006379 if (run == 2 || curfrp->fr_height >= ROWS_AVAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006380 {
Bram Moolenaarfe154992022-03-22 20:42:12 +00006381 width = room;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382 break;
6383 }
6384 frame_setwidth(curfrp->fr_parent, width
6385 + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1);
6386 }
6387
Bram Moolenaar071d4272004-06-13 20:20:40 +00006388 /*
6389 * Compute the number of lines we will take from others frames (can be
6390 * negative!).
6391 */
6392 take = width - curfrp->fr_width;
6393
Bram Moolenaare38eab22019-12-05 21:50:01 +01006394 // If there is not enough room, also reduce the width of a window
6395 // with 'winfixwidth' set.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006396 if (width > room - room_reserved)
6397 room_reserved = room - width;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006398 // If there is only a 'winfixwidth' window and making the
6399 // window smaller, need to make the other window narrower.
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006400 if (take < 0 && room - curfrp->fr_width < room_reserved)
6401 room_reserved = 0;
6402
Bram Moolenaar071d4272004-06-13 20:20:40 +00006403 /*
6404 * set the current frame to the new width
6405 */
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006406 frame_new_width(curfrp, width, FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006407
6408 /*
6409 * First take lines from the frames right of the current frame. If
6410 * that is not enough, takes lines from frames left of the current
6411 * frame.
6412 */
6413 for (run = 0; run < 2; ++run)
6414 {
6415 if (run == 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006416 frp = curfrp->fr_next; // 1st run: start with next window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006417 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01006418 frp = curfrp->fr_prev; // 2nd run: start with prev window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006419 while (frp != NULL && take != 0)
6420 {
6421 w = frame_minwidth(frp, NULL);
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006422 if (room_reserved > 0
6423 && frp->fr_win != NULL
6424 && frp->fr_win->w_p_wfw)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006425 {
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006426 if (room_reserved >= frp->fr_width)
6427 room_reserved -= frp->fr_width;
6428 else
6429 {
6430 if (frp->fr_width - room_reserved > take)
6431 room_reserved = frp->fr_width - take;
6432 take -= frp->fr_width - room_reserved;
6433 frame_new_width(frp, room_reserved, FALSE, FALSE);
6434 room_reserved = 0;
6435 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006436 }
6437 else
6438 {
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006439 if (frp->fr_width - take < w)
6440 {
6441 take -= frp->fr_width - w;
6442 frame_new_width(frp, w, FALSE, FALSE);
6443 }
6444 else
6445 {
6446 frame_new_width(frp, frp->fr_width - take,
6447 FALSE, FALSE);
6448 take = 0;
6449 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450 }
6451 if (run == 0)
6452 frp = frp->fr_next;
6453 else
6454 frp = frp->fr_prev;
6455 }
6456 }
6457 }
6458}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459
6460/*
Bram Moolenaar1c3c1042018-06-12 16:49:30 +02006461 * Check 'winminheight' for a valid value and reduce it if needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462 */
6463 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006464win_setminheight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006465{
6466 int room;
Bram Moolenaar1c3c1042018-06-12 16:49:30 +02006467 int needed;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006468 int first = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006469
Bram Moolenaar1c3c1042018-06-12 16:49:30 +02006470 // loop until there is a 'winminheight' that is possible
Bram Moolenaar071d4272004-06-13 20:20:40 +00006471 while (p_wmh > 0)
6472 {
Bram Moolenaar9e813b32021-03-13 14:29:05 +01006473 room = Rows - p_ch;
Bram Moolenaara2a89732022-08-31 14:46:18 +01006474 needed = min_rows() - 1; // 1 was added for the cmdline
Bram Moolenaar1c3c1042018-06-12 16:49:30 +02006475 if (room >= needed)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006476 break;
6477 --p_wmh;
6478 if (first)
6479 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02006480 emsg(_(e_not_enough_room));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006481 first = FALSE;
6482 }
6483 }
6484}
6485
Bram Moolenaar1c3c1042018-06-12 16:49:30 +02006486/*
6487 * Check 'winminwidth' for a valid value and reduce it if needed.
6488 */
6489 void
6490win_setminwidth(void)
6491{
6492 int room;
6493 int needed;
6494 int first = TRUE;
6495
6496 // loop until there is a 'winminheight' that is possible
6497 while (p_wmw > 0)
6498 {
6499 room = Columns;
6500 needed = frame_minwidth(topframe, NULL);
6501 if (room >= needed)
6502 break;
6503 --p_wmw;
6504 if (first)
6505 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02006506 emsg(_(e_not_enough_room));
Bram Moolenaar1c3c1042018-06-12 16:49:30 +02006507 first = FALSE;
6508 }
6509 }
6510}
6511
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512/*
6513 * Status line of dragwin is dragged "offset" lines down (negative is up).
6514 */
6515 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006516win_drag_status_line(win_T *dragwin, int offset)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006517{
6518 frame_T *curfr;
6519 frame_T *fr;
6520 int room;
6521 int row;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006522 int up; // if TRUE, drag status line up, otherwise down
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523 int n;
6524
6525 fr = dragwin->w_frame;
6526 curfr = fr;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006527 if (fr != topframe) // more than one window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528 {
6529 fr = fr->fr_parent;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006530 // When the parent frame is not a column of frames, its parent should
6531 // be.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006532 if (fr->fr_layout != FR_COL)
6533 {
6534 curfr = fr;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006535 if (fr != topframe) // only a row of windows, may drag statusline
Bram Moolenaar071d4272004-06-13 20:20:40 +00006536 fr = fr->fr_parent;
6537 }
6538 }
6539
Bram Moolenaare38eab22019-12-05 21:50:01 +01006540 // If this is the last frame in a column, may want to resize the parent
6541 // frame instead (go two up to skip a row of frames).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542 while (curfr != topframe && curfr->fr_next == NULL)
6543 {
6544 if (fr != topframe)
6545 fr = fr->fr_parent;
6546 curfr = fr;
6547 if (fr != topframe)
6548 fr = fr->fr_parent;
6549 }
6550
Bram Moolenaare38eab22019-12-05 21:50:01 +01006551 if (offset < 0) // drag up
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552 {
6553 up = TRUE;
6554 offset = -offset;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006555 // sum up the room of the current frame and above it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556 if (fr == curfr)
6557 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006558 // only one window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006559 room = fr->fr_height - frame_minheight(fr, NULL);
6560 }
6561 else
6562 {
6563 room = 0;
6564 for (fr = fr->fr_child; ; fr = fr->fr_next)
6565 {
6566 room += fr->fr_height - frame_minheight(fr, NULL);
6567 if (fr == curfr)
6568 break;
6569 }
6570 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01006571 fr = curfr->fr_next; // put fr at frame that grows
Bram Moolenaar071d4272004-06-13 20:20:40 +00006572 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01006573 else // drag down
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574 {
6575 up = FALSE;
6576 /*
6577 * Only dragging the last status line can reduce p_ch.
6578 */
6579 room = Rows - cmdline_row;
Bram Moolenaara2a89732022-08-31 14:46:18 +01006580 if (curfr->fr_next == NULL)
Bram Moolenaarf797e302022-08-11 13:17:30 +01006581 --room;
Bram Moolenaara2a89732022-08-31 14:46:18 +01006582 else
6583 room -= p_ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584 if (room < 0)
6585 room = 0;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006586 // sum up the room of frames below of the current one
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01006587 FOR_ALL_FRAMES(fr, curfr->fr_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006588 room += fr->fr_height - frame_minheight(fr, NULL);
Bram Moolenaare38eab22019-12-05 21:50:01 +01006589 fr = curfr; // put fr at window that grows
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590 }
6591
Bram Moolenaare38eab22019-12-05 21:50:01 +01006592 if (room < offset) // Not enough room
6593 offset = room; // Move as far as we can
Bram Moolenaar071d4272004-06-13 20:20:40 +00006594 if (offset <= 0)
6595 return;
6596
6597 /*
6598 * Grow frame fr by "offset" lines.
6599 * Doesn't happen when dragging the last status line up.
6600 */
6601 if (fr != NULL)
6602 frame_new_height(fr, fr->fr_height + offset, up, FALSE);
6603
6604 if (up)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006605 fr = curfr; // current frame gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00006606 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01006607 fr = curfr->fr_next; // next frame gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608
6609 /*
6610 * Now make the other frames smaller.
6611 */
6612 while (fr != NULL && offset > 0)
6613 {
6614 n = frame_minheight(fr, NULL);
6615 if (fr->fr_height - offset <= n)
6616 {
6617 offset -= fr->fr_height - n;
6618 frame_new_height(fr, n, !up, FALSE);
6619 }
6620 else
6621 {
6622 frame_new_height(fr, fr->fr_height - offset, !up, FALSE);
6623 break;
6624 }
6625 if (up)
6626 fr = fr->fr_prev;
6627 else
6628 fr = fr->fr_next;
6629 }
6630 row = win_comp_pos();
6631 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
6632 cmdline_row = row;
Bram Moolenaara2a89732022-08-31 14:46:18 +01006633 p_ch = MAX(Rows - cmdline_row, 1);
Bram Moolenaarc6fe9192006-04-09 21:54:49 +00006634 curtab->tp_ch_used = p_ch;
Bram Moolenaara2a89732022-08-31 14:46:18 +01006635
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006636 if (*p_spk != 'c')
Luuk van Baal29ab5242022-09-11 16:59:53 +01006637 win_fix_scroll(TRUE);
6638
Bram Moolenaara4d158b2022-08-14 14:17:45 +01006639 redraw_all_later(UPD_SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006640 showmode();
6641}
6642
Bram Moolenaar071d4272004-06-13 20:20:40 +00006643/*
6644 * Separator line of dragwin is dragged "offset" lines right (negative is left).
6645 */
6646 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006647win_drag_vsep_line(win_T *dragwin, int offset)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006648{
6649 frame_T *curfr;
6650 frame_T *fr;
6651 int room;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006652 int left; // if TRUE, drag separator line left, otherwise right
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653 int n;
6654
6655 fr = dragwin->w_frame;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006656 if (fr == topframe) // only one window (cannot happen?)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006657 return;
6658 curfr = fr;
6659 fr = fr->fr_parent;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006660 // When the parent frame is not a row of frames, its parent should be.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006661 if (fr->fr_layout != FR_ROW)
6662 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006663 if (fr == topframe) // only a column of windows (cannot happen?)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006664 return;
6665 curfr = fr;
6666 fr = fr->fr_parent;
6667 }
6668
Bram Moolenaare38eab22019-12-05 21:50:01 +01006669 // If this is the last frame in a row, may want to resize a parent
6670 // frame instead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006671 while (curfr->fr_next == NULL)
6672 {
6673 if (fr == topframe)
6674 break;
6675 curfr = fr;
6676 fr = fr->fr_parent;
6677 if (fr != topframe)
6678 {
6679 curfr = fr;
6680 fr = fr->fr_parent;
6681 }
6682 }
6683
Bram Moolenaare38eab22019-12-05 21:50:01 +01006684 if (offset < 0) // drag left
Bram Moolenaar071d4272004-06-13 20:20:40 +00006685 {
6686 left = TRUE;
6687 offset = -offset;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006688 // sum up the room of the current frame and left of it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006689 room = 0;
6690 for (fr = fr->fr_child; ; fr = fr->fr_next)
6691 {
6692 room += fr->fr_width - frame_minwidth(fr, NULL);
6693 if (fr == curfr)
6694 break;
6695 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01006696 fr = curfr->fr_next; // put fr at frame that grows
Bram Moolenaar071d4272004-06-13 20:20:40 +00006697 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01006698 else // drag right
Bram Moolenaar071d4272004-06-13 20:20:40 +00006699 {
6700 left = FALSE;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006701 // sum up the room of frames right of the current one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006702 room = 0;
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01006703 FOR_ALL_FRAMES(fr, curfr->fr_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006704 room += fr->fr_width - frame_minwidth(fr, NULL);
Bram Moolenaare38eab22019-12-05 21:50:01 +01006705 fr = curfr; // put fr at window that grows
Bram Moolenaar071d4272004-06-13 20:20:40 +00006706 }
6707
Bram Moolenaare38eab22019-12-05 21:50:01 +01006708 if (room < offset) // Not enough room
6709 offset = room; // Move as far as we can
6710 if (offset <= 0) // No room at all, quit.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006711 return;
Bram Moolenaar294a7e52015-11-22 19:39:38 +01006712 if (fr == NULL)
zeertzjqa0c4e2f2022-01-29 10:59:53 +00006713 // This can happen when calling win_move_separator() on the rightmost
6714 // window. Just don't do anything.
Martin Tournoijba43e762022-10-13 22:12:15 +01006715 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006716
Bram Moolenaare38eab22019-12-05 21:50:01 +01006717 // grow frame fr by offset lines
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006718 frame_new_width(fr, fr->fr_width + offset, left, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006719
Bram Moolenaare38eab22019-12-05 21:50:01 +01006720 // shrink other frames: current and at the left or at the right
Bram Moolenaar071d4272004-06-13 20:20:40 +00006721 if (left)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006722 fr = curfr; // current frame gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00006723 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01006724 fr = curfr->fr_next; // next frame gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00006725
6726 while (fr != NULL && offset > 0)
6727 {
6728 n = frame_minwidth(fr, NULL);
6729 if (fr->fr_width - offset <= n)
6730 {
6731 offset -= fr->fr_width - n;
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006732 frame_new_width(fr, n, !left, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006733 }
6734 else
6735 {
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00006736 frame_new_width(fr, fr->fr_width - offset, !left, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006737 break;
6738 }
6739 if (left)
6740 fr = fr->fr_prev;
6741 else
6742 fr = fr->fr_next;
6743 }
6744 (void)win_comp_pos();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01006745 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006746}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006747
Bram Moolenaar0215e8e2010-12-17 17:35:10 +01006748#define FRACTION_MULT 16384L
6749
6750/*
6751 * Set wp->w_fraction for the current w_wrow and w_height.
Bram Moolenaar3679c172017-11-22 22:22:11 +01006752 * Has no effect when the window is less than two lines.
Bram Moolenaar0215e8e2010-12-17 17:35:10 +01006753 */
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01006754 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006755set_fraction(win_T *wp)
Bram Moolenaar0215e8e2010-12-17 17:35:10 +01006756{
Bram Moolenaar3679c172017-11-22 22:22:11 +01006757 if (wp->w_height > 1)
Bram Moolenaar8fcb60f2019-03-04 13:18:30 +01006758 // When cursor is in the first line the percentage is computed as if
6759 // it's halfway that line. Thus with two lines it is 25%, with three
6760 // lines 17%, etc. Similarly for the last line: 75%, 83%, etc.
Bram Moolenaar3679c172017-11-22 22:22:11 +01006761 wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT
Bram Moolenaar8fcb60f2019-03-04 13:18:30 +01006762 + FRACTION_MULT / 2) / (long)wp->w_height;
Bram Moolenaar0215e8e2010-12-17 17:35:10 +01006763}
6764
Bram Moolenaar071d4272004-06-13 20:20:40 +00006765/*
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006766 * Handle scroll position for 'splitkeep'. Replaces scroll_to_fraction()
Luuk van Baal29ab5242022-09-11 16:59:53 +01006767 * call from win_new_height(). Instead we iterate over all windows in a
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006768 * tabpage and calculate the new scroll position.
Luuk van Baal29ab5242022-09-11 16:59:53 +01006769 * TODO: Ensure this also works with wrapped lines.
6770 * Requires topline to be able to be set to a bufferline with some
6771 * offset(row-wise scrolling/smoothscroll).
6772 */
6773 static void
6774win_fix_scroll(int resize)
6775{
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006776 int diff;
6777 win_T *wp;
6778 linenr_T lnum;
Luuk van Baal29ab5242022-09-11 16:59:53 +01006779
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006780 skip_update_topline = TRUE;
Luuk van Baal29ab5242022-09-11 16:59:53 +01006781 FOR_ALL_WINDOWS(wp)
6782 {
Luuk van Baalfaf1d412022-09-19 16:45:29 +01006783 // Skip when window height has not changed.
Luuk van Baald5bc7622022-09-17 16:16:35 +01006784 if (wp->w_height != wp->w_prev_height)
Luuk van Baal29ab5242022-09-11 16:59:53 +01006785 {
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006786 // If window has moved update botline to keep the same screenlines.
Luuk van Baal346823d2022-10-05 18:26:42 +01006787 if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow
6788 && wp->w_botline - 1 <= wp->w_buffer->b_ml.ml_line_count)
Luuk van Baal29ab5242022-09-11 16:59:53 +01006789 {
6790 lnum = wp->w_cursor.lnum;
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006791 diff = (wp->w_winrow - wp->w_prev_winrow)
6792 + (wp->w_height - wp->w_prev_height);
6793 wp->w_cursor.lnum = wp->w_botline - 1;
6794 // Add difference in height and row to botline.
6795 if (diff > 0)
6796 cursor_down_inner(wp, diff);
6797 else
6798 cursor_up_inner(wp, -diff);
Luuk van Baal29ab5242022-09-11 16:59:53 +01006799 // Bring the new cursor position to the bottom of the screen.
6800 wp->w_fraction = FRACTION_MULT;
6801 scroll_to_fraction(wp, wp->w_prev_height);
6802 wp->w_cursor.lnum = lnum;
6803 }
Luuk van Baal20e58562022-09-23 12:57:09 +01006804 else if (wp == curwin)
6805 wp->w_valid &= ~VALID_CROW;
Luuk van Baal29ab5242022-09-11 16:59:53 +01006806 invalidate_botline_win(wp);
6807 validate_botline_win(wp);
6808 }
6809 wp->w_prev_height = wp->w_height;
6810 wp->w_prev_winrow = wp->w_winrow;
6811 }
Luuk van Baalfaf1d412022-09-19 16:45:29 +01006812 skip_update_topline = FALSE;
Luuk van Baal29ab5242022-09-11 16:59:53 +01006813 // Ensure cursor is valid when not in normal mode or when resized.
Luuk van Baalfaf1d412022-09-19 16:45:29 +01006814 if (!(get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL)))
Luuk van Baal29ab5242022-09-11 16:59:53 +01006815 win_fix_cursor(FALSE);
6816 else if (resize)
6817 win_fix_cursor(TRUE);
6818}
6819
6820/*
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006821 * Make sure the cursor position is valid for 'splitkeep'.
Luuk van Baal29ab5242022-09-11 16:59:53 +01006822 * If it is not, put the cursor position in the jumplist and move it.
Luuk van Baalbc3dc292023-02-15 16:45:27 +00006823 * If we are not in normal mode ("normal" is zero), make it valid by scrolling
6824 * instead.
Luuk van Baal29ab5242022-09-11 16:59:53 +01006825 */
6826 static void
6827win_fix_cursor(int normal)
6828{
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006829 long so = get_scrolloff_value();
6830 win_T *wp = curwin;
6831 linenr_T nlnum = 0;
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006832 linenr_T lnum = wp->w_cursor.lnum;
6833 linenr_T bot;
6834 linenr_T top;
Luuk van Baal29ab5242022-09-11 16:59:53 +01006835
6836 if (wp->w_buffer->b_ml.ml_line_count < wp->w_height)
6837 return;
Luuk van Baal3735f112022-09-15 12:43:26 +01006838 if (skip_win_fix_cursor)
6839 return;
Martin Tournoij7904fa42022-10-04 16:28:45 +01006840
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006841 // Determine valid cursor range.
Luuk van Baal29ab5242022-09-11 16:59:53 +01006842 so = MIN(wp->w_height / 2, so);
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006843 wp->w_cursor.lnum = wp->w_topline;
6844 top = cursor_down_inner(wp, so);
6845 wp->w_cursor.lnum = wp->w_botline - 1;
6846 bot = cursor_up_inner(wp, so);
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006847 wp->w_cursor.lnum = lnum;
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006848 // Check if cursor position is above or below valid cursor range.
6849 if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
6850 nlnum = bot;
6851 else if (lnum < top && wp->w_topline != 1)
6852 nlnum = (so == wp->w_height / 2) ? bot : top;
6853
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006854 if (nlnum) // Cursor is invalid for current scroll position.
Luuk van Baal29ab5242022-09-11 16:59:53 +01006855 {
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006856 if (normal) // Save to jumplist and set cursor to avoid scrolling.
6857 {
6858 setmark('\'');
6859 wp->w_cursor.lnum = nlnum;
Luuk van Baal29ab5242022-09-11 16:59:53 +01006860 }
Luuk van Baal7c1cbb62022-09-27 12:31:15 +01006861 else // Scroll instead when not in normal mode.
6862 {
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006863 wp->w_fraction = (nlnum == bot) ? FRACTION_MULT : 0;
Luuk van Baal29ab5242022-09-11 16:59:53 +01006864 scroll_to_fraction(wp, wp->w_prev_height);
Luuk van Baalfaf1d412022-09-19 16:45:29 +01006865 validate_botline_win(curwin);
Luuk van Baal29ab5242022-09-11 16:59:53 +01006866 }
6867 }
6868}
6869
6870/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871 * Set the height of a window.
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02006872 * "height" excludes any window toolbar.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873 * This takes care of the things inside the window, not what happens to the
6874 * window position, the frame or to other windows.
6875 */
Bram Moolenaar6763c142012-07-19 18:05:44 +02006876 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01006877win_new_height(win_T *wp, int height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006878{
Bram Moolenaar56b3bf82014-05-07 20:25:35 +02006879 int prev_height = wp->w_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006880
Bram Moolenaare38eab22019-12-05 21:50:01 +01006881 // Don't want a negative height. Happens when splitting a tiny window.
6882 // Will equalize heights soon to fix it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006883 if (height < 0)
6884 height = 0;
Bram Moolenaar4c3f5362006-04-11 21:38:50 +00006885 if (wp->w_height == height)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006886 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00006887
Bram Moolenaar56b3bf82014-05-07 20:25:35 +02006888 if (wp->w_height > 0)
6889 {
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006890 if (wp == curwin && *p_spk == 'c')
Bram Moolenaare38eab22019-12-05 21:50:01 +01006891 // w_wrow needs to be valid. When setting 'laststatus' this may
6892 // call win_new_height() recursively.
Bram Moolenaar0ae36a52014-06-13 20:08:45 +02006893 validate_cursor();
6894 if (wp->w_height != prev_height)
Bram Moolenaare38eab22019-12-05 21:50:01 +01006895 return; // Recursive call already changed the size, bail out here
6896 // to avoid the following to mess things up.
Bram Moolenaar56b3bf82014-05-07 20:25:35 +02006897 if (wp->w_wrow != wp->w_prev_fraction_row)
6898 set_fraction(wp);
6899 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006900
6901 wp->w_height = height;
Luuk van Baal74a694d2022-11-28 16:49:36 +00006902 wp->w_redr_status = TRUE;
Luuk van Baala1a46da2022-10-17 14:22:03 +01006903 win_comp_scroll(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006904
Bram Moolenaare38eab22019-12-05 21:50:01 +01006905 // There is no point in adjusting the scroll position when exiting. Some
6906 // values might be invalid.
Luuk van Baal13ece2a2022-10-03 15:28:08 +01006907 if (!exiting && *p_spk == 'c')
Luuk van Baalb926bf42023-05-06 12:53:50 +01006908 {
6909 wp->w_skipcol = 0;
Bram Moolenaar955f1982017-02-05 15:10:51 +01006910 scroll_to_fraction(wp, prev_height);
Luuk van Baalb926bf42023-05-06 12:53:50 +01006911 }
Bram Moolenaar46328f92016-08-28 15:39:57 +02006912}
6913
6914 void
6915scroll_to_fraction(win_T *wp, int prev_height)
6916{
6917 linenr_T lnum;
6918 int sline, line_size;
6919 int height = wp->w_height;
6920
Bram Moolenaara9b25352019-05-12 14:25:30 +02006921 // Don't change w_topline in any of these cases:
6922 // - window height is 0
6923 // - 'scrollbind' is set and this isn't the current window
Bram Moolenaarbd2d68c2019-05-18 15:36:11 +02006924 // - window height is sufficient to display the whole buffer and first line
6925 // is visible.
Bram Moolenaara9b25352019-05-12 14:25:30 +02006926 if (height > 0
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01006927 && (!wp->w_p_scb || wp == curwin)
6928 && (height < wp->w_buffer->b_ml.ml_line_count || wp->w_topline > 1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006929 {
Bram Moolenaar34114692005-01-02 11:28:13 +00006930 /*
6931 * Find a value for w_topline that shows the cursor at the same
6932 * relative position in the window as before (more or less).
6933 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006934 lnum = wp->w_cursor.lnum;
Bram Moolenaare38eab22019-12-05 21:50:01 +01006935 if (lnum < 1) // can happen when starting up
Bram Moolenaar071d4272004-06-13 20:20:40 +00006936 lnum = 1;
Bram Moolenaar8fcb60f2019-03-04 13:18:30 +01006937 wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L)
6938 / FRACTION_MULT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006939 line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
6940 sline = wp->w_wrow - line_size;
Bram Moolenaar26470632006-10-24 19:12:40 +00006941
6942 if (sline >= 0)
6943 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006944 // Make sure the whole cursor line is visible, if possible.
Bram Moolenaar26470632006-10-24 19:12:40 +00006945 int rows = plines_win(wp, lnum, FALSE);
6946
6947 if (sline > wp->w_height - rows)
6948 {
6949 sline = wp->w_height - rows;
6950 wp->w_wrow -= rows - line_size;
6951 }
6952 }
6953
Bram Moolenaar071d4272004-06-13 20:20:40 +00006954 if (sline < 0)
6955 {
6956 /*
6957 * Cursor line would go off top of screen if w_wrow was this high.
Bram Moolenaar26470632006-10-24 19:12:40 +00006958 * Make cursor line the first line in the window. If not enough
6959 * room use w_skipcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960 */
6961 wp->w_wrow = line_size;
Bram Moolenaar26470632006-10-24 19:12:40 +00006962 if (wp->w_wrow >= wp->w_height
Bram Moolenaar02631462017-09-22 15:20:32 +02006963 && (wp->w_width - win_col_off(wp)) > 0)
Bram Moolenaar26470632006-10-24 19:12:40 +00006964 {
Bram Moolenaar02631462017-09-22 15:20:32 +02006965 wp->w_skipcol += wp->w_width - win_col_off(wp);
Bram Moolenaar26470632006-10-24 19:12:40 +00006966 --wp->w_wrow;
6967 while (wp->w_wrow >= wp->w_height)
6968 {
Bram Moolenaar02631462017-09-22 15:20:32 +02006969 wp->w_skipcol += wp->w_width - win_col_off(wp)
Bram Moolenaar26470632006-10-24 19:12:40 +00006970 + win_col_off2(wp);
6971 --wp->w_wrow;
6972 }
6973 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006974 }
Bram Moolenaardd0402a2014-05-28 13:43:04 +02006975 else if (sline > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006976 {
Bram Moolenaar26470632006-10-24 19:12:40 +00006977 while (sline > 0 && lnum > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006978 {
6979#ifdef FEAT_FOLDING
6980 hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
6981 if (lnum == 1)
6982 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01006983 // first line in buffer is folded
Bram Moolenaar071d4272004-06-13 20:20:40 +00006984 line_size = 1;
6985 --sline;
6986 break;
6987 }
6988#endif
6989 --lnum;
6990#ifdef FEAT_DIFF
6991 if (lnum == wp->w_topline)
6992 line_size = plines_win_nofill(wp, lnum, TRUE)
6993 + wp->w_topfill;
6994 else
6995#endif
6996 line_size = plines_win(wp, lnum, TRUE);
6997 sline -= line_size;
6998 }
Bram Moolenaar34114692005-01-02 11:28:13 +00006999
Bram Moolenaar071d4272004-06-13 20:20:40 +00007000 if (sline < 0)
7001 {
7002 /*
7003 * Line we want at top would go off top of screen. Use next
7004 * line instead.
7005 */
7006#ifdef FEAT_FOLDING
7007 hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL);
7008#endif
7009 lnum++;
7010 wp->w_wrow -= line_size + sline;
7011 }
Bram Moolenaardd0402a2014-05-28 13:43:04 +02007012 else if (sline > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007013 {
Bram Moolenaar8fcb60f2019-03-04 13:18:30 +01007014 // First line of file reached, use that as topline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007015 lnum = 1;
7016 wp->w_wrow -= sline;
7017 }
7018 }
Bram Moolenaar8fcb60f2019-03-04 13:18:30 +01007019 set_topline(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007020 }
7021
7022 if (wp == curwin)
7023 {
Luuk van Baal13ece2a2022-10-03 15:28:08 +01007024 if (get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025 update_topline();
Bram Moolenaare38eab22019-12-05 21:50:01 +01007026 curs_columns(FALSE); // validate w_wrow
Bram Moolenaar071d4272004-06-13 20:20:40 +00007027 }
Bram Moolenaar56b3bf82014-05-07 20:25:35 +02007028 if (prev_height > 0)
7029 wp->w_prev_fraction_row = wp->w_wrow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007030
Bram Moolenaara4d158b2022-08-14 14:17:45 +01007031 redraw_win_later(wp, UPD_SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007032 invalidate_botline_win(wp);
7033}
7034
Bram Moolenaar071d4272004-06-13 20:20:40 +00007035/*
7036 * Set the width of a window.
7037 */
Bram Moolenaar6763c142012-07-19 18:05:44 +02007038 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007039win_new_width(win_T *wp, int width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007040{
Bram Moolenaar8279af52022-09-26 23:08:22 +01007041 // Should we give an error if width < 0?
7042 wp->w_width = width < 0 ? 0 : width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007043 wp->w_lines_valid = 0;
7044 changed_line_abv_curs_win(wp);
Luuk van Baal13ece2a2022-10-03 15:28:08 +01007045 invalidate_botline_win(wp);
7046 if (wp == curwin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007047 {
Luuk van Baal13ece2a2022-10-03 15:28:08 +01007048 skip_update_topline = (*p_spk != 'c');
7049 update_topline();
7050 curs_columns(TRUE); // validate w_wrow
7051 skip_update_topline = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007052 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01007053 redraw_win_later(wp, UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007054 wp->w_redr_status = TRUE;
7055}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007056
7057 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007058win_comp_scroll(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007059{
Bram Moolenaar74667062020-12-28 15:41:41 +01007060#if defined(FEAT_EVAL)
7061 int old_w_p_scr = wp->w_p_scr;
7062#endif
7063
Bram Moolenaar071d4272004-06-13 20:20:40 +00007064 wp->w_p_scr = ((unsigned)wp->w_height >> 1);
7065 if (wp->w_p_scr == 0)
7066 wp->w_p_scr = 1;
Bram Moolenaar74667062020-12-28 15:41:41 +01007067#if defined(FEAT_EVAL)
7068 if (wp->w_p_scr != old_w_p_scr)
7069 {
7070 // Used by "verbose set scroll".
7071 wp->w_p_script_ctx[WV_SCROLL].sc_sid = SID_WINLAYOUT;
7072 wp->w_p_script_ctx[WV_SCROLL].sc_lnum = 0;
7073 }
7074#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007075}
7076
7077/*
Bram Moolenaar0816f472022-10-05 15:42:32 +01007078 * Command_height: called whenever p_ch has been changed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007079 */
7080 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007081command_height(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007082{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007083 int h;
7084 frame_T *frp;
Bram Moolenaarc6fe9192006-04-09 21:54:49 +00007085 int old_p_ch = curtab->tp_ch_used;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007086
Bram Moolenaare38eab22019-12-05 21:50:01 +01007087 // Use the value of p_ch that we remembered. This is needed for when the
7088 // GUI starts up, we can't be sure in what order things happen. And when
7089 // p_ch was changed in another tab page.
Bram Moolenaarc6fe9192006-04-09 21:54:49 +00007090 curtab->tp_ch_used = p_ch;
Bram Moolenaar05159a02005-02-26 23:04:13 +00007091
Bram Moolenaarf797e302022-08-11 13:17:30 +01007092 // If the space for the command line is already more than 'cmdheight' there
7093 // is nothing to do (window size must have decreased).
7094 if (p_ch > old_p_ch && cmdline_row <= Rows - p_ch)
7095 return;
7096
Bram Moolenaar0816f472022-10-05 15:42:32 +01007097 // Update cmdline_row to what it should be: just below the last window.
Bram Moolenaarc9f5f732022-10-06 11:39:06 +01007098 cmdline_row = topframe->fr_height + tabline_height();
Bram Moolenaar0816f472022-10-05 15:42:32 +01007099
Bram Moolenaard4cf9fc2022-08-11 14:13:37 +01007100 // If cmdline_row is smaller than what it is supposed to be for 'cmdheight'
7101 // then set old_p_ch to what it would be, so that the windows get resized
7102 // properly for the new value.
7103 if (cmdline_row < Rows - p_ch)
7104 old_p_ch = Rows - cmdline_row;
7105
Bram Moolenaare38eab22019-12-05 21:50:01 +01007106 // Find bottom frame with width of screen.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007107 frp = lastwin->w_frame;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007108 while (frp->fr_width != Columns && frp->fr_parent != NULL)
7109 frp = frp->fr_parent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110
Bram Moolenaare38eab22019-12-05 21:50:01 +01007111 // Avoid changing the height of a window with 'winfixheight' set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007112 while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF
7113 && frp->fr_win->w_p_wfh)
7114 frp = frp->fr_prev;
7115
7116 if (starting != NO_SCREEN)
7117 {
7118 cmdline_row = Rows - p_ch;
7119
Bram Moolenaare38eab22019-12-05 21:50:01 +01007120 if (p_ch > old_p_ch) // p_ch got bigger
Bram Moolenaar071d4272004-06-13 20:20:40 +00007121 {
7122 while (p_ch > old_p_ch)
7123 {
7124 if (frp == NULL)
7125 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02007126 emsg(_(e_not_enough_room));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007127 p_ch = old_p_ch;
Bram Moolenaar719939c2007-09-25 12:51:28 +00007128 curtab->tp_ch_used = p_ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007129 cmdline_row = Rows - p_ch;
7130 break;
7131 }
7132 h = frp->fr_height - frame_minheight(frp, NULL);
7133 if (h > p_ch - old_p_ch)
7134 h = p_ch - old_p_ch;
7135 old_p_ch += h;
7136 frame_add_height(frp, -h);
7137 frp = frp->fr_prev;
7138 }
7139
Bram Moolenaare38eab22019-12-05 21:50:01 +01007140 // Recompute window positions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007141 (void)win_comp_pos();
7142
Bram Moolenaare38eab22019-12-05 21:50:01 +01007143 // clear the lines added to cmdline
Bram Moolenaar071d4272004-06-13 20:20:40 +00007144 if (full_screen)
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00007145 screen_fill(cmdline_row, (int)Rows, 0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007146 (int)Columns, ' ', ' ', 0);
7147 msg_row = cmdline_row;
7148 redraw_cmdline = TRUE;
7149 return;
7150 }
7151
7152 if (msg_row < cmdline_row)
7153 msg_row = cmdline_row;
7154 redraw_cmdline = TRUE;
7155 }
7156 frame_add_height(frp, (int)(old_p_ch - p_ch));
7157
Bram Moolenaare38eab22019-12-05 21:50:01 +01007158 // Recompute window positions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007159 if (frp != lastwin->w_frame)
7160 (void)win_comp_pos();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007161}
7162
Bram Moolenaar071d4272004-06-13 20:20:40 +00007163/*
7164 * Resize frame "frp" to be "n" lines higher (negative for less high).
7165 * Also resize the frames it is contained in.
7166 */
7167 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007168frame_add_height(frame_T *frp, int n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007169{
7170 frame_new_height(frp, frp->fr_height + n, FALSE, FALSE);
7171 for (;;)
7172 {
7173 frp = frp->fr_parent;
7174 if (frp == NULL)
7175 break;
7176 frp->fr_height += n;
7177 }
7178}
7179
7180/*
7181 * Add or remove a status line for the bottom window(s), according to the
7182 * value of 'laststatus'.
7183 */
7184 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007185last_status(
Bram Moolenaare38eab22019-12-05 21:50:01 +01007186 int morewin) // pretend there are two or more windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00007187{
Bram Moolenaare38eab22019-12-05 21:50:01 +01007188 // Don't make a difference between horizontal or vertical split.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007189 last_status_rec(topframe, (p_ls == 2
Bram Moolenaar459ca562016-11-10 18:16:33 +01007190 || (p_ls == 1 && (morewin || !ONE_WINDOW))));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191}
7192
7193 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007194last_status_rec(frame_T *fr, int statusline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007195{
7196 frame_T *fp;
7197 win_T *wp;
7198
7199 if (fr->fr_layout == FR_LEAF)
7200 {
7201 wp = fr->fr_win;
7202 if (wp->w_status_height != 0 && !statusline)
7203 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01007204 // remove status line
Bram Moolenaar071d4272004-06-13 20:20:40 +00007205 win_new_height(wp, wp->w_height + 1);
7206 wp->w_status_height = 0;
7207 comp_col();
7208 }
7209 else if (wp->w_status_height == 0 && statusline)
7210 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01007211 // Find a frame to take a line from.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007212 fp = fr;
7213 while (fp->fr_height <= frame_minheight(fp, NULL))
7214 {
7215 if (fp == topframe)
7216 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02007217 emsg(_(e_not_enough_room));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007218 return;
7219 }
Bram Moolenaare38eab22019-12-05 21:50:01 +01007220 // In a column of frames: go to frame above. If already at
7221 // the top or in a row of frames: go to parent.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007222 if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL)
7223 fp = fp->fr_prev;
7224 else
7225 fp = fp->fr_parent;
7226 }
7227 wp->w_status_height = 1;
7228 if (fp != fr)
7229 {
7230 frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE);
7231 frame_fix_height(wp);
7232 (void)win_comp_pos();
7233 }
7234 else
7235 win_new_height(wp, wp->w_height - 1);
7236 comp_col();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01007237 redraw_all_later(UPD_SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007238 }
Luuk van Baal5ed39172022-09-13 11:55:10 +01007239 // Set prev_height when difference is due to 'laststatus'.
7240 if (abs(wp->w_height - wp->w_prev_height) == 1)
7241 wp->w_prev_height = wp->w_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007242 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007243 else if (fr->fr_layout == FR_ROW)
7244 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01007245 // vertically split windows, set status line for each one
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01007246 FOR_ALL_FRAMES(fp, fr->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007247 last_status_rec(fp, statusline);
7248 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007249 else
7250 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01007251 // horizontally split window, set status line for last one
Bram Moolenaar071d4272004-06-13 20:20:40 +00007252 for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
7253 ;
7254 last_status_rec(fp, statusline);
7255 }
7256}
7257
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00007258/*
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00007259 * Return the number of lines used by the tab page line.
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00007260 */
7261 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007262tabline_height(void)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00007263{
Bram Moolenaar32466aa2006-02-24 23:53:04 +00007264#ifdef FEAT_GUI_TABLINE
Bram Moolenaare38eab22019-12-05 21:50:01 +01007265 // When the GUI has the tabline then this always returns zero.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00007266 if (gui_use_tabline())
7267 return 0;
7268#endif
Bram Moolenaar2a0449d2006-02-20 21:27:21 +00007269 switch (p_stal)
Bram Moolenaar98ea5de2006-02-15 22:11:25 +00007270 {
7271 case 0: return 0;
7272 case 1: return (first_tabpage->tp_next == NULL) ? 0 : 1;
7273 }
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00007274 return 1;
7275}
7276
Bram Moolenaar071d4272004-06-13 20:20:40 +00007277/*
7278 * Return the minimal number of rows that is needed on the screen to display
7279 * the current number of windows.
7280 */
7281 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007282min_rows(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007283{
7284 int total;
Bram Moolenaarf740b292006-02-16 22:11:02 +00007285 tabpage_T *tp;
7286 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007287
Bram Moolenaare38eab22019-12-05 21:50:01 +01007288 if (firstwin == NULL) // not initialized yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00007289 return MIN_LINES;
7290
Bram Moolenaarf740b292006-02-16 22:11:02 +00007291 total = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02007292 FOR_ALL_TABPAGES(tp)
Bram Moolenaarf740b292006-02-16 22:11:02 +00007293 {
7294 n = frame_minheight(tp->tp_topframe, NULL);
7295 if (total < n)
7296 total = n;
7297 }
Bram Moolenaar32466aa2006-02-24 23:53:04 +00007298 total += tabline_height();
Bram Moolenaara2a89732022-08-31 14:46:18 +01007299 total += 1; // count the room for the command line
Bram Moolenaar071d4272004-06-13 20:20:40 +00007300 return total;
7301}
7302
7303/*
Bram Moolenaar1c329c02019-10-27 20:37:35 +01007304 * Return TRUE if there is only one window and only one tab page, not
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00007305 * counting a help or preview window, unless it is the current window.
Bram Moolenaar4d784b22019-05-25 19:51:39 +02007306 * Does not count unlisted windows.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007307 */
7308 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007309only_one_window(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007310{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007311 int count = 0;
7312 win_T *wp;
7313
Bram Moolenaard98c0b62020-02-02 15:25:16 +01007314#if defined(FEAT_PROP_POPUP)
7315 // If the current window is a popup then there always is another window.
7316 if (popup_is_popup(curwin))
7317 return FALSE;
7318#endif
7319
Bram Moolenaare38eab22019-12-05 21:50:01 +01007320 // If there is another tab page there always is another window.
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00007321 if (first_tabpage->tp_next != NULL)
7322 return FALSE;
7323
Bram Moolenaar29323592016-07-24 22:04:11 +02007324 FOR_ALL_WINDOWS(wp)
Bram Moolenaar802418d2013-01-17 14:00:11 +01007325 if (wp->w_buffer != NULL
Bram Moolenaard28cc3f2017-07-27 22:03:50 +02007326 && (!((bt_help(wp->w_buffer) && !bt_help(curbuf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007327# ifdef FEAT_QUICKFIX
7328 || wp->w_p_pvw
7329# endif
Bram Moolenaare76062c2022-11-28 18:51:43 +00007330 ) || wp == curwin) && !is_aucmd_win(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007331 ++count;
7332 return (count <= 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007333}
7334
Bram Moolenaar071d4272004-06-13 20:20:40 +00007335/*
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01007336 * Implementation of check_lnums() and check_lnums_nested().
Bram Moolenaar071d4272004-06-13 20:20:40 +00007337 */
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01007338 static void
7339check_lnums_both(int do_curwin, int nested)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007340{
7341 win_T *wp;
Bram Moolenaarf740b292006-02-16 22:11:02 +00007342 tabpage_T *tp;
7343
7344 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007345 if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007346 {
Bram Moolenaar3d6ee8b2022-07-27 15:23:35 +01007347 int need_adjust;
7348
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01007349 if (!nested)
7350 {
7351 // save the original cursor position and topline
7352 wp->w_save_cursor.w_cursor_save = wp->w_cursor;
7353 wp->w_save_cursor.w_topline_save = wp->w_topline;
7354 }
Bram Moolenaara68e5952019-04-25 22:22:01 +02007355
Bram Moolenaar3d6ee8b2022-07-27 15:23:35 +01007356 need_adjust = wp->w_cursor.lnum > curbuf->b_ml.ml_line_count;
7357 if (need_adjust)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007358 wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
Bram Moolenaar3d6ee8b2022-07-27 15:23:35 +01007359 if (need_adjust || !nested)
7360 // save the (corrected) cursor position
7361 wp->w_save_cursor.w_cursor_corr = wp->w_cursor;
Bram Moolenaara68e5952019-04-25 22:22:01 +02007362
Bram Moolenaar3d6ee8b2022-07-27 15:23:35 +01007363 need_adjust = wp->w_topline > curbuf->b_ml.ml_line_count;
7364 if (need_adjust)
7365 wp->w_topline = curbuf->b_ml.ml_line_count;
7366 if (need_adjust || !nested)
7367 // save the (corrected) topline
7368 wp->w_save_cursor.w_topline_corr = wp->w_topline;
Bram Moolenaara68e5952019-04-25 22:22:01 +02007369 }
7370}
7371
7372/*
Bram Moolenaar5fa9f232022-07-23 09:06:48 +01007373 * Correct the cursor line number in other windows. Used after changing the
7374 * current buffer, and before applying autocommands.
7375 * When "do_curwin" is TRUE, also check current window.
7376 */
7377 void
7378check_lnums(int do_curwin)
7379{
7380 check_lnums_both(do_curwin, FALSE);
7381}
7382
7383/*
7384 * Like check_lnums() but for when check_lnums() was already called.
7385 */
7386 void
7387check_lnums_nested(int do_curwin)
7388{
7389 check_lnums_both(do_curwin, TRUE);
7390}
7391
7392/*
Bram Moolenaara68e5952019-04-25 22:22:01 +02007393 * Reset cursor and topline to its stored values from check_lnums().
7394 * check_lnums() must have been called first!
7395 */
7396 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00007397reset_lnums(void)
Bram Moolenaara68e5952019-04-25 22:22:01 +02007398{
7399 win_T *wp;
7400 tabpage_T *tp;
7401
7402 FOR_ALL_TAB_WINDOWS(tp, wp)
7403 if (wp->w_buffer == curbuf)
7404 {
Bram Moolenaarb03950f2022-07-26 13:47:13 +01007405 // Restore the value if the autocommand didn't change it and it was
7406 // set.
7407 if (EQUAL_POS(wp->w_save_cursor.w_cursor_corr, wp->w_cursor)
7408 && wp->w_save_cursor.w_cursor_save.lnum != 0)
Bram Moolenaara68e5952019-04-25 22:22:01 +02007409 wp->w_cursor = wp->w_save_cursor.w_cursor_save;
Bram Moolenaarb03950f2022-07-26 13:47:13 +01007410 if (wp->w_save_cursor.w_topline_corr == wp->w_topline
7411 && wp->w_save_cursor.w_topline_save != 0)
Bram Moolenaara68e5952019-04-25 22:22:01 +02007412 wp->w_topline = wp->w_save_cursor.w_topline_save;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007413 }
7414}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007415
7416/*
7417 * A snapshot of the window sizes, to restore them after closing the help
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00007418 * or other window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007419 * Only these fields are used:
7420 * fr_layout
7421 * fr_width
7422 * fr_height
7423 * fr_next
7424 * fr_child
7425 * fr_win (only valid for the old curwin, NULL otherwise)
7426 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007427
7428/*
7429 * Create a snapshot of the current frame sizes.
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00007430 * "idx" is SNAP_HELP_IDX or SNAP_AUCMD_IDX.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007431 */
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007432 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007433make_snapshot(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007434{
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007435 clear_snapshot(curtab, idx);
7436 make_snapshot_rec(topframe, &curtab->tp_snapshot[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007437}
7438
7439 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007440make_snapshot_rec(frame_T *fr, frame_T **frp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007441{
Bram Moolenaarc799fe22019-05-28 23:08:19 +02007442 *frp = ALLOC_CLEAR_ONE(frame_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007443 if (*frp == NULL)
7444 return;
7445 (*frp)->fr_layout = fr->fr_layout;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007446 (*frp)->fr_width = fr->fr_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007447 (*frp)->fr_height = fr->fr_height;
7448 if (fr->fr_next != NULL)
7449 make_snapshot_rec(fr->fr_next, &((*frp)->fr_next));
7450 if (fr->fr_child != NULL)
7451 make_snapshot_rec(fr->fr_child, &((*frp)->fr_child));
7452 if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin)
7453 (*frp)->fr_win = curwin;
7454}
7455
7456/*
7457 * Remove any existing snapshot.
7458 */
7459 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007460clear_snapshot(tabpage_T *tp, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007461{
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007462 clear_snapshot_rec(tp->tp_snapshot[idx]);
7463 tp->tp_snapshot[idx] = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007464}
7465
7466 static void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007467clear_snapshot_rec(frame_T *fr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007468{
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00007469 if (fr == NULL)
7470 return;
7471 clear_snapshot_rec(fr->fr_next);
7472 clear_snapshot_rec(fr->fr_child);
7473 vim_free(fr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007474}
7475
7476/*
LemonBoy2a2707d2022-05-04 22:13:47 +01007477 * Traverse a snapshot to find the previous curwin.
7478 */
7479 static win_T *
7480get_snapshot_curwin_rec(frame_T *ft)
7481{
7482 win_T *wp;
7483
7484 if (ft->fr_next != NULL)
7485 {
7486 if ((wp = get_snapshot_curwin_rec(ft->fr_next)) != NULL)
7487 return wp;
7488 }
7489 if (ft->fr_child != NULL)
7490 {
7491 if ((wp = get_snapshot_curwin_rec(ft->fr_child)) != NULL)
7492 return wp;
7493 }
7494
7495 return ft->fr_win;
7496}
7497
7498/*
7499 * Return the current window stored in the snapshot or NULL.
7500 */
7501 static win_T *
7502get_snapshot_curwin(int idx)
7503{
7504 if (curtab->tp_snapshot[idx] == NULL)
7505 return NULL;
7506
7507 return get_snapshot_curwin_rec(curtab->tp_snapshot[idx]);
7508}
7509
7510/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007511 * Restore a previously created snapshot, if there is any.
7512 * This is only done if the screen size didn't change and the window layout is
7513 * still the same.
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00007514 * "idx" is SNAP_HELP_IDX or SNAP_AUCMD_IDX.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007515 */
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007516 void
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007517restore_snapshot(
7518 int idx,
Bram Moolenaare38eab22019-12-05 21:50:01 +01007519 int close_curwin) // closing current window
Bram Moolenaar071d4272004-06-13 20:20:40 +00007520{
7521 win_T *wp;
7522
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007523 if (curtab->tp_snapshot[idx] != NULL
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007524 && curtab->tp_snapshot[idx]->fr_width == topframe->fr_width
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007525 && curtab->tp_snapshot[idx]->fr_height == topframe->fr_height
7526 && check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007527 {
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007528 wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007529 win_comp_pos();
7530 if (wp != NULL && close_curwin)
7531 win_goto(wp);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01007532 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007533 }
Bram Moolenaar746ebd32009-06-16 14:01:43 +00007534 clear_snapshot(curtab, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007535}
7536
7537/*
7538 * Check if frames "sn" and "fr" have the same layout, same following frames
Bram Moolenaar343b8c02017-02-17 12:04:56 +01007539 * and same children. And the window pointer is valid.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007540 */
7541 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007542check_snapshot_rec(frame_T *sn, frame_T *fr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007543{
7544 if (sn->fr_layout != fr->fr_layout
7545 || (sn->fr_next == NULL) != (fr->fr_next == NULL)
7546 || (sn->fr_child == NULL) != (fr->fr_child == NULL)
7547 || (sn->fr_next != NULL
7548 && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL)
7549 || (sn->fr_child != NULL
Bram Moolenaar343b8c02017-02-17 12:04:56 +01007550 && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)
Bram Moolenaar2c90d512017-03-18 22:35:30 +01007551 || (sn->fr_win != NULL && !win_valid(sn->fr_win)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007552 return FAIL;
7553 return OK;
7554}
7555
7556/*
7557 * Copy the size of snapshot frame "sn" to frame "fr". Do the same for all
7558 * following frames and children.
7559 * Returns a pointer to the old current window, or NULL.
7560 */
7561 static win_T *
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007562restore_snapshot_rec(frame_T *sn, frame_T *fr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007563{
7564 win_T *wp = NULL;
7565 win_T *wp2;
7566
7567 fr->fr_height = sn->fr_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007568 fr->fr_width = sn->fr_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007569 if (fr->fr_layout == FR_LEAF)
7570 {
7571 frame_new_height(fr, fr->fr_height, FALSE, FALSE);
Bram Moolenaarbe4d5062006-03-18 21:30:13 +00007572 frame_new_width(fr, fr->fr_width, FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007573 wp = sn->fr_win;
7574 }
7575 if (sn->fr_next != NULL)
7576 {
7577 wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next);
7578 if (wp2 != NULL)
7579 wp = wp2;
7580 }
7581 if (sn->fr_child != NULL)
7582 {
7583 wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child);
7584 if (wp2 != NULL)
7585 wp = wp2;
7586 }
7587 return wp;
7588}
7589
Bram Moolenaar4033c552017-09-16 20:54:51 +02007590#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007591/*
7592 * Return TRUE if there is any vertically split window.
7593 */
7594 int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007595win_hasvertsplit(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007596{
7597 frame_T *fr;
7598
7599 if (topframe->fr_layout == FR_ROW)
7600 return TRUE;
7601
7602 if (topframe->fr_layout == FR_COL)
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01007603 FOR_ALL_FRAMES(fr, topframe->fr_child)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007604 if (fr->fr_layout == FR_ROW)
7605 return TRUE;
7606
7607 return FALSE;
7608}
7609#endif
Bram Moolenaar6ee10162007-07-26 20:58:42 +00007610
Bram Moolenaar6d216452013-05-12 19:00:41 +02007611#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) || defined(PROTO)
7612 int
Bram Moolenaar5e538ec2013-05-15 15:12:29 +02007613get_win_number(win_T *wp, win_T *first_win)
Bram Moolenaar6d216452013-05-12 19:00:41 +02007614{
7615 int i = 1;
7616 win_T *w;
7617
Bram Moolenaar5e538ec2013-05-15 15:12:29 +02007618 for (w = first_win; w != NULL && w != wp; w = W_NEXT(w))
Bram Moolenaar6d216452013-05-12 19:00:41 +02007619 ++i;
7620
7621 if (w == NULL)
7622 return 0;
7623 else
7624 return i;
7625}
Bram Moolenaar5e538ec2013-05-15 15:12:29 +02007626
7627 int
Bram Moolenaar8c0e3222013-06-16 17:32:40 +02007628get_tab_number(tabpage_T *tp UNUSED)
Bram Moolenaar5e538ec2013-05-15 15:12:29 +02007629{
7630 int i = 1;
7631 tabpage_T *t;
7632
7633 for (t = first_tabpage; t != NULL && t != tp; t = t->tp_next)
7634 ++i;
7635
7636 if (t == NULL)
7637 return 0;
7638 else
7639 return i;
7640}
Bram Moolenaar6d216452013-05-12 19:00:41 +02007641#endif
Bram Moolenaarb893ac22013-06-26 14:04:47 +02007642
7643/*
7644 * Return TRUE if "topfrp" and its children are at the right height.
7645 */
7646 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007647frame_check_height(frame_T *topfrp, int height)
Bram Moolenaarb893ac22013-06-26 14:04:47 +02007648{
7649 frame_T *frp;
7650
7651 if (topfrp->fr_height != height)
7652 return FALSE;
7653
7654 if (topfrp->fr_layout == FR_ROW)
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01007655 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaarb893ac22013-06-26 14:04:47 +02007656 if (frp->fr_height != height)
7657 return FALSE;
7658
7659 return TRUE;
7660}
7661
Bram Moolenaarb893ac22013-06-26 14:04:47 +02007662/*
7663 * Return TRUE if "topfrp" and its children are at the right width.
7664 */
7665 static int
Bram Moolenaarb638a7b2016-01-30 21:29:58 +01007666frame_check_width(frame_T *topfrp, int width)
Bram Moolenaarb893ac22013-06-26 14:04:47 +02007667{
7668 frame_T *frp;
7669
7670 if (topfrp->fr_width != width)
7671 return FALSE;
7672
7673 if (topfrp->fr_layout == FR_COL)
Bram Moolenaar3d1491e2018-12-22 17:07:50 +01007674 FOR_ALL_FRAMES(frp, topfrp->fr_child)
Bram Moolenaarb893ac22013-06-26 14:04:47 +02007675 if (frp->fr_width != width)
7676 return FALSE;
7677
7678 return TRUE;
7679}
Bram Moolenaarb893ac22013-06-26 14:04:47 +02007680
Bram Moolenaare677df82019-09-02 22:31:11 +02007681#if defined(FEAT_SYN_HL) || defined(PROTO)
7682/*
7683 * Simple int comparison function for use with qsort()
7684 */
7685 static int
7686int_cmp(const void *a, const void *b)
7687{
7688 return *(const int *)a - *(const int *)b;
7689}
7690
7691/*
7692 * Handle setting 'colorcolumn' or 'textwidth' in window "wp".
7693 * Returns error message, NULL if it's OK.
7694 */
7695 char *
7696check_colorcolumn(win_T *wp)
7697{
7698 char_u *s;
7699 int col;
7700 int count = 0;
7701 int color_cols[256];
7702 int i;
7703 int j = 0;
7704
7705 if (wp->w_buffer == NULL)
7706 return NULL; // buffer was closed
7707
7708 for (s = wp->w_p_cc; *s != NUL && count < 255;)
7709 {
7710 if (*s == '-' || *s == '+')
7711 {
7712 // -N and +N: add to 'textwidth'
7713 col = (*s == '-') ? -1 : 1;
7714 ++s;
7715 if (!VIM_ISDIGIT(*s))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00007716 return e_invalid_argument;
Bram Moolenaare677df82019-09-02 22:31:11 +02007717 col = col * getdigits(&s);
7718 if (wp->w_buffer->b_p_tw == 0)
7719 goto skip; // 'textwidth' not set, skip this item
7720 col += wp->w_buffer->b_p_tw;
7721 if (col < 0)
7722 goto skip;
7723 }
7724 else if (VIM_ISDIGIT(*s))
7725 col = getdigits(&s);
7726 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00007727 return e_invalid_argument;
Bram Moolenaare677df82019-09-02 22:31:11 +02007728 color_cols[count++] = col - 1; // 1-based to 0-based
7729skip:
7730 if (*s == NUL)
7731 break;
7732 if (*s != ',')
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00007733 return e_invalid_argument;
Bram Moolenaare677df82019-09-02 22:31:11 +02007734 if (*++s == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00007735 return e_invalid_argument; // illegal trailing comma as in "set cc=80,"
Bram Moolenaare677df82019-09-02 22:31:11 +02007736 }
7737
7738 vim_free(wp->w_p_cc_cols);
7739 if (count == 0)
7740 wp->w_p_cc_cols = NULL;
7741 else
7742 {
7743 wp->w_p_cc_cols = ALLOC_MULT(int, count + 1);
7744 if (wp->w_p_cc_cols != NULL)
7745 {
7746 // sort the columns for faster usage on screen redraw inside
7747 // win_line()
7748 qsort(color_cols, count, sizeof(int), int_cmp);
7749
7750 for (i = 0; i < count; ++i)
7751 // skip duplicates
7752 if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i])
7753 wp->w_p_cc_cols[j++] = color_cols[i];
7754 wp->w_p_cc_cols[j] = -1; // end marker
7755 }
7756 }
7757
7758 return NULL; // no error
7759}
7760#endif