/* $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); }