summaryrefslogtreecommitdiff
path: root/usr.bin/vim/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/vim/window.c')
-rw-r--r--usr.bin/vim/window.c1570
1 files changed, 1570 insertions, 0 deletions
diff --git a/usr.bin/vim/window.c b/usr.bin/vim/window.c
new file mode 100644
index 00000000000..ae23d08b665
--- /dev/null
+++ b/usr.bin/vim/window.c
@@ -0,0 +1,1570 @@
+/* $OpenBSD: window.c,v 1.1 1996/09/07 21:40:24 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * 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.
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+
+static void reset_VIsual __ARGS((void));
+static int win_comp_pos __ARGS((void));
+static void win_exchange __ARGS((long));
+static void win_rotate __ARGS((int, int));
+static void win_append __ARGS((WIN *, WIN *));
+static void win_remove __ARGS((WIN *));
+static void win_new_height __ARGS((WIN *, int));
+
+static WIN *prevwin = NULL; /* previous window */
+
+/*
+ * all CTRL-W window commands are handled here, called from normal().
+ */
+ void
+do_window(nchar, Prenum)
+ int nchar;
+ long Prenum;
+{
+ long Prenum1;
+ WIN *wp;
+ char_u *ptr;
+ int len;
+ int type = -1;
+ WIN *wp2;
+
+ if (Prenum == 0)
+ Prenum1 = 1;
+ else
+ Prenum1 = Prenum;
+
+ switch (nchar)
+ {
+/* split current window in two parts */
+ case 'S':
+ case Ctrl('S'):
+ case 's': reset_VIsual(); /* stop Visual mode */
+ win_split((int)Prenum, TRUE);
+ break;
+
+/* split current window and edit alternate file */
+ case K_CCIRCM:
+ case '^':
+ reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":split #");
+ if (Prenum)
+ stuffnumReadbuff(Prenum); /* buffer number */
+ stuffcharReadbuff('\n');
+ break;
+
+/* open new window */
+ case Ctrl('N'):
+ case 'n': reset_VIsual(); /* stop Visual mode */
+ stuffcharReadbuff(':');
+ if (Prenum)
+ stuffnumReadbuff(Prenum); /* window height */
+ stuffReadbuff((char_u *)"new\n"); /* it is cmdline.c */
+ break;
+
+/* quit current window */
+ case Ctrl('Q'):
+ case 'q': reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":quit\n"); /* it is cmdline.c */
+ break;
+
+/* close current window */
+ case Ctrl('C'):
+ case 'c': reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":close\n"); /* it is cmdline.c */
+ break;
+
+/* close all but current window */
+ case Ctrl('O'):
+ case 'o': reset_VIsual(); /* stop Visual mode */
+ stuffReadbuff((char_u *)":only\n"); /* it is cmdline.c */
+ break;
+
+/* cursor to next window */
+ case 'j':
+ case K_DOWN:
+ case Ctrl('J'):
+ for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0;
+ wp = wp->w_next)
+ ;
+new_win:
+ /*
+ * When jumping to another buffer, stop visual mode
+ * Do this before changing windows so we can yank the
+ * selection into the '"*' register.
+ */
+ if (wp->w_buffer != curbuf && VIsual_active)
+ {
+ end_visual_mode();
+ for (wp2 = firstwin; wp2 != NULL; wp2 = wp2->w_next)
+ if (wp2->w_buffer == curbuf &&
+ wp2->w_redr_type < NOT_VALID)
+ {
+ wp2->w_redr_type = NOT_VALID;
+ redraw_later(NOT_VALID);
+ }
+ }
+ win_enter(wp, TRUE);
+ cursupdate();
+ break;
+
+/* cursor to next window with wrap around */
+ case Ctrl('W'):
+ case 'w':
+/* cursor to previous window with wrap around */
+ case 'W':
+ if (lastwin == firstwin) /* just one window */
+ beep_flush();
+ else
+ {
+ if (Prenum) /* go to specified window */
+ {
+ for (wp = firstwin; --Prenum > 0; )
+ {
+ if (wp->w_next == NULL)
+ break;
+ else
+ wp = wp->w_next;
+ }
+ }
+ else
+ {
+ if (nchar == 'W') /* go to previous window */
+ {
+ wp = curwin->w_prev;
+ if (wp == NULL)
+ wp = lastwin; /* wrap around */
+ }
+ else /* go to next window */
+ {
+ wp = curwin->w_next;
+ if (wp == NULL)
+ wp = firstwin; /* wrap around */
+ }
+ }
+ goto new_win;
+ }
+ break;
+
+/* cursor to window above */
+ case 'k':
+ case K_UP:
+ case Ctrl('K'):
+ for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0;
+ wp = wp->w_prev)
+ ;
+ goto new_win;
+
+/* cursor to top window */
+ case 't':
+ case Ctrl('T'):
+ wp = firstwin;
+ goto new_win;
+
+/* cursor to bottom window */
+ case 'b':
+ case Ctrl('B'):
+ wp = lastwin;
+ goto new_win;
+
+/* cursor to last accessed (previous) window */
+ case 'p':
+ case Ctrl('P'):
+ if (prevwin == NULL)
+ beep_flush();
+ else
+ {
+ wp = prevwin;
+ goto new_win;
+ }
+ break;
+
+/* exchange current and next window */
+ case 'x':
+ case Ctrl('X'):
+ win_exchange(Prenum);
+ break;
+
+/* rotate windows downwards */
+ case Ctrl('R'):
+ case 'r': reset_VIsual(); /* stop Visual mode */
+ win_rotate(FALSE, (int)Prenum1); /* downwards */
+ break;
+
+/* rotate windows upwards */
+ case 'R': reset_VIsual(); /* stop Visual mode */
+ win_rotate(TRUE, (int)Prenum1); /* upwards */
+ break;
+
+/* make all windows the same height */
+ case '=': win_equal(NULL, TRUE);
+ break;
+
+/* increase current window height */
+ case '+': win_setheight(curwin->w_height + (int)Prenum1);
+ break;
+
+/* decrease current window height */
+ case '-': win_setheight(curwin->w_height - (int)Prenum1);
+ break;
+
+/* set current window height */
+ case Ctrl('_'):
+ case '_': win_setheight(Prenum ? (int)Prenum : 9999);
+ break;
+
+/* jump to tag and split window if tag exists */
+ case ']':
+ case Ctrl(']'):
+ reset_VIsual(); /* stop Visual mode */
+ postponed_split = TRUE;
+ stuffcharReadbuff(Ctrl(']'));
+ break;
+
+/* edit file name under cursor in a new window */
+ case 'f':
+ case Ctrl('F'):
+ reset_VIsual(); /* stop Visual mode */
+ ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
+ if (ptr != NULL)
+ {
+ setpcmark();
+ if (win_split(0, FALSE) == OK)
+ (void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0, FALSE);
+ vim_free(ptr);
+ }
+ break;
+
+/* Go to the first occurence of the identifier under cursor along path in a
+ * new window -- webb
+ */
+ case 'i': /* Go to any match */
+ case Ctrl('I'):
+ type = FIND_ANY;
+ /* FALLTHROUGH */
+ case 'd': /* Go to definition, using p_def */
+ case Ctrl('D'):
+ if (type == -1)
+ type = FIND_DEFINE;
+
+ if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
+ break;
+ find_pattern_in_path(ptr, len, TRUE, TRUE, type,
+ Prenum1, ACTION_SPLIT, (linenr_t)1, (linenr_t)MAXLNUM);
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ default: beep_flush();
+ break;
+ }
+}
+
+ static void
+reset_VIsual()
+{
+ if (VIsual_active)
+ {
+ end_visual_mode();
+ update_curbuf(NOT_VALID); /* delete the inversion */
+ }
+}
+
+/*
+ * split the current window, implements CTRL-W s and :split
+ *
+ * new_height is the height for the new window, 0 to make half of current
+ * height redraw is TRUE when redraw now
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+win_split(new_height, redraw)
+ int new_height;
+ int redraw;
+{
+ WIN *wp;
+ linenr_t lnum;
+ int h;
+ int i;
+ int need_status;
+ int do_equal = (p_ea && new_height == 0);
+ int needed;
+ int available;
+ int curwin_height;
+
+ /* add a status line when p_ls == 1 and splitting the first window */
+ if (lastwin == firstwin && p_ls == 1 && curwin->w_status_height == 0)
+ need_status = STATUS_HEIGHT;
+ else
+ need_status = 0;
+
+/*
+ * check if we are able to split the current window and compute its height
+ */
+ available = curwin->w_height;
+ needed = 2 * MIN_ROWS + STATUS_HEIGHT + need_status;
+ if (p_ea)
+ {
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp != curwin)
+ {
+ available += wp->w_height;
+ needed += MIN_ROWS;
+ }
+ }
+ if (available < needed)
+ {
+ EMSG(e_noroom);
+ return FAIL;
+ }
+ curwin_height = curwin->w_height;
+ if (need_status)
+ {
+ curwin->w_status_height = STATUS_HEIGHT;
+ curwin_height -= STATUS_HEIGHT;
+ }
+ if (new_height == 0)
+ new_height = curwin_height / 2;
+
+ if (new_height > curwin_height - MIN_ROWS - STATUS_HEIGHT)
+ new_height = curwin_height - MIN_ROWS - STATUS_HEIGHT;
+
+ if (new_height < MIN_ROWS)
+ new_height = MIN_ROWS;
+
+ /* if it doesn't fit in the current window, need win_equal() */
+ if (curwin_height - new_height - STATUS_HEIGHT < MIN_ROWS)
+ do_equal = TRUE;
+/*
+ * allocate new window structure and link it in the window list
+ */
+ if (p_sb) /* new window below current one */
+ wp = win_alloc(curwin);
+ else
+ wp = win_alloc(curwin->w_prev);
+ if (wp == NULL)
+ return FAIL;
+/*
+ * compute the new screen positions
+ */
+ win_new_height(wp, new_height);
+ win_new_height(curwin, curwin_height - (new_height + STATUS_HEIGHT));
+ if (p_sb) /* new window below current one */
+ {
+ wp->w_winpos = curwin->w_winpos + curwin->w_height + STATUS_HEIGHT;
+ wp->w_status_height = curwin->w_status_height;
+ curwin->w_status_height = STATUS_HEIGHT;
+ }
+ else /* new window above current one */
+ {
+ wp->w_winpos = curwin->w_winpos;
+ wp->w_status_height = STATUS_HEIGHT;
+ curwin->w_winpos = wp->w_winpos + wp->w_height + STATUS_HEIGHT;
+ }
+/*
+ * make the contents of the new window the same as the current one
+ */
+ wp->w_buffer = curbuf;
+ curbuf->b_nwindows++;
+ wp->w_cursor = curwin->w_cursor;
+ wp->w_row = curwin->w_row;
+ wp->w_col = curwin->w_col;
+ wp->w_virtcol = curwin->w_virtcol;
+ wp->w_curswant = curwin->w_curswant;
+ wp->w_set_curswant = curwin->w_set_curswant;
+ wp->w_empty_rows = curwin->w_empty_rows;
+ wp->w_leftcol = curwin->w_leftcol;
+ wp->w_pcmark = curwin->w_pcmark;
+ wp->w_prev_pcmark = curwin->w_prev_pcmark;
+ wp->w_alt_fnum = curwin->w_alt_fnum;
+
+ wp->w_arg_idx = curwin->w_arg_idx;
+ /*
+ * copy tagstack and options from existing window
+ */
+ for (i = 0; i < curwin->w_tagstacklen; i++)
+ {
+ wp->w_tagstack[i].fmark = curwin->w_tagstack[i].fmark;
+ wp->w_tagstack[i].tagname = strsave(curwin->w_tagstack[i].tagname);
+ }
+ wp->w_tagstackidx = curwin->w_tagstackidx;
+ wp->w_tagstacklen = curwin->w_tagstacklen;
+ win_copy_options(curwin, wp);
+/*
+ * Both windows need redrawing
+ */
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+ curwin->w_redr_type = NOT_VALID;
+ curwin->w_redr_status = TRUE;
+/*
+ * Cursor is put in middle of window in both windows
+ */
+ if (wp->w_height < curwin->w_height) /* use smallest of two heights */
+ h = wp->w_height;
+ else
+ h = curwin->w_height;
+ h >>= 1;
+ for (lnum = wp->w_cursor.lnum; lnum > 1; --lnum)
+ {
+ h -= plines(lnum);
+ if (h <= 0)
+ break;
+ }
+ wp->w_topline = lnum;
+ curwin->w_topline = lnum;
+ if (need_status)
+ {
+ msg_pos((int)Rows - 1, sc_col);
+ msg_clr_eos(); /* Old command/ruler may still be there -- webb */
+ comp_col();
+ msg_pos((int)Rows - 1, 0); /* put position back at start of line */
+ }
+/*
+ * make the new window the current window and redraw
+ */
+ if (do_equal)
+ win_equal(wp, FALSE);
+ win_enter(wp, FALSE);
+
+ if (redraw)
+ updateScreen(NOT_VALID);
+
+ return OK;
+}
+
+/*
+ * Return the number of windows.
+ */
+ int
+win_count()
+{
+ WIN *wp;
+ int count = 0;
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ ++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(count)
+ int count;
+{
+ int maxcount;
+ int todo;
+ int p_sb_save;
+
+/*
+ * Each window needs at least MIN_ROWS lines and a status line.
+ * Add 4 lines for one window, otherwise we may end up with all one-line
+ * windows. Use value of 'winheight' if it is set
+ */
+ maxcount = (curwin->w_height + curwin->w_status_height -
+ (p_wh ? (p_wh - 1) : 4)) / (MIN_ROWS + STATUS_HEIGHT);
+ if (maxcount < 2)
+ maxcount = 2;
+ if (count > maxcount)
+ count = maxcount;
+
+ /*
+ * add status line now, otherwise first window will be too big
+ */
+ if ((p_ls == 2 || (count > 1 && p_ls == 1)) && curwin->w_status_height == 0)
+ {
+ curwin->w_status_height = STATUS_HEIGHT;
+ win_new_height(curwin, curwin->w_height - STATUS_HEIGHT);
+ }
+
+/*
+ * set 'splitbelow' off for a moment, don't want that now
+ */
+ p_sb_save = p_sb;
+ p_sb = FALSE;
+ /* todo is number of windows left to create */
+ for (todo = count - 1; todo > 0; --todo)
+ if (win_split(curwin->w_height - (curwin->w_height - todo
+ * STATUS_HEIGHT) / (todo + 1) - STATUS_HEIGHT, FALSE) == FAIL)
+ break;
+ p_sb = p_sb_save;
+
+ /* return actual number of windows */
+ return (count - todo);
+}
+
+/*
+ * Exchange current and next window
+ */
+ static void
+win_exchange(Prenum)
+ long Prenum;
+{
+ WIN *wp;
+ WIN *wp2;
+ int temp;
+
+ if (lastwin == firstwin) /* just one window */
+ {
+ beep_flush();
+ return;
+ }
+
+/*
+ * find window to exchange with
+ */
+ if (Prenum)
+ {
+ wp = firstwin;
+ while (wp != NULL && --Prenum > 0)
+ wp = wp->w_next;
+ }
+ else if (curwin->w_next != NULL) /* Swap with next */
+ wp = curwin->w_next;
+ else /* Swap last window with previous */
+ wp = curwin->w_prev;
+
+ if (wp == curwin || wp == NULL)
+ return;
+
+/*
+ * 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
+ */
+ wp2 = curwin->w_prev;
+ win_remove(curwin);
+ win_append(wp->w_prev, curwin);
+ if (wp != wp2)
+ {
+ win_remove(wp);
+ win_append(wp2, wp);
+ }
+ temp = curwin->w_status_height;
+ curwin->w_status_height = wp->w_status_height;
+ wp->w_status_height = temp;
+
+ win_comp_pos(); /* recompute window positions */
+
+ win_enter(wp, TRUE);
+ cursupdate();
+ updateScreen(CLEAR);
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ if (gui.which_scrollbars[SB_LEFT])
+ gui_mch_reorder_scrollbars(SB_LEFT);
+ if (gui.which_scrollbars[SB_RIGHT])
+ gui_mch_reorder_scrollbars(SB_RIGHT);
+ }
+#endif
+}
+
+/*
+ * 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(upwards, count)
+ int upwards;
+ int count;
+{
+ WIN *wp;
+ int height;
+
+ if (firstwin == lastwin) /* nothing to do */
+ {
+ beep_flush();
+ return;
+ }
+ while (count--)
+ {
+ if (upwards) /* first window becomes last window */
+ {
+ wp = firstwin;
+ win_remove(wp);
+ win_append(lastwin, wp);
+ wp = lastwin->w_prev; /* previously last window */
+ }
+ else /* last window becomes first window */
+ {
+ wp = lastwin;
+ win_remove(lastwin);
+ win_append(NULL, wp);
+ wp = firstwin; /* previously last window */
+ }
+ /* exchange status height of old and new last window */
+ height = lastwin->w_status_height;
+ lastwin->w_status_height = wp->w_status_height;
+ wp->w_status_height = height;
+
+ /* recompute w_winpos for all windows */
+ (void) win_comp_pos();
+ }
+
+ cursupdate();
+ updateScreen(CLEAR);
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ {
+ if (gui.which_scrollbars[SB_LEFT])
+ gui_mch_reorder_scrollbars(SB_LEFT);
+ if (gui.which_scrollbars[SB_RIGHT])
+ gui_mch_reorder_scrollbars(SB_RIGHT);
+ }
+#endif
+}
+
+/*
+ * Make all windows the same height.
+ * 'next_curwin' will soon be the current window, make sure it has enough
+ * rows.
+ */
+ void
+win_equal(next_curwin, redraw)
+ WIN *next_curwin; /* pointer to current window to be */
+ int redraw;
+{
+ int total;
+ int less;
+ int wincount;
+ int winpos;
+ int temp;
+ WIN *wp;
+ int new_height;
+
+/*
+ * count the number of lines available
+ */
+ total = 0;
+ wincount = 0;
+ for (wp = firstwin; wp; wp = wp->w_next)
+ {
+ total += wp->w_height - MIN_ROWS;
+ wincount++;
+ }
+
+/*
+ * If next_curwin given and 'winheight' set, make next_curwin p_wh lines.
+ */
+ less = 0;
+ if (next_curwin != NULL)
+ {
+ if (p_wh)
+ {
+ if (p_wh - MIN_ROWS > total) /* all lines go to current window */
+ less = total;
+ else
+ {
+ less = p_wh - MIN_ROWS - total / wincount;
+ if (less < 0)
+ less = 0;
+ }
+ }
+ }
+
+/*
+ * spread the available lines over the windows
+ */
+ winpos = 0;
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ {
+ if (wp == next_curwin && less)
+ {
+ less = 0;
+ temp = p_wh - MIN_ROWS;
+ if (temp > total)
+ temp = total;
+ }
+ else
+ temp = (total - less + (wincount >> 1)) / wincount;
+ new_height = MIN_ROWS + temp;
+ if (wp->w_winpos != winpos || wp->w_height != new_height)
+ {
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+ }
+ wp->w_winpos = winpos;
+ win_new_height(wp, new_height);
+ total -= temp;
+ --wincount;
+ winpos += wp->w_height + wp->w_status_height;
+ }
+ if (redraw)
+ {
+ cursupdate();
+ updateScreen(CLEAR);
+ }
+}
+
+/*
+ * close all windows for buffer 'buf'
+ */
+ void
+close_windows(buf)
+ BUF *buf;
+{
+ WIN *win;
+
+ ++RedrawingDisabled;
+ for (win = firstwin; win != NULL && lastwin != firstwin; )
+ {
+ if (win->w_buffer == buf)
+ {
+ close_window(win, FALSE);
+ win = firstwin; /* go back to the start */
+ }
+ else
+ win = win->w_next;
+ }
+ --RedrawingDisabled;
+}
+
+/*
+ * close window "win"
+ * If "free_buf" is TRUE related buffer may be freed.
+ *
+ * called by :quit, :close, :xit, :wq and findtag()
+ */
+ void
+close_window(win, free_buf)
+ WIN *win;
+ int free_buf;
+{
+ WIN *wp;
+
+ if (lastwin == firstwin)
+ {
+ EMSG("Cannot close last window");
+ return;
+ }
+
+/*
+ * Remove the window.
+ * if 'splitbelow' the free space goes to the window above it.
+ * if 'nosplitbelow' the free space goes to the window below it.
+ * This makes opening a window and closing it immediately keep the same window
+ * layout.
+ */
+ /* freed space goes to next window */
+ if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
+ {
+ wp = win->w_next;
+ wp->w_winpos = win->w_winpos;
+ }
+ else /* freed space goes to previous window */
+ wp = win->w_prev;
+ win_new_height(wp, wp->w_height + win->w_height + win->w_status_height);
+
+#ifdef AUTOCMD
+ if (win == curwin)
+ {
+ if (wp->w_buffer != curbuf)
+ apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
+ apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
+ }
+#endif
+
+/*
+ * Close the link to the buffer.
+ */
+ close_buffer(win, win->w_buffer, free_buf, FALSE);
+
+ win_free(win);
+ if (win == curwin)
+ curwin = NULL;
+ if (p_ea)
+ win_equal(wp, FALSE);
+ if (curwin == NULL)
+ win_enter(wp, FALSE);
+ /*
+ * if last window has status line now and we don't want one,
+ * remove the status line
+ */
+ if (lastwin->w_status_height &&
+ (p_ls == 0 || (p_ls == 1 && firstwin == lastwin)))
+ {
+ win_new_height(lastwin, lastwin->w_height + lastwin->w_status_height);
+ lastwin->w_status_height = 0;
+ comp_col();
+ }
+
+ updateScreen(NOT_VALID);
+ if (RedrawingDisabled)
+ comp_Botline(wp); /* need to do this before cursupdate() */
+}
+
+/*
+ * close all windows except current one
+ * buffers in the windows become hidden
+ *
+ * called by :only and do_arg_all();
+ */
+ void
+close_others(message)
+ int message;
+{
+ WIN *wp;
+ WIN *nextwp;
+
+ if (lastwin == firstwin)
+ {
+ if (message
+#ifdef AUTOCMD
+ && !autocmd_busy
+#endif
+ )
+ MSG("Already only one window");
+ return;
+ }
+
+ for (wp = firstwin; wp != NULL; wp = nextwp)
+ {
+ nextwp = wp->w_next;
+ if (wp == curwin) /* don't close current window */
+ continue;
+ /*
+ * Close the link to the buffer.
+ */
+ close_buffer(wp, wp->w_buffer, FALSE, FALSE);
+
+ /*
+ * Remove the window. All lines go to current window.
+ */
+ win_new_height(curwin,
+ curwin->w_height + wp->w_height + wp->w_status_height);
+ win_free(wp);
+ }
+
+ /*
+ * If current window has status line and we don't want one,
+ * remove the status line.
+ */
+ if (curwin->w_status_height && p_ls != 2)
+ {
+ win_new_height(curwin, curwin->w_height + curwin->w_status_height);
+ curwin->w_status_height = 0;
+ }
+ curwin->w_winpos = 0; /* put current window at top of the screen */
+ if (message)
+ updateScreen(NOT_VALID);
+}
+
+/*
+ * init the cursor in the window
+ *
+ * called when a new file is being edited
+ */
+ void
+win_init(wp)
+ WIN *wp;
+{
+ wp->w_redr_type = NOT_VALID;
+ wp->w_cursor.lnum = 1;
+ wp->w_curswant = wp->w_cursor.col = 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;
+ wp->w_botline = 2;
+}
+
+/*
+ * make window wp the current window
+ */
+ void
+win_enter(wp, undo_sync)
+ WIN *wp;
+ int undo_sync;
+{
+#ifdef AUTOCMD
+ int other_buffer = FALSE;
+#endif
+
+ if (wp == curwin) /* nothing to do */
+ return;
+
+#ifdef AUTOCMD
+ if (curwin != NULL)
+ {
+ if (wp->w_buffer != curbuf)
+ {
+ apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
+ other_buffer = TRUE;
+ }
+ apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
+ }
+#endif
+
+ /* sync undo before leaving the current buffer */
+ if (undo_sync && curbuf != wp->w_buffer)
+ u_sync();
+ /* may have to copy the buffer options when 'cpo' contains 'S' */
+ if (wp->w_buffer != curbuf)
+ buf_copy_options(curbuf, wp->w_buffer, TRUE);
+ if (curwin != NULL)
+ prevwin = curwin; /* remember for CTRL-W p */
+ curwin = wp;
+ curbuf = wp->w_buffer;
+
+#ifdef AUTOCMD
+ apply_autocmds(EVENT_WINENTER, NULL, NULL);
+ if (other_buffer)
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL);
+#endif
+
+ maketitle();
+ /* set window height to desired minimal value */
+ if (p_wh && curwin->w_height < p_wh)
+ win_setheight((int)p_wh);
+#ifdef USE_MOUSE
+ setmouse(); /* in case jumped to/from help buffer */
+#endif
+}
+
+/*
+ * allocate a window structure and link it in the window list
+ */
+ WIN *
+win_alloc(after)
+ WIN *after;
+{
+ WIN *newwin;
+
+/*
+ * allocate window structure and linesizes arrays
+ */
+ newwin = (WIN *)alloc((unsigned)sizeof(WIN));
+ if (newwin)
+ {
+/*
+ * most stucture members have to be zero
+ */
+ (void)vim_memset(newwin, 0, sizeof(WIN));
+/*
+ * link the window in the window list
+ */
+ win_append(after, newwin);
+
+ win_alloc_lsize(newwin);
+
+ /* position the display and the cursor at the top of the file. */
+ newwin->w_topline = 1;
+ newwin->w_botline = 2;
+ newwin->w_cursor.lnum = 1;
+
+#ifdef USE_GUI
+ /* Let the GUI know this is a new scrollbar */
+ newwin->w_scrollbar.height = 0;
+#endif /* USE_GUI */
+ }
+ return newwin;
+}
+
+/*
+ * remove window 'wp' from the window list and free the structure
+ */
+ void
+win_free(wp)
+ WIN *wp;
+{
+ int i;
+
+ if (prevwin == wp)
+ prevwin = NULL;
+ win_free_lsize(wp);
+
+ for (i = 0; i < wp->w_tagstacklen; ++i)
+ free(wp->w_tagstack[i].tagname);
+
+#ifdef USE_GUI
+ if (gui.in_use)
+ gui_mch_destroy_scrollbar(wp);
+#endif /* USE_GUI */
+
+ win_remove(wp);
+ vim_free(wp);
+}
+
+ static void
+win_append(after, wp)
+ WIN *after, *wp;
+{
+ WIN *before;
+
+ if (after == NULL) /* after NULL is in front of the first */
+ 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 window from the window list
+ */
+ static void
+win_remove(wp)
+ WIN *wp;
+{
+ if (wp->w_prev)
+ wp->w_prev->w_next = wp->w_next;
+ else
+ firstwin = wp->w_next;
+ if (wp->w_next)
+ wp->w_next->w_prev = wp->w_prev;
+ else
+ lastwin = wp->w_prev;
+}
+
+/*
+ * allocate lsize arrays for a window
+ * return FAIL for failure, OK for success
+ */
+ int
+win_alloc_lsize(wp)
+ WIN *wp;
+{
+ wp->w_lsize_valid = 0;
+ wp->w_lsize_lnum = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t)));
+ wp->w_lsize = (char_u *)malloc((size_t) Rows);
+ if (wp->w_lsize_lnum == NULL || wp->w_lsize == NULL)
+ {
+ win_free_lsize(wp); /* one of the two may have worked */
+ wp->w_lsize_lnum = NULL;
+ wp->w_lsize = NULL;
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * free lsize arrays for a window
+ */
+ void
+win_free_lsize(wp)
+ WIN *wp;
+{
+ vim_free(wp->w_lsize_lnum);
+ vim_free(wp->w_lsize);
+}
+
+/*
+ * call this fuction whenever Rows changes value
+ */
+ void
+screen_new_rows()
+{
+ WIN *wp;
+ int extra_lines;
+
+ if (firstwin == NULL) /* not initialized yet */
+ return;
+/*
+ * the number of extra lines is the difference between the position where
+ * the command line should be and where it is now
+ */
+ compute_cmdrow();
+ extra_lines = Rows - p_ch - cmdline_row;
+ if (extra_lines < 0) /* reduce windows height */
+ {
+ for (wp = lastwin; wp; wp = wp->w_prev)
+ {
+ if (wp->w_height - MIN_ROWS < -extra_lines)
+ {
+ extra_lines += wp->w_height - MIN_ROWS;
+ win_new_height(wp, MIN_ROWS);
+ }
+ else
+ {
+ win_new_height(wp, wp->w_height + extra_lines);
+ break;
+ }
+ }
+ (void)win_comp_pos(); /* compute w_winpos */
+ }
+ else if (extra_lines > 0) /* increase height of last window */
+ win_new_height(lastwin, lastwin->w_height + extra_lines);
+
+ compute_cmdrow();
+
+ if (p_ea)
+ win_equal(curwin, FALSE);
+}
+
+/*
+ * update the w_winpos field for all windows
+ * returns the row just after the last window
+ */
+ static int
+win_comp_pos()
+{
+ WIN *wp;
+ int row;
+
+ row = 0;
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ {
+ if (wp->w_winpos != row) /* if position changes, redraw */
+ {
+ wp->w_winpos = row;
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+ }
+ row += wp->w_height + wp->w_status_height;
+ }
+ return row;
+}
+
+/*
+ * set current window height
+ */
+ void
+win_setheight(height)
+ int height;
+{
+ WIN *wp;
+ int room; /* total number of lines available */
+ int take; /* number of lines taken from other windows */
+ int room_cmdline; /* lines available from cmdline */
+ int row;
+ int run;
+
+ if (height < MIN_ROWS) /* need at least some lines */
+ height = MIN_ROWS;
+/*
+ * compute the room we have from all the windows
+ */
+ room = MIN_ROWS; /* count the MIN_ROWS for the current window */
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ room += wp->w_height - MIN_ROWS;
+/*
+ * compute the room available from the command line
+ */
+ room_cmdline = Rows - p_ch - cmdline_row;
+/*
+ * limit new height to the room available
+ */
+ if (height > room + room_cmdline) /* can't make it that large */
+ height = room + room_cmdline; /* use all available room */
+/*
+ * compute the number of lines we will take from the windows (can be negative)
+ */
+ take = height - curwin->w_height;
+ if (take == 0) /* no change, nothing to do */
+ return;
+
+ if (take > 0)
+ {
+ take -= room_cmdline; /* use lines from cmdline first */
+ if (take < 0)
+ take = 0;
+ }
+/*
+ * set the current window to the new height
+ */
+ win_new_height(curwin, height);
+
+/*
+ * First take lines from the windows below the current window.
+ * If that is not enough, takes lines from windows above the current window.
+ */
+ for (run = 0; run < 2; ++run)
+ {
+ if (run == 0)
+ wp = curwin->w_next; /* 1st run: start with next window */
+ else
+ wp = curwin->w_prev; /* 2nd run: start with prev window */
+ while (wp != NULL && take != 0)
+ {
+ if (wp->w_height - take < MIN_ROWS)
+ {
+ take -= wp->w_height - MIN_ROWS;
+ win_new_height(wp, MIN_ROWS);
+ }
+ else
+ {
+ win_new_height(wp, wp->w_height - take);
+ take = 0;
+ }
+ if (run == 0)
+ wp = wp->w_next;
+ else
+ wp = wp->w_prev;
+ }
+ }
+
+/* recompute the window positions */
+ row = win_comp_pos();
+
+/*
+ * If there is extra space created between the last window and the command line,
+ * clear it.
+ */
+ screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
+ cmdline_row = row;
+
+ updateScreen(NOT_VALID);
+}
+
+#ifdef USE_MOUSE
+ void
+win_drag_status_line(offset)
+ int offset;
+{
+ WIN *wp;
+ int room;
+ int row;
+ int up; /* if TRUE, drag status line up, otherwise down */
+
+ if (offset < 0)
+ {
+ up = TRUE;
+ offset = -offset;
+ }
+ else
+ up = FALSE;
+
+ if (up) /* drag up */
+ {
+ room = 0;
+ for (wp = curwin; wp != NULL && room < offset; wp = wp->w_prev)
+ room += wp->w_height - MIN_ROWS;
+ wp = curwin->w_next; /* put wp at window that grows */
+ }
+ else /* drag down */
+ {
+ /*
+ * Only dragging the last status line can reduce p_ch.
+ */
+ room = Rows - cmdline_row;
+ if (curwin->w_next == NULL)
+ room -= 1;
+ else
+ room -= p_ch;
+ for (wp = curwin->w_next; wp != NULL && room < offset; wp = wp->w_next)
+ room += wp->w_height - MIN_ROWS;
+ wp = curwin; /* put wp at window that grows */
+ }
+
+ if (room < offset) /* Not enough room */
+ offset = room; /* Move as far as we can */
+ if (offset <= 0)
+ return;
+
+ if (wp != NULL) /* grow window wp by offset lines */
+ win_new_height(wp, wp->w_height + offset);
+
+ if (up)
+ wp = curwin; /* current window gets smaller */
+ else
+ wp = curwin->w_next; /* next window gets smaller */
+
+ while (wp != NULL && offset > 0)
+ {
+ if (wp->w_height - offset < MIN_ROWS)
+ {
+ offset -= wp->w_height - MIN_ROWS;
+ win_new_height(wp, MIN_ROWS);
+ }
+ else
+ {
+ win_new_height(wp, wp->w_height - offset);
+ offset = 0;
+ }
+ if (up)
+ wp = wp->w_prev;
+ else
+ wp = wp->w_next;
+ }
+ row = win_comp_pos();
+ screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
+ cmdline_row = row;
+ p_ch = Rows - cmdline_row;
+ updateScreen(NOT_VALID);
+ showmode();
+}
+#endif /* USE_MOUSE */
+
+/*
+ * Set new window height.
+ */
+ static void
+win_new_height(wp, height)
+ WIN *wp;
+ int height;
+{
+ /* should adjust topline to keep cursor at same relative postition */
+
+ wp->w_height = height;
+ win_comp_scroll(wp);
+ wp->w_redr_type = NOT_VALID;
+ wp->w_redr_status = TRUE;
+}
+
+ void
+win_comp_scroll(wp)
+ WIN *wp;
+{
+ wp->w_p_scroll = (wp->w_height >> 1);
+ if (wp->w_p_scroll == 0)
+ wp->w_p_scroll = 1;
+}
+
+/*
+ * command_height: called whenever p_ch has been changed
+ */
+ void
+command_height()
+{
+ int current;
+
+ current = Rows - cmdline_row;
+ if (p_ch > current) /* p_ch got bigger */
+ {
+ if (lastwin->w_height - (p_ch - current) < MIN_ROWS)
+ {
+ emsg(e_noroom);
+ p_ch = lastwin->w_height - MIN_ROWS + current;
+ }
+ /* clear the lines added to cmdline */
+ screen_fill((int)(Rows - p_ch), (int)Rows, 0, (int)Columns, ' ', ' ');
+ }
+ win_new_height(lastwin, lastwin->w_height + current - (int)p_ch);
+ cmdline_row = Rows - p_ch;
+ redraw_cmdline = TRUE;
+}
+
+ void
+last_status()
+{
+ if (lastwin->w_status_height)
+ {
+ /* remove status line */
+ if (p_ls == 0 || (p_ls == 1 && firstwin == lastwin))
+ {
+ win_new_height(lastwin, lastwin->w_height + 1);
+ lastwin->w_status_height = 0;
+ }
+ }
+ else
+ {
+ /* add status line */
+ if (p_ls == 2 || (p_ls == 1 && firstwin != lastwin))
+ {
+ if (lastwin->w_height <= MIN_ROWS) /* can't do it */
+ emsg(e_noroom);
+ else
+ {
+ win_new_height(lastwin, lastwin->w_height - 1);
+ lastwin->w_status_height = 1;
+ }
+ }
+ }
+}
+
+/*
+ * file_name_at_cursor()
+ *
+ * Return the name of the file under (or to the right of) the cursor. The
+ * p_path variable is searched if the file name does not start with '/'.
+ * The string returned has been alloc'ed and should be freed by the caller.
+ * NULL is returned if the file name or file is not found.
+ */
+ char_u *
+file_name_at_cursor(options)
+ int options;
+{
+ return get_file_name_in_path(ml_get_curline(),
+ curwin->w_cursor.col, options);
+}
+/* options:
+ * FNAME_MESS give error messages
+ * FNAME_EXP expand to path
+ * FNAME_HYP check for hypertext link
+ */
+ char_u *
+get_file_name_in_path(ptr, col, options)
+ char_u *ptr;
+ int col;
+ int options;
+{
+ char_u *dir;
+ char_u *file_name;
+ char_u save_char;
+ char_u *curr_path = NULL;
+ int curr_path_len;
+ int len;
+
+ /* search forward for what could be the start of a file name */
+ while (ptr[col] != NUL && !isfilechar(ptr[col]))
+ ++col;
+ if (ptr[col] == NUL) /* nothing found */
+ {
+ if (options & FNAME_MESS)
+ EMSG("No file name under cursor");
+ return NULL;
+ }
+
+ /* search backward for char that cannot be in a file name */
+ while (col >= 0 && isfilechar(ptr[col]))
+ --col;
+ ptr += col + 1;
+ col = 0;
+
+ /* search forward for a char that cannot be in a file name */
+ while (isfilechar(ptr[col]))
+ ++col;
+
+ if (options & FNAME_HYP)
+ {
+ /* For hypertext links, ignore the name of the machine.
+ * Such a link looks like "type://machine/path". Only "/path" is used.
+ * First search for the string "://", then for the extra '/'
+ */
+ if ((file_name = vim_strchr(ptr, ':')) != NULL &&
+ STRNCMP(file_name, "://", (size_t)3) == 0 &&
+ (file_name = vim_strchr(file_name + 3, '/')) != NULL &&
+ file_name < ptr + col)
+ {
+ col -= file_name - ptr;
+ ptr = file_name;
+ if (ptr[1] == '~') /* skip '/' for /~user/path */
+ {
+ ++ptr;
+ --col;
+ }
+ }
+ }
+
+ if (!(options & FNAME_EXP))
+ return strnsave(ptr, col);
+
+ /* copy file name into NameBuff, expanding environment variables */
+ save_char = ptr[col];
+ ptr[col] = NUL;
+ expand_env(ptr, NameBuff, MAXPATHL);
+ ptr[col] = save_char;
+
+ if (isFullName(NameBuff)) /* absolute path */
+ {
+ if ((file_name = strsave(NameBuff)) == NULL)
+ return NULL;
+ if (getperm(file_name) >= 0)
+ return file_name;
+ if (options & FNAME_MESS)
+ {
+ sprintf((char *)IObuff, "Can't find file `%s'", NameBuff);
+ emsg(IObuff);
+ }
+ }
+ else /* relative path, use 'path' option */
+ {
+ if (curbuf->b_xfilename != NULL)
+ {
+ curr_path = curbuf->b_xfilename;
+ ptr = gettail(curr_path);
+ curr_path_len = ptr - curr_path;
+ }
+ else
+ curr_path_len = 0;
+ if ((file_name = alloc((int)(curr_path_len + STRLEN(p_path) +
+ STRLEN(NameBuff) + 3))) == NULL)
+ return NULL;
+
+ for (dir = p_path; *dir;)
+ {
+ len = copy_option_part(&dir, file_name, 31000, " ,");
+ /* len == 0 means: use current directory */
+ if (len != 0)
+ {
+ /* Look for file relative to current file */
+ if (file_name[0] == '.' && curr_path_len > 0)
+ {
+ if (len == 1) /* just a "." */
+ len = 0;
+ else /* "./path": move "path" */
+ {
+ len -= 2;
+ vim_memmove(file_name + curr_path_len, file_name + 2,
+ (size_t)len);
+ }
+ STRNCPY(file_name, curr_path, curr_path_len);
+ len += curr_path_len;
+ }
+ if (!ispathsep(file_name[len - 1]))
+ file_name[len++] = PATHSEP;
+ }
+ STRCPY(file_name + len, NameBuff);
+ if (getperm(file_name) >= 0)
+ return file_name;
+ }
+ if (options & FNAME_MESS)
+ EMSG2("Can't find file \"%s\" in path", NameBuff);
+ }
+ vim_free(file_name); /* file doesn't exist */
+ return NULL;
+}
+
+/*
+ * Return the minimal number of rows that is needed on the screen to display
+ * the current number of windows.
+ */
+ int
+min_rows()
+{
+ WIN *wp;
+ int total;
+
+ if (firstwin == NULL) /* not initialized yet */
+ return MIN_ROWS + 1; /* one window plus command line */
+
+ total = p_ch; /* count the room for the status line */
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ total += MIN_ROWS + wp->w_status_height;
+ return total;
+}
+
+/*
+ * Return TRUE if there is only one window, not counting a help window, unless
+ * it is the current window.
+ */
+ int
+only_one_window()
+{
+ int count = 0;
+ WIN *wp;
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (!wp->w_buffer->b_help || wp == curwin)
+ ++count;
+ return (count <= 1);
+}