0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00
vim/src/window.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

8006 lines
190 KiB
C
Raw Normal View History

/* vi:set ts=8 sts=4 sw=4 noet:
2004-06-13 20:20:40 +00:00
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read a list of people who contributed.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
#include "vim.h"
static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, long Prenum);
static void win_init(win_T *newp, win_T *oldp, int flags);
static void win_init_some(win_T *newp, win_T *oldp);
static void frame_comp_pos(frame_T *topfrp, int *row, int *col);
static void frame_setheight(frame_T *curfrp, int height);
static void frame_setwidth(frame_T *curfrp, int width);
static void win_exchange(long);
static void win_rotate(int, int);
static void win_equal_rec(win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height);
static void trigger_winnewpre(void);
static void trigger_winclosed(win_T *win);
static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp);
static frame_T *win_altframe(win_T *win, tabpage_T *tp);
static tabpage_T *alt_tabpage(void);
static win_T *frame2win(frame_T *frp);
static int frame_has_win(frame_T *frp, win_T *wp);
static void win_fix_scroll(int resize);
static void win_fix_cursor(int normal);
static void frame_new_height(frame_T *topfrp, int height, int topfirst, int wfh, int set_ch);
static int frame_fixed_height(frame_T *frp);
static int frame_fixed_width(frame_T *frp);
static void frame_add_statusline(frame_T *frp);
static void frame_new_width(frame_T *topfrp, int width, int leftfirst, int wfw);
static void frame_add_vsep(frame_T *frp);
static int frame_minwidth(frame_T *topfrp, win_T *next_curwin);
static void frame_fix_width(win_T *wp);
static int win_alloc_firstwin(win_T *oldwin);
static void new_frame(win_T *wp);
static tabpage_T *alloc_tabpage(void);
static int leave_tabpage(buf_T *new_curbuf, int trigger_leave_autocmds);
static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_autocmds, int trigger_leave_autocmds);
static void frame_fix_height(win_T *wp);
static int frame_minheight(frame_T *topfrp, win_T *next_curwin);
static int may_open_tabpage(void);
static int win_enter_ext(win_T *wp, int flags);
static void win_free(win_T *wp, tabpage_T *tp);
static void win_append(win_T *after, win_T *wp);
static void frame_append(frame_T *after, frame_T *frp);
static void frame_insert(frame_T *before, frame_T *frp);
static void frame_remove(frame_T *frp);
static void win_goto_ver(int up, long count);
static void win_goto_hor(int left, long count);
static void frame_add_height(frame_T *frp, int n);
static void last_status_rec(frame_T *fr, int statusline);
static void frame_flatten(frame_T *frp);
static void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr);
2004-06-13 20:20:40 +00:00
static int make_snapshot_rec(frame_T *fr, frame_T **frp);
static void clear_snapshot(tabpage_T *tp, int idx);
static void clear_snapshot_rec(frame_T *fr);
static int check_snapshot_rec(frame_T *sn, frame_T *fr);
static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr);
static win_T *get_snapshot_curwin(int idx);
2004-06-13 20:20:40 +00:00
static int frame_check_height(frame_T *topfrp, int height);
static int frame_check_width(frame_T *topfrp, int width);
static win_T *win_alloc(win_T *after, int hidden);
2004-06-13 20:20:40 +00:00
#define NOWIN ((win_T *)-1) // non-existing window
2004-06-13 20:20:40 +00:00
#define ROWS_AVAIL (Rows - p_ch - tabline_height())
2006-04-11 21:38:50 +00:00
// flags for win_enter_ext()
#define WEE_UNDO_SYNC 0x01
#define WEE_CURWIN_INVALID 0x02
#define WEE_TRIGGER_NEW_AUTOCMDS 0x04
#define WEE_TRIGGER_ENTER_AUTOCMDS 0x08
#define WEE_TRIGGER_LEAVE_AUTOCMDS 0x10
#define WEE_ALLOW_PARSE_MESSAGES 0x20
2006-04-11 21:38:50 +00:00
static char *m_onlyone = N_("Already only one window");
// When non-zero splitting a window is forbidden. Used to avoid that nasty
// autocommands mess up the window structure.
static int split_disallowed = 0;
// When non-zero closing a window is forbidden. Used to avoid that nasty
// autocommands mess up the window structure.
static int close_disallowed = 0;
/*
* Disallow changing the window layout (split window, close window, move
* window). Resizing is still allowed.
* Used for autocommands that temporarily use another window and need to
* make sure the previously selected window is still there.
* Must be matched with exactly one call to window_layout_unlock()!
*/
static void
window_layout_lock(void)
{
++split_disallowed;
++close_disallowed;
}
static void
window_layout_unlock(void)
{
--split_disallowed;
--close_disallowed;
}
/*
* When the window layout cannot be changed give an error and return TRUE.
* "cmd" indicates the action being performed and is used to pick the relevant
* error message.
*/
int
window_layout_locked(enum CMD_index cmd)
{
if (split_disallowed > 0 || close_disallowed > 0)
{
if (close_disallowed == 0 && cmd == CMD_tabnew)
emsg(_(e_cannot_split_window_when_closing_buffer));
else
emsg(_(e_not_allowed_to_change_window_layout_in_this_autocmd));
return TRUE;
}
return FALSE;
}
// #define WIN_DEBUG
#ifdef WIN_DEBUG
/*
* Call this method to log the current window layout.
*/
static void
log_frame_layout(frame_T *frame)
{
ch_log(NULL, "layout %s, wi: %d, he: %d, wwi: %d, whe: %d, id: %d",
frame->fr_layout == FR_LEAF ? "LEAF"
: frame->fr_layout == FR_ROW ? "ROW" : "COL",
frame->fr_width,
frame->fr_height,
frame->fr_win == NULL ? -1 : frame->fr_win->w_width,
frame->fr_win == NULL ? -1 : frame->fr_win->w_height,
frame->fr_win == NULL ? -1 : frame->fr_win->w_id);
if (frame->fr_child != NULL)
{
ch_log(NULL, "children");
log_frame_layout(frame->fr_child);
if (frame->fr_next != NULL)
ch_log(NULL, "END of children");
}
if (frame->fr_next != NULL)
log_frame_layout(frame->fr_next);
}
#endif
/*
* Check if the current window is allowed to move to a different buffer.
* If the window has 'winfixbuf', this function will return FALSE.
*/
int
check_can_set_curbuf_disabled(void)
{
if (curwin->w_p_wfb)
{
patch 9.1.0150: Several minor 'winfixbuf' issues Problem: several minor 'winfixbuf' issues exist, mostly relating to the quickfix list Solution: address them and adjust tests. Retab and reflow a few things too. (Sean Dewar) Things touched include: - Replace the semsgs with gettext'd emsgs. - Handle window switching in ex_listdo properly, so curbuf and curwin are kept in-sync and trigger autocommands; handle those properly. - Don't change the list entry index in qf_jump_edit_buffer if we fail due to 'wfb' (achieved by returning FAIL; QF_ABORT should only be used if the list was changed). - Make qf_jump_edit_buffer actually switch to prevwin when using `:cXX` commands **outside** of the list window if 'wfb' is set in curwin. Handle autocommands properly in case they mess with the list. NOTE: previously, it seemed to split if 'wfb' was set, but do nothing and fail if prevwin is *valid*. This behaviour seemed strange, and maybe unintentional? Now it aligns more with what's described for the `:cXX` commands in the original PR description when used outside a list window, I think. - In both functions, only consider prevwin if 'wfb' isn't set for it; fallback to splitting otherwise. - Use win_split to split. Not sure if there was a specific reason for using ex_splitview. win_split is simpler and respects modifiers like :vertical that may have been used. Plus, its return value can be checked for setting opened_window in qf code (technically win_split_ins autocmds could immediately close it or change windows, in which the qf code might close some other window on failure; it's already the case elsewhere, though). closes: #14142 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-03-05 20:39:07 +01:00
emsg(_(e_winfixbuf_cannot_go_to_buffer));
return FALSE;
}
return TRUE;
}
/*
* Check if the current window is allowed to move to a different buffer.
* If the window has 'winfixbuf', then forceit must be TRUE or this function
* will return FALSE.
*/
int
check_can_set_curbuf_forceit(int forceit)
{
if (!forceit && curwin->w_p_wfb)
{
patch 9.1.0150: Several minor 'winfixbuf' issues Problem: several minor 'winfixbuf' issues exist, mostly relating to the quickfix list Solution: address them and adjust tests. Retab and reflow a few things too. (Sean Dewar) Things touched include: - Replace the semsgs with gettext'd emsgs. - Handle window switching in ex_listdo properly, so curbuf and curwin are kept in-sync and trigger autocommands; handle those properly. - Don't change the list entry index in qf_jump_edit_buffer if we fail due to 'wfb' (achieved by returning FAIL; QF_ABORT should only be used if the list was changed). - Make qf_jump_edit_buffer actually switch to prevwin when using `:cXX` commands **outside** of the list window if 'wfb' is set in curwin. Handle autocommands properly in case they mess with the list. NOTE: previously, it seemed to split if 'wfb' was set, but do nothing and fail if prevwin is *valid*. This behaviour seemed strange, and maybe unintentional? Now it aligns more with what's described for the `:cXX` commands in the original PR description when used outside a list window, I think. - In both functions, only consider prevwin if 'wfb' isn't set for it; fallback to splitting otherwise. - Use win_split to split. Not sure if there was a specific reason for using ex_splitview. win_split is simpler and respects modifiers like :vertical that may have been used. Plus, its return value can be checked for setting opened_window in qf code (technically win_split_ins autocmds could immediately close it or change windows, in which the qf code might close some other window on failure; it's already the case elsewhere, though). closes: #14142 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-03-05 20:39:07 +01:00
emsg(_(e_winfixbuf_cannot_go_to_buffer));
return FALSE;
}
return TRUE;
}
/*
* Return the current window, unless in the cmdline window and "prevwin" is
* set, then return "prevwin".
*/
win_T *
prevwin_curwin(void)
{
// In cmdwin, the alternative buffer should be used.
return is_in_cmdwin() && prevwin != NULL ? prevwin : curwin;
}
/*
* If the 'switchbuf' option contains "useopen" or "usetab", then try to jump
* to a window containing "buf".
* Returns the pointer to the window that was jumped to or NULL.
*/
win_T *
swbuf_goto_win_with_buf(buf_T *buf)
{
win_T *wp = NULL;
if (buf == NULL)
return wp;
// If 'switchbuf' contains "useopen": jump to first window in the current
// tab page containing "buf" if one exists.
if (swb_flags & SWB_USEOPEN)
wp = buf_jump_open_win(buf);
// If 'switchbuf' contains "usetab": jump to first window in any tab page
// containing "buf" if one exists.
if (wp == NULL && (swb_flags & SWB_USETAB))
wp = buf_jump_open_tab(buf);
return wp;
}
2004-06-13 20:20:40 +00:00
/*
* All CTRL-W window commands are handled here, called from normal_cmd().
2004-06-13 20:20:40 +00:00
*/
void
do_window(
int nchar,
long Prenum,
int xchar) // extra char from ":wincmd gx" or NUL
2004-06-13 20:20:40 +00:00
{
long Prenum1;
win_T *wp;
char_u *ptr;
2006-02-22 21:25:37 +00:00
linenr_T lnum = -1;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_FIND_ID
int type = FIND_DEFINE;
int len;
#endif
char_u cbuf[40];
if (ERROR_IF_ANY_POPUP_WINDOW)
return;
2004-06-13 20:20:40 +00:00
#define CHECK_CMDWIN \
do { \
if (cmdwin_type != 0) \
{ \
emsg(_(e_invalid_in_cmdline_window)); \
return; \
} \
} while (0)
2004-06-13 20:20:40 +00:00
Prenum1 = Prenum == 0 ? 1 : Prenum;
2004-06-13 20:20:40 +00:00
switch (nchar)
{
// split current window in two parts, horizontally
2004-06-13 20:20:40 +00:00
case 'S':
case Ctrl_S:
case 's':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
// When splitting the quickfix window open a new buffer in it,
// don't replicate the quickfix buffer.
2006-01-21 22:09:43 +00:00
if (bt_quickfix(curbuf))
goto newwindow;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
(void)win_split((int)Prenum, 0);
2004-06-13 20:20:40 +00:00
break;
// split current window in two parts, vertically
2004-06-13 20:20:40 +00:00
case Ctrl_V:
case 'v':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
// When splitting the quickfix window open a new buffer in it,
// don't replicate the quickfix buffer.
2008-07-07 19:23:37 +00:00
if (bt_quickfix(curbuf))
goto newwindow;
#ifdef FEAT_GUI
2004-06-13 20:20:40 +00:00
need_mouse_correct = TRUE;
#endif
(void)win_split((int)Prenum, WSP_VERT);
2004-06-13 20:20:40 +00:00
break;
// split current window and edit alternate file
2004-06-13 20:20:40 +00:00
case Ctrl_HAT:
case '^':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
if (buflist_findnr(Prenum == 0
? curwin->w_alt_fnum : Prenum) == NULL)
{
if (Prenum == 0)
emsg(_(e_no_alternate_file));
else
semsg(_(e_buffer_nr_not_found), Prenum);
break;
}
if (!curbuf_locked() && win_split(0, 0) == OK)
(void)buflist_getfile(
Prenum == 0 ? curwin->w_alt_fnum : Prenum,
(linenr_T)0, GETF_ALT, FALSE);
2004-06-13 20:20:40 +00:00
break;
// open new window
2004-06-13 20:20:40 +00:00
case Ctrl_N:
case 'n':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
2006-01-21 22:09:43 +00:00
newwindow:
2004-06-13 20:20:40 +00:00
if (Prenum)
// window height
2008-07-07 19:23:37 +00:00
vim_snprintf((char *)cbuf, sizeof(cbuf) - 5, "%ld", Prenum);
2004-06-13 20:20:40 +00:00
else
cbuf[0] = NUL;
#if defined(FEAT_QUICKFIX)
2008-07-07 19:23:37 +00:00
if (nchar == 'v' || nchar == Ctrl_V)
STRCAT(cbuf, "v");
#endif
2004-06-13 20:20:40 +00:00
STRCAT(cbuf, "new");
do_cmdline_cmd(cbuf);
break;
// quit current window
2004-06-13 20:20:40 +00:00
case Ctrl_Q:
case 'q':
reset_VIsual_and_resel(); // stop Visual mode
cmd_with_count("quit", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
2004-06-13 20:20:40 +00:00
break;
// close current window
2004-06-13 20:20:40 +00:00
case Ctrl_C:
case 'c':
reset_VIsual_and_resel(); // stop Visual mode
cmd_with_count("close", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
2004-06-13 20:20:40 +00:00
break;
#if defined(FEAT_QUICKFIX)
// close preview window
2004-06-13 20:20:40 +00:00
case Ctrl_Z:
case 'z':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
2004-06-13 20:20:40 +00:00
do_cmdline_cmd((char_u *)"pclose");
break;
// cursor to preview window
2004-06-13 20:20:40 +00:00
case 'P':
FOR_ALL_WINDOWS(wp)
2004-06-13 20:20:40 +00:00
if (wp->w_p_pvw)
break;
if (wp == NULL)
emsg(_(e_there_is_no_preview_window));
2004-06-13 20:20:40 +00:00
else
win_goto(wp);
break;
#endif
// close all but current window
2004-06-13 20:20:40 +00:00
case Ctrl_O:
case 'o':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
cmd_with_count("only", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
2004-06-13 20:20:40 +00:00
break;
// cursor to next window with wrap around
2004-06-13 20:20:40 +00:00
case Ctrl_W:
case 'w':
// cursor to previous window with wrap around
2004-06-13 20:20:40 +00:00
case 'W':
CHECK_CMDWIN;
if (ONE_WINDOW && Prenum != 1) // just one window
2004-06-13 20:20:40 +00:00
beep_flush();
else
{
if (Prenum) // go to specified window
2004-06-13 20:20:40 +00:00
{
for (wp = firstwin; --Prenum > 0; )
{
if (wp->w_next == NULL)
break;
else
wp = wp->w_next;
}
}
else
{
if (nchar == 'W') // go to previous window
2004-06-13 20:20:40 +00:00
{
wp = curwin->w_prev;
if (wp == NULL)
wp = lastwin; // wrap around
2004-06-13 20:20:40 +00:00
}
else // go to next window
2004-06-13 20:20:40 +00:00
{
wp = curwin->w_next;
if (wp == NULL)
wp = firstwin; // wrap around
2004-06-13 20:20:40 +00:00
}
}
win_goto(wp);
}
break;
// cursor to window below
2004-06-13 20:20:40 +00:00
case 'j':
case K_DOWN:
case Ctrl_J:
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
win_goto_ver(FALSE, Prenum1);
break;
// cursor to window above
2004-06-13 20:20:40 +00:00
case 'k':
case K_UP:
case Ctrl_K:
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
win_goto_ver(TRUE, Prenum1);
break;
// cursor to left window
2004-06-13 20:20:40 +00:00
case 'h':
case K_LEFT:
case Ctrl_H:
case K_BS:
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
win_goto_hor(TRUE, Prenum1);
break;
// cursor to right window
2004-06-13 20:20:40 +00:00
case 'l':
case K_RIGHT:
case Ctrl_L:
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
win_goto_hor(FALSE, Prenum1);
break;
// move window to new tab page
2006-04-11 21:38:50 +00:00
case 'T':
CHECK_CMDWIN;
2009-06-16 14:01:43 +00:00
if (one_window())
msg(_(m_onlyone));
2006-04-11 21:38:50 +00:00
else
{
tabpage_T *oldtab = curtab;
tabpage_T *newtab;
// First create a new tab with the window, then go back to
// the old tab and close the window there.
2006-08-29 15:30:07 +00:00
wp = curwin;
2006-04-11 21:38:50 +00:00
if (win_new_tabpage((int)Prenum) == OK
&& valid_tabpage(oldtab))
{
newtab = curtab;
goto_tabpage_tp(oldtab, TRUE, TRUE);
2006-04-11 21:38:50 +00:00
if (curwin == wp)
win_close(curwin, FALSE);
if (valid_tabpage(newtab))
goto_tabpage_tp(newtab, TRUE, TRUE);
2006-04-11 21:38:50 +00:00
}
}
break;
// cursor to top-left window
2004-06-13 20:20:40 +00:00
case 't':
case Ctrl_T:
win_goto(firstwin);
break;
// cursor to bottom-right window
2004-06-13 20:20:40 +00:00
case 'b':
case Ctrl_B:
win_goto(lastwin);
break;
// cursor to last accessed (previous) window
2004-06-13 20:20:40 +00:00
case 'p':
case Ctrl_P:
if (!win_valid(prevwin))
2004-06-13 20:20:40 +00:00
beep_flush();
else
win_goto(prevwin);
break;
// exchange current and next window
2004-06-13 20:20:40 +00:00
case 'x':
case Ctrl_X:
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
win_exchange(Prenum);
break;
// rotate windows downwards
2004-06-13 20:20:40 +00:00
case Ctrl_R:
case 'r':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
win_rotate(FALSE, (int)Prenum1); // downwards
2004-06-13 20:20:40 +00:00
break;
// rotate windows upwards
2004-06-13 20:20:40 +00:00
case 'R':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
win_rotate(TRUE, (int)Prenum1); // upwards
2004-06-13 20:20:40 +00:00
break;
// move window to the very top/bottom/left/right
2004-06-13 20:20:40 +00:00
case 'K':
case 'J':
case 'H':
case 'L':
CHECK_CMDWIN;
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
if (ONE_WINDOW)
beep_flush();
else
{
int dir = ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
| ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT);
(void)win_splitmove(curwin, (int)Prenum, dir);
}
2004-06-13 20:20:40 +00:00
break;
// make all windows the same width and/or height
2004-06-13 20:20:40 +00:00
case '=':
{
int mod = cmdmod.cmod_split & (WSP_VERT | WSP_HOR);
2004-06-13 20:20:40 +00:00
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
2004-06-13 20:20:40 +00:00
#endif
win_equal(NULL, FALSE,
mod == WSP_VERT ? 'v' : mod == WSP_HOR ? 'h' : 'b');
}
2004-06-13 20:20:40 +00:00
break;
// increase current window height
2004-06-13 20:20:40 +00:00
case '+':
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_setheight(curwin->w_height + (int)Prenum1);
break;
// decrease current window height
2004-06-13 20:20:40 +00:00
case '-':
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_setheight(curwin->w_height - (int)Prenum1);
break;
// set current window height
2004-06-13 20:20:40 +00:00
case Ctrl__:
case '_':
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_setheight(Prenum ? (int)Prenum : 9999);
break;
// increase current window width
2004-06-13 20:20:40 +00:00
case '>':
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_setwidth(curwin->w_width + (int)Prenum1);
break;
// decrease current window width
2004-06-13 20:20:40 +00:00
case '<':
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_setwidth(curwin->w_width - (int)Prenum1);
break;
// set current window width
2004-06-13 20:20:40 +00:00
case '|':
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_setwidth(Prenum != 0 ? (int)Prenum : 9999);
break;
// jump to tag and split window if tag exists (in preview window)
2004-06-13 20:20:40 +00:00
#if defined(FEAT_QUICKFIX)
case '}':
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
if (Prenum)
g_do_tagpreview = Prenum;
else
g_do_tagpreview = p_pvh;
#endif
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case ']':
case Ctrl_RSB:
CHECK_CMDWIN;
// keep Visual mode, can select words to use as a tag
2004-06-13 20:20:40 +00:00
if (Prenum)
postponed_split = Prenum;
else
postponed_split = -1;
#ifdef FEAT_QUICKFIX
if (nchar != '}')
g_do_tagpreview = 0;
#endif
2004-06-13 20:20:40 +00:00
// Execute the command right here, required when "wincmd ]"
// was used in a function.
2004-06-13 20:20:40 +00:00
do_nv_ident(Ctrl_RSB, NUL);
postponed_split = 0;
2004-06-13 20:20:40 +00:00
break;
// edit file name under cursor in a new window
2004-06-13 20:20:40 +00:00
case 'f':
2006-02-22 21:25:37 +00:00
case 'F':
2004-06-13 20:20:40 +00:00
case Ctrl_F:
2006-04-06 20:18:50 +00:00
wingotofile:
CHECK_CMDWIN;
if (check_text_or_curbuf_locked(NULL))
break;
2005-06-21 22:37:39 +00:00
2006-02-22 21:25:37 +00:00
ptr = grab_file_name(Prenum1, &lnum);
2004-06-13 20:20:40 +00:00
if (ptr != NULL)
{
tabpage_T *oldtab = curtab;
win_T *oldwin = curwin;
#ifdef FEAT_GUI
2004-06-13 20:20:40 +00:00
need_mouse_correct = TRUE;
#endif
2004-06-13 20:20:40 +00:00
setpcmark();
// If 'switchbuf' is set to 'useopen' or 'usetab' and the
// file is already opened in a window, then jump to it.
wp = NULL;
if ((swb_flags & (SWB_USEOPEN | SWB_USETAB))
&& cmdmod.cmod_tab == 0)
wp = swbuf_goto_win_with_buf(buflist_findname_exp(ptr));
if (wp == NULL && win_split(0, 0) == OK)
2004-06-13 20:20:40 +00:00
{
RESET_BINDING(curwin);
if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL,
ECMD_HIDE, NULL) == FAIL)
{
// Failed to open the file, close the window
// opened for it.
win_close(curwin, FALSE);
goto_tabpage_win(oldtab, oldwin);
}
else
wp = curwin;
}
if (wp != NULL && nchar == 'F' && lnum >= 0)
{
curwin->w_cursor.lnum = lnum;
check_cursor_lnum();
beginline(BL_SOL | BL_FIX);
2004-06-13 20:20:40 +00:00
}
vim_free(ptr);
}
break;
#ifdef FEAT_FIND_ID
// Go to the first occurrence of the identifier under cursor along path in a
// new window -- webb
case 'i': // Go to any match
2004-06-13 20:20:40 +00:00
case Ctrl_I:
type = FIND_ANY;
// FALLTHROUGH
case 'd': // Go to definition, using 'define'
2004-06-13 20:20:40 +00:00
case Ctrl_D:
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
break;
// Make a copy, if the line was changed it will be freed.
ptr = vim_strnsave(ptr, len);
if (ptr == NULL)
break;
2004-06-13 20:20:40 +00:00
find_pattern_in_path(ptr, 0, len, TRUE,
Prenum == 0 ? TRUE : FALSE, type,
Prenum1, ACTION_SPLIT, (linenr_T)1, (linenr_T)MAXLNUM, FALSE);
vim_free(ptr);
2004-06-13 20:20:40 +00:00
curwin->w_set_curswant = TRUE;
break;
#endif
// Quickfix window only: view the result under the cursor in a new split.
#if defined(FEAT_QUICKFIX)
2005-02-26 23:04:13 +00:00
case K_KENTER:
case CAR:
if (bt_quickfix(curbuf))
qf_view_result(TRUE);
2005-02-26 23:04:13 +00:00
break;
#endif
2005-02-26 23:04:13 +00:00
// CTRL-W g extended commands
2004-06-13 20:20:40 +00:00
case 'g':
case Ctrl_G:
CHECK_CMDWIN;
2004-06-13 20:20:40 +00:00
#ifdef USE_ON_FLY_SCROLL
dont_scroll = TRUE; // disallow scrolling here
2004-06-13 20:20:40 +00:00
#endif
++no_mapping;
++allow_keys; // no mapping for xchar, but allow key codes
2004-06-13 20:20:40 +00:00
if (xchar == NUL)
2007-09-13 16:26:47 +00:00
xchar = plain_vgetc();
2004-06-13 20:20:40 +00:00
LANGMAP_ADJUST(xchar, TRUE);
--no_mapping;
--allow_keys;
(void)add_to_showcmd(xchar);
2004-06-13 20:20:40 +00:00
switch (xchar)
{
#if defined(FEAT_QUICKFIX)
case '}':
xchar = Ctrl_RSB;
if (Prenum)
g_do_tagpreview = Prenum;
else
g_do_tagpreview = p_pvh;
#endif
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case ']':
case Ctrl_RSB:
// keep Visual mode, can select words to use as a tag
2004-06-13 20:20:40 +00:00
if (Prenum)
postponed_split = Prenum;
else
postponed_split = -1;
// Execute the command right here, required when
// "wincmd g}" was used in a function.
2004-06-13 20:20:40 +00:00
do_nv_ident('g', xchar);
postponed_split = 0;
2004-06-13 20:20:40 +00:00
break;
case 'f': // CTRL-W gf: "gf" in a new tab page
case 'F': // CTRL-W gF: "gF" in a new tab page
cmdmod.cmod_tab = tabpage_index(curtab) + 1;
2006-04-21 22:12:41 +00:00
nchar = xchar;
2006-04-06 20:18:50 +00:00
goto wingotofile;
case 't': // CTRL-W gt: go to next tab page
goto_tabpage((int)Prenum);
break;
case 'T': // CTRL-W gT: go to previous tab page
goto_tabpage(-(int)Prenum1);
break;
case TAB: // CTRL-W g<Tab>: go to last used tab page
if (goto_tabpage_lastused() == FAIL)
beep_flush();
break;
2004-06-13 20:20:40 +00:00
default:
beep_flush();
break;
}
break;
default: beep_flush();
break;
}
}
/*
* Figure out the address type for ":wincmd".
*/
void
get_wincmd_addr_type(char_u *arg, exarg_T *eap)
{
switch (*arg)
{
case 'S':
case Ctrl_S:
case 's':
case Ctrl_N:
case 'n':
case 'j':
case Ctrl_J:
case 'k':
case Ctrl_K:
case 'T':
case Ctrl_R:
case 'r':
case 'R':
case 'K':
case 'J':
case '+':
case '-':
case Ctrl__:
case '_':
case '|':
case ']':
case Ctrl_RSB:
case 'g':
case Ctrl_G:
case Ctrl_V:
case 'v':
case 'h':
case Ctrl_H:
case 'l':
case Ctrl_L:
case 'H':
case 'L':
case '>':
case '<':
#if defined(FEAT_QUICKFIX)
case '}':
#endif
case 'f':
case 'F':
case Ctrl_F:
#ifdef FEAT_FIND_ID
case 'i':
case Ctrl_I:
case 'd':
case Ctrl_D:
#endif
// window size or any count
eap->addr_type = ADDR_OTHER;
break;
case Ctrl_HAT:
case '^':
// buffer number
eap->addr_type = ADDR_BUFFERS;
break;
case Ctrl_Q:
case 'q':
case Ctrl_C:
case 'c':
case Ctrl_O:
case 'o':
case Ctrl_W:
case 'w':
case 'W':
case 'x':
case Ctrl_X:
// window number
eap->addr_type = ADDR_WINDOWS;
break;
#if defined(FEAT_QUICKFIX)
case Ctrl_Z:
case 'z':
case 'P':
#endif
case 't':
case Ctrl_T:
case 'b':
case Ctrl_B:
case 'p':
case Ctrl_P:
case '=':
case CAR:
// no count
eap->addr_type = ADDR_NONE;
break;
}
}
static void
cmd_with_count(
char *cmd,
char_u *bufp,
size_t bufsize,
long Prenum)
{
if (Prenum > 0)
vim_snprintf((char *)bufp, bufsize, "%s %ld", cmd, Prenum);
else
STRCPY(bufp, cmd);
}
/*
* If "split_disallowed" is set, or "wp"'s buffer is closing, give an error and
* return FAIL. Otherwise return OK.
*/
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
int
check_split_disallowed(win_T *wp)
{
if (split_disallowed > 0)
{
emsg(_(e_cant_split_window_while_closing_another));
return FAIL;
}
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
if (wp->w_buffer->b_locked_split)
{
emsg(_(e_cannot_split_window_when_closing_buffer));
return FAIL;
}
return OK;
}
2004-06-13 20:20:40 +00:00
/*
* split the current window, implements CTRL-W s and :split
*
* "size" is the height or width for the new window, 0 to use half of current
* height or width.
*
* "flags":
* WSP_ROOM: require enough room for new window
* WSP_VERT: vertical split.
* WSP_TOP: open window at the top-left of the shell (help window).
* WSP_BOT: open window at the bottom-right of the shell (quickfix window).
* WSP_HELP: creating the help window, keep layout snapshot
*
* return FAIL for failure, OK otherwise
*/
int
win_split(int size, int flags)
2004-06-13 20:20:40 +00:00
{
if (ERROR_IF_ANY_POPUP_WINDOW)
return FAIL;
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
if (check_split_disallowed(curwin) == FAIL)
return FAIL;
// When the ":tab" modifier was used open a new tab page instead.
2006-02-23 21:26:58 +00:00
if (may_open_tabpage() == OK)
return OK;
// Add flags from ":vertical", ":topleft" and ":botright".
flags |= cmdmod.cmod_split;
2004-06-13 20:20:40 +00:00
if ((flags & WSP_TOP) && (flags & WSP_BOT))
{
emsg(_(e_cant_split_topleft_and_botright_at_the_same_time));
2004-06-13 20:20:40 +00:00
return FAIL;
}
// When creating the help window make a snapshot of the window layout.
// Otherwise clear the snapshot, it's now invalid.
2004-06-13 20:20:40 +00:00
if (flags & WSP_HELP)
2009-06-16 14:01:43 +00:00
make_snapshot(SNAP_HELP_IDX);
2004-06-13 20:20:40 +00:00
else
2009-06-16 14:01:43 +00:00
clear_snapshot(curtab, SNAP_HELP_IDX);
2004-06-13 20:20:40 +00:00
return win_split_ins(size, flags, NULL, 0, NULL);
2004-06-13 20:20:40 +00:00
}
/*
* When "new_wp" is NULL: split the current window in two.
* When "new_wp" is not NULL: insert this window at the far
2004-06-13 20:20:40 +00:00
* top/left/right/bottom.
* When "to_flatten" is not NULL: flatten this frame before reorganising frames;
* remains unflattened on failure.
*
patch 9.1.0117: Stop split-moving from firing WinNew and WinNewPre autocommands Problem: win_splitmove fires WinNewPre and possibly WinNew when moving windows, even though no new windows are created. Solution: don't fire WinNew and WinNewPre when inserting an existing window, even if it isn't the current window. Improve the accuracy of related documentation. (Sean Dewar) Likewise, before this patch, WinClosed was not fired anyway (even for :wincmd H/J/K/L, which also didn't fire WinNew, but did still fire WinNewPre), despite documentation saying windows are "closed". Note that :wincmd T actually indeed works by creating a new window (and closing the old one), unlike the others. This also fixes issues where WinNewPre is fired when split-moving while curwin doesn't yet have a frame or entry in the window list, causing many things to not work (it's not considered valid at that point). This was guaranteed when using :wincmd H/J/K/L. Because WinNewPre is no longer fired when split-moving, this makes restoring the previous window layout on failure easier, as we can be sure that frames are not resized from WinNewPre autocommands if win_split_ins fails. This allows us to use a different strategy in the following commit. -- In my opinion, this leaves questions about the current usefulness of WinNewPre. A motivation described in #10635 states how creating a new window can steal room from other windows, and how WinNewPre will be useful for detecting that, but this is also true when inserting an existing window, which now doesn't fire it. Maybe the autocommand should be changed to have a better name? There are also other issues I found with the current implementation of WinNewPre that need addressing: - it allows switching windows and tabpages, which can cause incorrect windows to be split/moved, and big problems when switching tabpages. - it fires before win_split_ins checks for room, before it makes any changes to window sizes or before it considers allocating a new window. This should be changed or documented. I hope to address some of this stuff in a different PR, if possible. related: #14038 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 21:52:31 +01:00
* On failure, if "new_wp" was not NULL, no changes will have been made to the
* window layout or sizes.
* Return FAIL for failure, OK otherwise.
2004-06-13 20:20:40 +00:00
*/
2009-06-16 14:01:43 +00:00
int
win_split_ins(
int size,
int flags,
win_T *new_wp,
int dir,
frame_T *to_flatten)
2004-06-13 20:20:40 +00:00
{
win_T *wp = new_wp;
2004-06-13 20:20:40 +00:00
win_T *oldwin;
int new_size = size;
int i;
int need_status = 0;
int do_equal = FALSE;
int needed;
int available;
int oldwin_height = 0;
int layout;
frame_T *frp, *curfrp, *frp2, *prevfrp;
2004-06-13 20:20:40 +00:00
int before;
int minheight;
int wmh1;
int did_set_fraction = FALSE;
int retval = FAIL;
// Do not redraw here, curwin->w_buffer may be invalid.
++RedrawingDisabled;
2004-06-13 20:20:40 +00:00
patch 9.1.0117: Stop split-moving from firing WinNew and WinNewPre autocommands Problem: win_splitmove fires WinNewPre and possibly WinNew when moving windows, even though no new windows are created. Solution: don't fire WinNew and WinNewPre when inserting an existing window, even if it isn't the current window. Improve the accuracy of related documentation. (Sean Dewar) Likewise, before this patch, WinClosed was not fired anyway (even for :wincmd H/J/K/L, which also didn't fire WinNew, but did still fire WinNewPre), despite documentation saying windows are "closed". Note that :wincmd T actually indeed works by creating a new window (and closing the old one), unlike the others. This also fixes issues where WinNewPre is fired when split-moving while curwin doesn't yet have a frame or entry in the window list, causing many things to not work (it's not considered valid at that point). This was guaranteed when using :wincmd H/J/K/L. Because WinNewPre is no longer fired when split-moving, this makes restoring the previous window layout on failure easier, as we can be sure that frames are not resized from WinNewPre autocommands if win_split_ins fails. This allows us to use a different strategy in the following commit. -- In my opinion, this leaves questions about the current usefulness of WinNewPre. A motivation described in #10635 states how creating a new window can steal room from other windows, and how WinNewPre will be useful for detecting that, but this is also true when inserting an existing window, which now doesn't fire it. Maybe the autocommand should be changed to have a better name? There are also other issues I found with the current implementation of WinNewPre that need addressing: - it allows switching windows and tabpages, which can cause incorrect windows to be split/moved, and big problems when switching tabpages. - it fires before win_split_ins checks for room, before it makes any changes to window sizes or before it considers allocating a new window. This should be changed or documented. I hope to address some of this stuff in a different PR, if possible. related: #14038 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 21:52:31 +01:00
if (new_wp == NULL)
trigger_winnewpre();
2004-06-13 20:20:40 +00:00
if (flags & WSP_TOP)
oldwin = firstwin;
else if (flags & WSP_BOT)
oldwin = lastwin;
else
oldwin = curwin;
// add a status line when p_ls == 1 and splitting the first window
if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0)
2004-06-13 20:20:40 +00:00
{
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
if (!(flags & WSP_FORCE_ROOM) && VISIBLE_HEIGHT(oldwin) <= p_wmh)
2004-06-13 20:20:40 +00:00
{
emsg(_(e_not_enough_room));
goto theend;
2004-06-13 20:20:40 +00:00
}
need_status = STATUS_HEIGHT;
}
2007-05-02 19:50:14 +00:00
#ifdef FEAT_GUI
// May be needed for the scrollbars that are going to change.
2007-05-02 19:50:14 +00:00
if (gui.in_use)
out_flush();
#endif
2004-06-13 20:20:40 +00:00
if (flags & WSP_VERT)
{
int wmw1;
int minwidth;
2004-06-13 20:20:40 +00:00
layout = FR_ROW;
/*
* Check if we are able to split the current window and compute its
* width.
*/
// Current window requires at least 1 space.
wmw1 = (p_wmw == 0 ? 1 : p_wmw);
needed = wmw1 + 1;
2004-06-13 20:20:40 +00:00
if (flags & WSP_ROOM)
needed += p_wiw - wmw1;
if (flags & (WSP_BOT | WSP_TOP))
2004-06-13 20:20:40 +00:00
{
minwidth = frame_minwidth(topframe, NOWIN);
2004-06-13 20:20:40 +00:00
available = topframe->fr_width;
needed += minwidth;
2004-06-13 20:20:40 +00:00
}
else if (p_ea)
{
minwidth = frame_minwidth(oldwin->w_frame, NOWIN);
prevfrp = oldwin->w_frame;
for (frp = oldwin->w_frame->fr_parent; frp != NULL;
frp = frp->fr_parent)
{
if (frp->fr_layout == FR_ROW)
FOR_ALL_FRAMES(frp2, frp->fr_child)
if (frp2 != prevfrp)
minwidth += frame_minwidth(frp2, NOWIN);
prevfrp = frp;
}
available = topframe->fr_width;
needed += minwidth;
}
2004-06-13 20:20:40 +00:00
else
{
minwidth = frame_minwidth(oldwin->w_frame, NOWIN);
available = oldwin->w_frame->fr_width;
needed += minwidth;
}
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
if (!(flags & WSP_FORCE_ROOM) && available < needed)
2004-06-13 20:20:40 +00:00
{
emsg(_(e_not_enough_room));
goto theend;
2004-06-13 20:20:40 +00:00
}
if (new_size == 0)
new_size = oldwin->w_width / 2;
if (new_size > available - minwidth - 1)
new_size = available - minwidth - 1;
if (new_size < wmw1)
new_size = wmw1;
2004-06-13 20:20:40 +00:00
// if it doesn't fit in the current window, need win_equal()
2004-06-13 20:20:40 +00:00
if (oldwin->w_width - new_size - 1 < p_wmw)
do_equal = TRUE;
2006-03-18 21:30:13 +00:00
// We don't like to take lines for the new window from a
// 'winfixwidth' window. Take them from a window to the left or right
// instead, if possible. Add one for the separator.
2006-03-18 21:30:13 +00:00
if (oldwin->w_p_wfw)
win_setwidth_win(oldwin->w_width + new_size + 1, oldwin);
2007-08-12 14:55:56 +00:00
// Only make all windows the same width if one of them (except oldwin)
// is wider than one of the split windows.
2007-08-12 14:55:56 +00:00
if (!do_equal && p_ea && size == 0 && *p_ead != 'v'
&& oldwin->w_frame->fr_parent != NULL)
2007-08-12 14:55:56 +00:00
{
frp = oldwin->w_frame->fr_parent->fr_child;
while (frp != NULL)
{
if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_width > new_size
|| frp->fr_win->w_width > oldwin->w_width
- new_size - 1))
2007-08-12 14:55:56 +00:00
{
do_equal = TRUE;
break;
}
frp = frp->fr_next;
}
}
2004-06-13 20:20:40 +00:00
}
else
{
layout = FR_COL;
/*
* Check if we are able to split the current window and compute its
* height.
*/
// Current window requires at least 1 space.
wmh1 = (p_wmh == 0 ? 1 : p_wmh) + WINBAR_HEIGHT(curwin);
needed = wmh1 + STATUS_HEIGHT;
2004-06-13 20:20:40 +00:00
if (flags & WSP_ROOM)
needed += p_wh - wmh1;
if (flags & (WSP_BOT | WSP_TOP))
2004-06-13 20:20:40 +00:00
{
minheight = frame_minheight(topframe, NOWIN) + need_status;
2004-06-13 20:20:40 +00:00
available = topframe->fr_height;
needed += minheight;
}
else if (p_ea)
{
minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
prevfrp = oldwin->w_frame;
for (frp = oldwin->w_frame->fr_parent; frp != NULL;
frp = frp->fr_parent)
{
if (frp->fr_layout == FR_COL)
FOR_ALL_FRAMES(frp2, frp->fr_child)
if (frp2 != prevfrp)
minheight += frame_minheight(frp2, NOWIN);
prevfrp = frp;
}
available = topframe->fr_height;
needed += minheight;
2004-06-13 20:20:40 +00:00
}
else
{
minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
available = oldwin->w_frame->fr_height;
needed += minheight;
2004-06-13 20:20:40 +00:00
}
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
if (!(flags & WSP_FORCE_ROOM) && available < needed)
2004-06-13 20:20:40 +00:00
{
emsg(_(e_not_enough_room));
goto theend;
2004-06-13 20:20:40 +00:00
}
oldwin_height = oldwin->w_height;
if (need_status)
{
oldwin->w_status_height = STATUS_HEIGHT;
oldwin_height -= STATUS_HEIGHT;
}
if (new_size == 0)
new_size = oldwin_height / 2;
if (new_size > available - minheight - STATUS_HEIGHT)
new_size = available - minheight - STATUS_HEIGHT;
if (new_size < wmh1)
new_size = wmh1;
2004-06-13 20:20:40 +00:00
// if it doesn't fit in the current window, need win_equal()
2004-06-13 20:20:40 +00:00
if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh)
do_equal = TRUE;
// We don't like to take lines for the new window from a
// 'winfixheight' window. Take them from a window above or below
// instead, if possible.
2004-06-13 20:20:40 +00:00
if (oldwin->w_p_wfh)
{
// Set w_fraction now so that the cursor keeps the same relative
// vertical position using the old height.
set_fraction(oldwin);
did_set_fraction = TRUE;
2004-06-13 20:20:40 +00:00
win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT,
oldwin);
oldwin_height = oldwin->w_height;
if (need_status)
oldwin_height -= STATUS_HEIGHT;
}
2007-08-12 14:55:56 +00:00
// Only make all windows the same height if one of them (except oldwin)
// is higher than one of the split windows.
if (!do_equal && p_ea && size == 0 && *p_ead != 'h'
2007-08-12 14:55:56 +00:00
&& oldwin->w_frame->fr_parent != NULL)
{
frp = oldwin->w_frame->fr_parent->fr_child;
while (frp != NULL)
{
if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_height > new_size
|| frp->fr_win->w_height > oldwin_height - new_size
- STATUS_HEIGHT))
{
do_equal = TRUE;
break;
}
frp = frp->fr_next;
}
}
2004-06-13 20:20:40 +00:00
}
/*
* allocate new window structure and link it in the window list
*/
if ((flags & WSP_TOP) == 0
&& ((flags & WSP_BOT)
|| (flags & WSP_BELOW)
|| (!(flags & WSP_ABOVE)
&& ( (flags & WSP_VERT) ? p_spr : p_sb))))
2004-06-13 20:20:40 +00:00
{
// new window below/right of current one
if (new_wp == NULL)
2009-06-16 14:01:43 +00:00
wp = win_alloc(oldwin, FALSE);
2004-06-13 20:20:40 +00:00
else
win_append(oldwin, wp);
}
else
{
if (new_wp == NULL)
2009-06-16 14:01:43 +00:00
wp = win_alloc(oldwin->w_prev, FALSE);
2004-06-13 20:20:40 +00:00
else
win_append(oldwin->w_prev, wp);
}
if (new_wp == NULL)
2004-06-13 20:20:40 +00:00
{
if (wp == NULL)
goto theend;
2004-06-13 20:20:40 +00:00
2009-06-16 14:01:43 +00:00
new_frame(wp);
if (wp->w_frame == NULL)
{
win_free(wp, NULL);
goto theend;
2009-06-16 14:01:43 +00:00
}
// make the contents of the new window the same as the current one
2009-02-22 01:37:59 +00:00
win_init(wp, curwin, flags);
2004-06-13 20:20:40 +00:00
}
// Going to reorganize frames now, make sure they're flat.
if (to_flatten != NULL)
frame_flatten(to_flatten);
2004-06-13 20:20:40 +00:00
/*
* Reorganise the tree of frames to insert the new window.
*/
if (flags & (WSP_TOP | WSP_BOT))
{
if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0)
|| (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0))
{
curfrp = topframe->fr_child;
if (flags & WSP_BOT)
while (curfrp->fr_next != NULL)
curfrp = curfrp->fr_next;
}
else
curfrp = topframe;
before = (flags & WSP_TOP);
}
else
{
curfrp = oldwin->w_frame;
if (flags & WSP_BELOW)
before = FALSE;
else if (flags & WSP_ABOVE)
before = TRUE;
else if (flags & WSP_VERT)
2004-06-13 20:20:40 +00:00
before = !p_spr;
else
before = !p_sb;
}
if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout)
{
// Need to create a new frame in the tree to make a branch.
frp = ALLOC_CLEAR_ONE(frame_T);
2004-06-13 20:20:40 +00:00
*frp = *curfrp;
curfrp->fr_layout = layout;
frp->fr_parent = curfrp;
frp->fr_next = NULL;
frp->fr_prev = NULL;
curfrp->fr_child = frp;
curfrp->fr_win = NULL;
curfrp = frp;
if (frp->fr_win != NULL)
oldwin->w_frame = frp;
else
FOR_ALL_FRAMES(frp, frp->fr_child)
2004-06-13 20:20:40 +00:00
frp->fr_parent = curfrp;
}
if (new_wp == NULL)
2009-06-16 14:01:43 +00:00
frp = wp->w_frame;
2004-06-13 20:20:40 +00:00
else
frp = new_wp->w_frame;
2004-06-13 20:20:40 +00:00
frp->fr_parent = curfrp->fr_parent;
// Insert the new frame at the right place in the frame list.
2004-06-13 20:20:40 +00:00
if (before)
frame_insert(curfrp, frp);
else
frame_append(curfrp, frp);
// Set w_fraction now so that the cursor keeps the same relative
// vertical position.
if (!did_set_fraction)
set_fraction(oldwin);
wp->w_fraction = oldwin->w_fraction;
2004-06-13 20:20:40 +00:00
if (flags & WSP_VERT)
{
wp->w_p_scr = curwin->w_p_scr;
2004-06-13 20:20:40 +00:00
if (need_status)
{
win_new_height(oldwin, oldwin->w_height - 1);
2004-06-13 20:20:40 +00:00
oldwin->w_status_height = need_status;
}
if (flags & (WSP_TOP | WSP_BOT))
{
// set height and row of new window to full height
2006-02-24 23:53:04 +00:00
wp->w_winrow = tabline_height();
win_new_height(wp, curfrp->fr_height - (p_ls > 0)
- WINBAR_HEIGHT(wp));
2004-06-13 20:20:40 +00:00
wp->w_status_height = (p_ls > 0);
}
else
{
// height and row of new window is same as current window
2004-06-13 20:20:40 +00:00
wp->w_winrow = oldwin->w_winrow;
win_new_height(wp, VISIBLE_HEIGHT(oldwin));
2004-06-13 20:20:40 +00:00
wp->w_status_height = oldwin->w_status_height;
}
frp->fr_height = curfrp->fr_height;
// "new_size" of the current window goes to the new window, use
// one column for the vertical separator
win_new_width(wp, new_size);
2004-06-13 20:20:40 +00:00
if (before)
wp->w_vsep_width = 1;
else
{
wp->w_vsep_width = oldwin->w_vsep_width;
oldwin->w_vsep_width = 1;
}
if (flags & (WSP_TOP | WSP_BOT))
{
if (flags & WSP_BOT)
frame_add_vsep(curfrp);
// Set width of neighbor frame
2004-06-13 20:20:40 +00:00
frame_new_width(curfrp, curfrp->fr_width
2006-03-18 21:30:13 +00:00
- (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP,
FALSE);
2004-06-13 20:20:40 +00:00
}
else
2006-03-18 21:30:13 +00:00
win_new_width(oldwin, oldwin->w_width - (new_size + 1));
if (before) // new window left of current one
2004-06-13 20:20:40 +00:00
{
wp->w_wincol = oldwin->w_wincol;
oldwin->w_wincol += new_size + 1;
}
else // new window right of current one
2004-06-13 20:20:40 +00:00
wp->w_wincol = oldwin->w_wincol + oldwin->w_width + 1;
frame_fix_width(oldwin);
frame_fix_width(wp);
}
else
{
// width and column of new window is same as current window
2004-06-13 20:20:40 +00:00
if (flags & (WSP_TOP | WSP_BOT))
{
wp->w_wincol = 0;
win_new_width(wp, Columns);
2004-06-13 20:20:40 +00:00
wp->w_vsep_width = 0;
}
else
{
wp->w_wincol = oldwin->w_wincol;
win_new_width(wp, oldwin->w_width);
2004-06-13 20:20:40 +00:00
wp->w_vsep_width = oldwin->w_vsep_width;
}
frp->fr_width = curfrp->fr_width;
// "new_size" of the current window goes to the new window, use
// one row for the status line
2004-06-13 20:20:40 +00:00
win_new_height(wp, new_size);
int old_status_height = oldwin->w_status_height;
2004-06-13 20:20:40 +00:00
if (flags & (WSP_TOP | WSP_BOT))
{
int new_fr_height = curfrp->fr_height - new_size
+ WINBAR_HEIGHT(wp);
if (!((flags & WSP_BOT) && p_ls == 0))
new_fr_height -= STATUS_HEIGHT;
if (flags & WSP_BOT)
frame_add_statusline(curfrp);
frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, FALSE,
FALSE);
}
2004-06-13 20:20:40 +00:00
else
win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
if (before) // new window above current one
2004-06-13 20:20:40 +00:00
{
wp->w_winrow = oldwin->w_winrow;
wp->w_status_height = STATUS_HEIGHT;
oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
}
else // new window below current one
2004-06-13 20:20:40 +00:00
{
wp->w_winrow = oldwin->w_winrow + VISIBLE_HEIGHT(oldwin)
+ STATUS_HEIGHT;
wp->w_status_height = old_status_height;
if (!(flags & WSP_BOT))
oldwin->w_status_height = STATUS_HEIGHT;
2004-06-13 20:20:40 +00:00
}
frame_fix_height(wp);
frame_fix_height(oldwin);
}
if (flags & (WSP_TOP | WSP_BOT))
(void)win_comp_pos();
// Both windows need redrawing. Update all status lines, in case they
// show something related to the window count or position.
redraw_win_later(wp, UPD_NOT_VALID);
redraw_win_later(oldwin, UPD_NOT_VALID);
status_redraw_all();
2004-06-13 20:20:40 +00:00
if (need_status)
{
msg_row = Rows - 1;
msg_col = sc_col;
msg_clr_eos_force(); // Old command/ruler may still be there
2004-06-13 20:20:40 +00:00
comp_col();
msg_row = Rows - 1;
msg_col = 0; // put position back at start of line
2004-06-13 20:20:40 +00:00
}
/*
* equalize the window sizes.
2004-06-13 20:20:40 +00:00
*/
if (do_equal || dir != 0)
win_equal(wp, TRUE,
(flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
: dir == 'h' ? 'b' : 'v');
else if (!is_aucmd_win(wp))
win_fix_scroll(FALSE);
2004-06-13 20:20:40 +00:00
// Don't change the window height/width to 'winheight' / 'winwidth' if a
// size was given.
2004-06-13 20:20:40 +00:00
if (flags & WSP_VERT)
{
i = p_wiw;
if (size != 0)
p_wiw = size;
# ifdef FEAT_GUI
// When 'guioptions' includes 'L' or 'R' may have to add scrollbars.
2004-06-13 20:20:40 +00:00
if (gui.in_use)
gui_init_which_components(NULL);
# endif
}
else
{
i = p_wh;
if (size != 0)
p_wh = size;
}
/*
* make the new window the current window
*/
patch 9.1.0117: Stop split-moving from firing WinNew and WinNewPre autocommands Problem: win_splitmove fires WinNewPre and possibly WinNew when moving windows, even though no new windows are created. Solution: don't fire WinNew and WinNewPre when inserting an existing window, even if it isn't the current window. Improve the accuracy of related documentation. (Sean Dewar) Likewise, before this patch, WinClosed was not fired anyway (even for :wincmd H/J/K/L, which also didn't fire WinNew, but did still fire WinNewPre), despite documentation saying windows are "closed". Note that :wincmd T actually indeed works by creating a new window (and closing the old one), unlike the others. This also fixes issues where WinNewPre is fired when split-moving while curwin doesn't yet have a frame or entry in the window list, causing many things to not work (it's not considered valid at that point). This was guaranteed when using :wincmd H/J/K/L. Because WinNewPre is no longer fired when split-moving, this makes restoring the previous window layout on failure easier, as we can be sure that frames are not resized from WinNewPre autocommands if win_split_ins fails. This allows us to use a different strategy in the following commit. -- In my opinion, this leaves questions about the current usefulness of WinNewPre. A motivation described in #10635 states how creating a new window can steal room from other windows, and how WinNewPre will be useful for detecting that, but this is also true when inserting an existing window, which now doesn't fire it. Maybe the autocommand should be changed to have a better name? There are also other issues I found with the current implementation of WinNewPre that need addressing: - it allows switching windows and tabpages, which can cause incorrect windows to be split/moved, and big problems when switching tabpages. - it fires before win_split_ins checks for room, before it makes any changes to window sizes or before it considers allocating a new window. This should be changed or documented. I hope to address some of this stuff in a different PR, if possible. related: #14038 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 21:52:31 +01:00
(void)win_enter_ext(wp, (new_wp == NULL ? WEE_TRIGGER_NEW_AUTOCMDS : 0)
| WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS);
2004-06-13 20:20:40 +00:00
if (flags & WSP_VERT)
p_wiw = i;
else
p_wh = i;
retval = OK;
2004-06-13 20:20:40 +00:00
theend:
if (RedrawingDisabled > 0)
--RedrawingDisabled;
return retval;
2004-06-13 20:20:40 +00:00
}
2009-06-16 14:01:43 +00:00
2006-02-20 21:27:21 +00:00
/*
* Initialize window "newp" from window "oldp".
* Used when splitting a window and when creating a new tab page.
* The windows will both edit the same buffer.
2009-02-22 01:37:59 +00:00
* WSP_NEWLOC may be specified in flags to prevent the location list from
* being copied.
2006-02-20 21:27:21 +00:00
*/
static void
win_init(win_T *newp, win_T *oldp, int flags UNUSED)
2006-02-20 21:27:21 +00:00
{
int i;
newp->w_buffer = oldp->w_buffer;
#ifdef FEAT_SYN_HL
newp->w_s = &(oldp->w_buffer->b_s);
#endif
2006-02-20 21:27:21 +00:00
oldp->w_buffer->b_nwindows++;
newp->w_cursor = oldp->w_cursor;
newp->w_valid = 0;
newp->w_curswant = oldp->w_curswant;
newp->w_set_curswant = oldp->w_set_curswant;
newp->w_topline = oldp->w_topline;
#ifdef FEAT_DIFF
newp->w_topfill = oldp->w_topfill;
#endif
newp->w_leftcol = oldp->w_leftcol;
newp->w_pcmark = oldp->w_pcmark;
newp->w_prev_pcmark = oldp->w_prev_pcmark;
newp->w_alt_fnum = oldp->w_alt_fnum;
2006-04-11 21:38:50 +00:00
newp->w_wrow = oldp->w_wrow;
2006-02-20 21:27:21 +00:00
newp->w_fraction = oldp->w_fraction;
newp->w_prev_fraction_row = oldp->w_prev_fraction_row;
copy_jumplist(oldp, newp);
#ifdef FEAT_QUICKFIX
2009-02-22 01:37:59 +00:00
if (flags & WSP_NEWLOC)
{
// Don't copy the location list.
2009-02-22 01:37:59 +00:00
newp->w_llist = NULL;
newp->w_llist_ref = NULL;
}
else
copy_loclist_stack(oldp, newp);
2006-02-20 21:27:21 +00:00
#endif
newp->w_localdir = (oldp->w_localdir == NULL)
? NULL : vim_strsave(oldp->w_localdir);
newp->w_prevdir = (oldp->w_prevdir == NULL)
? NULL : vim_strsave(oldp->w_prevdir);
2006-02-20 21:27:21 +00:00
if (*p_spk != 'c')
{
if (*p_spk == 't')
newp->w_skipcol = oldp->w_skipcol;
newp->w_botline = oldp->w_botline;
newp->w_prev_height = oldp->w_height - WINBAR_HEIGHT(oldp);
newp->w_prev_winrow = oldp->w_winrow + 2 * WINBAR_HEIGHT(oldp);
}
// copy tagstack and folds
2006-02-20 21:27:21 +00:00
for (i = 0; i < oldp->w_tagstacklen; i++)
{
taggy_T *tag = &newp->w_tagstack[i];
*tag = oldp->w_tagstack[i];
if (tag->tagname != NULL)
tag->tagname = vim_strsave(tag->tagname);
if (tag->user_data != NULL)
tag->user_data = vim_strsave(tag->user_data);
2006-02-20 21:27:21 +00:00
}
newp->w_tagstackidx = oldp->w_tagstackidx;
newp->w_tagstacklen = oldp->w_tagstacklen;
// Keep same changelist position in new window.
newp->w_changelistidx = oldp->w_changelistidx;
#ifdef FEAT_FOLDING
2006-02-20 21:27:21 +00:00
copyFoldingState(oldp, newp);
#endif
2009-06-16 14:01:43 +00:00
win_init_some(newp, oldp);
#ifdef FEAT_TERMINAL
term_update_wincolor(newp);
#endif
2009-06-16 14:01:43 +00:00
}
/*
* Initialize window "newp" from window "old".
2009-06-16 14:01:43 +00:00
* Only the essential things are copied.
*/
static void
win_init_some(win_T *newp, win_T *oldp)
2009-06-16 14:01:43 +00:00
{
// Use the same argument list.
2009-06-16 14:01:43 +00:00
newp->w_alist = oldp->w_alist;
++newp->w_alist->al_refcount;
newp->w_arg_idx = oldp->w_arg_idx;
// copy options from existing window
2009-06-16 14:01:43 +00:00
win_copy_options(oldp, newp);
2006-02-20 21:27:21 +00:00
}
/*
* Return TRUE if "win" is a global popup or a popup in the current tab page.
*/
int
win_valid_popup(win_T *win UNUSED)
{
#ifdef FEAT_PROP_POPUP
win_T *wp;
FOR_ALL_POPUPWINS(wp)
if (wp == win)
return TRUE;
FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
if (wp == win)
return TRUE;
#endif
return FALSE;
}
2004-06-13 20:20:40 +00:00
/*
* Check if "win" is a pointer to an existing window in the current tab page.
2004-06-13 20:20:40 +00:00
*/
int
win_valid(win_T *win)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
if (win == NULL)
return FALSE;
FOR_ALL_WINDOWS(wp)
2004-06-13 20:20:40 +00:00
if (wp == win)
return TRUE;
return win_valid_popup(win);
2004-06-13 20:20:40 +00:00
}
/*
* Find window "id" in the current tab page.
* Also find popup windows.
* Return NULL if not found.
*/
win_T *
win_find_by_id(int id)
{
win_T *wp;
FOR_ALL_WINDOWS(wp)
if (wp->w_id == id)
return wp;
#ifdef FEAT_PROP_POPUP
FOR_ALL_POPUPWINS(wp)
if (wp->w_id == id)
return wp;
FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
if (wp->w_id == id)
return wp;
#endif
return NULL;
}
/*
* Check if "win" is a pointer to an existing window in any tab page.
*/
int
win_valid_any_tab(win_T *win)
{
win_T *wp;
tabpage_T *tp;
if (win == NULL)
return FALSE;
FOR_ALL_TABPAGES(tp)
{
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
{
if (wp == win)
return TRUE;
}
#ifdef FEAT_PROP_POPUP
FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
if (wp == win)
return TRUE;
#endif
}
return win_valid_popup(win);
}
2004-06-13 20:20:40 +00:00
/*
* Return the number of windows.
*/
int
win_count(void)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
int count = 0;
FOR_ALL_WINDOWS(wp)
2004-06-13 20:20:40 +00:00
++count;
return count;
}
/*
* Make "count" windows on the screen.
* Return actual number of windows on the screen.
* Must be called when there is just one window, filling the whole screen
* (excluding the command line).
*/
int
make_windows(
int count,
int vertical) // split windows vertically if TRUE
2004-06-13 20:20:40 +00:00
{
int maxcount;
int todo;
if (vertical)
{
// Each window needs at least 'winminwidth' lines and a separator
// column.
2004-06-13 20:20:40 +00:00
maxcount = (curwin->w_width + curwin->w_vsep_width
- (p_wiw - p_wmw)) / (p_wmw + 1);
}
else
{
// Each window needs at least 'winminheight' lines and a status line.
maxcount = (VISIBLE_HEIGHT(curwin) + curwin->w_status_height
2004-06-13 20:20:40 +00:00
- (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
}
if (maxcount < 2)
maxcount = 2;
if (count > maxcount)
count = maxcount;
/*
* add status line now, otherwise first window will be too big
*/
if (count > 1)
last_status(TRUE);
/*
* Don't execute autocommands while creating the windows. Must do that
* when putting the buffers in the windows.
*/
2007-09-29 12:16:41 +00:00
block_autocmds();
2004-06-13 20:20:40 +00:00
// todo is number of windows left to create
2004-06-13 20:20:40 +00:00
for (todo = count - 1; todo > 0; --todo)
if (vertical)
{
if (win_split(curwin->w_width - (curwin->w_width - todo)
/ (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL)
break;
}
else
{
if (win_split(curwin->w_height - (curwin->w_height - todo
* STATUS_HEIGHT) / (todo + 1)
- STATUS_HEIGHT, WSP_ABOVE) == FAIL)
break;
}
2007-09-29 12:16:41 +00:00
unblock_autocmds();
2004-06-13 20:20:40 +00:00
// return actual number of windows
2004-06-13 20:20:40 +00:00
return (count - todo);
}
/*
* Exchange current and next window
*/
static void
win_exchange(long Prenum)
2004-06-13 20:20:40 +00:00
{
frame_T *frp;
frame_T *frp2;
win_T *wp;
win_T *wp2;
int temp;
if (ERROR_IF_ANY_POPUP_WINDOW)
return;
if (ONE_WINDOW) // just one window
2004-06-13 20:20:40 +00:00
{
beep_flush();
return;
}
if (text_or_buf_locked())
{
beep_flush();
return;
}
2004-06-13 20:20:40 +00:00
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
/*
* find window to exchange with
*/
if (Prenum)
{
frp = curwin->w_frame->fr_parent->fr_child;
while (frp != NULL && --Prenum > 0)
frp = frp->fr_next;
}
else if (curwin->w_frame->fr_next != NULL) // Swap with next
2004-06-13 20:20:40 +00:00
frp = curwin->w_frame->fr_next;
else // Swap last window in row/col with previous
2004-06-13 20:20:40 +00:00
frp = curwin->w_frame->fr_prev;
// We can only exchange a window with another window, not with a frame
// containing windows.
2004-06-13 20:20:40 +00:00
if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin)
return;
wp = frp->fr_win;
/*
* 1. remove curwin from the list. Remember after which window it was in wp2
* 2. insert curwin before wp in the list
* if wp != wp2
* 3. remove wp from the list
* 4. insert wp after wp2
* 5. exchange the status line height and vsep width.
*/
wp2 = curwin->w_prev;
frp2 = curwin->w_frame->fr_prev;
if (wp->w_prev != curwin)
{
2006-02-16 22:11:02 +00:00
win_remove(curwin, NULL);
2004-06-13 20:20:40 +00:00
frame_remove(curwin->w_frame);
win_append(wp->w_prev, curwin);
frame_insert(frp, curwin->w_frame);
}
if (wp != wp2)
{
2006-02-16 22:11:02 +00:00
win_remove(wp, NULL);
2004-06-13 20:20:40 +00:00
frame_remove(wp->w_frame);
win_append(wp2, wp);
if (frp2 == NULL)
frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame);
else
frame_append(frp2, wp->w_frame);
}
temp = curwin->w_status_height;
curwin->w_status_height = wp->w_status_height;
wp->w_status_height = temp;
temp = curwin->w_vsep_width;
curwin->w_vsep_width = wp->w_vsep_width;
wp->w_vsep_width = temp;
frame_fix_height(curwin);
frame_fix_height(wp);
frame_fix_width(curwin);
frame_fix_width(wp);
2004-06-13 20:20:40 +00:00
(void)win_comp_pos(); // recompute window positions
2004-06-13 20:20:40 +00:00
if (wp->w_buffer != curbuf)
reset_VIsual_and_resel();
else if (VIsual_active)
wp->w_cursor = curwin->w_cursor;
2004-06-13 20:20:40 +00:00
win_enter(wp, TRUE);
redraw_all_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
/*
* rotate windows: if upwards TRUE the second window becomes the first one
* if upwards FALSE the first window becomes the second one
*/
static void
win_rotate(int upwards, int count)
2004-06-13 20:20:40 +00:00
{
win_T *wp1;
win_T *wp2;
frame_T *frp;
int n;
if (ONE_WINDOW) // nothing to do
2004-06-13 20:20:40 +00:00
{
beep_flush();
return;
}
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
// Check if all frames in this row/col have one window.
FOR_ALL_FRAMES(frp, curwin->w_frame->fr_parent->fr_child)
2004-06-13 20:20:40 +00:00
if (frp->fr_win == NULL)
{
emsg(_(e_cannot_rotate_when_another_window_is_split));
2004-06-13 20:20:40 +00:00
return;
}
while (count--)
{
if (upwards) // first window becomes last window
2004-06-13 20:20:40 +00:00
{
// remove first window/frame from the list
2004-06-13 20:20:40 +00:00
frp = curwin->w_frame->fr_parent->fr_child;
wp1 = frp->fr_win;
2006-02-16 22:11:02 +00:00
win_remove(wp1, NULL);
2004-06-13 20:20:40 +00:00
frame_remove(frp);
// find last frame and append removed window/frame after it
2004-06-13 20:20:40 +00:00
for ( ; frp->fr_next != NULL; frp = frp->fr_next)
;
win_append(frp->fr_win, wp1);
frame_append(frp, wp1->w_frame);
wp2 = frp->fr_win; // previously last window
2004-06-13 20:20:40 +00:00
}
else // last window becomes first window
2004-06-13 20:20:40 +00:00
{
// find last window/frame in the list and remove it
2004-06-13 20:20:40 +00:00
for (frp = curwin->w_frame; frp->fr_next != NULL;
frp = frp->fr_next)
;
wp1 = frp->fr_win;
wp2 = wp1->w_prev; // will become last window
2006-02-16 22:11:02 +00:00
win_remove(wp1, NULL);
2004-06-13 20:20:40 +00:00
frame_remove(frp);
// append the removed window/frame before the first in the list
2004-06-13 20:20:40 +00:00
win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1);
frame_insert(frp->fr_parent->fr_child, frp);
}
// exchange status height and vsep width of old and new last window
2004-06-13 20:20:40 +00:00
n = wp2->w_status_height;
wp2->w_status_height = wp1->w_status_height;
wp1->w_status_height = n;
frame_fix_height(wp1);
frame_fix_height(wp2);
n = wp2->w_vsep_width;
wp2->w_vsep_width = wp1->w_vsep_width;
wp1->w_vsep_width = n;
frame_fix_width(wp1);
frame_fix_width(wp2);
// recompute w_winrow and w_wincol for all windows
2004-06-13 20:20:40 +00:00
(void)win_comp_pos();
}
redraw_all_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
/*
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
* Move "wp" into a new split in a given direction, possibly relative to the
* current window.
* "wp" must be valid in the current tabpage.
* Returns FAIL for failure, OK otherwise.
2004-06-13 20:20:40 +00:00
*/
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
int
win_splitmove(win_T *wp, int size, int flags)
2004-06-13 20:20:40 +00:00
{
int dir;
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
int height = wp->w_height;
frame_T *unflat_altfr;
2004-06-13 20:20:40 +00:00
if (ONE_WINDOW)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
return OK; // nothing to do
if (check_split_disallowed(wp) == FAIL)
return FAIL;
// Remove the window and frame from the tree of frames. Don't flatten any
// frames yet so we can restore things if win_split_ins fails.
winframe_remove(wp, &dir, NULL, &unflat_altfr);
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
win_remove(wp, NULL);
last_status(FALSE); // may need to remove last status line
(void)win_comp_pos(); // recompute window positions
2004-06-13 20:20:40 +00:00
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
// Split a window on the desired side and put "wp" there.
if (win_split_ins(size, flags, wp, dir, unflat_altfr) == FAIL)
2004-06-13 20:20:40 +00:00
{
// win_split_ins doesn't change sizes or layout if it fails to insert an
// existing window, so just undo winframe_remove.
winframe_restore(wp, dir, unflat_altfr);
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
win_append(wp->w_prev, wp);
return FAIL;
}
// If splitting horizontally, try to preserve height.
// Note that win_split_ins autocommands may have immediately closed "wp"!
if (size == 0 && !(flags & WSP_VERT) && win_valid(wp))
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
{
win_setheight_win(height, wp);
2004-06-13 20:20:40 +00:00
if (p_ea)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
{
// Equalize windows. Note that win_split_ins autocommands may have
// made a window other than "wp" current.
win_equal(curwin, curwin == wp, 'v');
}
2004-06-13 20:20:40 +00:00
}
#if defined(FEAT_GUI)
// When 'guioptions' includes 'L' or 'R' may have to remove or add
// scrollbars. Have to update them anyway.
2009-06-16 14:01:43 +00:00
gui_may_update_scrollbars();
2004-06-13 20:20:40 +00:00
#endif
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
return OK;
2004-06-13 20:20:40 +00:00
}
/*
* Move window "win1" to below/right of "win2" and make "win1" the current
* window. Only works within the same frame!
*/
void
win_move_after(win_T *win1, win_T *win2)
2004-06-13 20:20:40 +00:00
{
int height;
// check if the arguments are reasonable
2004-06-13 20:20:40 +00:00
if (win1 == win2)
return;
// check if there is something to do
2004-06-13 20:20:40 +00:00
if (win2->w_next != win1)
{
if (win1->w_frame->fr_parent != win2->w_frame->fr_parent)
{
iemsg("Trying to move a window into another frame");
return;
}
// may need to move the status line/vertical separator of the last
// window
2004-06-13 20:20:40 +00:00
if (win1 == lastwin)
{
height = win1->w_prev->w_status_height;
win1->w_prev->w_status_height = win1->w_status_height;
win1->w_status_height = height;
2007-02-19 23:14:18 +00:00
if (win1->w_prev->w_vsep_width == 1)
{
// Remove the vertical separator from the last-but-one window,
// add it to the last window. Adjust the frame widths.
2007-02-19 23:14:18 +00:00
win1->w_prev->w_vsep_width = 0;
win1->w_prev->w_frame->fr_width -= 1;
win1->w_vsep_width = 1;
win1->w_frame->fr_width += 1;
}
2004-06-13 20:20:40 +00:00
}
else if (win2 == lastwin)
{
height = win1->w_status_height;
win1->w_status_height = win2->w_status_height;
win2->w_status_height = height;
2007-02-19 23:14:18 +00:00
if (win1->w_vsep_width == 1)
{
// Remove the vertical separator from win1, add it to the last
// window, win2. Adjust the frame widths.
2007-02-19 23:14:18 +00:00
win2->w_vsep_width = 1;
win2->w_frame->fr_width += 1;
win1->w_vsep_width = 0;
win1->w_frame->fr_width -= 1;
}
2004-06-13 20:20:40 +00:00
}
2006-02-16 22:11:02 +00:00
win_remove(win1, NULL);
2004-06-13 20:20:40 +00:00
frame_remove(win1->w_frame);
win_append(win2, win1);
frame_append(win2->w_frame, win1->w_frame);
(void)win_comp_pos(); // recompute w_winrow for all windows
redraw_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
win_enter(win1, FALSE);
}
/*
* Make all windows the same height.
* 'next_curwin' will soon be the current window, make sure it has enough
* rows.
*/
void
win_equal(
win_T *next_curwin, // pointer to current window to be or NULL
int current, // do only frame with current window
int dir) // 'v' for vertically, 'h' for horizontally,
// 'b' for both, 0 for using p_ead
2004-06-13 20:20:40 +00:00
{
if (dir == 0)
dir = *p_ead;
win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
2006-02-24 23:53:04 +00:00
topframe, dir, 0, tabline_height(),
2006-02-14 22:29:30 +00:00
(int)Columns, topframe->fr_height);
if (!is_aucmd_win(next_curwin))
win_fix_scroll(TRUE);
2004-06-13 20:20:40 +00:00
}
/*
* Set a frame to a new position and height, spreading the available room
* equally over contained frames.
* The window "next_curwin" (if not NULL) should at least get the size from
* 'winheight' and 'winwidth' if possible.
*/
static void
win_equal_rec(
win_T *next_curwin, // pointer to current window to be or NULL
int current, // do only frame with current window
frame_T *topfr, // frame to set size off
int dir, // 'v', 'h' or 'b', see win_equal()
int col, // horizontal position for frame
int row, // vertical position for frame
int width, // new width of frame
int height) // new height of frame
2004-06-13 20:20:40 +00:00
{
int n, m;
int extra_sep = 0;
int wincount, totwincount = 0;
frame_T *fr;
int next_curwin_size = 0;
int room = 0;
int new_size;
int has_next_curwin = 0;
int hnc;
if (topfr->fr_layout == FR_LEAF)
{
// Set the width/height of this frame.
// Redraw when size or position changes
2004-06-13 20:20:40 +00:00
if (topfr->fr_height != height || topfr->fr_win->w_winrow != row
|| topfr->fr_width != width || topfr->fr_win->w_wincol != col
)
{
topfr->fr_win->w_winrow = row;
frame_new_height(topfr, height, FALSE, FALSE, FALSE);
2004-06-13 20:20:40 +00:00
topfr->fr_win->w_wincol = col;
2006-03-18 21:30:13 +00:00
frame_new_width(topfr, width, FALSE, FALSE);
redraw_all_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
}
else if (topfr->fr_layout == FR_ROW)
{
topfr->fr_width = width;
topfr->fr_height = height;
if (dir != 'v') // equalize frame widths
2004-06-13 20:20:40 +00:00
{
// Compute the maximum number of windows horizontally in this
// frame.
2004-06-13 20:20:40 +00:00
n = frame_minwidth(topfr, NOWIN);
// add one for the rightmost window, it doesn't have a separator
2004-06-13 20:20:40 +00:00
if (col + width == Columns)
extra_sep = 1;
else
extra_sep = 0;
totwincount = (n + extra_sep) / (p_wmw + 1);
2006-03-18 21:30:13 +00:00
has_next_curwin = frame_has_win(topfr, next_curwin);
2004-06-13 20:20:40 +00:00
2006-03-18 21:30:13 +00:00
/*
* Compute width for "next_curwin" window and room available for
* other windows.
* "m" is the minimal width when counting p_wiw for "next_curwin".
*/
2004-06-13 20:20:40 +00:00
m = frame_minwidth(topfr, next_curwin);
room = width - m;
if (room < 0)
{
next_curwin_size = p_wiw + room;
room = 0;
}
else
{
2006-03-18 21:30:13 +00:00
next_curwin_size = -1;
FOR_ALL_FRAMES(fr, topfr->fr_child)
2006-03-18 21:30:13 +00:00
{
if (!frame_fixed_width(fr))
continue;
// If 'winfixwidth' set keep the window width if possible.
// Watch out for this window being the next_curwin.
n = frame_minwidth(fr, NOWIN);
new_size = fr->fr_width;
if (frame_has_win(fr, next_curwin))
2006-03-18 21:30:13 +00:00
{
room += p_wiw - p_wmw;
next_curwin_size = 0;
if (new_size < p_wiw)
new_size = p_wiw;
2006-03-18 21:30:13 +00:00
}
else
// These windows don't use up room.
totwincount -= (n + (fr->fr_next == NULL
? extra_sep : 0)) / (p_wmw + 1);
room -= new_size - n;
if (room < 0)
{
new_size += room;
room = 0;
}
fr->fr_newwidth = new_size;
2006-03-18 21:30:13 +00:00
}
if (next_curwin_size == -1)
{
if (!has_next_curwin)
next_curwin_size = 0;
else if (totwincount > 1
&& (room + (totwincount - 2))
/ (totwincount - 1) > p_wiw)
{
// Can make all windows wider than 'winwidth', spread
// the room equally.
2006-04-16 18:30:08 +00:00
next_curwin_size = (room + p_wiw
+ (totwincount - 1) * p_wmw
+ (totwincount - 1)) / totwincount;
2006-03-18 21:30:13 +00:00
room -= next_curwin_size - p_wiw;
}
else
next_curwin_size = p_wiw;
}
2004-06-13 20:20:40 +00:00
}
2006-03-18 21:30:13 +00:00
if (has_next_curwin)
--totwincount; // don't count curwin
2004-06-13 20:20:40 +00:00
}
FOR_ALL_FRAMES(fr, topfr->fr_child)
2004-06-13 20:20:40 +00:00
{
wincount = 1;
if (fr->fr_next == NULL)
// last frame gets all that remains (avoid roundoff error)
2004-06-13 20:20:40 +00:00
new_size = width;
else if (dir == 'v')
new_size = fr->fr_width;
2006-03-18 21:30:13 +00:00
else if (frame_fixed_width(fr))
{
new_size = fr->fr_newwidth;
wincount = 0; // doesn't count as a sizeable window
2006-03-18 21:30:13 +00:00
}
2004-06-13 20:20:40 +00:00
else
{
// Compute the maximum number of windows horiz. in "fr".
2004-06-13 20:20:40 +00:00
n = frame_minwidth(fr, NOWIN);
wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
/ (p_wmw + 1);
m = frame_minwidth(fr, next_curwin);
2006-03-18 21:30:13 +00:00
if (has_next_curwin)
hnc = frame_has_win(fr, next_curwin);
else
hnc = FALSE;
if (hnc) // don't count next_curwin
2004-06-13 20:20:40 +00:00
--wincount;
2006-03-18 21:30:13 +00:00
if (totwincount == 0)
new_size = room;
else
new_size = (wincount * room + ((unsigned)totwincount >> 1))
2004-06-13 20:20:40 +00:00
/ totwincount;
if (hnc) // add next_curwin size
2004-06-13 20:20:40 +00:00
{
next_curwin_size -= p_wiw - (m - n);
if (next_curwin_size < 0)
next_curwin_size = 0;
2004-06-13 20:20:40 +00:00
new_size += next_curwin_size;
2006-03-18 21:30:13 +00:00
room -= new_size - next_curwin_size;
2004-06-13 20:20:40 +00:00
}
2006-03-18 21:30:13 +00:00
else
room -= new_size;
new_size += n;
2004-06-13 20:20:40 +00:00
}
// Skip frame that is full width when splitting or closing a
// window, unless equalizing all frames.
2004-06-13 20:20:40 +00:00
if (!current || dir != 'v' || topfr->fr_parent != NULL
|| (new_size != fr->fr_width)
|| frame_has_win(fr, next_curwin))
win_equal_rec(next_curwin, current, fr, dir, col, row,
2006-03-18 21:30:13 +00:00
new_size, height);
col += new_size;
width -= new_size;
2004-06-13 20:20:40 +00:00
totwincount -= wincount;
}
}
else // topfr->fr_layout == FR_COL
2004-06-13 20:20:40 +00:00
{
topfr->fr_width = width;
topfr->fr_height = height;
if (dir != 'h') // equalize frame heights
2004-06-13 20:20:40 +00:00
{
// Compute maximum number of windows vertically in this frame.
2004-06-13 20:20:40 +00:00
n = frame_minheight(topfr, NOWIN);
// add one for the bottom window if it doesn't have a statusline
2004-06-13 20:20:40 +00:00
if (row + height == cmdline_row && p_ls == 0)
extra_sep = 1;
else
extra_sep = 0;
totwincount = (n + extra_sep) / (p_wmh + 1);
has_next_curwin = frame_has_win(topfr, next_curwin);
/*
* Compute height for "next_curwin" window and room available for
* other windows.
* "m" is the minimal height when counting p_wh for "next_curwin".
*/
m = frame_minheight(topfr, next_curwin);
room = height - m;
if (room < 0)
{
// The room is less than 'winheight', use all space for the
// current window.
2004-06-13 20:20:40 +00:00
next_curwin_size = p_wh + room;
room = 0;
}
else
{
next_curwin_size = -1;
FOR_ALL_FRAMES(fr, topfr->fr_child)
2004-06-13 20:20:40 +00:00
{
if (!frame_fixed_height(fr))
continue;
// If 'winfixheight' set keep the window height if
// possible.
// Watch out for this window being the next_curwin.
n = frame_minheight(fr, NOWIN);
new_size = fr->fr_height;
if (frame_has_win(fr, next_curwin))
2004-06-13 20:20:40 +00:00
{
room += p_wh - p_wmh;
next_curwin_size = 0;
if (new_size < p_wh)
new_size = p_wh;
}
else
// These windows don't use up room.
totwincount -= (n + (fr->fr_next == NULL
? extra_sep : 0)) / (p_wmh + 1);
room -= new_size - n;
if (room < 0)
{
new_size += room;
room = 0;
2004-06-13 20:20:40 +00:00
}
fr->fr_newheight = new_size;
2004-06-13 20:20:40 +00:00
}
if (next_curwin_size == -1)
{
if (!has_next_curwin)
next_curwin_size = 0;
else if (totwincount > 1
&& (room + (totwincount - 2))
/ (totwincount - 1) > p_wh)
{
// can make all windows higher than 'winheight',
// spread the room equally.
2006-04-16 18:30:08 +00:00
next_curwin_size = (room + p_wh
+ (totwincount - 1) * p_wmh
2004-06-13 20:20:40 +00:00
+ (totwincount - 1)) / totwincount;
room -= next_curwin_size - p_wh;
}
else
next_curwin_size = p_wh;
}
}
if (has_next_curwin)
--totwincount; // don't count curwin
2004-06-13 20:20:40 +00:00
}
FOR_ALL_FRAMES(fr, topfr->fr_child)
2004-06-13 20:20:40 +00:00
{
wincount = 1;
if (fr->fr_next == NULL)
// last frame gets all that remains (avoid roundoff error)
2004-06-13 20:20:40 +00:00
new_size = height;
else if (dir == 'h')
new_size = fr->fr_height;
else if (frame_fixed_height(fr))
{
new_size = fr->fr_newheight;
wincount = 0; // doesn't count as a sizeable window
2004-06-13 20:20:40 +00:00
}
else
{
// Compute the maximum number of windows vert. in "fr".
2004-06-13 20:20:40 +00:00
n = frame_minheight(fr, NOWIN);
wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
/ (p_wmh + 1);
m = frame_minheight(fr, next_curwin);
if (has_next_curwin)
hnc = frame_has_win(fr, next_curwin);
else
hnc = FALSE;
if (hnc) // don't count next_curwin
2004-06-13 20:20:40 +00:00
--wincount;
if (totwincount == 0)
new_size = room;
else
new_size = (wincount * room + ((unsigned)totwincount >> 1))
/ totwincount;
if (hnc) // add next_curwin size
2004-06-13 20:20:40 +00:00
{
next_curwin_size -= p_wh - (m - n);
new_size += next_curwin_size;
room -= new_size - next_curwin_size;
}
else
room -= new_size;
new_size += n;
}
// Skip frame that is full width when splitting or closing a
// window, unless equalizing all frames.
2004-06-13 20:20:40 +00:00
if (!current || dir != 'h' || topfr->fr_parent != NULL
|| (new_size != fr->fr_height)
|| frame_has_win(fr, next_curwin))
win_equal_rec(next_curwin, current, fr, dir, col, row,
width, new_size);
row += new_size;
height -= new_size;
totwincount -= wincount;
}
}
}
#ifdef FEAT_JOB_CHANNEL
void
leaving_window(win_T *win)
{
// Only matters for a prompt window.
if (!bt_prompt(win->w_buffer))
return;
// When leaving a prompt window stop Insert mode and perhaps restart
// it when entering that window again.
win->w_buffer->b_prompt_insert = restart_edit;
if (restart_edit != 0 && mode_displayed)
clear_cmdline = TRUE; // unshow mode later
restart_edit = NUL;
// When leaving the window (or closing the window) was done from a
// callback we need to break out of the Insert mode loop and restart Insert
// mode when entering the window again.
if ((State & MODE_INSERT) && !stop_insert_mode)
{
stop_insert_mode = TRUE;
if (win->w_buffer->b_prompt_insert == NUL)
win->w_buffer->b_prompt_insert = 'A';
}
}
void
entering_window(win_T *win)
{
// Only matters for a prompt window.
if (!bt_prompt(win->w_buffer))
return;
// When switching to a prompt buffer that was in Insert mode, don't stop
// Insert mode, it may have been set in leaving_window().
if (win->w_buffer->b_prompt_insert != NUL)
stop_insert_mode = FALSE;
// When entering the prompt window restart Insert mode if we were in Insert
// mode when we left it and not already in Insert mode.
if ((State & MODE_INSERT) == 0)
restart_edit = win->w_buffer->b_prompt_insert;
}
#endif
static void
win_init_empty(win_T *wp)
{
redraw_win_later(wp, UPD_NOT_VALID);
wp->w_lines_valid = 0;
wp->w_cursor.lnum = 1;
wp->w_curswant = wp->w_cursor.col = 0;
wp->w_cursor.coladd = 0;
wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1
wp->w_pcmark.col = 0;
wp->w_prev_pcmark.lnum = 0;
wp->w_prev_pcmark.col = 0;
wp->w_topline = 1;
#ifdef FEAT_DIFF
wp->w_topfill = 0;
#endif
wp->w_botline = 2;
wp->w_valid = 0;
#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
wp->w_s = &wp->w_buffer->b_s;
#endif
#ifdef FEAT_TERMINAL
term_reset_wincolor(wp);
#endif
}
/*
* Init the current window "curwin".
* Called when a new file is being edited.
*/
void
curwin_init(void)
{
win_init_empty(curwin);
}
2004-06-13 20:20:40 +00:00
/*
* Close all windows for buffer "buf".
2004-06-13 20:20:40 +00:00
*/
void
close_windows(
buf_T *buf,
int keep_curwin) // don't close "curwin"
2004-06-13 20:20:40 +00:00
{
2006-02-16 22:11:02 +00:00
win_T *wp;
tabpage_T *tp, *nexttp;
int count = tabpage_index(NULL);
2004-06-13 20:20:40 +00:00
++RedrawingDisabled;
2006-02-16 22:11:02 +00:00
for (wp = firstwin; wp != NULL && !ONE_WINDOW; )
2004-06-13 20:20:40 +00:00
{
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
2004-06-13 20:20:40 +00:00
{
if (win_close(wp, FALSE) == FAIL)
// If closing the window fails give up, to avoid looping
// forever.
break;
2006-02-16 22:11:02 +00:00
// Start all over, autocommands may change the window layout.
2006-02-16 22:11:02 +00:00
wp = firstwin;
2004-06-13 20:20:40 +00:00
}
else
2006-02-16 22:11:02 +00:00
wp = wp->w_next;
2004-06-13 20:20:40 +00:00
}
2006-02-16 22:11:02 +00:00
// Also check windows in other tab pages.
2006-02-16 22:11:02 +00:00
for (tp = first_tabpage; tp != NULL; tp = nexttp)
{
nexttp = tp->tp_next;
2006-02-17 21:45:41 +00:00
if (tp != curtab)
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
if (wp->w_buffer == buf
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
2006-02-16 22:11:02 +00:00
{
win_close_othertab(wp, FALSE, tp);
// Start all over, the tab page may be closed and
// autocommands may change the window layout.
2006-02-16 22:11:02 +00:00
nexttp = first_tabpage;
break;
}
}
if (RedrawingDisabled > 0)
--RedrawingDisabled;
2006-02-16 22:11:02 +00:00
if (count != tabpage_index(NULL))
apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
2004-06-13 20:20:40 +00:00
}
2006-02-14 22:29:30 +00:00
/*
2009-06-16 14:01:43 +00:00
* Return TRUE if the current window is the only window that exists (ignoring
* "aucmd_win[]").
2006-02-17 21:45:41 +00:00
* Returns FALSE if there is a window, possibly in another tab page.
2006-02-14 22:29:30 +00:00
*/
int
last_window(void)
2006-02-14 22:29:30 +00:00
{
2009-06-16 14:01:43 +00:00
return (one_window() && first_tabpage->tp_next == NULL);
}
/*
* Return TRUE if there is only one window other than "aucmd_win[]" in the
2009-06-16 14:01:43 +00:00
* current tab page.
*/
int
one_window(void)
2009-06-16 14:01:43 +00:00
{
win_T *wp;
int seen_one = FALSE;
FOR_ALL_WINDOWS(wp)
{
if (!is_aucmd_win(wp))
2009-06-16 14:01:43 +00:00
{
if (seen_one)
return FALSE;
seen_one = TRUE;
}
}
return TRUE;
2006-02-14 22:29:30 +00:00
}
/*
* Close the possibly last window in a tab page.
* Return FALSE if there are other windows and nothing is done, TRUE otherwise.
*/
static int
close_last_window_tabpage(
win_T *win,
int free_buf,
tabpage_T *prev_curtab)
{
if (!ONE_WINDOW)
return FALSE;
buf_T *old_curbuf = curbuf;
/*
* Closing the last window in a tab page. First go to another tab
* page and then close the window and the tab page. This avoids that
* curwin and curtab are invalid while we are freeing memory, they may
* be used in GUI events.
* Don't trigger autocommands yet, they may use wrong values, so do
* that below.
*/
goto_tabpage_tp(alt_tabpage(), FALSE, TRUE);
// Safety check: Autocommands may have closed the window when jumping
// to the other tab page.
if (valid_tabpage(prev_curtab) && prev_curtab->tp_firstwin == win)
win_close_othertab(win, free_buf, prev_curtab);
#ifdef FEAT_JOB_CHANNEL
entering_window(curwin);
#endif
// Since goto_tabpage_tp above did not trigger *Enter autocommands, do
// that now.
apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
if (old_curbuf != curbuf)
apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
return TRUE;
}
/*
* Close the buffer of "win" and unload it if "action" is DOBUF_UNLOAD.
* "action" can also be zero (do nothing) or DOBUF_WIPE.
* "abort_if_last" is passed to close_buffer(): abort closing if all other
* windows are closed.
*/
static void
win_close_buffer(win_T *win, int action, int abort_if_last)
{
#ifdef FEAT_SYN_HL
// Free independent synblock before the buffer is freed.
if (win->w_buffer != NULL)
reset_synblock(win);
#endif
#ifdef FEAT_QUICKFIX
// When a quickfix/location list window is closed and the buffer is
// displayed in only one window, then unlist the buffer.
if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)
&& win->w_buffer->b_nwindows == 1)
win->w_buffer->b_p_bl = FALSE;
#endif
// Close the link to the buffer.
if (win->w_buffer != NULL)
{
bufref_T bufref;
set_bufref(&bufref, curbuf);
win->w_locked = TRUE;
close_buffer(win, win->w_buffer, action, abort_if_last, TRUE);
if (win_valid_any_tab(win))
win->w_locked = FALSE;
// Make sure curbuf is valid. It can become invalid if 'bufhidden' is
// "wipe".
if (!bufref_valid(&bufref))
curbuf = firstbuf;
}
}
2004-06-13 20:20:40 +00:00
/*
2006-04-05 20:41:53 +00:00
* Close window "win". Only works for the current tab page.
2004-06-13 20:20:40 +00:00
* If "free_buf" is TRUE related buffer may be unloaded.
*
* Called by :quit, :close, :xit, :wq and findtag().
* Returns FAIL when the window was not closed.
2004-06-13 20:20:40 +00:00
*/
int
win_close(win_T *win, int free_buf)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
int other_buffer = FALSE;
int close_curwin = FALSE;
int dir;
int help_window = FALSE;
2006-04-28 22:32:28 +00:00
tabpage_T *prev_curtab = curtab;
frame_T *win_frame = win->w_frame->fr_parent;
#ifdef FEAT_DIFF
int had_diffmode = win->w_p_diff;
#endif
#ifdef MESSAGE_QUEUE
int did_decrement = FALSE;
#endif
2004-06-13 20:20:40 +00:00
#if defined(FEAT_TERMINAL) && defined(FEAT_PROP_POPUP)
// Can close a popup window with a terminal if the job has finished.
if (may_close_term_popup() == OK)
return OK;
#endif
if (ERROR_IF_ANY_POPUP_WINDOW)
return FAIL;
2006-02-14 22:29:30 +00:00
if (last_window())
2004-06-13 20:20:40 +00:00
{
emsg(_(e_cannot_close_last_window));
return FAIL;
2004-06-13 20:20:40 +00:00
}
if (window_layout_locked(CMD_close))
return FAIL;
2004-06-13 20:20:40 +00:00
if (win_locked(win) || (win->w_buffer != NULL
&& win->w_buffer->b_locked > 0))
return FAIL; // window is already being closed
if (win_unlisted(win))
2009-06-16 14:01:43 +00:00
{
emsg(_(e_cannot_close_autocmd_or_popup_window));
return FAIL;
2009-06-16 14:01:43 +00:00
}
if ((is_aucmd_win(firstwin) || is_aucmd_win(lastwin)) && one_window())
2009-06-16 14:01:43 +00:00
{
emsg(_(e_cannot_close_window_only_autocmd_window_would_remain));
return FAIL;
2009-06-16 14:01:43 +00:00
}
// When closing the last window in a tab page first go to another tab page
// and then close the window and the tab page to avoid that curwin and
// curtab are invalid while we are freeing memory.
if (close_last_window_tabpage(win, free_buf, prev_curtab))
return FAIL;
2006-04-30 18:54:39 +00:00
// When closing the help window, try restoring a snapshot after closing
// the window. Otherwise clear the snapshot, it's now invalid.
if (bt_help(win->w_buffer))
2004-06-13 20:20:40 +00:00
help_window = TRUE;
else
2009-06-16 14:01:43 +00:00
clear_snapshot(curtab, SNAP_HELP_IDX);
2004-06-13 20:20:40 +00:00
if (win == curwin)
{
#ifdef FEAT_JOB_CHANNEL
leaving_window(curwin);
#endif
2004-06-13 20:20:40 +00:00
/*
* Guess which window is going to be the new current window.
* This may change because of the autocommands (sigh).
*/
2006-02-16 22:11:02 +00:00
wp = frame2win(win_altframe(win, NULL));
2004-06-13 20:20:40 +00:00
/*
* Be careful: If autocommands delete the window or cause this window
* to be the last one left, return now.
2004-06-13 20:20:40 +00:00
*/
if (wp->w_buffer != curbuf)
{
reset_VIsual_and_resel(); // stop Visual mode
2004-06-13 20:20:40 +00:00
other_buffer = TRUE;
if (!win_valid(win))
return FAIL;
win->w_locked = TRUE;
2004-06-13 20:20:40 +00:00
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
if (!win_valid(win))
return FAIL;
win->w_locked = FALSE;
if (last_window())
return FAIL;
2004-06-13 20:20:40 +00:00
}
win->w_locked = TRUE;
2004-06-13 20:20:40 +00:00
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
if (!win_valid(win))
return FAIL;
win->w_locked = FALSE;
if (last_window())
return FAIL;
#ifdef FEAT_EVAL
// autocmds may abort script processing
2004-06-13 20:20:40 +00:00
if (aborting())
return FAIL;
2004-06-13 20:20:40 +00:00
#endif
}
2004-06-13 20:20:40 +00:00
2007-04-26 14:09:42 +00:00
#ifdef FEAT_GUI
// Avoid trouble with scrollbars that are going to be deleted in
// win_free().
2007-04-26 14:09:42 +00:00
if (gui.in_use)
out_flush();
#endif
#ifdef FEAT_PROP_POPUP
if (popup_win_closed(win) && !win_valid(win))
return FAIL;
#endif
// Trigger WinClosed just before starting to free window-related resources.
trigger_winclosed(win);
// autocmd may have freed the window already.
if (!win_valid_any_tab(win))
return OK;
win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, TRUE);
2006-04-28 22:32:28 +00:00
if (win_valid(win) && win->w_buffer == NULL
#if defined(FEAT_PROP_POPUP)
&& !popup_is_popup(win)
#endif
&& last_window())
{
// Autocommands have closed all windows, quit now. Restore
// curwin->w_buffer, otherwise writing viminfo may fail.
if (curwin->w_buffer == NULL)
curwin->w_buffer = curbuf;
getout(0);
}
// Autocommands may have moved to another tab page.
if (curtab != prev_curtab && win_valid_any_tab(win)
&& win->w_buffer == NULL)
{
// Need to close the window anyway, since the buffer is NULL.
win_close_othertab(win, FALSE, prev_curtab);
return FAIL;
}
// Autocommands may have closed the window already or closed the only
// other window.
if (!win_valid(win) || last_window()
|| close_last_window_tabpage(win, free_buf, prev_curtab))
return FAIL;
2004-06-13 20:20:40 +00:00
// Now we are really going to close the window. Disallow any autocommand
// to split a window to avoid trouble.
// Also bail out of parse_queued_messages() to avoid it tries to update the
// screen.
++split_disallowed;
#ifdef MESSAGE_QUEUE
++dont_parse_messages;
#endif
// Free the memory used for the window and get the window that received
// the screen space.
2006-04-28 22:32:28 +00:00
wp = win_free_mem(win, &dir, NULL);
if (help_window)
{
// Closing the help window moves the cursor back to the current window
// of the snapshot.
win_T *prev_win = get_snapshot_curwin(SNAP_HELP_IDX);
if (win_valid(prev_win))
wp = prev_win;
}
// Make sure curwin isn't invalid. It can cause severe trouble when
// printing an error message. For win_equal() curbuf needs to be valid
// too.
2006-04-28 22:32:28 +00:00
if (win == curwin)
2004-06-13 20:20:40 +00:00
{
curwin = wp;
#ifdef FEAT_QUICKFIX
if (wp->w_p_pvw || bt_quickfix(wp->w_buffer))
{
/*
2007-08-11 11:39:45 +00:00
* If the cursor goes to the preview or the quickfix window, try
2004-06-13 20:20:40 +00:00
* finding another window to go to.
*/
for (;;)
{
if (wp->w_next == NULL)
wp = firstwin;
else
wp = wp->w_next;
if (wp == curwin)
break;
if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer))
{
curwin = wp;
break;
}
}
}
#endif
curbuf = curwin->w_buffer;
close_curwin = TRUE;
// The cursor position may be invalid if the buffer changed after last
// using the window.
check_cursor();
2004-06-13 20:20:40 +00:00
}
/*
* If last window has a status line now and we don't want one, remove the
* status line. Do this before win_equal(), because it may change the
* height of a window
*/
last_status(FALSE);
if (p_ea && (*p_ead == 'b' || *p_ead == dir))
// If the frame of the closed window contains the new current window,
// only resize that frame. Otherwise resize all windows.
win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
2004-06-13 20:20:40 +00:00
else
{
2004-06-13 20:20:40 +00:00
win_comp_pos();
win_fix_scroll(FALSE);
}
2004-06-13 20:20:40 +00:00
if (close_curwin)
{
// Pass WEE_ALLOW_PARSE_MESSAGES to decrement dont_parse_messages
// before autocommands.
#ifdef MESSAGE_QUEUE
did_decrement =
#else
(void)
#endif
win_enter_ext(wp,
WEE_CURWIN_INVALID | WEE_TRIGGER_ENTER_AUTOCMDS
| WEE_TRIGGER_LEAVE_AUTOCMDS | WEE_ALLOW_PARSE_MESSAGES);
2004-06-13 20:20:40 +00:00
if (other_buffer)
// careful: after this wp and win may be invalid!
2004-06-13 20:20:40 +00:00
apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
}
--split_disallowed;
#ifdef MESSAGE_QUEUE
if (!did_decrement)
--dont_parse_messages;
#endif
// After closing the help window, try restoring the window layout from
// before it was opened.
2004-06-13 20:20:40 +00:00
if (help_window)
2009-06-16 14:01:43 +00:00
restore_snapshot(SNAP_HELP_IDX, close_curwin);
2004-06-13 20:20:40 +00:00
#ifdef FEAT_DIFF
// If the window had 'diff' set and now there is only one window left in
// the tab page with 'diff' set, and "closeoff" is in 'diffopt', then
// execute ":diffoff!".
if (diffopt_closeoff() && had_diffmode && curtab == prev_curtab)
{
int diffcount = 0;
win_T *dwin;
FOR_ALL_WINDOWS(dwin)
if (dwin->w_p_diff)
++diffcount;
if (diffcount == 1)
do_cmdline_cmd((char_u *)"diffoff!");
}
#endif
#if defined(FEAT_GUI)
// When 'guioptions' includes 'L' or 'R' may have to remove scrollbars.
2004-06-13 20:20:40 +00:00
if (gui.in_use && !win_hasvertsplit())
gui_init_which_components(NULL);
#endif
redraw_all_later(UPD_NOT_VALID);
return OK;
2004-06-13 20:20:40 +00:00
}
static void
trigger_winnewpre(void)
{
window_layout_lock();
apply_autocmds(EVENT_WINNEWPRE, NULL, NULL, FALSE, NULL);
window_layout_unlock();
}
static void
trigger_winclosed(win_T *win)
{
static int recursive = FALSE;
char_u winid[NUMBUFLEN];
if (recursive)
return;
recursive = TRUE;
vim_snprintf((char *)winid, sizeof(winid), "%d", win->w_id);
apply_autocmds(EVENT_WINCLOSED, winid, winid, FALSE, win->w_buffer);
recursive = FALSE;
}
/*
* Make a snapshot of all the window scroll positions and sizes of the current
* tab page.
*/
void
snapshot_windows_scroll_size(void)
{
win_T *wp;
FOR_ALL_WINDOWS(wp)
{
wp->w_last_topline = wp->w_topline;
#ifdef FEAT_DIFF
wp->w_last_topfill = wp->w_topfill;
#endif
wp->w_last_leftcol = wp->w_leftcol;
wp->w_last_skipcol = wp->w_skipcol;
wp->w_last_width = wp->w_width;
wp->w_last_height = wp->w_height;
}
}
static int did_initial_scroll_size_snapshot = FALSE;
void
may_make_initial_scroll_size_snapshot(void)
{
if (!did_initial_scroll_size_snapshot)
{
did_initial_scroll_size_snapshot = TRUE;
snapshot_windows_scroll_size();
}
}
#ifdef FEAT_EVAL
/*
* Create a dictionary with information about size and scroll changes in a
* window.
* Returns the dictionary with refcount set to one.
* Returns NULL when out of memory.
*/
static dict_T *
make_win_info_dict(
int width,
int height,
int topline,
# ifdef FEAT_DIFF
int topfill,
# endif
int leftcol,
int skipcol)
{
dict_T *d = dict_alloc();
if (d == NULL)
return NULL;
d->dv_refcount = 1;
// not actually looping, for breaking out on error
while (1)
{
typval_T tv;
tv.v_lock = 0;
tv.v_type = VAR_NUMBER;
tv.vval.v_number = width;
if (dict_add_tv(d, "width", &tv) == FAIL)
break;
tv.vval.v_number = height;
if (dict_add_tv(d, "height", &tv) == FAIL)
break;
tv.vval.v_number = topline;
if (dict_add_tv(d, "topline", &tv) == FAIL)
break;
#ifdef FEAT_DIFF
tv.vval.v_number = topfill;
#else
tv.vval.v_number = 0;
#endif
if (dict_add_tv(d, "topfill", &tv) == FAIL)
break;
tv.vval.v_number = leftcol;
if (dict_add_tv(d, "leftcol", &tv) == FAIL)
break;
tv.vval.v_number = skipcol;
if (dict_add_tv(d, "skipcol", &tv) == FAIL)
break;
return d;
}
dict_unref(d);
return NULL;
}
#endif
// Return values of check_window_scroll_resize():
#define CWSR_SCROLLED 1 // at least one window scrolled
#define CWSR_RESIZED 2 // at least one window size changed
/*
* This function is used for three purposes:
* 1. Goes over all windows in the current tab page and sets:
* "size_count" to the nr of windows with size changes.
* "first_scroll_win" to the first window with any relevant changes.
* "first_size_win" to the first window with size changes.
*
* 2. When the first three arguments are NULL and "winlist" is not NULL,
* "winlist" is set to the list of window IDs with size changes.
*
* 3. When the first three arguments are NULL and "v_event" is not NULL,
* information about changed windows is added to "v_event".
*/
static void
check_window_scroll_resize(
int *size_count,
win_T **first_scroll_win,
win_T **first_size_win,
list_T *winlist UNUSED,
dict_T *v_event UNUSED)
{
#ifdef FEAT_EVAL
int listidx = 0;
int tot_width = 0;
int tot_height = 0;
int tot_topline = 0;
# ifdef FEAT_DIFF
int tot_topfill = 0;
# endif
int tot_leftcol = 0;
int tot_skipcol = 0;
#endif
win_T *wp;
FOR_ALL_WINDOWS(wp)
{
int ignore_scroll = event_ignored(EVENT_WINSCROLLED, wp->w_p_eiw);
int size_changed = !event_ignored(EVENT_WINRESIZED, wp->w_p_eiw)
&& (wp->w_last_width != wp->w_width
|| wp->w_last_height != wp->w_height);
if (size_changed)
{
#ifdef FEAT_EVAL
if (winlist != NULL)
{
// Add this window to the list of changed windows.
typval_T tv;
tv.v_lock = 0;
tv.v_type = VAR_NUMBER;
tv.vval.v_number = wp->w_id;
list_set_item(winlist, listidx++, &tv);
}
else
#endif
if (size_count != NULL)
{
++*size_count;
if (*first_size_win == NULL)
*first_size_win = wp;
// For WinScrolled the first window with a size change is used
// even when it didn't scroll.
if (*first_scroll_win == NULL && !ignore_scroll)
*first_scroll_win = wp;
}
}
int scroll_changed = !ignore_scroll
&& (wp->w_last_topline != wp->w_topline
#ifdef FEAT_DIFF
|| wp->w_last_topfill != wp->w_topfill
#endif
|| wp->w_last_leftcol != wp->w_leftcol
|| wp->w_last_skipcol != wp->w_skipcol);
if (scroll_changed
&& first_scroll_win != NULL && *first_scroll_win == NULL)
*first_scroll_win = wp;
#ifdef FEAT_EVAL
if ((size_changed || scroll_changed) && v_event != NULL)
{
// Add info about this window to the v:event dictionary.
int width = wp->w_width - wp->w_last_width;
int height = wp->w_height - wp->w_last_height;
int topline = wp->w_topline - wp->w_last_topline;
#ifdef FEAT_DIFF
int topfill = wp->w_topfill - wp->w_last_topfill;
#endif
int leftcol = wp->w_leftcol - wp->w_last_leftcol;
int skipcol = wp->w_skipcol - wp->w_last_skipcol;
dict_T *d = make_win_info_dict(width, height, topline,
#ifdef FEAT_DIFF
topfill,
#endif
leftcol, skipcol);
if (d == NULL)
break;
char winid[NUMBUFLEN];
vim_snprintf(winid, sizeof(winid), "%d", wp->w_id);
if (dict_add_dict(v_event, winid, d) == FAIL)
{
dict_unref(d);
break;
}
--d->dv_refcount;
tot_width += abs(width);
tot_height += abs(height);
tot_topline += abs(topline);
#ifdef FEAT_DIFF
tot_topfill += abs(topfill);
#endif
tot_leftcol += abs(leftcol);
tot_skipcol += abs(skipcol);
}
#endif
}
#ifdef FEAT_EVAL
if (v_event != NULL)
{
dict_T *alldict = make_win_info_dict(tot_width, tot_height, tot_topline,
# ifdef FEAT_DIFF
tot_topfill,
# endif
tot_leftcol, tot_skipcol);
if (alldict != NULL)
{
if (dict_add_dict(v_event, "all", alldict) == FAIL)
dict_unref(alldict);
else
--alldict->dv_refcount;
}
}
#endif
}
/*
* Trigger WinScrolled and/or WinResized if any window in the current tab page
* scrolled or changed size.
*/
void
may_trigger_win_scrolled_resized(void)
{
static int recursive = FALSE;
int do_resize = has_winresized();
int do_scroll = has_winscrolled();
// Do not trigger WinScrolled or WinResized recursively. Do not trigger
// before the initial snapshot of the w_last_ values was made.
if (recursive
|| !(do_scroll || do_resize)
|| !did_initial_scroll_size_snapshot)
return;
int size_count = 0;
win_T *first_scroll_win = NULL, *first_size_win = NULL;
check_window_scroll_resize(&size_count, &first_scroll_win, &first_size_win,
NULL, NULL);
int trigger_resize = do_resize && size_count > 0;
int trigger_scroll = do_scroll && first_scroll_win != NULL;
if (!trigger_resize && !trigger_scroll)
return; // no relevant changes
#ifdef FEAT_EVAL
list_T *windows_list = NULL;
if (trigger_resize)
{
// Create the list for v:event.windows before making the snapshot.
windows_list = list_alloc_with_items(size_count);
(void)check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL);
}
dict_T *scroll_dict = NULL;
if (trigger_scroll)
{
// Create the dict with entries for v:event before making the snapshot.
scroll_dict = dict_alloc();
if (scroll_dict != NULL)
{
scroll_dict->dv_refcount = 1;
(void)check_window_scroll_resize(NULL, NULL, NULL, NULL,
scroll_dict);
}
}
#endif
// WinScrolled/WinResized are triggered only once, even when multiple
// windows scrolled or changed size. Store the current values before
// triggering the event, if a scroll or resize happens as a side effect
// then WinScrolled/WinResized is triggered for that later.
snapshot_windows_scroll_size();
// "curwin" may be different from the actual current window, make
// sure it can be restored.
window_layout_lock();
recursive = TRUE;
// If both are to be triggered do WinResized first.
if (trigger_resize)
{
#ifdef FEAT_EVAL
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
if (dict_add_list(v_event, "windows", windows_list) == OK)
{
dict_set_items_ro(v_event);
#endif
char_u winid[NUMBUFLEN];
vim_snprintf((char *)winid, sizeof(winid), "%d",
first_size_win->w_id);
apply_autocmds(EVENT_WINRESIZED, winid, winid, FALSE,
first_size_win->w_buffer);
#ifdef FEAT_EVAL
}
restore_v_event(v_event, &save_v_event);
#endif
}
if (trigger_scroll
#ifdef FEAT_EVAL
&& scroll_dict != NULL
#endif
)
{
#ifdef FEAT_EVAL
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
// Move the entries from scroll_dict to v_event.
dict_extend(v_event, scroll_dict, (char_u *)"move", NULL);
dict_set_items_ro(v_event);
dict_unref(scroll_dict);
#endif
char_u winid[NUMBUFLEN];
vim_snprintf((char *)winid, sizeof(winid), "%d",
first_scroll_win->w_id);
apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE,
first_scroll_win->w_buffer);
#ifdef FEAT_EVAL
restore_v_event(v_event, &save_v_event);
#endif
}
recursive = FALSE;
window_layout_unlock();
}
2006-02-16 22:11:02 +00:00
/*
* Close window "win" in tab page "tp", which is not the current tab page.
* This may be the last window in that tab page and result in closing the tab,
2006-02-16 22:11:02 +00:00
* thus "tp" may become invalid!
2006-04-30 18:54:39 +00:00
* Caller must check if buffer is hidden and whether the tabline needs to be
* updated.
2006-02-16 22:11:02 +00:00
*/
void
win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
2006-02-16 22:11:02 +00:00
{
win_T *wp;
int dir;
tabpage_T *ptp = NULL;
int free_tp = FALSE;
2006-02-16 22:11:02 +00:00
// Get here with win->w_buffer == NULL when win_close() detects the tab
// page changed.
if (win_locked(win) || (win->w_buffer != NULL
&& win->w_buffer->b_locked > 0))
return; // window is already being closed
// Trigger WinClosed just before starting to free window-related resources.
// If the buffer is NULL, it isn't safe to trigger autocommands,
// and win_close() should have already triggered WinClosed.
if (win->w_buffer != NULL)
{
trigger_winclosed(win);
// autocmd may have freed the window already.
if (!win_valid_any_tab(win))
return;
}
if (win->w_buffer != NULL)
// Close the link to the buffer.
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0,
FALSE, TRUE);
2006-02-16 22:11:02 +00:00
// Careful: Autocommands may have closed the tab page or made it the
// current tab page.
2006-02-16 22:11:02 +00:00
for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next)
;
2006-02-17 21:45:41 +00:00
if (ptp == NULL || tp == curtab)
{
// If the buffer was removed from the window we have to give it any
// buffer.
if (win_valid_any_tab(win) && win->w_buffer == NULL)
{
win->w_buffer = firstbuf;
++firstbuf->b_nwindows;
win_init_empty(win);
}
2006-02-16 22:11:02 +00:00
return;
}
2006-02-16 22:11:02 +00:00
// Autocommands may have closed the window already.
2006-02-16 22:11:02 +00:00
for (wp = tp->tp_firstwin; wp != NULL && wp != win; wp = wp->w_next)
;
if (wp == NULL)
return;
// When closing the last window in a tab page remove the tab page.
if (tp->tp_firstwin == tp->tp_lastwin)
2006-02-16 22:11:02 +00:00
{
int h = tabline_height();
2006-02-16 22:11:02 +00:00
if (tp == first_tabpage)
first_tabpage = tp->tp_next;
else
{
for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp;
ptp = ptp->tp_next)
;
if (ptp == NULL)
{
internal_error("win_close_othertab()");
2006-02-16 22:11:02 +00:00
return;
}
ptp->tp_next = tp->tp_next;
}
free_tp = TRUE;
redraw_tabline = TRUE;
if (h != tabline_height())
shell_new_rows();
2006-02-16 22:11:02 +00:00
}
// Free the memory used for the window.
win_free_mem(win, &dir, tp);
if (free_tp)
free_tabpage(tp);
2006-02-16 22:11:02 +00:00
}
2005-06-24 23:01:23 +00:00
/*
* Free the memory used for a window.
* Returns a pointer to the window that got the freed up space.
*/
static win_T *
win_free_mem(
win_T *win,
int *dirp, // set to 'v' or 'h' for direction if 'ea'
tabpage_T *tp) // tab page "win" is in, NULL for current
2005-06-24 23:01:23 +00:00
{
frame_T *frp;
win_T *wp;
tabpage_T *win_tp = tp == NULL ? curtab : tp;
2005-06-24 23:01:23 +00:00
// Remove the window and its frame from the tree of frames.
2005-06-24 23:01:23 +00:00
frp = win->w_frame;
wp = winframe_remove(win, dirp, tp, NULL);
2005-06-24 23:01:23 +00:00
vim_free(frp);
2006-02-16 22:11:02 +00:00
win_free(win, tp);
2005-06-24 23:01:23 +00:00
// When deleting the current window in the tab, select a new current
// window.
if (win == win_tp->tp_curwin)
win_tp->tp_curwin = wp;
2006-04-05 20:41:53 +00:00
2005-06-24 23:01:23 +00:00
return wp;
}
#if defined(EXITFREE) || defined(PROTO)
void
win_free_all(void)
2005-06-24 23:01:23 +00:00
{
int dummy;
// avoid an error for switching tabpage with the cmdline window open
cmdwin_type = 0;
cmdwin_buf = NULL;
cmdwin_win = NULL;
2006-02-16 22:11:02 +00:00
while (first_tabpage->tp_next != NULL)
tabpage_close(TRUE);
for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
if (aucmd_win[i].auc_win != NULL)
{
(void)win_free_mem(aucmd_win[i].auc_win, &dummy, NULL);
aucmd_win[i].auc_win = NULL;
}
2009-06-24 15:32:01 +00:00
while (firstwin != NULL)
(void)win_free_mem(firstwin, &dummy, NULL);
// No window should be used after this. Set curwin to NULL to crash
// instead of using freed memory.
curwin = NULL;
2005-06-24 23:01:23 +00:00
}
#endif
2004-06-13 20:20:40 +00:00
/*
* Remove a window and its frame from the tree of frames.
* Returns a pointer to the window that got the freed up space.
*/
2009-06-16 14:01:43 +00:00
win_T *
winframe_remove(
win_T *win,
int *dirp, // set to 'v' or 'h' for direction if 'ea'
tabpage_T *tp, // tab page "win" is in, NULL for current
frame_T **unflat_altfr) // if not NULL, set to pointer of frame that got
// the space, and it is not flattened
2004-06-13 20:20:40 +00:00
{
frame_T *frp, *frp2, *frp3;
frame_T *frp_close = win->w_frame;
win_T *wp;
int row, col;
2004-06-13 20:20:40 +00:00
/*
2006-02-16 22:11:02 +00:00
* If there is only one window there is nothing to remove.
2004-06-13 20:20:40 +00:00
*/
if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
2006-02-16 22:11:02 +00:00
return NULL;
2005-06-24 23:01:23 +00:00
// Save the position of the containing frame (which will also contain the
// altframe) before we remove anything, to recompute window positions later.
wp = frame2win(frp_close->fr_parent);
row = wp->w_winrow;
col = wp->w_wincol;
2006-02-16 22:11:02 +00:00
/*
* Remove the window from its frame.
*/
frp2 = win_altframe(win, tp);
2004-06-13 20:20:40 +00:00
wp = frame2win(frp2);
// Remove this frame from the list of frames.
2004-06-13 20:20:40 +00:00
frame_remove(frp_close);
if (frp_close->fr_parent->fr_layout == FR_COL)
{
// When 'winfixheight' is set, try to find another frame in the column
// (as close to the closed frame as possible) to distribute the height
// to.
2007-08-11 11:39:45 +00:00
if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfh)
{
frp = frp_close->fr_prev;
frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL)
{
if (frp != NULL)
{
if (!frame_fixed_height(frp))
2007-08-11 11:39:45 +00:00
{
frp2 = frp;
wp = frame2win(frp2);
2007-08-11 11:39:45 +00:00
break;
}
frp = frp->fr_prev;
}
if (frp3 != NULL)
{
if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfh)
{
frp2 = frp3;
wp = frp3->fr_win;
break;
}
frp3 = frp3->fr_next;
}
}
}
2004-06-13 20:20:40 +00:00
frame_new_height(frp2, frp2->fr_height + frp_close->fr_height,
frp2 == frp_close->fr_next, FALSE, FALSE);
2004-06-13 20:20:40 +00:00
*dirp = 'v';
}
else
{
// When 'winfixwidth' is set, try to find another frame in the column
// (as close to the closed frame as possible) to distribute the width
// to.
2007-08-11 11:39:45 +00:00
if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfw)
{
frp = frp_close->fr_prev;
frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL)
{
if (frp != NULL)
{
if (!frame_fixed_width(frp))
2007-08-11 11:39:45 +00:00
{
frp2 = frp;
wp = frame2win(frp2);
2007-08-11 11:39:45 +00:00
break;
}
frp = frp->fr_prev;
}
if (frp3 != NULL)
{
if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfw)
{
frp2 = frp3;
wp = frp3->fr_win;
break;
}
frp3 = frp3->fr_next;
}
}
}
2004-06-13 20:20:40 +00:00
frame_new_width(frp2, frp2->fr_width + frp_close->fr_width,
frp2 == frp_close->fr_next, FALSE);
2004-06-13 20:20:40 +00:00
*dirp = 'h';
}
// If the altframe wasn't adjacent and left/above, resizing it will have
// changed window positions within the parent frame. Recompute them.
if (frp2 != frp_close->fr_prev)
frame_comp_pos(frp_close->fr_parent, &row, &col);
2004-06-13 20:20:40 +00:00
if (unflat_altfr == NULL)
frame_flatten(frp2);
else
*unflat_altfr = frp2;
2004-06-13 20:20:40 +00:00
return wp;
}
/*
* Flatten "frp" into its parent frame if it's the only child, also merging its
* list with the grandparent if they share the same layout.
* Frees "frp" if flattened; also "frp->fr_parent" if it has the same layout.
*/
static void
frame_flatten(frame_T *frp)
{
frame_T *frp2, *frp3;
if (frp->fr_next != NULL || frp->fr_prev != NULL)
return;
// There is no other frame in this list, move its info to the parent
// and remove it.
frp->fr_parent->fr_layout = frp->fr_layout;
frp->fr_parent->fr_child = frp->fr_child;
FOR_ALL_FRAMES(frp2, frp->fr_child)
frp2->fr_parent = frp->fr_parent;
frp->fr_parent->fr_win = frp->fr_win;
if (frp->fr_win != NULL)
frp->fr_win->w_frame = frp->fr_parent;
frp2 = frp->fr_parent;
if (topframe->fr_child == frp)
topframe->fr_child = frp2;
vim_free(frp);
frp = frp2->fr_parent;
if (frp != NULL && frp->fr_layout == frp2->fr_layout)
{
// The frame above the parent has the same layout, have to merge
// the frames into this list.
if (frp->fr_child == frp2)
frp->fr_child = frp2->fr_child;
frp2->fr_child->fr_prev = frp2->fr_prev;
if (frp2->fr_prev != NULL)
frp2->fr_prev->fr_next = frp2->fr_child;
for (frp3 = frp2->fr_child; ; frp3 = frp3->fr_next)
2004-06-13 20:20:40 +00:00
{
frp3->fr_parent = frp;
if (frp3->fr_next == NULL)
2004-06-13 20:20:40 +00:00
{
frp3->fr_next = frp2->fr_next;
if (frp2->fr_next != NULL)
frp2->fr_next->fr_prev = frp3;
break;
2004-06-13 20:20:40 +00:00
}
}
if (topframe->fr_child == frp2)
topframe->fr_child = frp;
vim_free(frp2);
2004-06-13 20:20:40 +00:00
}
}
2004-06-13 20:20:40 +00:00
/*
* Undo changes from a prior call to winframe_remove, also restoring lost
* vertical separators and statuslines, and changed window positions for
* windows within "unflat_altfr".
* Caller must ensure no other changes were made to the layout or window sizes!
*/
static void
winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr)
{
frame_T *frp = wp->w_frame;
// Put "wp"'s frame back where it was.
if (frp->fr_prev != NULL)
frame_append(frp->fr_prev, frp);
else
frame_insert(frp->fr_next, frp);
// Vertical separators to the left may have been lost. Restore them.
if (wp->w_vsep_width == 0
&& frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL)
frame_add_vsep(frp->fr_prev);
// Statuslines above may have been lost. Restore them.
if (wp->w_status_height == 0
&& frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL)
frame_add_statusline(frp->fr_prev);
// Restore the lost room that was redistributed to the altframe. Also
// adjusts window sizes to fit restored statuslines/separators, if needed.
if (dir == 'v')
{
frame_new_height(unflat_altfr, unflat_altfr->fr_height - frp->fr_height,
unflat_altfr == frp->fr_next, FALSE, FALSE);
}
else if (dir == 'h')
{
frame_new_width(unflat_altfr, unflat_altfr->fr_width - frp->fr_width,
unflat_altfr == frp->fr_next, FALSE);
}
// Recompute window positions within the parent frame to restore them.
// Positions were unchanged if the altframe was adjacent and left/above.
if (unflat_altfr != frp->fr_prev)
{
win_T *topleft = frame2win(frp->fr_parent);
int row = topleft->w_winrow;
int col = topleft->w_wincol;
frame_comp_pos(frp->fr_parent, &row, &col);
}
2004-06-13 20:20:40 +00:00
}
/*
* Return a pointer to the frame that will receive the empty screen space that
* is left over after "win" is closed.
*
* If 'splitbelow' or 'splitright' is set, the space goes above or to the left
* by default. Otherwise, the free space goes below or to the right. The
* result is that opening a window and then immediately closing it will
* preserve the initial window layout. The 'wfh' and 'wfw' settings are
* respected when possible.
2004-06-13 20:20:40 +00:00
*/
static frame_T *
win_altframe(
win_T *win,
tabpage_T *tp) // tab page "win" is in, NULL for current
2004-06-13 20:20:40 +00:00
{
frame_T *frp;
frame_T *other_fr, *target_fr;
2004-06-13 20:20:40 +00:00
if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
2006-02-14 22:29:30 +00:00
return alt_tabpage()->tp_curwin->w_frame;
2004-06-13 20:20:40 +00:00
frp = win->w_frame;
if (frp->fr_prev == NULL)
return frp->fr_next;
if (frp->fr_next == NULL)
return frp->fr_prev;
// By default the next window will get the space that was abandoned by this
// window
target_fr = frp->fr_next;
other_fr = frp->fr_prev;
// If this is part of a column of windows and 'splitbelow' is true then the
// previous window will get the space.
if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_COL && p_sb)
{
target_fr = frp->fr_prev;
other_fr = frp->fr_next;
}
// If this is part of a row of windows, and 'splitright' is true then the
// previous window will get the space.
if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW && p_spr)
{
target_fr = frp->fr_prev;
other_fr = frp->fr_next;
}
// If 'wfh' or 'wfw' is set for the target and not for the alternate
// window, reverse the selection.
2005-06-24 23:01:23 +00:00
if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW)
{
if (frame_fixed_width(target_fr) && !frame_fixed_width(other_fr))
target_fr = other_fr;
}
2004-06-13 20:20:40 +00:00
else
{
if (frame_fixed_height(target_fr) && !frame_fixed_height(other_fr))
target_fr = other_fr;
}
return target_fr;
2004-06-13 20:20:40 +00:00
}
2006-02-14 22:29:30 +00:00
/*
* Return the tabpage that will be used if the current one is closed.
*/
static tabpage_T *
alt_tabpage(void)
2006-02-14 22:29:30 +00:00
{
tabpage_T *tp = NULL;
int forward;
2006-02-14 22:29:30 +00:00
// Use the last accessed tab page, if possible.
if ((tcl_flags & TCL_USELAST) && valid_tabpage(lastused_tabpage))
return lastused_tabpage;
// Use the next tab page, if possible.
forward = curtab->tp_next != NULL &&
((tcl_flags & TCL_LEFT) == 0 || curtab == first_tabpage);
if (forward)
tp = curtab->tp_next;
else
// Use the previous tab page.
for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next)
;
2006-02-17 21:45:41 +00:00
2006-02-18 22:14:51 +00:00
return tp;
2006-02-14 22:29:30 +00:00
}
2004-06-13 20:20:40 +00:00
/*
* Find the left-upper window in frame "frp".
*/
static win_T *
frame2win(frame_T *frp)
2004-06-13 20:20:40 +00:00
{
while (frp->fr_win == NULL)
frp = frp->fr_child;
return frp->fr_win;
}
/*
* Return TRUE if frame "frp" contains window "wp".
*/
static int
frame_has_win(frame_T *frp, win_T *wp)
2004-06-13 20:20:40 +00:00
{
frame_T *p;
if (frp->fr_layout == FR_LEAF)
return frp->fr_win == wp;
FOR_ALL_FRAMES(p, frp->fr_child)
2004-06-13 20:20:40 +00:00
if (frame_has_win(p, wp))
return TRUE;
return FALSE;
}
// 'cmdheight' value explicitly set by the user: window commands are allowed to
// resize the topframe to values higher than this minimum, but not lower.
static int min_set_ch = 1;
2004-06-13 20:20:40 +00:00
/*
* Set a new height for a frame. Recursively sets the height for contained
* frames and windows. Caller must take care of positions.
*/
static void
frame_new_height(
frame_T *topfrp,
int height,
int topfirst, // resize topmost contained frame first
int wfh, // obey 'winfixheight' when there is a choice;
// may cause the height not to be set
int set_ch) // set 'cmdheight' to resize topframe
2004-06-13 20:20:40 +00:00
{
frame_T *frp;
int extra_lines;
int h;
if (topfrp->fr_parent == NULL && set_ch)
{
// topframe: update the command line height, with side effects.
int new_ch = MAX(min_set_ch, p_ch + topfrp->fr_height - height);
int save_ch = min_set_ch;
if (new_ch != p_ch)
set_option_value((char_u *)"cmdheight", new_ch, NULL, 0);
min_set_ch = save_ch;
height = MIN(height, ROWS_AVAIL);
}
2004-06-13 20:20:40 +00:00
if (topfrp->fr_win != NULL)
{
// Simple case: just one window.
2004-06-13 20:20:40 +00:00
win_new_height(topfrp->fr_win,
height - topfrp->fr_win->w_status_height
- WINBAR_HEIGHT(topfrp->fr_win));
2004-06-13 20:20:40 +00:00
}
else if (topfrp->fr_layout == FR_ROW)
{
do
{
// All frames in this row get the same new height.
FOR_ALL_FRAMES(frp, topfrp->fr_child)
2004-06-13 20:20:40 +00:00
{
frame_new_height(frp, height, topfirst, wfh, set_ch);
2004-06-13 20:20:40 +00:00
if (frp->fr_height > height)
{
// Could not fit the windows, make the whole row higher.
2004-06-13 20:20:40 +00:00
height = frp->fr_height;
break;
}
}
}
while (frp != NULL);
}
else // fr_layout == FR_COL
2004-06-13 20:20:40 +00:00
{
// Complicated case: Resize a column of frames. Resize the bottom
// frame first, frames above that when needed.
2004-06-13 20:20:40 +00:00
frp = topfrp->fr_child;
if (wfh)
// Advance past frames with one window with 'wfh' set.
2004-06-13 20:20:40 +00:00
while (frame_fixed_height(frp))
{
frp = frp->fr_next;
if (frp == NULL)
return; // no frame without 'wfh', give up
2004-06-13 20:20:40 +00:00
}
if (!topfirst)
{
// Find the bottom frame of this column
2004-06-13 20:20:40 +00:00
while (frp->fr_next != NULL)
frp = frp->fr_next;
if (wfh)
// Advance back for frames with one window with 'wfh' set.
2004-06-13 20:20:40 +00:00
while (frame_fixed_height(frp))
frp = frp->fr_prev;
}
extra_lines = height - topfrp->fr_height;
if (extra_lines < 0)
{
// reduce height of contained frames, bottom or top frame first
2004-06-13 20:20:40 +00:00
while (frp != NULL)
{
h = frame_minheight(frp, NULL);
if (frp->fr_height + extra_lines < h)
{
extra_lines += frp->fr_height - h;
frame_new_height(frp, h, topfirst, wfh, set_ch);
2004-06-13 20:20:40 +00:00
}
else
{
frame_new_height(frp, frp->fr_height + extra_lines,
topfirst, wfh, set_ch);
2004-06-13 20:20:40 +00:00
break;
}
if (topfirst)
{
do
frp = frp->fr_next;
while (wfh && frp != NULL && frame_fixed_height(frp));
}
else
{
do
frp = frp->fr_prev;
while (wfh && frp != NULL && frame_fixed_height(frp));
}
// Increase "height" if we could not reduce enough frames.
2004-06-13 20:20:40 +00:00
if (frp == NULL)
height -= extra_lines;
}
}
else if (extra_lines > 0)
{
// increase height of bottom or top frame
frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh,
set_ch);
2004-06-13 20:20:40 +00:00
}
}
topfrp->fr_height = height;
}
/*
* Return TRUE if height of frame "frp" should not be changed because of
* the 'winfixheight' option.
*/
static int
frame_fixed_height(frame_T *frp)
2004-06-13 20:20:40 +00:00
{
// frame with one window: fixed height if 'winfixheight' set.
2004-06-13 20:20:40 +00:00
if (frp->fr_win != NULL)
return frp->fr_win->w_p_wfh;
if (frp->fr_layout == FR_ROW)
{
// The frame is fixed height if one of the frames in the row is fixed
// height.
FOR_ALL_FRAMES(frp, frp->fr_child)
2004-06-13 20:20:40 +00:00
if (frame_fixed_height(frp))
return TRUE;
return FALSE;
}
// frp->fr_layout == FR_COL: The frame is fixed height if all of the
// frames in the row are fixed height.
FOR_ALL_FRAMES(frp, frp->fr_child)
2004-06-13 20:20:40 +00:00
if (!frame_fixed_height(frp))
return FALSE;
return TRUE;
}
2006-03-18 21:30:13 +00:00
/*
* Return TRUE if width of frame "frp" should not be changed because of
* the 'winfixwidth' option.
*/
static int
frame_fixed_width(frame_T *frp)
2006-03-18 21:30:13 +00:00
{
// frame with one window: fixed width if 'winfixwidth' set.
2006-03-18 21:30:13 +00:00
if (frp->fr_win != NULL)
return frp->fr_win->w_p_wfw;
if (frp->fr_layout == FR_COL)
{
// The frame is fixed width if one of the frames in the row is fixed
// width.
FOR_ALL_FRAMES(frp, frp->fr_child)
2006-03-18 21:30:13 +00:00
if (frame_fixed_width(frp))
return TRUE;
return FALSE;
}
// frp->fr_layout == FR_ROW: The frame is fixed width if all of the
// frames in the row are fixed width.
FOR_ALL_FRAMES(frp, frp->fr_child)
2006-03-18 21:30:13 +00:00
if (!frame_fixed_width(frp))
return FALSE;
return TRUE;
}
2004-06-13 20:20:40 +00:00
/*
* Add a status line to windows at the bottom of "frp".
* Note: Does not check if there is room!
2004-06-13 20:20:40 +00:00
*/
static void
frame_add_statusline(frame_T *frp)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
if (frp->fr_layout == FR_LEAF)
{
wp = frp->fr_win;
wp->w_status_height = STATUS_HEIGHT;
2004-06-13 20:20:40 +00:00
}
else if (frp->fr_layout == FR_ROW)
{
// Handle all the frames in the row.
FOR_ALL_FRAMES(frp, frp->fr_child)
frame_add_statusline(frp);
2004-06-13 20:20:40 +00:00
}
else // frp->fr_layout == FR_COL
2004-06-13 20:20:40 +00:00
{
// Only need to handle the last frame in the column.
2004-06-13 20:20:40 +00:00
for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next)
;
frame_add_statusline(frp);
2004-06-13 20:20:40 +00:00
}
}
/*
* Set width of a frame. Handles recursively going through contained frames.
* May remove separator line for windows at the right side (for win_close()).
*/
static void
frame_new_width(
frame_T *topfrp,
int width,
int leftfirst, // resize leftmost contained frame first
int wfw) // obey 'winfixwidth' when there is a choice;
// may cause the width not to be set
2004-06-13 20:20:40 +00:00
{
frame_T *frp;
int extra_cols;
int w;
win_T *wp;
if (topfrp->fr_layout == FR_LEAF)
{
// Simple case: just one window.
2004-06-13 20:20:40 +00:00
wp = topfrp->fr_win;
// Find out if there are any windows right of this one.
2004-06-13 20:20:40 +00:00
for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent)
if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL)
break;
if (frp->fr_parent == NULL)
wp->w_vsep_width = 0;
win_new_width(wp, width - wp->w_vsep_width);
}
else if (topfrp->fr_layout == FR_COL)
{
2006-03-18 21:30:13 +00:00
do
{
// All frames in this column get the same new width.
FOR_ALL_FRAMES(frp, topfrp->fr_child)
2006-03-18 21:30:13 +00:00
{
frame_new_width(frp, width, leftfirst, wfw);
if (frp->fr_width > width)
{
// Could not fit the windows, make whole column wider.
2006-03-18 21:30:13 +00:00
width = frp->fr_width;
break;
}
}
} while (frp != NULL);
2004-06-13 20:20:40 +00:00
}
else // fr_layout == FR_ROW
2004-06-13 20:20:40 +00:00
{
// Complicated case: Resize a row of frames. Resize the rightmost
// frame first, frames left of it when needed.
2004-06-13 20:20:40 +00:00
frp = topfrp->fr_child;
2006-03-18 21:30:13 +00:00
if (wfw)
// Advance past frames with one window with 'wfw' set.
2006-03-18 21:30:13 +00:00
while (frame_fixed_width(frp))
{
frp = frp->fr_next;
if (frp == NULL)
return; // no frame without 'wfw', give up
2006-03-18 21:30:13 +00:00
}
2004-06-13 20:20:40 +00:00
if (!leftfirst)
2006-03-18 21:30:13 +00:00
{
// Find the rightmost frame of this row
2004-06-13 20:20:40 +00:00
while (frp->fr_next != NULL)
frp = frp->fr_next;
2006-03-18 21:30:13 +00:00
if (wfw)
// Advance back for frames with one window with 'wfw' set.
2006-03-18 21:30:13 +00:00
while (frame_fixed_width(frp))
frp = frp->fr_prev;
}
2004-06-13 20:20:40 +00:00
extra_cols = width - topfrp->fr_width;
if (extra_cols < 0)
{
// reduce frame width, rightmost frame first
2004-06-13 20:20:40 +00:00
while (frp != NULL)
{
w = frame_minwidth(frp, NULL);
if (frp->fr_width + extra_cols < w)
{
extra_cols += frp->fr_width - w;
2006-03-18 21:30:13 +00:00
frame_new_width(frp, w, leftfirst, wfw);
2004-06-13 20:20:40 +00:00
}
else
{
2006-03-18 21:30:13 +00:00
frame_new_width(frp, frp->fr_width + extra_cols,
leftfirst, wfw);
2004-06-13 20:20:40 +00:00
break;
}
if (leftfirst)
2006-03-18 21:30:13 +00:00
{
do
frp = frp->fr_next;
while (wfw && frp != NULL && frame_fixed_width(frp));
}
2004-06-13 20:20:40 +00:00
else
2006-03-18 21:30:13 +00:00
{
do
frp = frp->fr_prev;
while (wfw && frp != NULL && frame_fixed_width(frp));
}
// Increase "width" if we could not reduce enough frames.
2006-03-18 21:30:13 +00:00
if (frp == NULL)
width -= extra_cols;
2004-06-13 20:20:40 +00:00
}
}
else if (extra_cols > 0)
{
// increase width of rightmost frame
2006-03-18 21:30:13 +00:00
frame_new_width(frp, frp->fr_width + extra_cols, leftfirst, wfw);
2004-06-13 20:20:40 +00:00
}
}
topfrp->fr_width = width;
}
/*
* Add the vertical separator to windows at the right side of "frp".
* Note: Does not check if there is room!
*/
static void
frame_add_vsep(frame_T *frp)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
if (frp->fr_layout == FR_LEAF)
{
wp = frp->fr_win;
if (wp->w_vsep_width == 0)
{
if (wp->w_width > 0) // don't make it negative
2004-06-13 20:20:40 +00:00
--wp->w_width;
wp->w_vsep_width = 1;
}
}
else if (frp->fr_layout == FR_COL)
{
// Handle all the frames in the column.
FOR_ALL_FRAMES(frp, frp->fr_child)
2004-06-13 20:20:40 +00:00
frame_add_vsep(frp);
}
else // frp->fr_layout == FR_ROW
2004-06-13 20:20:40 +00:00
{
// Only need to handle the last frame in the row.
2004-06-13 20:20:40 +00:00
frp = frp->fr_child;
while (frp->fr_next != NULL)
frp = frp->fr_next;
frame_add_vsep(frp);
}
}
/*
* Set frame width from the window it contains.
*/
static void
frame_fix_width(win_T *wp)
2004-06-13 20:20:40 +00:00
{
wp->w_frame->fr_width = wp->w_width + wp->w_vsep_width;
}
/*
* Set frame height from the window it contains.
*/
static void
frame_fix_height(win_T *wp)
2004-06-13 20:20:40 +00:00
{
wp->w_frame->fr_height = VISIBLE_HEIGHT(wp) + wp->w_status_height;
2004-06-13 20:20:40 +00:00
}
/*
* Compute the minimal height for frame "topfrp".
* Uses the 'winminheight' option.
* When "next_curwin" isn't NULL, use p_wh for this window.
* When "next_curwin" is NOWIN, don't use at least one line for the current
* window.
*/
static int
frame_minheight(frame_T *topfrp, win_T *next_curwin)
2004-06-13 20:20:40 +00:00
{
frame_T *frp;
int m;
int n;
if (topfrp->fr_win != NULL)
{
if (topfrp->fr_win == next_curwin)
m = p_wh + topfrp->fr_win->w_status_height;
else
{
// window: minimal height of the window plus status line
2004-06-13 20:20:40 +00:00
m = p_wmh + topfrp->fr_win->w_status_height;
if (topfrp->fr_win == curwin && next_curwin == NULL)
{
// Current window is minimal one line high and WinBar is
// visible.
if (p_wmh == 0)
++m;
m += WINBAR_HEIGHT(curwin);
}
2004-06-13 20:20:40 +00:00
}
}
else if (topfrp->fr_layout == FR_ROW)
{
// get the minimal height from each frame in this row
2004-06-13 20:20:40 +00:00
m = 0;
FOR_ALL_FRAMES(frp, topfrp->fr_child)
2004-06-13 20:20:40 +00:00
{
n = frame_minheight(frp, next_curwin);
if (n > m)
m = n;
}
}
else
{
// Add up the minimal heights for all frames in this column.
2004-06-13 20:20:40 +00:00
m = 0;
FOR_ALL_FRAMES(frp, topfrp->fr_child)
2004-06-13 20:20:40 +00:00
m += frame_minheight(frp, next_curwin);
}
return m;
}
/*
* Compute the minimal width for frame "topfrp".
* When "next_curwin" isn't NULL, use p_wiw for this window.
* When "next_curwin" is NOWIN, don't use at least one column for the current
* window.
*/
static int
frame_minwidth(
frame_T *topfrp,
win_T *next_curwin) // use p_wh and p_wiw for next_curwin
2004-06-13 20:20:40 +00:00
{
frame_T *frp;
int m, n;
if (topfrp->fr_win != NULL)
{
if (topfrp->fr_win == next_curwin)
m = p_wiw + topfrp->fr_win->w_vsep_width;
else
{
// window: minimal width of the window plus separator column
2004-06-13 20:20:40 +00:00
m = p_wmw + topfrp->fr_win->w_vsep_width;
// Current window is minimal one column wide
2004-06-13 20:20:40 +00:00
if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL)
++m;
}
}
else if (topfrp->fr_layout == FR_COL)
{
// get the minimal width from each frame in this column
2004-06-13 20:20:40 +00:00
m = 0;
FOR_ALL_FRAMES(frp, topfrp->fr_child)
2004-06-13 20:20:40 +00:00
{
n = frame_minwidth(frp, next_curwin);
if (n > m)
m = n;
}
}
else
{
// Add up the minimal widths for all frames in this row.
2004-06-13 20:20:40 +00:00
m = 0;
FOR_ALL_FRAMES(frp, topfrp->fr_child)
2004-06-13 20:20:40 +00:00
m += frame_minwidth(frp, next_curwin);
}
return m;
}
/*
* Try to close all windows except current one.
* Buffers in the other windows become hidden if 'hidden' is set, or '!' is
* used and the buffer was modified.
*
* Used by ":bdel" and ":only".
*/
void
close_others(
int message,
int forceit) // always hide all other windows
2004-06-13 20:20:40 +00:00
{
win_T *wp;
win_T *nextwp;
int r;
2009-06-16 14:01:43 +00:00
if (one_window())
2004-06-13 20:20:40 +00:00
{
if (message && !autocmd_busy)
msg(_(m_onlyone));
2004-06-13 20:20:40 +00:00
return;
}
// Be very careful here: autocommands may change the window layout.
2004-06-13 20:20:40 +00:00
for (wp = firstwin; win_valid(wp); wp = nextwp)
{
nextwp = wp->w_next;
if (wp == curwin) // don't close current window
continue;
2004-06-13 20:20:40 +00:00
// autoccommands messed this one up
if (!buf_valid(wp->w_buffer) && win_valid(wp))
{
wp->w_buffer = NULL;
win_close(wp, 0);
continue;
}
// Check if it's allowed to abandon this window
r = can_abandon(wp->w_buffer, forceit);
if (!win_valid(wp)) // autocommands messed wp up
{
nextwp = firstwin;
continue;
}
if (!r)
{
2004-06-13 20:20:40 +00:00
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
if (message && (p_confirm
|| (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
{
dialog_changed(wp->w_buffer, FALSE);
if (!win_valid(wp)) // autocommands messed wp up
2004-06-13 20:20:40 +00:00
{
nextwp = firstwin;
2004-06-13 20:20:40 +00:00
continue;
}
2004-06-13 20:20:40 +00:00
}
if (bufIsChanged(wp->w_buffer))
#endif
continue;
2004-06-13 20:20:40 +00:00
}
win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
2004-06-13 20:20:40 +00:00
}
if (message && !ONE_WINDOW)
emsg(_(e_other_window_contains_changes));
2004-06-13 20:20:40 +00:00
}
/*
* Store the relevant window pointers for tab page "tp". To be used before
* use_tabpage().
*/
void
unuse_tabpage(tabpage_T *tp)
{
tp->tp_topframe = topframe;
tp->tp_firstwin = firstwin;
tp->tp_lastwin = lastwin;
tp->tp_curwin = curwin;
}
// When switching tabpage, handle other side-effects in command_height(), but
// avoid setting frame sizes which are still correct.
static int command_frame_height = TRUE;
/*
* Set the relevant pointers to use tab page "tp". May want to call
* unuse_tabpage() first.
*/
void
use_tabpage(tabpage_T *tp)
{
curtab = tp;
topframe = curtab->tp_topframe;
firstwin = curtab->tp_firstwin;
lastwin = curtab->tp_lastwin;
curwin = curtab->tp_curwin;
}
2004-06-13 20:20:40 +00:00
/*
* Allocate the first window and put an empty buffer in it.
* Called from main().
2006-02-14 22:29:30 +00:00
* Return FAIL when something goes wrong (out of memory).
2004-06-13 20:20:40 +00:00
*/
2006-02-14 22:29:30 +00:00
int
win_alloc_first(void)
2006-02-14 22:29:30 +00:00
{
2006-02-20 21:27:21 +00:00
if (win_alloc_firstwin(NULL) == FAIL)
2006-02-14 22:29:30 +00:00
return FAIL;
2006-02-17 21:45:41 +00:00
first_tabpage = alloc_tabpage();
2006-02-14 22:29:30 +00:00
if (first_tabpage == NULL)
return FAIL;
2006-02-17 21:45:41 +00:00
curtab = first_tabpage;
unuse_tabpage(first_tabpage);
2009-06-16 14:01:43 +00:00
2006-02-14 22:29:30 +00:00
return OK;
}
2009-06-16 14:01:43 +00:00
/*
* Allocate and init a window that is not a regular window.
* This can only be done after the first window is fully initialized, thus it
* can't be in win_alloc_first().
2009-06-16 14:01:43 +00:00
*/
win_T *
win_alloc_popup_win(void)
2009-06-16 14:01:43 +00:00
{
win_T *wp;
wp = win_alloc(NULL, TRUE);
if (wp == NULL)
return NULL;
// We need to initialize options with something, using the current
// window makes most sense.
win_init_some(wp, curwin);
RESET_BINDING(wp);
new_frame(wp);
return wp;
}
/*
* Initialize window "wp" to display buffer "buf".
*/
void
win_init_popup_win(win_T *wp, buf_T *buf)
{
wp->w_buffer = buf;
++buf->b_nwindows;
win_init_empty(wp); // set cursor and topline to safe values
// Make sure w_localdir is NULL to avoid a chdir() in win_enter_ext().
VIM_CLEAR(wp->w_localdir);
2009-06-16 14:01:43 +00:00
}
2006-02-14 22:29:30 +00:00
/*
2006-02-20 21:27:21 +00:00
* Allocate the first window or the first window in a new tab page.
* When "oldwin" is NULL create an empty buffer for it.
* When "oldwin" is not NULL copy info from it to the new window.
2006-02-14 22:29:30 +00:00
* Return FAIL when something goes wrong (out of memory).
*/
static int
win_alloc_firstwin(win_T *oldwin)
2004-06-13 20:20:40 +00:00
{
2009-06-16 14:01:43 +00:00
curwin = win_alloc(NULL, FALSE);
if (curwin == NULL)
return FAIL;
2006-02-20 21:27:21 +00:00
if (oldwin == NULL)
{
// Very first window, need to create an empty buffer for it and
// initialize from scratch.
2006-02-20 21:27:21 +00:00
curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED);
if (curwin == NULL || curbuf == NULL)
return FAIL;
curwin->w_buffer = curbuf;
#ifdef FEAT_SYN_HL
curwin->w_s = &(curbuf->b_s);
#endif
curbuf->b_nwindows = 1; // there is one window
2006-02-20 21:27:21 +00:00
curwin->w_alist = &global_alist;
curwin_init(); // init current window
2006-02-20 21:27:21 +00:00
}
else
{
// First window in new tab page, initialize it from "oldwin".
2009-02-22 01:37:59 +00:00
win_init(curwin, oldwin, 0);
2006-02-20 21:27:21 +00:00
// We don't want cursor- and scroll-binding in the first window.
RESET_BINDING(curwin);
2006-02-20 21:27:21 +00:00
}
2004-06-13 20:20:40 +00:00
2009-06-16 14:01:43 +00:00
new_frame(curwin);
if (curwin->w_frame == NULL)
2006-02-14 22:29:30 +00:00
return FAIL;
2009-06-16 14:01:43 +00:00
topframe = curwin->w_frame;
2004-06-13 20:20:40 +00:00
topframe->fr_width = Columns;
topframe->fr_height = Rows - p_ch;
2006-02-14 22:29:30 +00:00
return OK;
}
2009-06-16 14:01:43 +00:00
/*
* Create a frame for window "wp".
*/
static void
new_frame(win_T *wp)
{
frame_T *frp = ALLOC_CLEAR_ONE(frame_T);
2009-06-16 14:01:43 +00:00
wp->w_frame = frp;
if (frp == NULL)
return;
frp->fr_layout = FR_LEAF;
frp->fr_win = wp;
2009-06-16 14:01:43 +00:00
}
2006-02-14 22:29:30 +00:00
/*
* Initialize the window and frame size to the maximum.
*/
void
win_init_size(void)
2006-02-14 22:29:30 +00:00
{
firstwin->w_height = ROWS_AVAIL;
firstwin->w_prev_height = ROWS_AVAIL;
2006-02-14 22:29:30 +00:00
topframe->fr_height = ROWS_AVAIL;
firstwin->w_width = Columns;
topframe->fr_width = Columns;
2004-06-13 20:20:40 +00:00
}
2006-02-17 21:45:41 +00:00
/*
* Allocate a new tabpage_T and init the values.
* Returns NULL when out of memory.
*/
static tabpage_T *
alloc_tabpage(void)
2006-02-17 21:45:41 +00:00
{
tabpage_T *tp;
# ifdef FEAT_GUI
int i;
# endif
2006-02-17 21:45:41 +00:00
tp = ALLOC_CLEAR_ONE(tabpage_T);
if (tp == NULL)
return NULL;
# ifdef FEAT_EVAL
// init t: variables
tp->tp_vars = dict_alloc_id(aid_newtabpage_tvars);
if (tp->tp_vars == NULL)
2006-02-17 21:45:41 +00:00
{
vim_free(tp);
return NULL;
}
init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE);
# endif
2006-03-20 21:47:49 +00:00
# ifdef FEAT_GUI
for (i = 0; i < 3; i++)
tp->tp_prev_which_scrollbars[i] = -1;
2006-03-20 21:47:49 +00:00
# endif
2006-02-17 21:45:41 +00:00
# ifdef FEAT_DIFF
tp->tp_diff_invalid = TRUE;
2006-02-17 21:45:41 +00:00
# endif
tp->tp_ch_used = p_ch;
2006-02-17 21:45:41 +00:00
return tp;
}
2006-04-29 21:55:22 +00:00
void
free_tabpage(tabpage_T *tp)
2006-02-17 21:45:41 +00:00
{
2009-06-16 14:01:43 +00:00
int idx;
2006-02-17 21:45:41 +00:00
# ifdef FEAT_DIFF
diff_clear(tp);
# endif
# ifdef FEAT_PROP_POPUP
while (tp->tp_first_popupwin != NULL)
popup_close_tabpage(tp, tp->tp_first_popupwin->w_id, TRUE);
#endif
2009-06-16 14:01:43 +00:00
for (idx = 0; idx < SNAP_COUNT; ++idx)
clear_snapshot(tp, idx);
2006-04-05 20:41:53 +00:00
#ifdef FEAT_EVAL
vars_clear(&tp->tp_vars->dv_hashtab); // free all t: variables
hash_init(&tp->tp_vars->dv_hashtab);
unref_var_dict(tp->tp_vars);
2006-04-05 20:41:53 +00:00
#endif
if (tp == lastused_tabpage)
lastused_tabpage = NULL;
vim_free(tp->tp_localdir);
vim_free(tp->tp_prevdir);
#ifdef FEAT_PYTHON
python_tabpage_free(tp);
#endif
#ifdef FEAT_PYTHON3
python3_tabpage_free(tp);
#endif
2006-02-17 21:45:41 +00:00
vim_free(tp);
}
2006-02-14 22:29:30 +00:00
/*
2006-02-20 21:27:21 +00:00
* Create a new Tab page with one window.
* It will edit the current buffer, like after ":split".
2006-02-23 21:26:58 +00:00
* When "after" is 0 put it just after the current Tab page.
* Otherwise put it just before tab page "after".
*
* Does not trigger WinNewPre, since the window structures
* are not completely setup yet and could cause dereferencing
* NULL pointers
*
2006-02-14 22:29:30 +00:00
* Return FAIL or OK.
*/
int
win_new_tabpage(int after)
2006-02-14 22:29:30 +00:00
{
2006-02-17 21:45:41 +00:00
tabpage_T *tp = curtab;
tabpage_T *prev_tp = curtab;
2006-02-14 22:29:30 +00:00
tabpage_T *newtp;
2006-02-23 21:26:58 +00:00
int n;
2006-02-14 22:29:30 +00:00
if (cmdwin_type != 0)
{
emsg(_(e_invalid_in_cmdline_window));
return FAIL;
}
if (window_layout_locked(CMD_tabnew))
return FAIL;
2006-02-17 21:45:41 +00:00
newtp = alloc_tabpage();
2006-02-14 22:29:30 +00:00
if (newtp == NULL)
return FAIL;
// Remember the current windows in this Tab page.
if (leave_tabpage(curbuf, TRUE) == FAIL)
2006-02-18 22:14:51 +00:00
{
vim_free(newtp);
return FAIL;
}
2006-02-17 21:45:41 +00:00
curtab = newtp;
2006-02-14 22:29:30 +00:00
newtp->tp_localdir = (tp->tp_localdir == NULL)
? NULL : vim_strsave(tp->tp_localdir);
// Create a new empty window.
2006-02-20 21:27:21 +00:00
if (win_alloc_firstwin(tp->tp_curwin) == OK)
2006-02-14 22:29:30 +00:00
{
// Make the new Tab page the new topframe.
2006-02-23 21:26:58 +00:00
if (after == 1)
{
// New tab page becomes the first one.
2006-02-23 21:26:58 +00:00
newtp->tp_next = first_tabpage;
first_tabpage = newtp;
}
else
{
if (after > 0)
{
// Put new tab page before tab page "after".
2006-02-23 21:26:58 +00:00
n = 2;
for (tp = first_tabpage; tp->tp_next != NULL
&& n < after; tp = tp->tp_next)
++n;
}
newtp->tp_next = tp->tp_next;
tp->tp_next = newtp;
}
newtp->tp_firstwin = newtp->tp_lastwin = newtp->tp_curwin = curwin;
2006-02-14 22:29:30 +00:00
win_init_size();
2006-02-24 23:53:04 +00:00
firstwin->w_winrow = tabline_height();
firstwin->w_prev_winrow = firstwin->w_winrow;
2006-04-05 20:41:53 +00:00
win_comp_scroll(curwin);
2006-02-14 22:29:30 +00:00
newtp->tp_topframe = topframe;
2006-02-16 22:11:02 +00:00
last_status(FALSE);
2006-03-28 20:57:42 +00:00
lastused_tabpage = prev_tp;
2006-03-28 20:57:42 +00:00
#if defined(FEAT_GUI)
// When 'guioptions' includes 'L' or 'R' may have to remove or add
// scrollbars. Have to update them anyway.
2009-06-16 14:01:43 +00:00
gui_may_update_scrollbars();
2006-03-28 20:57:42 +00:00
#endif
#ifdef FEAT_JOB_CHANNEL
entering_window(curwin);
#endif
2006-03-28 20:57:42 +00:00
redraw_all_later(UPD_NOT_VALID);
apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf);
2006-02-20 21:27:21 +00:00
apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
apply_autocmds(EVENT_TABNEW, NULL, NULL, FALSE, curbuf);
apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
2006-02-14 22:29:30 +00:00
return OK;
}
// Failed, get back the previous Tab page
enter_tabpage(curtab, curbuf, TRUE, TRUE);
2006-02-14 22:29:30 +00:00
return FAIL;
}
2006-02-23 21:26:58 +00:00
/*
* Open a new tab page if ":tab cmd" was used. It will edit the same buffer,
* like with ":split".
* Returns OK if a new tab page was created, FAIL otherwise.
*/
static int
may_open_tabpage(void)
2006-02-23 21:26:58 +00:00
{
int n = (cmdmod.cmod_tab == 0)
? postponed_split_tab : cmdmod.cmod_tab;
2006-02-23 21:26:58 +00:00
if (n == 0)
return FAIL;
cmdmod.cmod_tab = 0; // reset it to avoid doing it twice
postponed_split_tab = 0;
return win_new_tabpage(n);
2006-02-23 21:26:58 +00:00
}
2006-02-14 22:29:30 +00:00
/*
2006-02-17 21:45:41 +00:00
* Create up to "maxcount" tabpages with empty windows.
* Returns the number of resulting tab pages.
2006-02-14 22:29:30 +00:00
*/
2006-02-17 21:45:41 +00:00
int
make_tabpages(int maxcount)
2006-02-14 22:29:30 +00:00
{
2006-02-17 21:45:41 +00:00
int count = maxcount;
int todo;
2006-02-14 22:29:30 +00:00
// Limit to 'tabpagemax' tabs.
2006-03-01 22:01:55 +00:00
if (count > p_tpm)
count = p_tpm;
2006-02-17 21:45:41 +00:00
/*
* Don't execute autocommands while creating the tab pages. Must do that
* when putting the buffers in the windows.
*/
2007-09-29 12:16:41 +00:00
block_autocmds();
2006-02-17 21:45:41 +00:00
for (todo = count - 1; todo > 0; --todo)
2006-02-23 21:26:58 +00:00
if (win_new_tabpage(0) == FAIL)
2006-02-14 22:29:30 +00:00
break;
2006-02-17 21:45:41 +00:00
2007-09-29 12:16:41 +00:00
unblock_autocmds();
2006-02-17 21:45:41 +00:00
// return actual number of tab pages
2006-02-17 21:45:41 +00:00
return (count - todo);
2006-02-14 22:29:30 +00:00
}
2006-02-16 22:11:02 +00:00
/*
* Return TRUE when "tpc" points to a valid tab page.
*/
int
valid_tabpage(tabpage_T *tpc)
2006-02-16 22:11:02 +00:00
{
tabpage_T *tp;
FOR_ALL_TABPAGES(tp)
2006-02-16 22:11:02 +00:00
if (tp == tpc)
return TRUE;
return FALSE;
}
/*
* Return TRUE when "tpc" points to a valid tab page and at least one window is
* valid.
*/
int
valid_tabpage_win(tabpage_T *tpc)
{
tabpage_T *tp;
win_T *wp;
FOR_ALL_TABPAGES(tp)
{
if (tp == tpc)
{
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
{
if (win_valid_any_tab(wp))
return TRUE;
}
return FALSE;
}
}
// shouldn't happen
return FALSE;
}
/*
* Close tabpage "tab", assuming it has no windows in it.
* There must be another tabpage or this will crash.
*/
void
close_tabpage(tabpage_T *tab)
{
tabpage_T *ptp;
if (tab == first_tabpage)
{
first_tabpage = tab->tp_next;
ptp = first_tabpage;
}
else
{
for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab;
ptp = ptp->tp_next)
;
assert(ptp != NULL);
ptp->tp_next = tab->tp_next;
}
goto_tabpage_tp(ptp, FALSE, FALSE);
free_tabpage(tab);
}
2006-02-16 22:11:02 +00:00
/*
* Find tab page "n" (first one is 1). Returns NULL when not found.
*/
tabpage_T *
find_tabpage(int n)
2006-02-16 22:11:02 +00:00
{
tabpage_T *tp;
int i = 1;
if (n == 0)
return curtab;
2006-02-16 22:11:02 +00:00
for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next)
++i;
return tp;
}
2006-02-24 23:53:04 +00:00
/*
* Get index of tab page "tp". First one has index 1.
2006-02-25 21:45:02 +00:00
* When not found returns number of tab pages plus one.
2006-02-24 23:53:04 +00:00
*/
int
tabpage_index(tabpage_T *ftp)
2006-02-24 23:53:04 +00:00
{
int i = 1;
tabpage_T *tp;
for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next)
++i;
return i;
}
2006-02-14 22:29:30 +00:00
/*
2006-02-18 22:14:51 +00:00
* Prepare for leaving the current tab page.
* When autocommands change "curtab" we don't leave the tab page and return
2006-02-18 22:14:51 +00:00
* FAIL.
* Careful: When OK is returned need to get a new tab page very very soon!
2006-02-14 22:29:30 +00:00
*/
2006-02-18 22:14:51 +00:00
static int
leave_tabpage(
buf_T *new_curbuf, // what is going to be the new curbuf,
// NULL if unknown
int trigger_leave_autocmds)
2006-02-14 22:29:30 +00:00
{
2006-02-18 22:14:51 +00:00
tabpage_T *tp = curtab;
#ifdef FEAT_JOB_CHANNEL
leaving_window(curwin);
#endif
reset_VIsual_and_resel(); // stop Visual mode
if (trigger_leave_autocmds)
2006-02-18 22:14:51 +00:00
{
if (new_curbuf != curbuf)
{
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
if (curtab != tp)
return FAIL;
}
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
if (curtab != tp)
return FAIL;
apply_autocmds(EVENT_TABLEAVE, NULL, NULL, FALSE, curbuf);
2006-02-18 22:14:51 +00:00
if (curtab != tp)
return FAIL;
}
reset_dragwin();
2006-02-15 22:11:25 +00:00
#if defined(FEAT_GUI)
// Remove the scrollbars. They may be added back later.
2006-02-15 22:11:25 +00:00
if (gui.in_use)
gui_remove_scrollbars();
#endif
2006-02-14 22:29:30 +00:00
tp->tp_curwin = curwin;
2006-02-16 22:11:02 +00:00
tp->tp_prevwin = prevwin;
2006-02-14 22:29:30 +00:00
tp->tp_firstwin = firstwin;
tp->tp_lastwin = lastwin;
2006-02-15 22:11:25 +00:00
tp->tp_old_Rows = Rows;
if (tp->tp_old_Columns != -1)
tp->tp_old_Columns = Columns;
2006-02-14 22:29:30 +00:00
firstwin = NULL;
lastwin = NULL;
2006-02-18 22:14:51 +00:00
return OK;
2006-02-14 22:29:30 +00:00
}
/*
* Start using tab page "tp".
2006-02-20 21:27:21 +00:00
* Only to be used after leave_tabpage() or freeing the current tab page.
* Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE.
* Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE.
2006-02-14 22:29:30 +00:00
*/
static void
enter_tabpage(
tabpage_T *tp,
buf_T *old_curbuf,
int trigger_enter_autocmds,
int trigger_leave_autocmds)
2006-02-14 22:29:30 +00:00
{
2006-02-15 22:11:25 +00:00
int old_off = tp->tp_firstwin->w_winrow;
2006-05-06 21:38:18 +00:00
win_T *next_prevwin = tp->tp_prevwin;
tabpage_T *last_tab = curtab;
2006-02-15 22:11:25 +00:00
use_tabpage(tp);
2006-02-20 21:27:21 +00:00
if (p_ch != curtab->tp_ch_used)
{
// Use the stored value of p_ch, so that it can be different for each
// tab page.
int new_ch = curtab->tp_ch_used;
curtab->tp_ch_used = p_ch;
command_frame_height = FALSE;
set_option_value((char_u *)"cmdheight", new_ch, NULL, 0);
command_frame_height = TRUE;
}
// We would like doing the TabEnter event first, but we don't have a
// valid current window yet, which may break some commands.
// This triggers autocommands, thus may make "tp" invalid.
(void)win_enter_ext(tp->tp_curwin, WEE_CURWIN_INVALID
| (trigger_enter_autocmds ? WEE_TRIGGER_ENTER_AUTOCMDS : 0)
| (trigger_leave_autocmds ? WEE_TRIGGER_LEAVE_AUTOCMDS : 0));
2006-05-06 21:38:18 +00:00
prevwin = next_prevwin;
2006-02-14 22:29:30 +00:00
last_status(FALSE); // status line may appear or disappear
win_comp_pos(); // recompute w_winrow for all windows
2006-02-17 21:45:41 +00:00
#ifdef FEAT_DIFF
diff_need_scrollbind = TRUE;
#endif
2006-02-14 22:29:30 +00:00
// If there was a click in a window, it won't be usable for a following
// drag.
reset_dragwin();
// The tabpage line may have appeared or disappeared, may need to resize
// the frames for that. When the Vim window was resized need to update
// frame sizes too.
2006-02-24 23:53:04 +00:00
if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow
#ifdef FEAT_GUI_TABLINE
&& !gui_use_tabline()
#endif
))
2006-02-15 22:11:25 +00:00
shell_new_rows();
if (curtab->tp_old_Columns != Columns)
{
if (starting == 0)
{
shell_new_columns(); // update window widths
curtab->tp_old_Columns = Columns;
}
else
curtab->tp_old_Columns = -1; // update window widths later
}
2006-02-15 22:11:25 +00:00
lastused_tabpage = last_tab;
2006-02-15 22:11:25 +00:00
#if defined(FEAT_GUI)
// When 'guioptions' includes 'L' or 'R' may have to remove or add
// scrollbars. Have to update them anyway.
2009-06-16 14:01:43 +00:00
gui_may_update_scrollbars();
2006-02-14 22:29:30 +00:00
#endif
// Apply autocommands after updating the display, when 'rows' and
// 'columns' have been set correctly.
if (trigger_enter_autocmds)
{
apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
if (old_curbuf != curbuf)
apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
}
redraw_all_later(UPD_NOT_VALID);
2006-02-14 22:29:30 +00:00
}
/*
* Go to tab page "n". For ":tab N" and "Ngt".
2006-02-24 23:53:04 +00:00
* When "n" is 9999 go to the last tab page.
2006-02-14 22:29:30 +00:00
*/
void
goto_tabpage(int n)
2006-02-14 22:29:30 +00:00
{
tabpage_T *tp = NULL; // shut up compiler
2006-02-23 21:26:58 +00:00
tabpage_T *ttp;
2006-02-14 22:29:30 +00:00
int i;
2006-05-02 22:08:30 +00:00
if (text_locked())
{
// Not allowed when editing the command line.
text_locked_msg();
2006-05-02 22:08:30 +00:00
return;
}
// If there is only one it can't work.
2006-02-20 21:27:21 +00:00
if (first_tabpage->tp_next == NULL)
{
if (n > 1)
beep_flush();
return;
}
2006-02-14 22:29:30 +00:00
if (n == 0)
{
// No count, go to next tab page, wrap around end.
2006-02-18 22:14:51 +00:00
if (curtab->tp_next == NULL)
2006-02-14 22:29:30 +00:00
tp = first_tabpage;
else
2006-02-18 22:14:51 +00:00
tp = curtab->tp_next;
2006-02-14 22:29:30 +00:00
}
2006-02-23 21:26:58 +00:00
else if (n < 0)
{
// "gT": go to previous tab page, wrap around end. "N gT" repeats
// this N times.
2006-02-23 21:26:58 +00:00
ttp = curtab;
for (i = n; i < 0; ++i)
{
for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL;
tp = tp->tp_next)
;
ttp = tp;
}
}
2006-02-24 23:53:04 +00:00
else if (n == 9999)
{
// Go to last tab page.
2006-02-24 23:53:04 +00:00
for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next)
;
}
2006-02-14 22:29:30 +00:00
else
{
// Go to tab page "n".
2006-02-24 23:53:04 +00:00
tp = find_tabpage(n);
2006-02-16 22:11:02 +00:00
if (tp == NULL)
{
beep_flush();
return;
}
2006-02-14 22:29:30 +00:00
}
goto_tabpage_tp(tp, TRUE, TRUE);
2006-02-24 23:53:04 +00:00
#ifdef FEAT_GUI_TABLINE
if (gui_use_tabline())
2006-02-26 23:59:20 +00:00
gui_mch_set_curtab(tabpage_index(curtab));
2006-02-24 23:53:04 +00:00
#endif
}
/*
* Go to tabpage "tp".
* Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE.
* Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE.
2006-02-24 23:53:04 +00:00
* Note: doesn't update the GUI tab.
*/
void
goto_tabpage_tp(
tabpage_T *tp,
int trigger_enter_autocmds,
int trigger_leave_autocmds)
2006-02-24 23:53:04 +00:00
{
if (trigger_enter_autocmds || trigger_leave_autocmds)
CHECK_CMDWIN;
// Don't repeat a message in another tab page.
set_keep_msg(NULL, 0);
skip_win_fix_scroll = TRUE;
if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer,
trigger_leave_autocmds) == OK)
2006-02-18 22:14:51 +00:00
{
if (valid_tabpage(tp))
enter_tabpage(tp, curbuf, trigger_enter_autocmds,
trigger_leave_autocmds);
2006-02-18 22:14:51 +00:00
else
enter_tabpage(curtab, curbuf, trigger_enter_autocmds,
trigger_leave_autocmds);
2006-02-18 22:14:51 +00:00
}
skip_win_fix_scroll = FALSE;
2006-02-14 22:29:30 +00:00
}
2004-06-13 20:20:40 +00:00
/*
* Go to the last accessed tab page, if there is one.
* Return OK or FAIL
*/
int
goto_tabpage_lastused(void)
{
if (!valid_tabpage(lastused_tabpage))
return FAIL;
goto_tabpage_tp(lastused_tabpage, TRUE, TRUE);
return OK;
}
2006-04-10 14:55:34 +00:00
/*
* Enter window "wp" in tab page "tp".
* Also updates the GUI tab.
*/
void
goto_tabpage_win(tabpage_T *tp, win_T *wp)
2006-04-10 14:55:34 +00:00
{
goto_tabpage_tp(tp, TRUE, TRUE);
2006-04-10 14:55:34 +00:00
if (curtab == tp && win_valid(wp))
{
win_enter(wp, TRUE);
# ifdef FEAT_GUI_TABLINE
if (gui_use_tabline())
gui_mch_set_curtab(tabpage_index(curtab));
# endif
}
}
2006-02-23 21:26:58 +00:00
/*
* Move the current tab page to after tab page "nr".
2006-02-23 21:26:58 +00:00
*/
void
tabpage_move(int nr)
2006-02-23 21:26:58 +00:00
{
int n = 1;
tabpage_T *tp, *tp_dst;
2006-02-23 21:26:58 +00:00
if (first_tabpage->tp_next == NULL)
return;
if (tabpage_move_disallowed)
return;
for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next)
++n;
if (tp == curtab || (nr > 0 && tp->tp_next != NULL
&& tp->tp_next == curtab))
return;
tp_dst = tp;
// Remove the current tab page from the list of tab pages.
2006-02-23 21:26:58 +00:00
if (curtab == first_tabpage)
first_tabpage = curtab->tp_next;
else
{
FOR_ALL_TABPAGES(tp)
2006-02-23 21:26:58 +00:00
if (tp->tp_next == curtab)
break;
if (tp == NULL) // "cannot happen"
2006-02-23 21:26:58 +00:00
return;
tp->tp_next = curtab->tp_next;
}
// Re-insert it at the specified position.
if (nr <= 0)
2006-02-23 21:26:58 +00:00
{
curtab->tp_next = first_tabpage;
first_tabpage = curtab;
}
else
{
curtab->tp_next = tp_dst->tp_next;
tp_dst->tp_next = curtab;
2006-02-23 21:26:58 +00:00
}
// Need to redraw the tabline. Tab page contents doesn't change.
2006-02-23 21:26:58 +00:00
redraw_tabline = TRUE;
}
2004-06-13 20:20:40 +00:00
/*
* Go to another window.
* When jumping to another buffer, stop Visual mode. Do this before
* changing windows so we can yank the selection into the '*' register.
* (note: this may trigger ModeChanged autocommand!)
2004-06-13 20:20:40 +00:00
* When jumping to another window on the same buffer, adjust its cursor
* position to keep the same Visual area.
*/
void
win_goto(win_T *wp)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_CONCEAL
win_T *owp = curwin;
#endif
#ifdef FEAT_PROP_POPUP
if (ERROR_IF_ANY_POPUP_WINDOW)
return;
if (popup_is_popup(wp))
{
emsg(_(e_not_allowed_to_enter_popup_window));
return;
}
#endif
if (text_or_buf_locked())
2004-06-13 20:20:40 +00:00
{
beep_flush();
return;
}
2006-01-19 22:09:32 +00:00
2004-06-13 20:20:40 +00:00
if (wp->w_buffer != curbuf)
// careful: triggers ModeChanged autocommand
2004-06-13 20:20:40 +00:00
reset_VIsual_and_resel();
else if (VIsual_active)
wp->w_cursor = curwin->w_cursor;
// autocommand may have made wp invalid
if (!win_valid(wp))
return;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_enter(wp, TRUE);
#ifdef FEAT_CONCEAL
// Conceal cursor line in previous window, unconceal in current window.
if (win_valid(owp) && owp->w_p_cole > 0 && !msg_scrolled)
redrawWinline(owp, owp->w_cursor.lnum);
if (curwin->w_p_cole > 0 && !msg_scrolled)
need_cursor_line_redraw = TRUE;
#endif
2004-06-13 20:20:40 +00:00
}
#if defined(FEAT_PERL) || defined(PROTO)
2004-06-13 20:20:40 +00:00
/*
* Find window number "winnr" (counting top to bottom).
*/
win_T *
win_find_nr(int winnr)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
FOR_ALL_WINDOWS(wp)
2004-06-13 20:20:40 +00:00
if (--winnr == 0)
break;
return wp;
}
#endif
#if ((defined(FEAT_PYTHON) || defined(FEAT_PYTHON3))) || defined(PROTO)
/*
* Find the tabpage for window "win".
*/
tabpage_T *
win_find_tabpage(win_T *win)
{
win_T *wp;
tabpage_T *tp;
FOR_ALL_TAB_WINDOWS(tp, wp)
if (wp == win)
return tp;
return NULL;
}
#endif
2004-06-13 20:20:40 +00:00
/*
* Get the above or below neighbor window of the specified window.
* up - TRUE for the above neighbor
* count - nth neighbor window
* Returns the specified window if the neighbor is not found.
2004-06-13 20:20:40 +00:00
*/
win_T *
win_vert_neighbor(tabpage_T *tp, win_T *wp, int up, long count)
2004-06-13 20:20:40 +00:00
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
#ifdef FEAT_PROP_POPUP
if (popup_is_popup(wp))
// popups don't have neighbors.
return NULL;
#endif
foundfr = wp->w_frame;
2004-06-13 20:20:40 +00:00
while (count--)
{
/*
* First go upwards in the tree of frames until we find a upwards or
* downwards neighbor.
*/
fr = foundfr;
for (;;)
{
if (fr == tp->tp_topframe)
2004-06-13 20:20:40 +00:00
goto end;
if (up)
nfr = fr->fr_prev;
else
nfr = fr->fr_next;
if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL)
break;
fr = fr->fr_parent;
}
/*
* Now go downwards to find the bottom or top frame in it.
*/
for (;;)
{
if (nfr->fr_layout == FR_LEAF)
{
foundfr = nfr;
break;
}
fr = nfr->fr_child;
if (nfr->fr_layout == FR_ROW)
{
// Find the frame at the cursor row.
2004-06-13 20:20:40 +00:00
while (fr->fr_next != NULL
&& frame2win(fr)->w_wincol + fr->fr_width
<= wp->w_wincol + wp->w_wcol)
2004-06-13 20:20:40 +00:00
fr = fr->fr_next;
}
if (nfr->fr_layout == FR_COL && up)
while (fr->fr_next != NULL)
fr = fr->fr_next;
nfr = fr;
}
}
end:
return foundfr != NULL ? foundfr->fr_win : NULL;
2004-06-13 20:20:40 +00:00
}
/*
* Move to window above or below "count" times.
2004-06-13 20:20:40 +00:00
*/
static void
win_goto_ver(
int up, // TRUE to go to win above
long count)
{
win_T *win;
#ifdef FEAT_PROP_POPUP
if (ERROR_IF_TERM_POPUP_WINDOW)
return;
#endif
win = win_vert_neighbor(curtab, curwin, up, count);
if (win != NULL)
win_goto(win);
}
/*
* Get the left or right neighbor window of the specified window.
* left - TRUE for the left neighbor
* count - nth neighbor window
* Returns the specified window if the neighbor is not found.
*/
win_T *
win_horz_neighbor(tabpage_T *tp, win_T *wp, int left, long count)
2004-06-13 20:20:40 +00:00
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
#ifdef FEAT_PROP_POPUP
if (popup_is_popup(wp))
// popups don't have neighbors.
return NULL;
#endif
foundfr = wp->w_frame;
2004-06-13 20:20:40 +00:00
while (count--)
{
/*
* First go upwards in the tree of frames until we find a left or
* right neighbor.
*/
fr = foundfr;
for (;;)
{
if (fr == tp->tp_topframe)
2004-06-13 20:20:40 +00:00
goto end;
if (left)
nfr = fr->fr_prev;
else
nfr = fr->fr_next;
if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
break;
fr = fr->fr_parent;
}
/*
* Now go downwards to find the leftmost or rightmost frame in it.
*/
for (;;)
{
if (nfr->fr_layout == FR_LEAF)
{
foundfr = nfr;
break;
}
fr = nfr->fr_child;
if (nfr->fr_layout == FR_COL)
{
// Find the frame at the cursor row.
2004-06-13 20:20:40 +00:00
while (fr->fr_next != NULL
&& frame2win(fr)->w_winrow + fr->fr_height
<= wp->w_winrow + wp->w_wrow)
2004-06-13 20:20:40 +00:00
fr = fr->fr_next;
}
if (nfr->fr_layout == FR_ROW && left)
while (fr->fr_next != NULL)
fr = fr->fr_next;
nfr = fr;
}
}
end:
return foundfr != NULL ? foundfr->fr_win : NULL;
}
/*
* Move to left or right window.
*/
static void
win_goto_hor(
int left, // TRUE to go to left win
long count)
{
win_T *win;
#ifdef FEAT_PROP_POPUP
if (ERROR_IF_TERM_POPUP_WINDOW)
return;
#endif
win = win_horz_neighbor(curtab, curwin, left, count);
if (win != NULL)
win_goto(win);
2004-06-13 20:20:40 +00:00
}
/*
* Make window "wp" the current window.
*/
void
win_enter(win_T *wp, int undo_sync)
2004-06-13 20:20:40 +00:00
{
(void)win_enter_ext(wp, (undo_sync ? WEE_UNDO_SYNC : 0)
| WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS);
2004-06-13 20:20:40 +00:00
}
/*
* Used after making another window the current one: change directory if
* needed.
*/
void
win_fix_current_dir(void)
{
if (curwin->w_localdir != NULL || curtab->tp_localdir != NULL)
{
char_u *dirname;
// Window or tab has a local directory: Save current directory as
// global directory (unless that was done already) and change to the
// local directory.
if (globaldir == NULL)
{
char_u cwd[MAXPATHL];
if (mch_dirname(cwd, MAXPATHL) == OK)
globaldir = vim_strsave(cwd);
}
if (curwin->w_localdir != NULL)
dirname = curwin->w_localdir;
else
dirname = curtab->tp_localdir;
if (mch_chdir((char *)dirname) == 0)
{
last_chdir_reason = NULL;
shorten_fnames(TRUE);
}
}
else if (globaldir != NULL)
{
// Window doesn't have a local directory and we are not in the global
// directory: Change to the global directory.
vim_ignored = mch_chdir((char *)globaldir);
VIM_CLEAR(globaldir);
last_chdir_reason = NULL;
shorten_fnames(TRUE);
}
}
2004-06-13 20:20:40 +00:00
/*
* Make window "wp" the current window.
* Can be called with "flags" containing WEE_CURWIN_INVALID, which means that
* curwin has just been closed and isn't valid.
* Returns TRUE when dont_parse_messages was decremented.
2004-06-13 20:20:40 +00:00
*/
static int
win_enter_ext(win_T *wp, int flags)
2004-06-13 20:20:40 +00:00
{
int other_buffer = FALSE;
int curwin_invalid = (flags & WEE_CURWIN_INVALID);
int did_decrement = FALSE;
2004-06-13 20:20:40 +00:00
if (wp == curwin && curwin_invalid == 0) // nothing to do
return FALSE;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_JOB_CHANNEL
if (curwin_invalid == 0)
leaving_window(curwin);
#endif
if (curwin_invalid == 0 && (flags & WEE_TRIGGER_LEAVE_AUTOCMDS))
2004-06-13 20:20:40 +00:00
{
/*
* Be careful: If autocommands delete the window, return now.
*/
if (wp->w_buffer != curbuf)
{
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
other_buffer = TRUE;
if (!win_valid(wp))
return FALSE;
2004-06-13 20:20:40 +00:00
}
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
if (!win_valid(wp))
return FALSE;
#ifdef FEAT_EVAL
// autocmds may abort script processing
2004-06-13 20:20:40 +00:00
if (aborting())
return FALSE;
2004-06-13 20:20:40 +00:00
#endif
}
2004-06-13 20:20:40 +00:00
// sync undo before leaving the current buffer
if ((flags & WEE_UNDO_SYNC) && curbuf != wp->w_buffer)
2006-04-10 14:55:34 +00:00
u_sync(FALSE);
// Might need to scroll the old window before switching, e.g., when the
// cursor was moved.
if (*p_spk == 'c' && curwin_invalid == 0)
update_topline();
// may have to copy the buffer options when 'cpo' contains 'S'
2004-06-13 20:20:40 +00:00
if (wp->w_buffer != curbuf)
buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP);
if (curwin_invalid == 0)
2004-06-13 20:20:40 +00:00
{
prevwin = curwin; // remember for CTRL-W p
2004-06-13 20:20:40 +00:00
curwin->w_redr_status = TRUE;
}
curwin = wp;
curbuf = wp->w_buffer;
check_cursor();
if (!virtual_active())
curwin->w_cursor.coladd = 0;
if (*p_spk == 'c') // assume cursor position needs updating
changed_line_abv_curs();
else
// Make sure the cursor position is valid, either by moving the cursor
// or by scrolling the text.
win_fix_cursor(
get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL));
2004-06-13 20:20:40 +00:00
// Now it is OK to parse messages again, which may be needed in
// autocommands.
#ifdef MESSAGE_QUEUE
if (flags & WEE_ALLOW_PARSE_MESSAGES)
{
--dont_parse_messages;
did_decrement = TRUE;
}
#endif
#ifdef FEAT_TERMINAL
if (bt_terminal(curwin->w_buffer))
update_topline();
#endif
win_fix_current_dir();
2004-06-13 20:20:40 +00:00
#ifdef FEAT_JOB_CHANNEL
entering_window(curwin);
#endif
// Careful: autocommands may close the window and make "wp" invalid
if (flags & WEE_TRIGGER_NEW_AUTOCMDS)
apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf);
if (flags & WEE_TRIGGER_ENTER_AUTOCMDS)
{
apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
if (other_buffer)
apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
}
2004-06-13 20:20:40 +00:00
maketitle();
curwin->w_redr_status = TRUE;
#ifdef FEAT_TERMINAL
if (bt_terminal(curwin->w_buffer))
// terminal is likely in another mode
redraw_mode = TRUE;
#endif
2006-02-17 21:45:41 +00:00
redraw_tabline = TRUE;
2004-06-13 20:20:40 +00:00
if (restart_edit)
redraw_later(UPD_VALID); // causes status line redraw
2004-06-13 20:20:40 +00:00
// set window height to desired minimal value
if (curwin->w_height < p_wh && !curwin->w_p_wfh
#ifdef FEAT_PROP_POPUP
&& !popup_is_popup(curwin)
#endif
)
2004-06-13 20:20:40 +00:00
win_setheight((int)p_wh);
else if (curwin->w_height == 0)
win_setheight(1);
// set window width to desired minimal value
2006-03-18 21:30:13 +00:00
if (curwin->w_width < p_wiw && !curwin->w_p_wfw)
2004-06-13 20:20:40 +00:00
win_setwidth((int)p_wiw);
setmouse(); // in case jumped to/from help buffer
2004-06-13 20:20:40 +00:00
// Change directories when the 'acd' option is set.
DO_AUTOCHDIR;
return did_decrement;
2004-06-13 20:20:40 +00:00
}
/*
2006-04-10 14:55:34 +00:00
* Jump to the first open window that contains buffer "buf", if one exists.
* Returns a pointer to the window found, otherwise NULL.
2004-06-13 20:20:40 +00:00
*/
win_T *
buf_jump_open_win(buf_T *buf)
2004-06-13 20:20:40 +00:00
{
win_T *wp = NULL;
2004-06-13 20:20:40 +00:00
if (curwin->w_buffer == buf)
wp = curwin;
else
FOR_ALL_WINDOWS(wp)
if (wp->w_buffer == buf)
break;
2004-06-13 20:20:40 +00:00
if (wp != NULL)
win_enter(wp, FALSE);
return wp;
2006-04-10 14:55:34 +00:00
}
/*
* Jump to the first open window in any tab page that contains buffer "buf",
* if one exists. First search in the windows present in the current tab page.
2006-04-10 14:55:34 +00:00
* Returns a pointer to the window found, otherwise NULL.
*/
win_T *
buf_jump_open_tab(buf_T *buf)
2006-04-10 14:55:34 +00:00
{
win_T *wp = buf_jump_open_win(buf);
2006-04-10 14:55:34 +00:00
tabpage_T *tp;
if (wp != NULL)
return wp;
FOR_ALL_TABPAGES(tp)
2006-04-10 14:55:34 +00:00
if (tp != curtab)
{
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
2006-04-10 14:55:34 +00:00
if (wp->w_buffer == buf)
break;
if (wp != NULL)
{
goto_tabpage_win(tp, wp);
if (curwin != wp)
wp = NULL; // something went wrong
2006-04-10 14:55:34 +00:00
break;
}
}
return wp;
2004-06-13 20:20:40 +00:00
}
static int last_win_id = LOWEST_WIN_ID - 1;
2004-06-13 20:20:40 +00:00
/*
2009-06-16 14:01:43 +00:00
* Allocate a window structure and link it in the window list when "hidden" is
* FALSE.
2004-06-13 20:20:40 +00:00
*/
static win_T *
win_alloc(win_T *after, int hidden)
2004-06-13 20:20:40 +00:00
{
win_T *new_wp;
2004-06-13 20:20:40 +00:00
/*
* allocate window structure and linesizes arrays
*/
new_wp = ALLOC_CLEAR_ONE(win_T);
if (new_wp == NULL)
return NULL;
if (win_alloc_lines(new_wp) == FAIL)
2004-06-13 20:20:40 +00:00
{
vim_free(new_wp);
return NULL;
2004-06-13 20:20:40 +00:00
}
new_wp->w_id = ++last_win_id;
#ifdef FEAT_EVAL
// init w: variables
new_wp->w_vars = dict_alloc_id(aid_newwin_wvars);
if (new_wp->w_vars == NULL)
2004-06-13 20:20:40 +00:00
{
win_free_lsize(new_wp);
vim_free(new_wp);
return NULL;
}
init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE);
#endif
// Don't execute autocommands while the window is not properly
// initialized yet. gui_create_scrollbar() may trigger a FocusGained
// event.
block_autocmds();
/*
* link the window in the window list
*/
if (!hidden)
win_append(after, new_wp);
new_wp->w_wincol = 0;
new_wp->w_width = Columns;
2004-06-13 20:20:40 +00:00
// position the display and the cursor at the top of the file.
new_wp->w_topline = 1;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_DIFF
new_wp->w_topfill = 0;
2004-06-13 20:20:40 +00:00
#endif
new_wp->w_botline = 2;
new_wp->w_cursor.lnum = 1;
new_wp->w_scbind_pos = 1;
2004-06-13 20:20:40 +00:00
// use global option value for global-local options
new_wp->w_allbuf_opt.wo_so = new_wp->w_p_so = -1;
new_wp->w_allbuf_opt.wo_siso = new_wp->w_p_siso = -1;
// We won't calculate w_fraction until resizing the window
new_wp->w_fraction = 0;
new_wp->w_prev_fraction_row = -1;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_GUI
if (gui.in_use)
{
gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_LEFT],
SBAR_LEFT, new_wp);
gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_RIGHT],
SBAR_RIGHT, new_wp);
}
2004-06-13 20:20:40 +00:00
#endif
#ifdef FEAT_FOLDING
foldInitWin(new_wp);
2007-05-02 19:50:14 +00:00
#endif
unblock_autocmds();
2007-07-26 20:58:42 +00:00
#ifdef FEAT_SEARCH_EXTRA
new_wp->w_next_match_id = 1000; // up to 1000 can be picked by the user
2004-06-13 20:20:40 +00:00
#endif
return new_wp;
2004-06-13 20:20:40 +00:00
}
/*
* Remove window 'wp' from the window list and free the structure.
2004-06-13 20:20:40 +00:00
*/
static void
win_free(
win_T *wp,
tabpage_T *tp) // tab page "win" is in, NULL for current
2004-06-13 20:20:40 +00:00
{
int i;
buf_T *buf;
wininfo_T *wip;
2004-06-13 20:20:40 +00:00
2009-06-24 15:32:01 +00:00
#ifdef FEAT_FOLDING
clearFolding(wp);
#endif
// reduce the reference count to the argument list.
2009-06-24 15:32:01 +00:00
alist_unlink(wp->w_alist);
// Don't execute autocommands while the window is halfway being deleted.
// gui_mch_destroy_scrollbar() may trigger a FocusGained event.
2007-09-29 12:16:41 +00:00
block_autocmds();
2007-05-02 19:50:14 +00:00
2010-07-14 23:23:17 +02:00
#ifdef FEAT_LUA
lua_window_free(wp);
#endif
2004-07-05 15:58:32 +00:00
#ifdef FEAT_MZSCHEME
mzscheme_window_free(wp);
#endif
2004-06-13 20:20:40 +00:00
#ifdef FEAT_PERL
perl_win_free(wp);
#endif
#ifdef FEAT_PYTHON
python_window_free(wp);
#endif
#ifdef FEAT_PYTHON3
python3_window_free(wp);
#endif
2004-06-13 20:20:40 +00:00
#ifdef FEAT_TCL
tcl_window_free(wp);
#endif
#ifdef FEAT_RUBY
ruby_window_free(wp);
#endif
clear_winopt(&wp->w_onebuf_opt);
clear_winopt(&wp->w_allbuf_opt);
vim_free(wp->w_lcs_chars.multispace);
vim_free(wp->w_lcs_chars.leadmultispace);
2004-06-13 20:20:40 +00:00
#ifdef FEAT_EVAL
vars_clear(&wp->w_vars->dv_hashtab); // free all w: variables
hash_init(&wp->w_vars->dv_hashtab);
unref_var_dict(wp->w_vars);
2004-06-13 20:20:40 +00:00
#endif
{
tabpage_T *ttp;
if (prevwin == wp)
prevwin = NULL;
FOR_ALL_TABPAGES(ttp)
if (ttp->tp_prevwin == wp)
ttp->tp_prevwin = NULL;
}
2004-06-13 20:20:40 +00:00
win_free_lsize(wp);
for (i = 0; i < wp->w_tagstacklen; ++i)
tagstack_clear_entry(&wp->w_tagstack[i]);
2004-06-13 20:20:40 +00:00
vim_free(wp->w_localdir);
vim_free(wp->w_prevdir);
2007-07-26 20:58:42 +00:00
// Remove the window from the b_wininfo lists, it may happen that the
// freed memory is re-used for another window.
FOR_ALL_BUFFERS(buf)
FOR_ALL_BUF_WININFO(buf, wip)
if (wip->wi_win == wp)
{
wininfo_T *wip2;
// If there already is an entry with "wi_win" set to NULL it
// must be removed, it would never be used.
// Skip "wip" itself, otherwise Coverity complains.
FOR_ALL_BUF_WININFO(buf, wip2)
if (wip2 != wip && wip2->wi_win == NULL)
{
if (wip2->wi_next != NULL)
wip2->wi_next->wi_prev = wip2->wi_prev;
if (wip2->wi_prev == NULL)
buf->b_wininfo = wip2->wi_next;
else
wip2->wi_prev->wi_next = wip2->wi_next;
free_wininfo(wip2);
break;
}
wip->wi_win = NULL;
}
2004-06-13 20:20:40 +00:00
#ifdef FEAT_SEARCH_EXTRA
2007-07-26 20:58:42 +00:00
clear_matches(wp);
2004-06-13 20:20:40 +00:00
#endif
2007-07-26 20:58:42 +00:00
2004-06-13 20:20:40 +00:00
free_jumplist(wp);
2006-01-25 22:02:51 +00:00
#ifdef FEAT_QUICKFIX
qf_free_all(wp);
#endif
2004-06-13 20:20:40 +00:00
#ifdef FEAT_GUI
if (gui.in_use)
{
gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]);
gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]);
}
#endif // FEAT_GUI
2004-06-13 20:20:40 +00:00
#ifdef FEAT_MENU
remove_winbar(wp);
#endif
#ifdef FEAT_PROP_POPUP
free_callback(&wp->w_close_cb);
free_callback(&wp->w_filter_cb);
for (i = 0; i < 4; ++i)
VIM_CLEAR(wp->w_border_highlight[i]);
vim_free(wp->w_scrollbar_highlight);
vim_free(wp->w_thumb_highlight);
vim_free(wp->w_popup_title);
list_unref(wp->w_popup_mask);
vim_free(wp->w_popup_mask_cells);
#endif
#ifdef FEAT_SYN_HL
vim_free(wp->w_p_cc_cols);
#endif
if (win_valid_any_tab(wp))
win_remove(wp, tp);
if (autocmd_busy)
{
wp->w_next = au_pending_free_win;
au_pending_free_win = wp;
}
else
vim_free(wp);
2007-05-02 19:50:14 +00:00
2007-09-29 12:16:41 +00:00
unblock_autocmds();
2004-06-13 20:20:40 +00:00
}
/*
* Return TRUE if "wp" is not in the list of windows: the autocmd window or a
* popup window.
*/
int
win_unlisted(win_T *wp)
{
return is_aucmd_win(wp) || WIN_IS_POPUP(wp);
}
#if defined(FEAT_PROP_POPUP) || defined(PROTO)
/*
* Free a popup window. This does not take the window out of the window list
* and assumes there is only one toplevel frame, no split.
*/
void
win_free_popup(win_T *win)
{
if (win->w_buffer != NULL)
{
if (bt_popup(win->w_buffer))
win_close_buffer(win, DOBUF_WIPE_REUSE, FALSE);
else
close_buffer(win, win->w_buffer, 0, FALSE, FALSE);
}
# if defined(FEAT_TIMERS)
// the timer may have been cleared, making the pointer invalid
if (timer_valid(win->w_popup_timer))
stop_timer(win->w_popup_timer);
# endif
vim_free(win->w_frame);
win_free(win, NULL);
}
#endif
2004-06-13 20:20:40 +00:00
/*
* Append window "wp" in the window list after window "after".
*/
static void
win_append(win_T *after, win_T *wp)
2004-06-13 20:20:40 +00:00
{
win_T *before;
if (after == NULL) // after NULL is in front of the first
2004-06-13 20:20:40 +00:00
before = firstwin;
else
before = after->w_next;
wp->w_next = before;
wp->w_prev = after;
if (after == NULL)
firstwin = wp;
else
after->w_next = wp;
if (before == NULL)
lastwin = wp;
else
before->w_prev = wp;
}
/*
* Remove a window from the window list.
*/
2009-06-16 14:01:43 +00:00
void
win_remove(
win_T *wp,
tabpage_T *tp) // tab page "win" is in, NULL for current
2004-06-13 20:20:40 +00:00
{
if (wp->w_prev != NULL)
wp->w_prev->w_next = wp->w_next;
2006-02-16 22:11:02 +00:00
else if (tp == NULL)
firstwin = curtab->tp_firstwin = wp->w_next;
2006-02-16 22:11:02 +00:00
else
tp->tp_firstwin = wp->w_next;
2004-06-13 20:20:40 +00:00
if (wp->w_next != NULL)
wp->w_next->w_prev = wp->w_prev;
2006-02-16 22:11:02 +00:00
else if (tp == NULL)
lastwin = curtab->tp_lastwin = wp->w_prev;
2006-02-16 22:11:02 +00:00
else
tp->tp_lastwin = wp->w_prev;
2004-06-13 20:20:40 +00:00
}
/*
* Append frame "frp" in a frame list after frame "after".
*/
static void
frame_append(frame_T *after, frame_T *frp)
2004-06-13 20:20:40 +00:00
{
frp->fr_next = after->fr_next;
after->fr_next = frp;
if (frp->fr_next != NULL)
frp->fr_next->fr_prev = frp;
frp->fr_prev = after;
}
/*
* Insert frame "frp" in a frame list before frame "before".
*/
static void
frame_insert(frame_T *before, frame_T *frp)
2004-06-13 20:20:40 +00:00
{
frp->fr_next = before;
frp->fr_prev = before->fr_prev;
before->fr_prev = frp;
if (frp->fr_prev != NULL)
frp->fr_prev->fr_next = frp;
else
frp->fr_parent->fr_child = frp;
}
/*
* Remove a frame from a frame list.
*/
static void
frame_remove(frame_T *frp)
2004-06-13 20:20:40 +00:00
{
if (frp->fr_prev != NULL)
frp->fr_prev->fr_next = frp->fr_next;
else
frp->fr_parent->fr_child = frp->fr_next;
if (frp->fr_next != NULL)
frp->fr_next->fr_prev = frp->fr_prev;
}
/*
* Allocate w_lines[] for window "wp".
* Return FAIL for failure, OK for success.
*/
int
win_alloc_lines(win_T *wp)
2004-06-13 20:20:40 +00:00
{
wp->w_lines_valid = 0;
wp->w_lines = ALLOC_CLEAR_MULT(wline_T, Rows);
2004-06-13 20:20:40 +00:00
if (wp->w_lines == NULL)
return FAIL;
return OK;
}
/*
* free lsize arrays for a window
*/
void
win_free_lsize(win_T *wp)
2004-06-13 20:20:40 +00:00
{
// TODO: why would wp be NULL here?
if (wp != NULL)
VIM_CLEAR(wp->w_lines);
2004-06-13 20:20:40 +00:00
}
/*
* Called from win_new_shellsize() after Rows changed.
2006-02-16 22:11:02 +00:00
* This only does the current tab page, others must be done when made active.
2004-06-13 20:20:40 +00:00
*/
void
shell_new_rows(void)
2004-06-13 20:20:40 +00:00
{
int h = (int)ROWS_AVAIL;
2004-06-13 20:20:40 +00:00
if (firstwin == NULL) // not initialized yet
2004-06-13 20:20:40 +00:00
return;
if (h < frame_minheight(topframe, NULL))
h = frame_minheight(topframe, NULL);
2006-03-18 21:30:13 +00:00
// First try setting the heights of windows with 'winfixheight'. If
// that doesn't result in the right height, forget about that option.
frame_new_height(topframe, h, FALSE, TRUE, FALSE);
if (!frame_check_height(topframe, h))
frame_new_height(topframe, h, FALSE, FALSE, FALSE);
2004-06-13 20:20:40 +00:00
(void)win_comp_pos(); // recompute w_winrow and w_wincol
2004-06-13 20:20:40 +00:00
compute_cmdrow();
2006-04-09 21:54:49 +00:00
curtab->tp_ch_used = p_ch;
2005-02-26 23:04:13 +00:00
if (!skip_win_fix_scroll)
win_fix_scroll(TRUE);
2004-06-13 20:20:40 +00:00
#if 0
// Disabled: don't want making the screen smaller make a window larger.
2004-06-13 20:20:40 +00:00
if (p_ea)
win_equal(curwin, FALSE, 'v');
#endif
}
/*
* Called from win_new_shellsize() after Columns changed.
*/
void
shell_new_columns(void)
2004-06-13 20:20:40 +00:00
{
if (firstwin == NULL) // not initialized yet
2004-06-13 20:20:40 +00:00
return;
2006-03-18 21:30:13 +00:00
// First try setting the widths of windows with 'winfixwidth'. If that
// doesn't result in the right width, forget about that option.
frame_new_width(topframe, (int)Columns, FALSE, TRUE);
if (!frame_check_width(topframe, Columns))
frame_new_width(topframe, (int)Columns, FALSE, FALSE);
2006-03-18 21:30:13 +00:00
(void)win_comp_pos(); // recompute w_winrow and w_wincol
2004-06-13 20:20:40 +00:00
#if 0
// Disabled: don't want making the screen smaller make a window larger.
2004-06-13 20:20:40 +00:00
if (p_ea)
win_equal(curwin, FALSE, 'h');
#endif
}
/*
* Save the size of all windows in "gap".
*/
void
win_size_save(garray_T *gap)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
ga_init2(gap, sizeof(int), 1);
if (ga_grow(gap, win_count() * 2 + 1) == FAIL)
return;
// first entry is the total lines available for windows
((int *)gap->ga_data)[gap->ga_len++] = ROWS_AVAIL - last_stl_height(FALSE);
FOR_ALL_WINDOWS(wp)
{
((int *)gap->ga_data)[gap->ga_len++] =
wp->w_width + wp->w_vsep_width;
((int *)gap->ga_data)[gap->ga_len++] = wp->w_height;
}
2004-06-13 20:20:40 +00:00
}
/*
* Restore window sizes, but only if the number of windows is still the same
* and total lines available for windows didn't change.
2004-06-13 20:20:40 +00:00
* Does not free the growarray.
*/
void
win_size_restore(garray_T *gap)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
int i, j;
2004-06-13 20:20:40 +00:00
if (win_count() * 2 + 1 == gap->ga_len
&& ((int *)gap->ga_data)[0] == ROWS_AVAIL - last_stl_height(FALSE))
2004-06-13 20:20:40 +00:00
{
// The order matters, because frames contain other frames, but it's
// difficult to get right. The easy way out is to do it twice.
for (j = 0; j < 2; ++j)
2004-06-13 20:20:40 +00:00
{
i = 1;
FOR_ALL_WINDOWS(wp)
{
frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]);
win_setheight_win(((int *)gap->ga_data)[i++], wp);
}
2004-06-13 20:20:40 +00:00
}
// recompute the window positions
2004-06-13 20:20:40 +00:00
(void)win_comp_pos();
}
}
/*
* Update the position for all windows, using the width and height of the
* frames.
* Returns the row just after the last window.
*/
2006-02-15 22:11:25 +00:00
int
win_comp_pos(void)
2004-06-13 20:20:40 +00:00
{
2006-02-24 23:53:04 +00:00
int row = tabline_height();
2004-06-13 20:20:40 +00:00
int col = 0;
frame_comp_pos(topframe, &row, &col);
return row;
}
/*
* Update the position of the windows in frame "topfrp", using the width and
* height of the frames.
* "*row" and "*col" are the top-left position of the frame. They are updated
* to the bottom-right position plus one.
*/
static void
frame_comp_pos(frame_T *topfrp, int *row, int *col)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
frame_T *frp;
int startcol;
int startrow;
int h;
2004-06-13 20:20:40 +00:00
wp = topfrp->fr_win;
if (wp != NULL)
{
if (wp->w_winrow != *row || wp->w_wincol != *col)
2004-06-13 20:20:40 +00:00
{
// position changed, redraw
2004-06-13 20:20:40 +00:00
wp->w_winrow = *row;
wp->w_wincol = *col;
redraw_win_later(wp, UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
wp->w_redr_status = TRUE;
}
// WinBar will not show if the window height is zero
h = VISIBLE_HEIGHT(wp) + wp->w_status_height;
*row += h > topfrp->fr_height ? topfrp->fr_height : h;
2004-06-13 20:20:40 +00:00
*col += wp->w_width + wp->w_vsep_width;
}
else
{
startrow = *row;
startcol = *col;
FOR_ALL_FRAMES(frp, topfrp->fr_child)
2004-06-13 20:20:40 +00:00
{
if (topfrp->fr_layout == FR_ROW)
*row = startrow; // all frames are at the same row
2004-06-13 20:20:40 +00:00
else
*col = startcol; // all frames are at the same col
2004-06-13 20:20:40 +00:00
frame_comp_pos(frp, row, col);
}
}
}
/*
* Make the current window show at least one line and one column.
*/
void
win_ensure_size(void)
{
if (curwin->w_height == 0)
win_setheight(1);
if (curwin->w_width == 0)
win_setwidth(1);
}
2004-06-13 20:20:40 +00:00
/*
* Set current window height and take care of repositioning other windows to
* fit around it.
*/
void
win_setheight(int height)
2004-06-13 20:20:40 +00:00
{
win_setheight_win(height, curwin);
}
/*
* Set the window height of window "win" and take care of repositioning other
* windows to fit around it.
*/
void
win_setheight_win(int height, win_T *win)
2004-06-13 20:20:40 +00:00
{
if (win == curwin)
{
// Always keep current window at least one line high, even when
// 'winminheight' is zero.
2004-06-13 20:20:40 +00:00
if (height < p_wmh)
height = p_wmh;
if (height == 0)
height = 1;
height += WINBAR_HEIGHT(curwin);
2004-06-13 20:20:40 +00:00
}
frame_setheight(win->w_frame, height + win->w_status_height);
// recompute the window positions
win_comp_pos();
win_fix_scroll(TRUE);
redraw_all_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
/*
* Set the height of a frame to "height" and take care that all frames and
* windows inside it are resized. Also resize frames on the left and right if
* the are in the same FR_ROW frame.
*
* Strategy:
* If the frame is part of a FR_COL frame, try fitting the frame in that
* frame. If that doesn't work (the FR_COL frame is too small), recursively
* go to containing frames to resize them and make room.
* If the frame is part of a FR_ROW frame, all frames must be resized as well.
* Check for the minimal height of the FR_ROW frame.
* At the top level we can also use change the command line height.
*/
static void
frame_setheight(frame_T *curfrp, int height)
2004-06-13 20:20:40 +00:00
{
int room; // total number of lines available
int take; // number of lines taken from other windows
int room_cmdline; // lines available from cmdline
2004-06-13 20:20:40 +00:00
int run;
frame_T *frp;
int h;
int room_reserved;
// If the height already is the desired value, nothing to do.
2004-06-13 20:20:40 +00:00
if (curfrp->fr_height == height)
return;
if (curfrp->fr_parent == NULL)
{
// topframe: can only change the command line height
2004-06-13 20:20:40 +00:00
if (height > 0)
frame_new_height(curfrp, height, FALSE, FALSE, TRUE);
2004-06-13 20:20:40 +00:00
}
else if (curfrp->fr_parent->fr_layout == FR_ROW)
{
// Row of frames: Also need to resize frames left and right of this
// one. First check for the minimal height of these.
2004-06-13 20:20:40 +00:00
h = frame_minheight(curfrp->fr_parent, NULL);
if (height < h)
height = h;
frame_setheight(curfrp->fr_parent, height);
}
else
{
/*
* Column of frames: try to change only frames in this column.
*/
/*
* Do this twice:
* 1: compute room available, if it's not enough try resizing the
* containing frame.
* 2: compute the room available and adjust the height to it.
* Try not to reduce the height of a window with 'winfixheight' set.
*/
for (run = 1; run <= 2; ++run)
{
room = 0;
room_reserved = 0;
FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child)
2004-06-13 20:20:40 +00:00
{
if (frp != curfrp
&& frp->fr_win != NULL
&& frp->fr_win->w_p_wfh)
room_reserved += frp->fr_height;
room += frp->fr_height;
if (frp != curfrp)
room -= frame_minheight(frp, NULL);
}
if (curfrp->fr_width != Columns)
room_cmdline = 0;
else
{
room_cmdline = Rows - p_ch - (lastwin->w_winrow
+ VISIBLE_HEIGHT(lastwin)
+ lastwin->w_status_height);
2004-06-13 20:20:40 +00:00
if (room_cmdline < 0)
room_cmdline = 0;
}
if (height <= room + room_cmdline)
break;
if (run == 2 || curfrp->fr_width == Columns)
{
height = room + room_cmdline;
2004-06-13 20:20:40 +00:00
break;
}
frame_setheight(curfrp->fr_parent, height
+ frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1);
}
/*
* Compute the number of lines we will take from others frames (can be
* negative!).
*/
take = height - curfrp->fr_height;
// If there is not enough room, also reduce the height of a window
// with 'winfixheight' set.
2004-06-13 20:20:40 +00:00
if (height > room + room_cmdline - room_reserved)
room_reserved = room + room_cmdline - height;
// If there is only a 'winfixheight' window and making the
// window smaller, need to make the other window taller.
2004-06-13 20:20:40 +00:00
if (take < 0 && room - curfrp->fr_height < room_reserved)
room_reserved = 0;
if (take > 0 && room_cmdline > 0)
{
// use lines from cmdline first
2004-06-13 20:20:40 +00:00
if (take < room_cmdline)
room_cmdline = take;
take -= room_cmdline;
topframe->fr_height += room_cmdline;
}
/*
* set the current frame to the new height
*/
frame_new_height(curfrp, height, FALSE, FALSE, TRUE);
2004-06-13 20:20:40 +00:00
/*
* First take lines from the frames after the current frame. If
* that is not enough, takes lines from frames above the current
* frame.
*/
for (run = 0; run < 2; ++run)
{
if (run == 0)
frp = curfrp->fr_next; // 1st run: start with next window
2004-06-13 20:20:40 +00:00
else
frp = curfrp->fr_prev; // 2nd run: start with prev window
2004-06-13 20:20:40 +00:00
while (frp != NULL && take != 0)
{
h = frame_minheight(frp, NULL);
if (room_reserved > 0
&& frp->fr_win != NULL
&& frp->fr_win->w_p_wfh)
{
if (room_reserved >= frp->fr_height)
room_reserved -= frp->fr_height;
else
{
if (frp->fr_height - room_reserved > take)
room_reserved = frp->fr_height - take;
take -= frp->fr_height - room_reserved;
frame_new_height(frp, room_reserved, FALSE, FALSE,
TRUE);
2004-06-13 20:20:40 +00:00
room_reserved = 0;
}
}
else
{
if (frp->fr_height - take < h)
{
take -= frp->fr_height - h;
frame_new_height(frp, h, FALSE, FALSE, TRUE);
2004-06-13 20:20:40 +00:00
}
else
{
frame_new_height(frp, frp->fr_height - take,
FALSE, FALSE, TRUE);
2004-06-13 20:20:40 +00:00
take = 0;
}
}
if (run == 0)
frp = frp->fr_next;
else
frp = frp->fr_prev;
}
}
}
}
/*
* Set current window width and take care of repositioning other windows to
* fit around it.
*/
void
win_setwidth(int width)
2004-06-13 20:20:40 +00:00
{
win_setwidth_win(width, curwin);
}
void
win_setwidth_win(int width, win_T *wp)
2004-06-13 20:20:40 +00:00
{
// Always keep current window at least one column wide, even when
// 'winminwidth' is zero.
2004-06-13 20:20:40 +00:00
if (wp == curwin)
{
if (width < p_wmw)
width = p_wmw;
if (width == 0)
width = 1;
}
else if (width < 0)
width = 0;
2004-06-13 20:20:40 +00:00
frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
// recompute the window positions
2004-06-13 20:20:40 +00:00
(void)win_comp_pos();
redraw_all_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
/*
* Set the width of a frame to "width" and take care that all frames and
* windows inside it are resized. Also resize frames above and below if the
* are in the same FR_ROW frame.
*
* Strategy is similar to frame_setheight().
*/
static void
frame_setwidth(frame_T *curfrp, int width)
2004-06-13 20:20:40 +00:00
{
int room; // total number of lines available
int take; // number of lines taken from other windows
2004-06-13 20:20:40 +00:00
int run;
frame_T *frp;
int w;
2006-03-18 21:30:13 +00:00
int room_reserved;
2004-06-13 20:20:40 +00:00
// If the width already is the desired value, nothing to do.
2004-06-13 20:20:40 +00:00
if (curfrp->fr_width == width)
return;
if (curfrp->fr_parent == NULL)
// topframe: can't change width
2004-06-13 20:20:40 +00:00
return;
if (curfrp->fr_parent->fr_layout == FR_COL)
{
// Column of frames: Also need to resize frames above and below of
// this one. First check for the minimal width of these.
2004-06-13 20:20:40 +00:00
w = frame_minwidth(curfrp->fr_parent, NULL);
if (width < w)
width = w;
frame_setwidth(curfrp->fr_parent, width);
}
else
{
/*
* Row of frames: try to change only frames in this row.
*
* Do this twice:
* 1: compute room available, if it's not enough try resizing the
* containing frame.
* 2: compute the room available and adjust the width to it.
*/
for (run = 1; run <= 2; ++run)
{
room = 0;
2006-03-18 21:30:13 +00:00
room_reserved = 0;
FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child)
2004-06-13 20:20:40 +00:00
{
2006-03-18 21:30:13 +00:00
if (frp != curfrp
&& frp->fr_win != NULL
&& frp->fr_win->w_p_wfw)
room_reserved += frp->fr_width;
2004-06-13 20:20:40 +00:00
room += frp->fr_width;
if (frp != curfrp)
room -= frame_minwidth(frp, NULL);
}
if (width <= room)
break;
2006-02-14 22:29:30 +00:00
if (run == 2 || curfrp->fr_height >= ROWS_AVAIL)
2004-06-13 20:20:40 +00:00
{
width = room;
2004-06-13 20:20:40 +00:00
break;
}
frame_setwidth(curfrp->fr_parent, width
+ frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1);
}
/*
* Compute the number of lines we will take from others frames (can be
* negative!).
*/
take = width - curfrp->fr_width;
// If there is not enough room, also reduce the width of a window
// with 'winfixwidth' set.
2006-03-18 21:30:13 +00:00
if (width > room - room_reserved)
room_reserved = room - width;
// If there is only a 'winfixwidth' window and making the
// window smaller, need to make the other window narrower.
2006-03-18 21:30:13 +00:00
if (take < 0 && room - curfrp->fr_width < room_reserved)
room_reserved = 0;
2004-06-13 20:20:40 +00:00
/*
* set the current frame to the new width
*/
2006-03-18 21:30:13 +00:00
frame_new_width(curfrp, width, FALSE, FALSE);
2004-06-13 20:20:40 +00:00
/*
* First take lines from the frames right of the current frame. If
* that is not enough, takes lines from frames left of the current
* frame.
*/
for (run = 0; run < 2; ++run)
{
if (run == 0)
frp = curfrp->fr_next; // 1st run: start with next window
2004-06-13 20:20:40 +00:00
else
frp = curfrp->fr_prev; // 2nd run: start with prev window
2004-06-13 20:20:40 +00:00
while (frp != NULL && take != 0)
{
w = frame_minwidth(frp, NULL);
2006-03-18 21:30:13 +00:00
if (room_reserved > 0
&& frp->fr_win != NULL
&& frp->fr_win->w_p_wfw)
2004-06-13 20:20:40 +00:00
{
2006-03-18 21:30:13 +00:00
if (room_reserved >= frp->fr_width)
room_reserved -= frp->fr_width;
else
{
if (frp->fr_width - room_reserved > take)
room_reserved = frp->fr_width - take;
take -= frp->fr_width - room_reserved;
frame_new_width(frp, room_reserved, FALSE, FALSE);
room_reserved = 0;
}
2004-06-13 20:20:40 +00:00
}
else
{
2006-03-18 21:30:13 +00:00
if (frp->fr_width - take < w)
{
take -= frp->fr_width - w;
frame_new_width(frp, w, FALSE, FALSE);
}
else
{
frame_new_width(frp, frp->fr_width - take,
FALSE, FALSE);
take = 0;
}
2004-06-13 20:20:40 +00:00
}
if (run == 0)
frp = frp->fr_next;
else
frp = frp->fr_prev;
}
}
}
}
/*
* Check 'winminheight' for a valid value and reduce it if needed.
2004-06-13 20:20:40 +00:00
*/
void
win_setminheight(void)
2004-06-13 20:20:40 +00:00
{
int room;
int needed;
2004-06-13 20:20:40 +00:00
int first = TRUE;
// loop until there is a 'winminheight' that is possible
2004-06-13 20:20:40 +00:00
while (p_wmh > 0)
{
room = Rows - p_ch;
needed = min_rows_for_all_tabpages() - 1; // 1 was added for the cmdline
if (room >= needed)
2004-06-13 20:20:40 +00:00
break;
--p_wmh;
if (first)
{
emsg(_(e_not_enough_room));
2004-06-13 20:20:40 +00:00
first = FALSE;
}
}
}
/*
* Check 'winminwidth' for a valid value and reduce it if needed.
*/
void
win_setminwidth(void)
{
int room;
int needed;
int first = TRUE;
// loop until there is a 'winminheight' that is possible
while (p_wmw > 0)
{
room = Columns;
needed = frame_minwidth(topframe, NULL);
if (room >= needed)
break;
--p_wmw;
if (first)
{
emsg(_(e_not_enough_room));
first = FALSE;
}
}
}
2004-06-13 20:20:40 +00:00
/*
* Status line of dragwin is dragged "offset" lines down (negative is up).
*/
void
win_drag_status_line(win_T *dragwin, int offset)
2004-06-13 20:20:40 +00:00
{
frame_T *curfr;
frame_T *fr;
int room;
int up; // if TRUE, drag status line up, otherwise down
2004-06-13 20:20:40 +00:00
int n;
fr = dragwin->w_frame;
curfr = fr;
if (fr != topframe) // more than one window
2004-06-13 20:20:40 +00:00
{
fr = fr->fr_parent;
// When the parent frame is not a column of frames, its parent should
// be.
2004-06-13 20:20:40 +00:00
if (fr->fr_layout != FR_COL)
{
curfr = fr;
if (fr != topframe) // only a row of windows, may drag statusline
2004-06-13 20:20:40 +00:00
fr = fr->fr_parent;
}
}
// If this is the last frame in a column, may want to resize the parent
// frame instead (go two up to skip a row of frames).
2004-06-13 20:20:40 +00:00
while (curfr != topframe && curfr->fr_next == NULL)
{
if (fr != topframe)
fr = fr->fr_parent;
curfr = fr;
if (fr != topframe)
fr = fr->fr_parent;
}
if (offset < 0) // drag up
2004-06-13 20:20:40 +00:00
{
up = TRUE;
offset = -offset;
// sum up the room of the current frame and above it
2004-06-13 20:20:40 +00:00
if (fr == curfr)
{
// only one window
2004-06-13 20:20:40 +00:00
room = fr->fr_height - frame_minheight(fr, NULL);
}
else
{
room = 0;
for (fr = fr->fr_child; ; fr = fr->fr_next)
{
room += fr->fr_height - frame_minheight(fr, NULL);
if (fr == curfr)
break;
}
}
fr = curfr->fr_next; // put fr at frame that grows
2004-06-13 20:20:40 +00:00
}
else // drag down
2004-06-13 20:20:40 +00:00
{
up = FALSE;
/*
* Only dragging the last status line can reduce p_ch.
*/
room = Rows - cmdline_row;
if (curfr->fr_next == NULL)
--room;
else
room -= p_ch;
2004-06-13 20:20:40 +00:00
if (room < 0)
room = 0;
// sum up the room of frames below of the current one
FOR_ALL_FRAMES(fr, curfr->fr_next)
2004-06-13 20:20:40 +00:00
room += fr->fr_height - frame_minheight(fr, NULL);
fr = curfr; // put fr at window that grows
2004-06-13 20:20:40 +00:00
}
if (room < offset) // Not enough room
offset = room; // Move as far as we can
2004-06-13 20:20:40 +00:00
if (offset <= 0)
return;
/*
* Grow frame fr by "offset" lines.
* Doesn't happen when dragging the last status line up.
*/
if (fr != NULL)
frame_new_height(fr, fr->fr_height + offset, up, FALSE, TRUE);
2004-06-13 20:20:40 +00:00
if (up)
fr = curfr; // current frame gets smaller
2004-06-13 20:20:40 +00:00
else
fr = curfr->fr_next; // next frame gets smaller
2004-06-13 20:20:40 +00:00
/*
* Now make the other frames smaller.
*/
while (fr != NULL && offset > 0)
{
n = frame_minheight(fr, NULL);
if (fr->fr_height - offset <= n)
{
offset -= fr->fr_height - n;
frame_new_height(fr, n, !up, FALSE, TRUE);
2004-06-13 20:20:40 +00:00
}
else
{
frame_new_height(fr, fr->fr_height - offset, !up, FALSE, TRUE);
2004-06-13 20:20:40 +00:00
break;
}
if (up)
fr = fr->fr_prev;
else
fr = fr->fr_next;
}
win_comp_pos();
win_fix_scroll(TRUE);
redraw_all_later(UPD_SOME_VALID);
2004-06-13 20:20:40 +00:00
showmode();
}
/*
* Separator line of dragwin is dragged "offset" lines right (negative is left).
*/
void
win_drag_vsep_line(win_T *dragwin, int offset)
2004-06-13 20:20:40 +00:00
{
frame_T *curfr;
frame_T *fr;
int room;
int left; // if TRUE, drag separator line left, otherwise right
2004-06-13 20:20:40 +00:00
int n;
fr = dragwin->w_frame;
if (fr == topframe) // only one window (cannot happen?)
2004-06-13 20:20:40 +00:00
return;
curfr = fr;
fr = fr->fr_parent;
// When the parent frame is not a row of frames, its parent should be.
2004-06-13 20:20:40 +00:00
if (fr->fr_layout != FR_ROW)
{
if (fr == topframe) // only a column of windows (cannot happen?)
2004-06-13 20:20:40 +00:00
return;
curfr = fr;
fr = fr->fr_parent;
}
// If this is the last frame in a row, may want to resize a parent
// frame instead.
2004-06-13 20:20:40 +00:00
while (curfr->fr_next == NULL)
{
if (fr == topframe)
break;
curfr = fr;
fr = fr->fr_parent;
if (fr != topframe)
{
curfr = fr;
fr = fr->fr_parent;
}
}
if (offset < 0) // drag left
2004-06-13 20:20:40 +00:00
{
left = TRUE;
offset = -offset;
// sum up the room of the current frame and left of it
2004-06-13 20:20:40 +00:00
room = 0;
for (fr = fr->fr_child; ; fr = fr->fr_next)
{
room += fr->fr_width - frame_minwidth(fr, NULL);
if (fr == curfr)
break;
}
fr = curfr->fr_next; // put fr at frame that grows
2004-06-13 20:20:40 +00:00
}
else // drag right
2004-06-13 20:20:40 +00:00
{
left = FALSE;
// sum up the room of frames right of the current one
2004-06-13 20:20:40 +00:00
room = 0;
FOR_ALL_FRAMES(fr, curfr->fr_next)
2004-06-13 20:20:40 +00:00
room += fr->fr_width - frame_minwidth(fr, NULL);
fr = curfr; // put fr at window that grows
2004-06-13 20:20:40 +00:00
}
if (room < offset) // Not enough room
offset = room; // Move as far as we can
if (offset <= 0) // No room at all, quit.
2004-06-13 20:20:40 +00:00
return;
if (fr == NULL)
// This can happen when calling win_move_separator() on the rightmost
// window. Just don't do anything.
return;
2004-06-13 20:20:40 +00:00
// grow frame fr by offset lines
2006-03-18 21:30:13 +00:00
frame_new_width(fr, fr->fr_width + offset, left, FALSE);
2004-06-13 20:20:40 +00:00
// shrink other frames: current and at the left or at the right
2004-06-13 20:20:40 +00:00
if (left)
fr = curfr; // current frame gets smaller
2004-06-13 20:20:40 +00:00
else
fr = curfr->fr_next; // next frame gets smaller
2004-06-13 20:20:40 +00:00
while (fr != NULL && offset > 0)
{
n = frame_minwidth(fr, NULL);
if (fr->fr_width - offset <= n)
{
offset -= fr->fr_width - n;
2006-03-18 21:30:13 +00:00
frame_new_width(fr, n, !left, FALSE);
2004-06-13 20:20:40 +00:00
}
else
{
2006-03-18 21:30:13 +00:00
frame_new_width(fr, fr->fr_width - offset, !left, FALSE);
2004-06-13 20:20:40 +00:00
break;
}
if (left)
fr = fr->fr_prev;
else
fr = fr->fr_next;
}
(void)win_comp_pos();
redraw_all_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
#define FRACTION_MULT 16384L
/*
* Set wp->w_fraction for the current w_wrow and w_height.
* Has no effect when the window is less than two lines.
*/
void
set_fraction(win_T *wp)
{
if (wp->w_height > 1)
// When cursor is in the first line the percentage is computed as if
// it's halfway that line. Thus with two lines it is 25%, with three
// lines 17%, etc. Similarly for the last line: 75%, 83%, etc.
wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT
+ FRACTION_MULT / 2) / (long)wp->w_height;
}
/*
* Handle scroll position, depending on 'splitkeep'. Replaces the
* scroll_to_fraction() call from win_new_height() if 'splitkeep' is "screen"
* or "topline". Instead we iterate over all windows in a tabpage and
* calculate the new scroll position.
* TODO: Ensure this also works with wrapped lines.
* Requires a not fully visible cursor line to be allowed at the bottom of
* a window("zb"), probably only when 'smoothscroll' is also set.
*/
static void
win_fix_scroll(int resize)
{
if (*p_spk == 'c')
return; // 'splitkeep' is "cursor"
skip_update_topline = TRUE;
win_T *wp;
FOR_ALL_WINDOWS(wp)
{
// Skip when window height has not changed.
if (wp->w_height != wp->w_prev_height)
{
// Cursor position in this window may now be invalid. It is kept
// potentially invalid until the window is made the current window.
wp->w_do_win_fix_cursor = TRUE;
// If window has moved update botline to keep the same screenlines.
if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow
&& wp->w_botline - 1 <= wp->w_buffer->b_ml.ml_line_count)
{
int diff = (wp->w_winrow - wp->w_prev_winrow)
+ (wp->w_height - wp->w_prev_height);
linenr_T lnum = wp->w_cursor.lnum;
wp->w_cursor.lnum = wp->w_botline - 1;
// Add difference in height and row to botline.
if (diff > 0)
cursor_down_inner(wp, diff);
else
cursor_up_inner(wp, -diff);
// Scroll to put the new cursor position at the bottom of the
// screen.
wp->w_fraction = FRACTION_MULT;
scroll_to_fraction(wp, wp->w_prev_height);
wp->w_cursor.lnum = lnum;
}
else if (wp == curwin)
wp->w_valid &= ~VALID_CROW;
invalidate_botline_win(wp);
validate_botline_win(wp);
}
wp->w_prev_height = wp->w_height;
wp->w_prev_winrow = wp->w_winrow;
}
skip_update_topline = FALSE;
// Ensure cursor is valid when not in normal mode or when resized.
if (!(get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL)))
win_fix_cursor(FALSE);
else if (resize)
win_fix_cursor(TRUE);
}
/*
* Make sure the cursor position is valid for 'splitkeep'.
* If it is not, put the cursor position in the jumplist and move it.
* If we are not in normal mode ("normal" is FALSE), make it valid by scrolling
* instead.
*/
static void
win_fix_cursor(int normal)
{
win_T *wp = curwin;
if (skip_win_fix_cursor
|| !wp->w_do_win_fix_cursor
|| wp->w_buffer->b_ml.ml_line_count < wp->w_height)
return;
wp->w_do_win_fix_cursor = FALSE;
// Determine valid cursor range.
long so = MIN(wp->w_height / 2, get_scrolloff_value());
linenr_T lnum = wp->w_cursor.lnum;
wp->w_cursor.lnum = wp->w_topline;
cursor_down_inner(wp, so);
linenr_T top = wp->w_cursor.lnum;
wp->w_cursor.lnum = wp->w_botline - 1;
cursor_up_inner(wp, so);
linenr_T bot = wp->w_cursor.lnum;
wp->w_cursor.lnum = lnum;
// Check if cursor position is above or below valid cursor range.
linenr_T nlnum = 0;
if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
nlnum = bot;
else if (lnum < top && wp->w_topline != 1)
nlnum = (so == wp->w_height / 2) ? bot : top;
if (nlnum != 0) // Cursor is invalid for current scroll position.
{
if (normal) // Save to jumplist and set cursor to avoid scrolling.
{
setmark('\'');
wp->w_cursor.lnum = nlnum;
}
else // Scroll instead when not in normal mode.
{
wp->w_fraction = (nlnum == bot) ? FRACTION_MULT : 0;
scroll_to_fraction(wp, wp->w_prev_height);
validate_botline();
}
}
}
2004-06-13 20:20:40 +00:00
/*
* Set the height of a window.
* "height" excludes any window toolbar.
2004-06-13 20:20:40 +00:00
* This takes care of the things inside the window, not what happens to the
* window position, the frame or to other windows.
*/
void
win_new_height(win_T *wp, int height)
2004-06-13 20:20:40 +00:00
{
int prev_height = wp->w_height;
2004-06-13 20:20:40 +00:00
// Don't want a negative height. Happens when splitting a tiny window.
// Will equalize heights soon to fix it.
2004-06-13 20:20:40 +00:00
if (height < 0)
height = 0;
2006-04-11 21:38:50 +00:00
if (wp->w_height == height)
return; // nothing to do
2004-06-13 20:20:40 +00:00
if (wp->w_height > 0)
{
if (wp == curwin && *p_spk == 'c')
// w_wrow needs to be valid. When setting 'laststatus' this may
// call win_new_height() recursively.
validate_cursor();
if (wp->w_height != prev_height)
return; // Recursive call already changed the size, bail out here
// to avoid the following to mess things up.
if (wp->w_wrow != wp->w_prev_fraction_row)
set_fraction(wp);
}
2004-06-13 20:20:40 +00:00
wp->w_height = height;
wp->w_redr_status = TRUE;
win_comp_scroll(wp);
2004-06-13 20:20:40 +00:00
// There is no point in adjusting the scroll position when exiting. Some
// values might be invalid.
if (!exiting && *p_spk == 'c')
{
wp->w_skipcol = 0;
scroll_to_fraction(wp, prev_height);
}
}
void
scroll_to_fraction(win_T *wp, int prev_height)
{
linenr_T lnum;
int sline, line_size;
int height = wp->w_height;
// Don't change w_topline in any of these cases:
// - window height is 0
// - 'scrollbind' is set and this isn't the current window
// - window height is sufficient to display the whole buffer and first line
// is visible.
if (height > 0
&& (!wp->w_p_scb || wp == curwin)
&& (height < wp->w_buffer->b_ml.ml_line_count || wp->w_topline > 1))
2004-06-13 20:20:40 +00:00
{
2005-01-02 11:28:13 +00:00
/*
* Find a value for w_topline that shows the cursor at the same
* relative position in the window as before (more or less).
*/
2004-06-13 20:20:40 +00:00
lnum = wp->w_cursor.lnum;
if (lnum < 1) // can happen when starting up
2004-06-13 20:20:40 +00:00
lnum = 1;
wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L)
/ FRACTION_MULT;
2004-06-13 20:20:40 +00:00
line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
sline = wp->w_wrow - line_size;
2006-10-24 19:12:40 +00:00
if (sline >= 0)
{
// Make sure the whole cursor line is visible, if possible.
2006-10-24 19:12:40 +00:00
int rows = plines_win(wp, lnum, FALSE);
if (sline > wp->w_height - rows)
{
sline = wp->w_height - rows;
wp->w_wrow -= rows - line_size;
}
}
2004-06-13 20:20:40 +00:00
if (sline < 0)
{
/*
* Cursor line would go off top of screen if w_wrow was this high.
2006-10-24 19:12:40 +00:00
* Make cursor line the first line in the window. If not enough
* room use w_skipcol;
2004-06-13 20:20:40 +00:00
*/
wp->w_wrow = line_size;
2006-10-24 19:12:40 +00:00
if (wp->w_wrow >= wp->w_height
&& (wp->w_width - win_col_off(wp)) > 0)
2006-10-24 19:12:40 +00:00
{
wp->w_skipcol += wp->w_width - win_col_off(wp);
2006-10-24 19:12:40 +00:00
--wp->w_wrow;
while (wp->w_wrow >= wp->w_height)
{
wp->w_skipcol += wp->w_width - win_col_off(wp)
2006-10-24 19:12:40 +00:00
+ win_col_off2(wp);
--wp->w_wrow;
}
}
2004-06-13 20:20:40 +00:00
}
else if (sline > 0)
2004-06-13 20:20:40 +00:00
{
2006-10-24 19:12:40 +00:00
while (sline > 0 && lnum > 1)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_FOLDING
hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
if (lnum == 1)
{
// first line in buffer is folded
2004-06-13 20:20:40 +00:00
line_size = 1;
--sline;
break;
}
#endif
--lnum;
#ifdef FEAT_DIFF
if (lnum == wp->w_topline)
line_size = plines_win_nofill(wp, lnum, TRUE)
+ wp->w_topfill;
else
#endif
line_size = plines_win(wp, lnum, TRUE);
sline -= line_size;
}
2005-01-02 11:28:13 +00:00
2004-06-13 20:20:40 +00:00
if (sline < 0)
{
/*
* Line we want at top would go off top of screen. Use next
* line instead.
*/
#ifdef FEAT_FOLDING
hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL);
#endif
lnum++;
wp->w_wrow -= line_size + sline;
}
else if (sline > 0)
2004-06-13 20:20:40 +00:00
{
// First line of file reached, use that as topline.
2004-06-13 20:20:40 +00:00
lnum = 1;
wp->w_wrow -= sline;
}
}
set_topline(wp, lnum);
2004-06-13 20:20:40 +00:00
}
if (wp == curwin)
curs_columns(FALSE); // validate w_wrow
if (prev_height > 0)
wp->w_prev_fraction_row = wp->w_wrow;
2004-06-13 20:20:40 +00:00
redraw_win_later(wp, UPD_SOME_VALID);
2004-06-13 20:20:40 +00:00
invalidate_botline_win(wp);
}
/*
* Set the width of a window.
*/
void
win_new_width(win_T *wp, int width)
2004-06-13 20:20:40 +00:00
{
// Should we give an error if width < 0?
wp->w_width = width < 0 ? 0 : width;
2004-06-13 20:20:40 +00:00
wp->w_lines_valid = 0;
changed_line_abv_curs_win(wp);
invalidate_botline_win(wp);
if (wp == curwin && *p_spk == 'c')
curs_columns(TRUE); // validate w_wrow
redraw_win_later(wp, UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
wp->w_redr_status = TRUE;
}
void
win_comp_scroll(win_T *wp)
2004-06-13 20:20:40 +00:00
{
#if defined(FEAT_EVAL)
int old_w_p_scr = wp->w_p_scr;
#endif
2004-06-13 20:20:40 +00:00
wp->w_p_scr = ((unsigned)wp->w_height >> 1);
if (wp->w_p_scr == 0)
wp->w_p_scr = 1;
#if defined(FEAT_EVAL)
if (wp->w_p_scr != old_w_p_scr)
{
// Used by "verbose set scroll".
wp->w_p_script_ctx[WV_SCROLL].sc_sid = SID_WINLAYOUT;
wp->w_p_script_ctx[WV_SCROLL].sc_lnum = 0;
}
#endif
2004-06-13 20:20:40 +00:00
}
/*
* Command_height: called whenever p_ch has been changed.
2004-06-13 20:20:40 +00:00
*/
void
command_height(void)
2004-06-13 20:20:40 +00:00
{
2006-04-09 21:54:49 +00:00
int old_p_ch = curtab->tp_ch_used;
2004-06-13 20:20:40 +00:00
// Find bottom frame with width of screen.
frame_T *frp = lastwin->w_frame;
2004-06-13 20:20:40 +00:00
while (frp->fr_width != Columns && frp->fr_parent != NULL)
frp = frp->fr_parent;
// Avoid changing the height of a window with 'winfixheight' set.
2004-06-13 20:20:40 +00:00
while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF
&& frp->fr_win->w_p_wfh)
frp = frp->fr_prev;
while (p_ch > old_p_ch && command_frame_height)
2004-06-13 20:20:40 +00:00
{
if (frp == NULL)
2004-06-13 20:20:40 +00:00
{
emsg(_(e_not_enough_room));
p_ch = old_p_ch;
break;
2004-06-13 20:20:40 +00:00
}
int h = MIN(p_ch - old_p_ch,
frp->fr_height - frame_minheight(frp, NULL));
frame_add_height(frp, -h);
old_p_ch += h;
frp = frp->fr_prev;
2004-06-13 20:20:40 +00:00
}
if (p_ch < old_p_ch && command_frame_height && frp != NULL)
frame_add_height(frp, (int)(old_p_ch - p_ch));
2004-06-13 20:20:40 +00:00
// Recompute window positions.
win_comp_pos();
cmdline_row = Rows - p_ch;
redraw_cmdline = TRUE;
// Clear the cmdheight area.
if (msg_scrolled == 0 && full_screen)
{
screen_fill(cmdline_row, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
msg_row = cmdline_row;
}
// Use the value of p_ch that we remembered. This is needed for when the
// GUI starts up, we can't be sure in what order things happen. And when
// p_ch was changed in another tab page.
curtab->tp_ch_used = p_ch;
min_set_ch = p_ch;
2004-06-13 20:20:40 +00:00
}
/*
* Resize frame "frp" to be "n" lines higher (negative for less high).
* Also resize the frames it is contained in.
*/
static void
frame_add_height(frame_T *frp, int n)
2004-06-13 20:20:40 +00:00
{
frame_new_height(frp, frp->fr_height + n, FALSE, FALSE, FALSE);
2004-06-13 20:20:40 +00:00
for (;;)
{
frp = frp->fr_parent;
if (frp == NULL)
break;
frp->fr_height += n;
}
}
/*
* Add or remove a status line for the bottom window(s), according to the
* value of 'laststatus'.
*/
void
last_status(
int morewin) // pretend there are two or more windows
2004-06-13 20:20:40 +00:00
{
// Don't make a difference between horizontal or vertical split.
last_status_rec(topframe, last_stl_height(morewin) > 0);
2004-06-13 20:20:40 +00:00
}
static void
last_status_rec(frame_T *fr, int statusline)
2004-06-13 20:20:40 +00:00
{
frame_T *fp;
win_T *wp;
if (fr->fr_layout == FR_LEAF)
{
wp = fr->fr_win;
if (wp->w_status_height != 0 && !statusline)
{
// remove status line
2004-06-13 20:20:40 +00:00
win_new_height(wp, wp->w_height + 1);
wp->w_status_height = 0;
comp_col();
}
else if (wp->w_status_height == 0 && statusline)
{
// Find a frame to take a line from.
2004-06-13 20:20:40 +00:00
fp = fr;
while (fp->fr_height <= frame_minheight(fp, NULL))
{
if (fp == topframe)
{
emsg(_(e_not_enough_room));
2004-06-13 20:20:40 +00:00
return;
}
// In a column of frames: go to frame above. If already at
// the top or in a row of frames: go to parent.
2004-06-13 20:20:40 +00:00
if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL)
fp = fp->fr_prev;
else
fp = fp->fr_parent;
}
wp->w_status_height = 1;
if (fp != fr)
{
frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE, FALSE);
2004-06-13 20:20:40 +00:00
frame_fix_height(wp);
(void)win_comp_pos();
}
else
win_new_height(wp, wp->w_height - 1);
comp_col();
redraw_all_later(UPD_SOME_VALID);
2004-06-13 20:20:40 +00:00
}
// Set prev_height when difference is due to 'laststatus'.
if (abs(wp->w_height - wp->w_prev_height) == 1)
wp->w_prev_height = wp->w_height;
2004-06-13 20:20:40 +00:00
}
else if (fr->fr_layout == FR_ROW)
{
// vertically split windows, set status line for each one
FOR_ALL_FRAMES(fp, fr->fr_child)
2004-06-13 20:20:40 +00:00
last_status_rec(fp, statusline);
}
else
{
// horizontally split window, set status line for last one
2004-06-13 20:20:40 +00:00
for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next)
;
last_status_rec(fp, statusline);
}
}
2006-02-14 22:29:30 +00:00
/*
2006-02-15 22:11:25 +00:00
* Return the number of lines used by the tab page line.
2006-02-14 22:29:30 +00:00
*/
int
tabline_height(void)
2006-02-14 22:29:30 +00:00
{
2006-02-24 23:53:04 +00:00
#ifdef FEAT_GUI_TABLINE
// When the GUI has the tabline then this always returns zero.
2006-02-24 23:53:04 +00:00
if (gui_use_tabline())
return 0;
#endif
2006-02-20 21:27:21 +00:00
switch (p_stal)
2006-02-15 22:11:25 +00:00
{
case 0: return 0;
case 1: return (first_tabpage->tp_next == NULL) ? 0 : 1;
}
2006-02-14 22:29:30 +00:00
return 1;
}
/*
* Return the height of the last window's statusline.
*/
int
last_stl_height(
int morewin) // pretend there are two or more windows
{
return (p_ls == 2 || (p_ls == 1 && (morewin || !ONE_WINDOW)))
? STATUS_HEIGHT : 0;
}
2004-06-13 20:20:40 +00:00
/*
* Return the minimal number of rows that is needed on the screen to display
* the current number of windows.
*/
int
min_rows(void)
{
if (firstwin == NULL) // not initialized yet
return MIN_LINES;
return frame_minheight(curtab->tp_topframe, NULL) + tabline_height()
+ MIN_CMDHEIGHT;
}
/*
* Return the minimal number of rows that is needed on the screen to display
* the current number of windows for all tab pages.
*/
int
min_rows_for_all_tabpages(void)
2004-06-13 20:20:40 +00:00
{
int total;
2006-02-16 22:11:02 +00:00
tabpage_T *tp;
int n;
2004-06-13 20:20:40 +00:00
if (firstwin == NULL) // not initialized yet
2004-06-13 20:20:40 +00:00
return MIN_LINES;
2006-02-16 22:11:02 +00:00
total = 0;
FOR_ALL_TABPAGES(tp)
2006-02-16 22:11:02 +00:00
{
n = frame_minheight(tp->tp_topframe, NULL);
if (total < n)
total = n;
}
2006-02-24 23:53:04 +00:00
total += tabline_height();
total += MIN_CMDHEIGHT;
2004-06-13 20:20:40 +00:00
return total;
}
/*
* Return TRUE if there is only one window and only one tab page, not
2006-02-17 21:45:41 +00:00
* counting a help or preview window, unless it is the current window.
* Does not count unlisted windows.
2004-06-13 20:20:40 +00:00
*/
int
only_one_window(void)
2004-06-13 20:20:40 +00:00
{
int count = 0;
win_T *wp;
#if defined(FEAT_PROP_POPUP)
// If the current window is a popup then there always is another window.
if (popup_is_popup(curwin))
return FALSE;
#endif
// If there is another tab page there always is another window.
2006-02-14 22:29:30 +00:00
if (first_tabpage->tp_next != NULL)
return FALSE;
FOR_ALL_WINDOWS(wp)
if (wp->w_buffer != NULL
&& (!((bt_help(wp->w_buffer) && !bt_help(curbuf))
2004-06-13 20:20:40 +00:00
# ifdef FEAT_QUICKFIX
|| wp->w_p_pvw
# endif
) || wp == curwin) && !is_aucmd_win(wp))
2004-06-13 20:20:40 +00:00
++count;
return (count <= 1);
}
/*
* Implementation of check_lnums() and check_lnums_nested().
2004-06-13 20:20:40 +00:00
*/
static void
check_lnums_both(int do_curwin, int nested)
2004-06-13 20:20:40 +00:00
{
win_T *wp;
2006-02-16 22:11:02 +00:00
tabpage_T *tp;
FOR_ALL_TAB_WINDOWS(tp, wp)
2004-06-13 20:20:40 +00:00
if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf)
{
int need_adjust;
if (!nested)
{
// save the original cursor position and topline
wp->w_save_cursor.w_cursor_save = wp->w_cursor;
wp->w_save_cursor.w_topline_save = wp->w_topline;
}
need_adjust = wp->w_cursor.lnum > curbuf->b_ml.ml_line_count;
if (need_adjust)
2004-06-13 20:20:40 +00:00
wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
if (need_adjust || !nested)
// save the (corrected) cursor position
wp->w_save_cursor.w_cursor_corr = wp->w_cursor;
need_adjust = wp->w_topline > curbuf->b_ml.ml_line_count;
if (need_adjust)
wp->w_topline = curbuf->b_ml.ml_line_count;
if (need_adjust || !nested)
// save the (corrected) topline
wp->w_save_cursor.w_topline_corr = wp->w_topline;
}
}
/*
* Correct the cursor line number in other windows. Used after changing the
* current buffer, and before applying autocommands.
* When "do_curwin" is TRUE, also check current window.
*/
void
check_lnums(int do_curwin)
{
check_lnums_both(do_curwin, FALSE);
}
/*
* Like check_lnums() but for when check_lnums() was already called.
*/
void
check_lnums_nested(int do_curwin)
{
check_lnums_both(do_curwin, TRUE);
}
/*
* Reset cursor and topline to its stored values from check_lnums().
* check_lnums() must have been called first!
*/
void
reset_lnums(void)
{
win_T *wp;
tabpage_T *tp;
FOR_ALL_TAB_WINDOWS(tp, wp)
if (wp->w_buffer == curbuf)
{
// Restore the value if the autocommand didn't change it and it was
// set.
// Note: This triggers e.g. on BufReadPre, when the buffer is not yet
// loaded, so cannot validate the buffer line
if (EQUAL_POS(wp->w_save_cursor.w_cursor_corr, wp->w_cursor)
&& wp->w_save_cursor.w_cursor_save.lnum != 0)
wp->w_cursor = wp->w_save_cursor.w_cursor_save;
if (wp->w_save_cursor.w_topline_corr == wp->w_topline
&& wp->w_save_cursor.w_topline_save != 0)
wp->w_topline = wp->w_save_cursor.w_topline_save;
if (wp->w_save_cursor.w_topline_save > wp->w_buffer->b_ml.ml_line_count)
wp->w_valid &= ~VALID_TOPLINE;
2004-06-13 20:20:40 +00:00
}
}
/*
* A snapshot of the window sizes, to restore them after closing the help
* or other window.
2004-06-13 20:20:40 +00:00
* Only these fields are used:
* fr_layout
* fr_width
* fr_height
* fr_next
* fr_child
* fr_win (only valid for the old curwin, NULL otherwise)
*/
/*
* Create a snapshot of the current frame sizes.
* "idx" is SNAP_HELP_IDX or SNAP_AUCMD_IDX.
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
* Return FAIL if out of memory, OK otherwise.
2004-06-13 20:20:40 +00:00
*/
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
int
make_snapshot(int idx)
2004-06-13 20:20:40 +00:00
{
2009-06-16 14:01:43 +00:00
clear_snapshot(curtab, idx);
if (make_snapshot_rec(topframe, &curtab->tp_snapshot[idx]) == FAIL)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
{
clear_snapshot(curtab, idx);
return FAIL;
}
return OK;
2004-06-13 20:20:40 +00:00
}
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
static int
make_snapshot_rec(frame_T *fr, frame_T **frp)
2004-06-13 20:20:40 +00:00
{
*frp = ALLOC_CLEAR_ONE(frame_T);
2004-06-13 20:20:40 +00:00
if (*frp == NULL)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
return FAIL;
2004-06-13 20:20:40 +00:00
(*frp)->fr_layout = fr->fr_layout;
(*frp)->fr_width = fr->fr_width;
(*frp)->fr_height = fr->fr_height;
if (fr->fr_next != NULL)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
{
if (make_snapshot_rec(fr->fr_next, &((*frp)->fr_next)) == FAIL)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
return FAIL;
}
2004-06-13 20:20:40 +00:00
if (fr->fr_child != NULL)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
{
if (make_snapshot_rec(fr->fr_child, &((*frp)->fr_child)) == FAIL)
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
return FAIL;
}
if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin)
(*frp)->fr_win = curwin;
patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-20 20:28:15 +01:00
return OK;
2004-06-13 20:20:40 +00:00
}
/*
* Remove any existing snapshot.
*/
static void
clear_snapshot(tabpage_T *tp, int idx)
2004-06-13 20:20:40 +00:00
{
2009-06-16 14:01:43 +00:00
clear_snapshot_rec(tp->tp_snapshot[idx]);
tp->tp_snapshot[idx] = NULL;
2004-06-13 20:20:40 +00:00
}
static void
clear_snapshot_rec(frame_T *fr)
2004-06-13 20:20:40 +00:00
{
if (fr == NULL)
return;
clear_snapshot_rec(fr->fr_next);
clear_snapshot_rec(fr->fr_child);
vim_free(fr);
2004-06-13 20:20:40 +00:00
}
/*
* Traverse a snapshot to find the previous curwin.
*/
static win_T *
get_snapshot_curwin_rec(frame_T *ft)
{
win_T *wp;
if (ft->fr_next != NULL)
{
if ((wp = get_snapshot_curwin_rec(ft->fr_next)) != NULL)
return wp;
}
if (ft->fr_child != NULL)
{
if ((wp = get_snapshot_curwin_rec(ft->fr_child)) != NULL)
return wp;
}
return ft->fr_win;
}
/*
* Return the current window stored in the snapshot or NULL.
*/
static win_T *
get_snapshot_curwin(int idx)
{
if (curtab->tp_snapshot[idx] == NULL)
return NULL;
return get_snapshot_curwin_rec(curtab->tp_snapshot[idx]);
}
2004-06-13 20:20:40 +00:00
/*
* Restore a previously created snapshot, if there is any.
* This is only done if the screen size didn't change and the window layout is
* still the same.
* "idx" is SNAP_HELP_IDX or SNAP_AUCMD_IDX.
2004-06-13 20:20:40 +00:00
*/
2009-06-16 14:01:43 +00:00
void
restore_snapshot(
int idx,
int close_curwin) // closing current window
2004-06-13 20:20:40 +00:00
{
win_T *wp;
2009-06-16 14:01:43 +00:00
if (curtab->tp_snapshot[idx] != NULL
&& curtab->tp_snapshot[idx]->fr_width == topframe->fr_width
&& curtab->tp_snapshot[idx]->fr_height == topframe->fr_height
&& check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK)
2004-06-13 20:20:40 +00:00
{
2009-06-16 14:01:43 +00:00
wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe);
2004-06-13 20:20:40 +00:00
win_comp_pos();
if (wp != NULL && close_curwin)
win_goto(wp);
redraw_all_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
2009-06-16 14:01:43 +00:00
clear_snapshot(curtab, idx);
2004-06-13 20:20:40 +00:00
}
/*
* Check if frames "sn" and "fr" have the same layout, same following frames
* and same children. And the window pointer is valid.
2004-06-13 20:20:40 +00:00
*/
static int
check_snapshot_rec(frame_T *sn, frame_T *fr)
2004-06-13 20:20:40 +00:00
{
if (sn->fr_layout != fr->fr_layout
|| (sn->fr_next == NULL) != (fr->fr_next == NULL)
|| (sn->fr_child == NULL) != (fr->fr_child == NULL)
|| (sn->fr_next != NULL
&& check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL)
|| (sn->fr_child != NULL
&& check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)
|| (sn->fr_win != NULL && !win_valid(sn->fr_win)))
2004-06-13 20:20:40 +00:00
return FAIL;
return OK;
}
/*
* Copy the size of snapshot frame "sn" to frame "fr". Do the same for all
* following frames and children.
* Returns a pointer to the old current window, or NULL.
*/
static win_T *
restore_snapshot_rec(frame_T *sn, frame_T *fr)
2004-06-13 20:20:40 +00:00
{
win_T *wp = NULL;
win_T *wp2;
fr->fr_height = sn->fr_height;
fr->fr_width = sn->fr_width;
if (fr->fr_layout == FR_LEAF)
{
frame_new_height(fr, fr->fr_height, FALSE, FALSE, FALSE);
2006-03-18 21:30:13 +00:00
frame_new_width(fr, fr->fr_width, FALSE, FALSE);
2004-06-13 20:20:40 +00:00
wp = sn->fr_win;
}
if (sn->fr_next != NULL)
{
wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next);
if (wp2 != NULL)
wp = wp2;
}
if (sn->fr_child != NULL)
{
wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child);
if (wp2 != NULL)
wp = wp2;
}
return wp;
}
#if defined(FEAT_GUI) || defined(PROTO)
2004-06-13 20:20:40 +00:00
/*
* Return TRUE if there is any vertically split window.
*/
int
win_hasvertsplit(void)
2004-06-13 20:20:40 +00:00
{
frame_T *fr;
if (topframe->fr_layout == FR_ROW)
return TRUE;
if (topframe->fr_layout == FR_COL)
FOR_ALL_FRAMES(fr, topframe->fr_child)
2004-06-13 20:20:40 +00:00
if (fr->fr_layout == FR_ROW)
return TRUE;
return FALSE;
}
#endif
2007-07-26 20:58:42 +00:00
#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) || defined(PROTO)
int
get_win_number(win_T *wp, win_T *first_win)
{
int i = 1;
win_T *w;
for (w = first_win; w != NULL && w != wp; w = W_NEXT(w))
++i;
if (w == NULL)
return 0;
else
return i;
}
int
get_tab_number(tabpage_T *tp)
{
int i = 1;
tabpage_T *t;
for (t = first_tabpage; t != NULL && t != tp; t = t->tp_next)
++i;
if (t == NULL)
return 0;
else
return i;
}
#endif
/*
* Return TRUE if "topfrp" and its children are at the right height.
*/
static int
frame_check_height(frame_T *topfrp, int height)
{
frame_T *frp;
if (topfrp->fr_height != height)
return FALSE;
if (topfrp->fr_layout == FR_ROW)
FOR_ALL_FRAMES(frp, topfrp->fr_child)
if (frp->fr_height != height)
return FALSE;
return TRUE;
}
/*
* Return TRUE if "topfrp" and its children are at the right width.
*/
static int
frame_check_width(frame_T *topfrp, int width)
{
frame_T *frp;
if (topfrp->fr_width != width)
return FALSE;
if (topfrp->fr_layout == FR_COL)
FOR_ALL_FRAMES(frp, topfrp->fr_child)
if (frp->fr_width != width)
return FALSE;
return TRUE;
}
#if defined(FEAT_SYN_HL) || defined(PROTO)
/*
* Simple int comparison function for use with qsort()
*/
static int
int_cmp(const void *pa, const void *pb)
{
const int a = *(const int *)pa;
const int b = *(const int *)pb;
if (a > b)
return 1;
if (a < b)
return -1;
return 0;
}
/*
* Check "cc" as 'colorcolumn' and update the members of "wp".
* This is called when 'colorcolumn' or 'textwidth' is changed.
* Returns error message, NULL if it's OK.
*/
char *
check_colorcolumn(
char_u *cc, // when NULL: use "wp->w_p_cc"
win_T *wp) // when NULL: only parse "cc"
{
char_u *s = empty_option;
int tw;
int col;
int count = 0;
int color_cols[256];
int i;
int j = 0;
if (wp != NULL && wp->w_buffer == NULL)
return NULL; // buffer was closed
if (cc != NULL)
s = cc;
else if (wp != NULL)
s = wp->w_p_cc;
if (wp != NULL)
tw = wp->w_buffer->b_p_tw;
else
// buffer-local value not set, assume zero
tw = 0;
while (*s != NUL && count < 255)
{
if (*s == '-' || *s == '+')
{
// -N and +N: add to 'textwidth'
col = (*s == '-') ? -1 : 1;
++s;
if (!VIM_ISDIGIT(*s))
return e_invalid_argument;
col = col * getdigits(&s);
if (tw == 0)
goto skip; // 'textwidth' not set, skip this item
col += tw;
if (col < 0)
goto skip;
}
else if (VIM_ISDIGIT(*s))
col = getdigits(&s);
else
return e_invalid_argument;
color_cols[count++] = col - 1; // 1-based to 0-based
skip:
if (*s == NUL)
break;
if (*s != ',')
return e_invalid_argument;
if (*++s == NUL)
return e_invalid_argument; // illegal trailing comma as in "set cc=80,"
}
if (wp == NULL)
return NULL; // only parse "cc"
vim_free(wp->w_p_cc_cols);
if (count == 0)
wp->w_p_cc_cols = NULL;
else
{
wp->w_p_cc_cols = ALLOC_MULT(int, count + 1);
if (wp->w_p_cc_cols != NULL)
{
// sort the columns for faster usage on screen redraw inside
// win_line()
qsort(color_cols, count, sizeof(int), int_cmp);
for (i = 0; i < count; ++i)
// skip duplicates
if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i])
wp->w_p_cc_cols[j++] = color_cols[i];
wp->w_p_cc_cols[j] = -1; // end marker
}
}
return NULL; // no error
}
#endif
patch 9.1.0143: [security]: autocmd causes use-after-free in set_curbuf() Problem: [security]: autocmd cause use-after-free in set_curbuf() (kawarimidoll) Solution: check side-effect of BufLeave autocommand, when the number of windows changed, close windows containing buffers that will be wiped, if curbuf changed unexpectedly make sure b_nwindows is decremented otherwise it cannot be wiped set_curbuf() already makes some efforts to ensure the BufLeave autocommands do not cause issues. However there are still 2 issues that are not taken care of: 1) If a BufLeave autocommand opens a new window containing the same buffer as that is going got be closed in close_buffer() a bit later, we suddenly have another window open, containing a free'd buffer. So we must check if the number of windows changed and if it does (and the current buffer is going to be wiped (according to the 'bufhidden' setting), let's immediately close all windows containing the current buffer using close_windows() 2) If a BufLeave autocommand changes our current buffer (displays it in the current window), buf->b_nwindow will be incremented. As part of set_curbuf() we will however enter another buffer soon, which means, the newly created curbuf will have b_nwindows still have set, even so the buffer is no longer displayed in a window. This causes later problems, because it will no longer be possible to wipe such a buffer. So just before entering the final buffer, check if the curbuf changed when calling the BufLeave autocommand and if it does (and curbuf is still valid), decrement curbuf->b_nwindows. Both issues can be verified using the provided test (however the second issue only because such an impacted buffer won't be wiped, causing futher issues in later tests). fixes: #13839 closes: #14104 Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-28 23:32:00 +01:00
int
get_last_winid(void)
{
return last_win_id;
}
/*
* Don't let autocommands close the given window
*/
int
win_locked(win_T *wp)
{
return wp->w_locked;
}