window.c
上传用户:gddssl
上传日期:2007-01-06
资源大小:1003k
文件大小:51k
源码类别:

编辑器/阅读器

开发平台:

DOS

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read a list of people who contributed.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8. #include "vim.h"
  9. #ifdef FILE_IN_PATH
  10. static char_u *find_file_in_path __ARGS((char_u *ptr, int len, int options, long count));
  11. static char_u *find_file_in_wildcard_path __ARGS((char_u *path_so_far, char_u *wildcards, int level, long *countptr));
  12. static int path_is_url __ARGS((char_u *p));
  13. #endif
  14. static void reset_VIsual __ARGS((void));
  15. static int win_comp_pos __ARGS((void));
  16. static void win_exchange __ARGS((long));
  17. static void win_rotate __ARGS((int, int));
  18. static void win_goto __ARGS((WIN *wp));
  19. static void win_append __ARGS((WIN *, WIN *));
  20. static void win_remove __ARGS((WIN *));
  21. static void win_new_height __ARGS((WIN *, int));
  22. static WIN *prevwin = NULL; /* previous window */
  23. #define URL_SLASH 1 /* path_is_url() has found "://" */
  24. #define URL_BACKSLASH 2 /* path_is_url() has found ":\" */
  25. /*
  26.  * all CTRL-W window commands are handled here, called from normal_cmd().
  27.  */
  28.     void
  29. do_window(nchar, Prenum)
  30.     int nchar;
  31.     long Prenum;
  32. {
  33.     long Prenum1;
  34.     WIN *wp;
  35.     int xchar;
  36. #if defined(FILE_IN_PATH) || defined(FIND_IN_PATH)
  37.     char_u *ptr;
  38. #endif
  39. #ifdef FIND_IN_PATH
  40.     int type = FIND_DEFINE;
  41.     int len;
  42. #endif
  43.     if (Prenum == 0)
  44. Prenum1 = 1;
  45.     else
  46. Prenum1 = Prenum;
  47.     switch (nchar)
  48.     {
  49. /* split current window in two parts */
  50.     case 'S':
  51.     case Ctrl('S'):
  52.     case 's': reset_VIsual(); /* stop Visual mode */
  53. #ifdef USE_GUI
  54. need_mouse_correct = TRUE;
  55. #endif
  56. win_split((int)Prenum, TRUE, FALSE);
  57. break;
  58. /* split current window and edit alternate file */
  59.     case K_CCIRCM:
  60.     case '^':
  61. reset_VIsual(); /* stop Visual mode */
  62. stuffReadbuff((char_u *)":split #");
  63. if (Prenum)
  64.     stuffnumReadbuff(Prenum); /* buffer number */
  65. stuffcharReadbuff('n');
  66. break;
  67. /* open new window */
  68.     case Ctrl('N'):
  69.     case 'n': reset_VIsual(); /* stop Visual mode */
  70. stuffcharReadbuff(':');
  71. if (Prenum)
  72.     stuffnumReadbuff(Prenum);     /* window height */
  73. stuffReadbuff((char_u *)"newn");   /* it is ex_docmd.c */
  74. break;
  75. /* quit current window */
  76.     case Ctrl('Q'):
  77.     case 'q': reset_VIsual(); /* stop Visual mode */
  78. stuffReadbuff((char_u *)":quitn"); /* it is ex_docmd.c */
  79. break;
  80. /* close current window */
  81.     case Ctrl('C'):
  82.     case 'c': reset_VIsual(); /* stop Visual mode */
  83. stuffReadbuff((char_u *)":closen"); /* it is ex_docmd.c */
  84. break;
  85. /* close all but current window */
  86.     case Ctrl('O'):
  87.     case 'o': reset_VIsual(); /* stop Visual mode */
  88. stuffReadbuff((char_u *)":onlyn"); /* it is ex_docmd.c */
  89. break;
  90. /* cursor to next window */
  91.     case 'j':
  92.     case K_DOWN:
  93.     case Ctrl('J'):
  94. for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0;
  95.     wp = wp->w_next)
  96.     ;
  97. win_goto(wp);
  98. break;
  99. /* cursor to next window with wrap around */
  100.     case Ctrl('W'):
  101.     case 'w':
  102. /* cursor to previous window with wrap around */
  103.     case 'W':
  104. if (lastwin == firstwin) /* just one window */
  105.     beep_flush();
  106. else
  107. {
  108.     if (Prenum) /* go to specified window */
  109.     {
  110. for (wp = firstwin; --Prenum > 0; )
  111. {
  112.     if (wp->w_next == NULL)
  113. break;
  114.     else
  115. wp = wp->w_next;
  116. }
  117.     }
  118.     else
  119.     {
  120. if (nchar == 'W')     /* go to previous window */
  121. {
  122.     wp = curwin->w_prev;
  123.     if (wp == NULL)
  124. wp = lastwin;     /* wrap around */
  125. }
  126. else     /* go to next window */
  127. {
  128.     wp = curwin->w_next;
  129.     if (wp == NULL)
  130. wp = firstwin;     /* wrap around */
  131. }
  132.     }
  133.     win_goto(wp);
  134. }
  135. break;
  136. /* cursor to window above */
  137.     case 'k':
  138.     case K_UP:
  139.     case Ctrl('K'):
  140. for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0;
  141.     wp = wp->w_prev)
  142.     ;
  143. win_goto(wp);
  144. break;
  145. /* cursor to top window */
  146.     case 't':
  147.     case Ctrl('T'):
  148. wp = firstwin;
  149. win_goto(wp);
  150. break;
  151. /* cursor to bottom window */
  152.     case 'b':
  153.     case Ctrl('B'):
  154. wp = lastwin;
  155. win_goto(wp);
  156. break;
  157. /* cursor to last accessed (previous) window */
  158.     case 'p':
  159.     case Ctrl('P'):
  160. if (prevwin == NULL)
  161.     beep_flush();
  162. else
  163. {
  164.     wp = prevwin;
  165.     win_goto(wp);
  166. }
  167. break;
  168. /* exchange current and next window */
  169.     case 'x':
  170.     case Ctrl('X'):
  171. win_exchange(Prenum);
  172. break;
  173. /* rotate windows downwards */
  174.     case Ctrl('R'):
  175.     case 'r': reset_VIsual(); /* stop Visual mode */
  176. win_rotate(FALSE, (int)Prenum1);    /* downwards */
  177. break;
  178. /* rotate windows upwards */
  179.     case 'R': reset_VIsual(); /* stop Visual mode */
  180. win_rotate(TRUE, (int)Prenum1);     /* upwards */
  181. break;
  182. /* make all windows the same height */
  183.     case '=':
  184. #ifdef USE_GUI
  185. need_mouse_correct = TRUE;
  186. #endif
  187. win_equal(NULL, TRUE);
  188. break;
  189. /* increase current window height */
  190.     case '+':
  191. #ifdef USE_GUI
  192. need_mouse_correct = TRUE;
  193. #endif
  194. win_setheight(curwin->w_height + (int)Prenum1);
  195. break;
  196. /* decrease current window height */
  197.     case '-':
  198. #ifdef USE_GUI
  199. need_mouse_correct = TRUE;
  200. #endif
  201. win_setheight(curwin->w_height - (int)Prenum1);
  202. break;
  203. /* set current window height */
  204.     case Ctrl('_'):
  205.     case '_':
  206. #ifdef USE_GUI
  207. need_mouse_correct = TRUE;
  208. #endif
  209. win_setheight(Prenum ? (int)Prenum : 9999);
  210. break;
  211. /* jump to tag and split window if tag exists */
  212.     case ']':
  213.     case Ctrl(']'):
  214. reset_VIsual(); /* stop Visual mode */
  215. if (Prenum)
  216.     postponed_split = Prenum;
  217. else
  218.     postponed_split = -1;
  219. stuffcharReadbuff(Ctrl(']'));
  220. break;
  221. #ifdef FILE_IN_PATH
  222. /* edit file name under cursor in a new window */
  223.     case 'f':
  224.     case Ctrl('F'):
  225. reset_VIsual(); /* stop Visual mode */
  226. ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP,
  227.      Prenum1);
  228. if (ptr != NULL)
  229. {
  230. #ifdef USE_GUI
  231.     need_mouse_correct = TRUE;
  232. #endif
  233.     setpcmark();
  234.     if (win_split(0, FALSE, FALSE) == OK)
  235. (void)do_ecmd(0, ptr, NULL, NULL, (linenr_t)0,
  236.    ECMD_HIDE);
  237.     vim_free(ptr);
  238. }
  239. break;
  240. #endif
  241. #ifdef FIND_IN_PATH
  242. /* Go to the first occurence of the identifier under cursor along path in a
  243.  * new window -- webb
  244.  */
  245.     case 'i':     /* Go to any match */
  246.     case Ctrl('I'):
  247. type = FIND_ANY;
  248. /* FALLTHROUGH */
  249.     case 'd':     /* Go to definition, using p_def */
  250.     case Ctrl('D'):
  251. if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
  252.     break;
  253. find_pattern_in_path(ptr, 0, len, TRUE,
  254. Prenum == 0 ? TRUE : FALSE, type,
  255. Prenum1, ACTION_SPLIT, (linenr_t)1, (linenr_t)MAXLNUM);
  256. curwin->w_set_curswant = TRUE;
  257. break;
  258. #endif
  259. /* CTRL-W g  extended commands */
  260.     case 'g':
  261. #ifdef USE_GUI_WIN32
  262. dont_scroll = TRUE; /* disallow scrolling here */
  263. #endif
  264. ++no_mapping;
  265. ++allow_keys;   /* no mapping for xchar, but allow key codes */
  266. xchar = vgetc();
  267. #ifdef HAVE_LANGMAP
  268. LANGMAP_ADJUST(xchar, TRUE);
  269. #endif
  270. --no_mapping;
  271. --allow_keys;
  272. #ifdef SHOWCMD
  273. (void)add_to_showcmd(xchar);
  274. #endif
  275. switch (xchar)
  276. {
  277.     case ']':
  278.     case Ctrl(']'):
  279. reset_VIsual(); /* stop Visual mode */
  280. if (Prenum)
  281.     postponed_split = Prenum;
  282. else
  283.     postponed_split = -1;
  284. stuffcharReadbuff('g');
  285. stuffcharReadbuff(xchar);
  286. break;
  287.     default:
  288. beep_flush();
  289. break;
  290. }
  291. break;
  292.     default: beep_flush();
  293. break;
  294.     }
  295. }
  296.     static void
  297. reset_VIsual()
  298. {
  299.     if (VIsual_active)
  300.     {
  301. end_visual_mode();
  302. update_curbuf(NOT_VALID); /* delete the inversion */
  303.     }
  304.     VIsual_reselect = FALSE;
  305. }
  306. /*
  307.  * split the current window, implements CTRL-W s and :split
  308.  *
  309.  * new_height is the height for the new window, 0 to make half of current
  310.  * height
  311.  * redraw is TRUE when redraw now
  312.  *
  313.  * return FAIL for failure, OK otherwise
  314.  */
  315.     int
  316. win_split(new_height, redraw, req_room)
  317.     int     new_height;
  318.     int     redraw;
  319.     int     req_room;     /* require enough room for new window */
  320. {
  321.     WIN *wp;
  322.     int i;
  323.     int need_status;
  324.     int do_equal = (p_ea && new_height == 0);
  325.     int needed;
  326.     int available;
  327.     int curwin_height;
  328.     /* add a status line when p_ls == 1 and splitting the first window */
  329.     if (lastwin == firstwin && p_ls == 1 && curwin->w_status_height == 0)
  330. need_status = STATUS_HEIGHT;
  331.     else
  332. need_status = 0;
  333. /*
  334.  * check if we are able to split the current window and compute its height
  335.  */
  336.     available = curwin->w_height;
  337.     needed = 2 * p_wmh + STATUS_HEIGHT + need_status;
  338.     if (req_room)
  339. needed += p_wh - p_wmh;
  340.     if (p_ea)
  341.     {
  342. for (wp = firstwin; wp != NULL; wp = wp->w_next)
  343.     if (wp != curwin)
  344.     {
  345. available += wp->w_height;
  346. needed += p_wmh;
  347.     }
  348.     }
  349.     if (available < needed)
  350.     {
  351. EMSG(e_noroom);
  352. return FAIL;
  353.     }
  354.     curwin_height = curwin->w_height;
  355.     if (need_status)
  356.     {
  357. curwin->w_status_height = STATUS_HEIGHT;
  358. curwin_height -= STATUS_HEIGHT;
  359.     }
  360.     if (new_height == 0)
  361. new_height = curwin_height / 2;
  362.     if (new_height > curwin_height - p_wmh - STATUS_HEIGHT)
  363. new_height = curwin_height - p_wmh - STATUS_HEIGHT;
  364.     if (new_height < p_wmh)
  365. new_height = p_wmh;
  366.     /* if it doesn't fit in the current window, need win_equal() */
  367.     if (curwin_height - new_height - STATUS_HEIGHT < p_wmh)
  368. do_equal = TRUE;
  369. /*
  370.  * allocate new window structure and link it in the window list
  371.  */
  372.     if (p_sb)     /* new window below current one */
  373. wp = win_alloc(curwin);
  374.     else
  375. wp = win_alloc(curwin->w_prev);
  376.     if (wp == NULL)
  377. return FAIL;
  378. /*
  379.  * compute the new screen positions
  380.  */
  381.     win_new_height(wp, new_height);
  382.     win_new_height(curwin, curwin_height - (new_height + STATUS_HEIGHT));
  383.     if (p_sb)     /* new window below current one */
  384.     {
  385. wp->w_winpos = curwin->w_winpos + curwin->w_height + STATUS_HEIGHT;
  386. wp->w_status_height = curwin->w_status_height;
  387. curwin->w_status_height = STATUS_HEIGHT;
  388.     }
  389.     else     /* new window above current one */
  390.     {
  391. wp->w_winpos = curwin->w_winpos;
  392. wp->w_status_height = STATUS_HEIGHT;
  393. curwin->w_winpos = wp->w_winpos + wp->w_height + STATUS_HEIGHT;
  394.     }
  395. /*
  396.  * make the contents of the new window the same as the current one
  397.  */
  398.     wp->w_buffer = curbuf;
  399.     curbuf->b_nwindows++;
  400.     wp->w_cursor = curwin->w_cursor;
  401.     wp->w_valid = 0;
  402.     wp->w_curswant = curwin->w_curswant;
  403.     wp->w_set_curswant = curwin->w_set_curswant;
  404.     wp->w_topline = curwin->w_topline;
  405.     wp->w_leftcol = curwin->w_leftcol;
  406.     wp->w_pcmark = curwin->w_pcmark;
  407.     wp->w_prev_pcmark = curwin->w_prev_pcmark;
  408.     wp->w_alt_fnum = curwin->w_alt_fnum;
  409.     wp->w_fraction = curwin->w_fraction;
  410.     wp->w_prev_fraction_row = curwin->w_prev_fraction_row;
  411.     wp->w_arg_idx = curwin->w_arg_idx;
  412.     /*
  413.      * copy tagstack and options from existing window
  414.      */
  415.     for (i = 0; i < curwin->w_tagstacklen; i++)
  416.     {
  417. wp->w_tagstack[i].fmark = curwin->w_tagstack[i].fmark;
  418. wp->w_tagstack[i].tagname = vim_strsave(curwin->w_tagstack[i].tagname);
  419.     }
  420.     wp->w_tagstackidx = curwin->w_tagstackidx;
  421.     wp->w_tagstacklen = curwin->w_tagstacklen;
  422.     win_copy_options(curwin, wp);
  423. /*
  424.  * Both windows need redrawing
  425.  */
  426.     wp->w_redr_type = NOT_VALID;
  427.     wp->w_redr_status = TRUE;
  428.     curwin->w_redr_type = NOT_VALID;
  429.     curwin->w_redr_status = TRUE;
  430.     if (need_status)
  431.     {
  432. msg_row = Rows - 1;
  433. msg_col = sc_col;
  434. msg_clr_eos(); /* Old command/ruler may still be there -- webb */
  435. comp_col();
  436. msg_row = Rows - 1;
  437. msg_col = 0; /* put position back at start of line */
  438.     }
  439. /*
  440.  * make the new window the current window and redraw
  441.  */
  442.     if (do_equal)
  443. win_equal(wp, FALSE);
  444.     win_enter(wp, FALSE);
  445.     if (redraw)
  446. update_screen(NOT_VALID);
  447.     else
  448. redraw_later(NOT_VALID);
  449.     return OK;
  450. }
  451. /*
  452.  * Check if "win" is a pointer to an existing window.
  453.  */
  454.     int
  455. win_valid(win)
  456.     WIN     *win;
  457. {
  458.     WIN     *wp;
  459.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  460. if (wp == win)
  461.     return TRUE;
  462.     return FALSE;
  463. }
  464. /*
  465.  * Return the number of windows.
  466.  */
  467.     int
  468. win_count()
  469. {
  470.     WIN     *wp;
  471.     int     count = 0;
  472.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  473. ++count;
  474.     return count;
  475. }
  476. /*
  477.  * Make 'count' windows on the screen.
  478.  * Return actual number of windows on the screen.
  479.  * Must be called when there is just one window, filling the whole screen
  480.  * (excluding the command line).
  481.  */
  482.     int
  483. make_windows(count)
  484.     int     count;
  485. {
  486.     int     maxcount;
  487.     int     todo;
  488.     int     p_sb_save;
  489. /*
  490.  * Each window needs at least 'winminheight' lines and a status line.
  491.  * Add 4 lines for one window, otherwise we may end up with all zero-line
  492.  * windows. Use value of 'winheight' if it is set
  493.  */
  494.     maxcount = (curwin->w_height + curwin->w_status_height
  495.   - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
  496.     if (maxcount < 2)
  497. maxcount = 2;
  498.     if (count > maxcount)
  499. count = maxcount;
  500.     /*
  501.      * add status line now, otherwise first window will be too big
  502.      */
  503.     if ((p_ls == 2 || (count > 1 && p_ls == 1)) && curwin->w_status_height == 0)
  504.     {
  505. curwin->w_status_height = STATUS_HEIGHT;
  506. win_new_height(curwin, curwin->w_height - STATUS_HEIGHT);
  507.     }
  508. #ifdef AUTOCMD
  509. /*
  510.  * Don't execute autocommands while creating the windows.  Must do that
  511.  * when putting the buffers in the windows.
  512.  */
  513.     ++autocmd_busy;
  514. #endif
  515. /*
  516.  * set 'splitbelow' off for a moment, don't want that now
  517.  */
  518.     p_sb_save = p_sb;
  519.     p_sb = FALSE;
  520. /* todo is number of windows left to create */
  521.     for (todo = count - 1; todo > 0; --todo)
  522. if (win_split(curwin->w_height - (curwin->w_height - todo
  523. * STATUS_HEIGHT) / (todo + 1) - STATUS_HEIGHT,
  524. FALSE, FALSE) == FAIL)
  525.     break;
  526.     p_sb = p_sb_save;
  527. #ifdef AUTOCMD
  528.     --autocmd_busy;
  529. #endif
  530. /* return actual number of windows */
  531.     return (count - todo);
  532. }
  533. /*
  534.  * Exchange current and next window
  535.  */
  536.     static void
  537. win_exchange(Prenum)
  538.     long Prenum;
  539. {
  540.     WIN     *wp;
  541.     WIN     *wp2;
  542.     int     temp;
  543.     if (lastwin == firstwin)     /* just one window */
  544.     {
  545. beep_flush();
  546. return;
  547.     }
  548. #ifdef USE_GUI
  549.     need_mouse_correct = TRUE;
  550. #endif
  551. /*
  552.  * find window to exchange with
  553.  */
  554.     if (Prenum)
  555.     {
  556. wp = firstwin;
  557. while (wp != NULL && --Prenum > 0)
  558.     wp = wp->w_next;
  559.     }
  560.     else if (curwin->w_next != NULL) /* Swap with next */
  561. wp = curwin->w_next;
  562.     else    /* Swap last window with previous */
  563. wp = curwin->w_prev;
  564.     if (wp == curwin || wp == NULL)
  565. return;
  566. /*
  567.  * 1. remove curwin from the list. Remember after which window it was in wp2
  568.  * 2. insert curwin before wp in the list
  569.  * if wp != wp2
  570.  *    3. remove wp from the list
  571.  *    4. insert wp after wp2
  572.  * 5. exchange the status line height
  573.  */
  574.     wp2 = curwin->w_prev;
  575.     win_remove(curwin);
  576.     win_append(wp->w_prev, curwin);
  577.     if (wp != wp2)
  578.     {
  579. win_remove(wp);
  580. win_append(wp2, wp);
  581.     }
  582.     temp = curwin->w_status_height;
  583.     curwin->w_status_height = wp->w_status_height;
  584.     wp->w_status_height = temp;
  585.     win_comp_pos(); /* recompute window positions */
  586.     win_enter(wp, TRUE);
  587.     update_screen(CLEAR);
  588. }
  589. /*
  590.  * rotate windows: if upwards TRUE the second window becomes the first one
  591.  *    if upwards FALSE the first window becomes the second one
  592.  */
  593.     static void
  594. win_rotate(upwards, count)
  595.     int     upwards;
  596.     int     count;
  597. {
  598.     WIN  *wp;
  599.     int  height;
  600.     if (firstwin == lastwin) /* nothing to do */
  601.     {
  602. beep_flush();
  603. return;
  604.     }
  605. #ifdef USE_GUI
  606.     need_mouse_correct = TRUE;
  607. #endif
  608.     while (count--)
  609.     {
  610. if (upwards) /* first window becomes last window */
  611. {
  612.     wp = firstwin;
  613.     win_remove(wp);
  614.     win_append(lastwin, wp);
  615.     wp = lastwin->w_prev;     /* previously last window */
  616. }
  617. else /* last window becomes first window */
  618. {
  619.     wp = lastwin;
  620.     win_remove(lastwin);
  621.     win_append(NULL, wp);
  622.     wp = firstwin;     /* previously last window */
  623. }
  624.     /* exchange status height of old and new last window */
  625. height = lastwin->w_status_height;
  626. lastwin->w_status_height = wp->w_status_height;
  627. wp->w_status_height = height;
  628.     /* recompute w_winpos for all windows */
  629. (void)win_comp_pos();
  630.     }
  631.     update_screen(CLEAR);
  632. }
  633. /*
  634.  * Move window "win1" to below "win2" and make "win1" the current window.
  635.  */
  636.     void
  637. win_move_after(win1, win2)
  638.     WIN *win1, *win2;
  639. {
  640.     int     height;
  641.     /* check if the arguments are reasonable */
  642.     if (win1 == win2)
  643. return;
  644.     /* check if there is something to do */
  645.     if (win2->w_next != win1)
  646.     {
  647. /* may need move the status line of the last window */
  648. if (win1 == lastwin)
  649. {
  650.     height = win1->w_prev->w_status_height;
  651.     win1->w_prev->w_status_height = win1->w_status_height;
  652.     win1->w_status_height = height;
  653. }
  654. else if (win2 == lastwin)
  655. {
  656.     height = win1->w_status_height;
  657.     win1->w_status_height = win2->w_status_height;
  658.     win2->w_status_height = height;
  659. }
  660. win_remove(win1);
  661. win_append(win2, win1);
  662. (void)win_comp_pos(); /* recompute w_winpos for all windows */
  663. redraw_later(NOT_VALID);
  664.     }
  665.     win_enter(win1, FALSE);
  666. }
  667. /*
  668.  * Make all windows the same height.
  669.  * 'next_curwin' will soon be the current window, make sure it has enough
  670.  * rows.
  671.  */
  672.     void
  673. win_equal(next_curwin, redraw)
  674.     WIN     *next_curwin;     /* pointer to current window to be */
  675.     int     redraw;
  676. {
  677.     int     total;
  678.     int     less;
  679.     int     wincount;
  680.     int     winpos;
  681.     int     temp;
  682.     WIN     *wp;
  683.     int     new_height;
  684. /*
  685.  * count the number of lines available
  686.  */
  687.     total = 0;
  688.     wincount = 0;
  689.     for (wp = firstwin; wp; wp = wp->w_next)
  690.     {
  691. total += wp->w_height - p_wmh;
  692. wincount++;
  693.     }
  694. /*
  695.  * If next_curwin given and 'winheight' set, make next_curwin p_wh lines.
  696.  */
  697.     less = 0;
  698.     if (next_curwin != NULL)
  699.     {
  700. if (p_wh - p_wmh > total)    /* all lines go to current window */
  701.     less = total;
  702. else
  703. {
  704.     less = p_wh - p_wmh - total / wincount;
  705.     if (less < 0)
  706. less = 0;
  707. }
  708.     }
  709. /*
  710.  * spread the available lines over the windows
  711.  */
  712.     winpos = 0;
  713.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  714.     {
  715. if (wp == next_curwin && less)
  716. {
  717.     less = 0;
  718.     temp = p_wh - p_wmh;
  719.     if (temp > total)
  720. temp = total;
  721. }
  722. else
  723.     temp = (total - less + ((unsigned)wincount >> 1)) / wincount;
  724. new_height = p_wmh + temp;
  725. if (wp->w_winpos != winpos || wp->w_height != new_height)
  726. {
  727.     wp->w_redr_type = NOT_VALID;
  728.     wp->w_redr_status = TRUE;
  729. }
  730. wp->w_winpos = winpos;
  731. win_new_height(wp, new_height);
  732. total -= temp;
  733. --wincount;
  734. winpos += wp->w_height + wp->w_status_height;
  735.     }
  736.     if (redraw)
  737. must_redraw = CLEAR;
  738. }
  739. /*
  740.  * close all windows for buffer 'buf'
  741.  */
  742.     void
  743. close_windows(buf)
  744.     BUF     *buf;
  745. {
  746.     WIN     *win;
  747.     ++RedrawingDisabled;
  748.     for (win = firstwin; win != NULL && lastwin != firstwin; )
  749.     {
  750. if (win->w_buffer == buf)
  751. {
  752.     close_window(win, FALSE);
  753.     win = firstwin;     /* go back to the start */
  754. }
  755. else
  756.     win = win->w_next;
  757.     }
  758.     --RedrawingDisabled;
  759. }
  760. /*
  761.  * close window "win"
  762.  * If "free_buf" is TRUE related buffer may be freed.
  763.  *
  764.  * called by :quit, :close, :xit, :wq and findtag()
  765.  */
  766.     void
  767. close_window(win, free_buf)
  768.     WIN     *win;
  769.     int     free_buf;
  770. {
  771.     WIN     *wp;
  772. #ifdef AUTOCMD
  773.     int     other_buffer = FALSE;
  774. #endif
  775.     if (lastwin == firstwin)
  776.     {
  777. EMSG("Cannot close last window");
  778. return;
  779.     }
  780. #ifdef AUTOCMD
  781.     if (win == curwin)
  782.     {
  783. /*
  784.  * Guess which window is going to be the new current window.
  785.  * This may change because of the autocommands (sigh).
  786.  */
  787. if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
  788.     wp = win->w_next;
  789. else
  790.     wp = win->w_prev;
  791. /*
  792.  * Be careful: If autocommands delete the window, return now.
  793.  */
  794. if (wp->w_buffer != curbuf)
  795. {
  796.     other_buffer = TRUE;
  797.     apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
  798.     if (!win_valid(win))
  799. return;
  800. }
  801. apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
  802. if (!win_valid(win))
  803.     return;
  804.     }
  805. #endif
  806. /*
  807.  * Remove the window.
  808.  * if 'splitbelow' the free space goes to the window above it.
  809.  * if 'nosplitbelow' the free space goes to the window below it.
  810.  * This makes opening a window and closing it immediately keep the same window
  811.  * layout.
  812.  */
  813.     /* freed space goes to next window */
  814.     if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
  815.     {
  816. wp = win->w_next;
  817. wp->w_winpos = win->w_winpos;
  818.     }
  819.     else     /* freed space goes to previous window */
  820. wp = win->w_prev;
  821.     win_new_height(wp, wp->w_height + win->w_height + win->w_status_height);
  822. /*
  823.  * Close the link to the buffer.
  824.  */
  825.     close_buffer(win, win->w_buffer, free_buf, FALSE);
  826.     win_free(win);
  827.     if (win == curwin)
  828. curwin = NULL;
  829.     if (p_ea)
  830. win_equal(wp, FALSE);
  831.     if (curwin == NULL)
  832.     {
  833. win_enter(wp, FALSE);
  834. #ifdef AUTOCMD
  835. if (other_buffer)
  836.     /* careful: after this wp and win may be invalid! */
  837.     apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
  838. #endif
  839.     }
  840.     /*
  841.      * if last window has status line now and we don't want one,
  842.      * remove the status line
  843.      */
  844.     if (lastwin->w_status_height &&
  845. (p_ls == 0 || (p_ls == 1 && firstwin == lastwin)))
  846.     {
  847. win_new_height(lastwin, lastwin->w_height + lastwin->w_status_height);
  848. lastwin->w_status_height = 0;
  849. comp_col();
  850.     }
  851.     update_screen(NOT_VALID);
  852. }
  853. /*
  854.  * Close all windows except current one.
  855.  * Buffers in the other windows become hidden if 'hidden' is set, or '!' is
  856.  * used and the buffer was modified.
  857.  *
  858.  * Used by ":bdel" and ":only".
  859.  */
  860.     void
  861. close_others(message, forceit)
  862.     int     message;
  863.     int     forceit;     /* always hide all other windows */
  864. {
  865.     WIN     *wp;
  866.     WIN     *nextwp;
  867.     if (lastwin == firstwin)
  868.     {
  869. if (message
  870. #ifdef AUTOCMD
  871.     && !autocmd_busy
  872. #endif
  873.     )
  874.     MSG("Already only one window");
  875. return;
  876.     }
  877.     for (wp = firstwin; wp != NULL; wp = nextwp)
  878.     {
  879. nextwp = wp->w_next;
  880. if (wp == curwin) /* don't close current window */
  881.     continue;
  882. /* Check if it's allowed to abandon this window */
  883. if (!can_abandon(wp->w_buffer, forceit))
  884.     continue;
  885. /* Close the link to the buffer. */
  886. close_buffer(wp, wp->w_buffer,
  887.  !p_hid && !buf_changed(wp->w_buffer), FALSE);
  888. /* Remove the window.  All lines go to previous or next window. */
  889. if (wp->w_prev != NULL)
  890.     win_new_height(wp->w_prev,
  891.    wp->w_prev->w_height + wp->w_height + wp->w_status_height);
  892. else
  893. {
  894.     win_new_height(wp->w_next,
  895.    wp->w_next->w_height + wp->w_height + wp->w_status_height);
  896.     wp->w_next->w_winpos = wp->w_winpos;
  897. }
  898. win_free(wp);
  899.     }
  900.     /*
  901.      * If current window has a status line and we don't want one,
  902.      * remove the status line.
  903.      */
  904.     if (lastwin != firstwin)
  905. EMSG("Other window contains changes");
  906.     else if (curwin->w_status_height && p_ls != 2)
  907.     {
  908. win_new_height(curwin, curwin->w_height + curwin->w_status_height);
  909. curwin->w_status_height = 0;
  910. comp_col();
  911.     }
  912.     if (message)
  913. update_screen(NOT_VALID);
  914. }
  915. /*
  916.  * init the cursor in the window
  917.  *
  918.  * called when a new file is being edited
  919.  */
  920.     void
  921. win_init(wp)
  922.     WIN     *wp;
  923. {
  924.     wp->w_redr_type = NOT_VALID;
  925.     wp->w_cursor.lnum = 1;
  926.     wp->w_curswant = wp->w_cursor.col = 0;
  927.     wp->w_pcmark.lnum = 1; /* pcmark not cleared but set to line 1 */
  928.     wp->w_pcmark.col = 0;
  929.     wp->w_prev_pcmark.lnum = 0;
  930.     wp->w_prev_pcmark.col = 0;
  931.     wp->w_topline = 1;
  932.     wp->w_botline = 2;
  933. #ifdef FKMAP
  934.     if (curwin->w_p_rl)
  935. wp->w_p_pers = W_CONV + W_R_L;
  936.     else
  937. wp->w_p_pers = W_CONV;
  938. #endif
  939. }
  940. /*
  941.  * Go to another window.
  942.  * When jumping to another buffer, stop visual mode.  Do this before
  943.  * changing windows so we can yank the selection into the '*' register.
  944.  */
  945.     static void
  946. win_goto(wp)
  947.     WIN     *wp;
  948. {
  949.     if (wp->w_buffer != curbuf && VIsual_active)
  950.     {
  951. end_visual_mode();
  952. redraw_curbuf_later(NOT_VALID);
  953.     }
  954.     VIsual_reselect = FALSE;
  955. #ifdef USE_GUI
  956.     need_mouse_correct = TRUE;
  957. #endif
  958.     win_enter(wp, TRUE);
  959. }
  960. /*
  961.  * Go to window nr "winnr" (counting top to bottom).
  962.  */
  963.     WIN *
  964. win_goto_nr(winnr)
  965.     int     winnr;
  966. {
  967.     WIN     *wp;
  968.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  969. if (--winnr == 0)
  970.     break;
  971.     return wp;
  972. }
  973. /*
  974.  * Make window wp the current window.
  975.  * Can be called when curwin == NULL, if curwin already has been closed.
  976.  */
  977.     void
  978. win_enter(wp, undo_sync)
  979.     WIN     *wp;
  980.     int     undo_sync;
  981. {
  982. #ifdef AUTOCMD
  983.     int other_buffer = FALSE;
  984. #endif
  985.     if (wp == curwin) /* nothing to do */
  986. return;
  987. #ifdef AUTOCMD
  988.     if (curwin != NULL)
  989.     {
  990. /*
  991.  * Be careful: If autocommands delete the window, return now.
  992.  */
  993. if (wp->w_buffer != curbuf)
  994. {
  995.     apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
  996.     other_buffer = TRUE;
  997.     if (!win_valid(wp))
  998. return;
  999. }
  1000. apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
  1001. if (!win_valid(wp))
  1002.     return;
  1003.     }
  1004. #endif
  1005. /* sync undo before leaving the current buffer */
  1006.     if (undo_sync && curbuf != wp->w_buffer)
  1007. u_sync();
  1008. /* may have to copy the buffer options when 'cpo' contains 'S' */
  1009.     if (wp->w_buffer != curbuf)
  1010. buf_copy_options(curbuf, wp->w_buffer, BCO_ENTER | BCO_NOHELP);
  1011.     if (curwin != NULL)
  1012.     {
  1013. prevwin = curwin; /* remember for CTRL-W p */
  1014. curwin->w_redr_status = TRUE;
  1015.     }
  1016.     curwin = wp;
  1017.     curbuf = wp->w_buffer;
  1018.     adjust_cursor();
  1019.     changed_line_abv_curs(); /* assume cursor position needs updating */
  1020. #ifdef AUTOCMD
  1021.     apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
  1022.     if (other_buffer)
  1023. apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
  1024. #endif
  1025.     maketitle();
  1026.     curwin->w_redr_status = TRUE;
  1027.     /* set window height to desired minimal value */
  1028.     if (curwin->w_height < p_wh)
  1029. win_setheight((int)p_wh);
  1030. #ifdef USE_MOUSE
  1031.     setmouse(); /* in case jumped to/from help buffer */
  1032. #endif
  1033. }
  1034. /*
  1035.  * allocate a window structure and link it in the window list
  1036.  */
  1037.     WIN *
  1038. win_alloc(after)
  1039.     WIN     *after;
  1040. {
  1041.     WIN     *newwin;
  1042.     /*
  1043.      * allocate window structure and linesizes arrays
  1044.      */
  1045.     newwin = (WIN *)alloc_clear((unsigned)sizeof(WIN));
  1046.     if (newwin != NULL && win_alloc_lsize(newwin) == FAIL)
  1047.     {
  1048. vim_free(newwin);
  1049. newwin = NULL;
  1050.     }
  1051.     if (newwin != NULL)
  1052.     {
  1053. /*
  1054.  * link the window in the window list
  1055.  */
  1056. win_append(after, newwin);
  1057. /* position the display and the cursor at the top of the file. */
  1058. newwin->w_topline = 1;
  1059. newwin->w_botline = 2;
  1060. newwin->w_cursor.lnum = 1;
  1061. /* We won't calculate w_fraction until resizing the window */
  1062. newwin->w_fraction = 0;
  1063. newwin->w_prev_fraction_row = -1;
  1064. #ifdef USE_GUI
  1065. if (gui.in_use)
  1066. {
  1067.     gui_create_scrollbar(&newwin->w_scrollbars[SBAR_LEFT], newwin);
  1068.     gui_create_scrollbar(&newwin->w_scrollbars[SBAR_RIGHT], newwin);
  1069. }
  1070. #endif
  1071. #ifdef WANT_EVAL
  1072. var_init(&newwin->w_vars);     /* init internal variables */
  1073. #endif
  1074.     }
  1075.     return newwin;
  1076. }
  1077. /*
  1078.  * remove window 'wp' from the window list and free the structure
  1079.  */
  1080.     void
  1081. win_free(wp)
  1082.     WIN     *wp;
  1083. {
  1084.     int     i;
  1085. #ifdef HAVE_PERL_INTERP
  1086.     perl_win_free(wp);
  1087. #endif
  1088. #ifdef HAVE_PYTHON
  1089.     python_window_free(wp);
  1090. #endif
  1091. #ifdef HAVE_TCL
  1092.     tcl_window_free(wp);
  1093. #endif
  1094. #ifdef WANT_EVAL
  1095.     var_clear(&wp->w_vars);     /* free all internal variables */
  1096. #endif
  1097.     if (prevwin == wp)
  1098. prevwin = NULL;
  1099.     win_free_lsize(wp);
  1100.     for (i = 0; i < wp->w_tagstacklen; ++i)
  1101. vim_free(wp->w_tagstack[i].tagname);
  1102. #ifdef USE_GUI
  1103.     if (gui.in_use)
  1104.     {
  1105. gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]);
  1106. gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]);
  1107.     }
  1108. #endif /* USE_GUI */
  1109.     win_remove(wp);
  1110.     vim_free(wp);
  1111. }
  1112.     static void
  1113. win_append(after, wp)
  1114.     WIN     *after, *wp;
  1115. {
  1116.     WIN     *before;
  1117.     if (after == NULL)     /* after NULL is in front of the first */
  1118. before = firstwin;
  1119.     else
  1120. before = after->w_next;
  1121.     wp->w_next = before;
  1122.     wp->w_prev = after;
  1123.     if (after == NULL)
  1124. firstwin = wp;
  1125.     else
  1126. after->w_next = wp;
  1127.     if (before == NULL)
  1128. lastwin = wp;
  1129.     else
  1130. before->w_prev = wp;
  1131. }
  1132. /*
  1133.  * remove window from the window list
  1134.  */
  1135.     static void
  1136. win_remove(wp)
  1137.     WIN     *wp;
  1138. {
  1139.     if (wp->w_prev)
  1140. wp->w_prev->w_next = wp->w_next;
  1141.     else
  1142. firstwin = wp->w_next;
  1143.     if (wp->w_next)
  1144. wp->w_next->w_prev = wp->w_prev;
  1145.     else
  1146. lastwin = wp->w_prev;
  1147. }
  1148. /*
  1149.  * allocate lsize arrays for a window
  1150.  * return FAIL for failure, OK for success
  1151.  */
  1152.     int
  1153. win_alloc_lsize(wp)
  1154.     WIN     *wp;
  1155. {
  1156.     wp->w_lsize_valid = 0;
  1157.     wp->w_lsize_lnum = (linenr_t *)alloc((unsigned)(Rows * sizeof(linenr_t)));
  1158.     wp->w_lsize = alloc((unsigned)Rows);
  1159.     if (wp->w_lsize_lnum == NULL || wp->w_lsize == NULL)
  1160.     {
  1161. win_free_lsize(wp); /* one of the two may have worked */
  1162. return FAIL;
  1163.     }
  1164.     return OK;
  1165. }
  1166. /*
  1167.  * free lsize arrays for a window
  1168.  */
  1169.     void
  1170. win_free_lsize(wp)
  1171.     WIN     *wp;
  1172. {
  1173.     vim_free(wp->w_lsize_lnum);
  1174.     vim_free(wp->w_lsize);
  1175.     wp->w_lsize_lnum = NULL;
  1176.     wp->w_lsize = NULL;
  1177. }
  1178. /*
  1179.  * call this fuction whenever Rows changes value
  1180.  */
  1181.     void
  1182. screen_new_rows()
  1183. {
  1184.     WIN     *wp;
  1185.     int     extra_lines;
  1186.     if (firstwin == NULL) /* not initialized yet */
  1187. return;
  1188. /*
  1189.  * the number of extra lines is the difference between the position where
  1190.  * the command line should be and where it is now
  1191.  */
  1192.     extra_lines = Rows - p_ch -
  1193.    (lastwin->w_winpos + lastwin->w_height + lastwin->w_status_height);
  1194.     if (extra_lines < 0) /* reduce windows height */
  1195.     {
  1196. for (wp = lastwin; wp; wp = wp->w_prev)
  1197. {
  1198.     if (wp->w_height - p_wmh < -extra_lines)
  1199.     {
  1200. extra_lines += wp->w_height - p_wmh;
  1201. win_new_height(wp, (int)p_wmh);
  1202.     }
  1203.     else
  1204.     {
  1205. win_new_height(wp, wp->w_height + extra_lines);
  1206. break;
  1207.     }
  1208. }
  1209. (void)win_comp_pos();     /* compute w_winpos */
  1210.     }
  1211.     else if (extra_lines > 0)     /* increase height of last window */
  1212. win_new_height(lastwin, lastwin->w_height + extra_lines);
  1213.     compute_cmdrow();
  1214.     if (p_ea)
  1215. win_equal(curwin, FALSE);
  1216. }
  1217. /*
  1218.  * update the w_winpos field for all windows
  1219.  * returns the row just after the last window
  1220.  */
  1221.     static int
  1222. win_comp_pos()
  1223. {
  1224.     WIN     *wp;
  1225.     int     row;
  1226.     row = 0;
  1227.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1228.     {
  1229. if (wp->w_winpos != row) /* if position changes, redraw */
  1230. {
  1231.     wp->w_winpos = row;
  1232.     wp->w_redr_type = NOT_VALID;
  1233.     wp->w_redr_status = TRUE;
  1234. }
  1235. row += wp->w_height + wp->w_status_height;
  1236.     }
  1237.     return row;
  1238. }
  1239. /*
  1240.  * set current window height
  1241.  */
  1242.     void
  1243. win_setheight(height)
  1244.     int     height;
  1245. {
  1246.     WIN     *wp;
  1247.     int     room; /* total number of lines available */
  1248.     int     take; /* number of lines taken from other windows */
  1249.     int     room_cmdline; /* lines available from cmdline */
  1250.     int     row;
  1251.     int     run;
  1252.     if (p_wmh == 0)
  1253.     {
  1254. /* Always keep current window at least one line high, even when
  1255.  * 'winminheight' is zero */
  1256. if (height == 0) /* need at least one line */
  1257. {
  1258.     height = 1;
  1259.     room = 1;
  1260. }
  1261. else
  1262.     room = p_wmh; /* count 'winminheight' for the curr. window */
  1263.     }
  1264.     else
  1265.     {
  1266. if (height < p_wmh) /* need at least some lines */
  1267.     height = p_wmh;
  1268. room = p_wmh; /* count 'winminheight' for the curr. window */
  1269.     }
  1270. /*
  1271.  * compute the room we have from all the windows
  1272.  */
  1273.     room_cmdline = Rows - p_ch;
  1274.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1275.     {
  1276. room += wp->w_height - p_wmh;
  1277. room_cmdline -= wp->w_height + wp->w_status_height;
  1278.     }
  1279. /*
  1280.  * limit new height to the room available
  1281.  */
  1282.     if (height > room + room_cmdline)     /* can't make it that large */
  1283. height = room + room_cmdline;     /* use all available room */
  1284. /*
  1285.  * compute the number of lines we will take from the windows (can be negative)
  1286.  */
  1287.     take = height - curwin->w_height;
  1288.     if (take == 0)     /* no change, nothing to do */
  1289. return;
  1290.     if (take > 0)
  1291.     {
  1292. take -= room_cmdline;     /* use lines from cmdline first */
  1293. if (take < 0)
  1294.     take = 0;
  1295.     }
  1296. /*
  1297.  * set the current window to the new height
  1298.  */
  1299.     win_new_height(curwin, height);
  1300. /*
  1301.  * First take lines from the windows below the current window.
  1302.  * If that is not enough, takes lines from windows above the current window.
  1303.  */
  1304.     for (run = 0; run < 2; ++run)
  1305.     {
  1306. if (run == 0)
  1307.     wp = curwin->w_next; /* 1st run: start with next window */
  1308. else
  1309.     wp = curwin->w_prev; /* 2nd run: start with prev window */
  1310. while (wp != NULL && take != 0)
  1311. {
  1312.     if (wp->w_height - take < p_wmh)
  1313.     {
  1314. take -= wp->w_height - p_wmh;
  1315. win_new_height(wp, (int)p_wmh);
  1316.     }
  1317.     else
  1318.     {
  1319. win_new_height(wp, wp->w_height - take);
  1320. take = 0;
  1321.     }
  1322.     if (run == 0)
  1323. wp = wp->w_next;
  1324.     else
  1325. wp = wp->w_prev;
  1326. }
  1327.     }
  1328. /* recompute the window positions */
  1329.     row = win_comp_pos();
  1330. /*
  1331.  * If there is extra space created between the last window and the command line,
  1332.  * clear it.
  1333.  */
  1334.     if (full_screen && msg_scrolled == 0)
  1335. screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
  1336.     cmdline_row = row;
  1337.     update_screen(NOT_VALID);
  1338. }
  1339. /*
  1340.  * Check 'winminheight' for a valid value.
  1341.  */
  1342.     void
  1343. win_setminheight()
  1344. {
  1345.     int room;
  1346.     int first = TRUE;
  1347.     WIN *wp;
  1348.     /* loop until there is a 'winminheight' that is possible */
  1349.     while (p_wmh > 0)
  1350.     {
  1351. room = -p_wh;
  1352. for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1353.     room += wp->w_height - p_wmh;
  1354. if (room >= 0)
  1355.     break;
  1356. --p_wmh;
  1357. if (first)
  1358. {
  1359.     EMSG(e_noroom);
  1360.     first = FALSE;
  1361. }
  1362.     }
  1363. }
  1364. #ifdef USE_MOUSE
  1365.     void
  1366. win_drag_status_line(offset)
  1367.     int     offset;
  1368. {
  1369.     WIN     *wp;
  1370.     int     room;
  1371.     int     row;
  1372.     int     up; /* if TRUE, drag status line up, otherwise down */
  1373.     if (offset < 0)
  1374.     {
  1375. up = TRUE;
  1376. offset = -offset;
  1377.     }
  1378.     else
  1379. up = FALSE;
  1380.     if (up) /* drag up */
  1381.     {
  1382. if (p_wmh == 0)
  1383.     room = -1; /* current window should be at least one line */
  1384. else
  1385.     room = 0;
  1386. for (wp = curwin; wp != NULL && room < offset; wp = wp->w_prev)
  1387.     room += wp->w_height - p_wmh;
  1388. wp = curwin->w_next;     /* put wp at window that grows */
  1389.     }
  1390.     else    /* drag down */
  1391.     {
  1392. /*
  1393.  * Only dragging the last status line can reduce p_ch.
  1394.  */
  1395. room = Rows - cmdline_row;
  1396. if (curwin->w_next == NULL)
  1397.     room -= 1;
  1398. else
  1399.     room -= p_ch;
  1400. for (wp = curwin->w_next; wp != NULL && room < offset; wp = wp->w_next)
  1401.     room += wp->w_height - p_wmh;
  1402. wp = curwin;     /* put wp at window that grows */
  1403.     }
  1404.     if (room < offset)     /* Not enough room */
  1405. offset = room;     /* Move as far as we can */
  1406.     if (offset <= 0)
  1407. return;
  1408.     if (wp != NULL)     /* grow window wp by offset lines */
  1409. win_new_height(wp, wp->w_height + offset);
  1410.     if (up)
  1411. wp = curwin;     /* current window gets smaller */
  1412.     else
  1413. wp = curwin->w_next;     /* next window gets smaller */
  1414.     while (wp != NULL && offset > 0)
  1415.     {
  1416. if (wp->w_height - offset <= p_wmh)
  1417. {
  1418.     offset -= wp->w_height - p_wmh;
  1419.     if (wp == curwin && p_wmh == 0)
  1420.     {
  1421. win_new_height(wp, 1);
  1422. offset += 1;
  1423.     }
  1424.     else
  1425. win_new_height(wp, (int)p_wmh);
  1426. }
  1427. else
  1428. {
  1429.     win_new_height(wp, wp->w_height - offset);
  1430.     offset = 0;
  1431. }
  1432. if (up)
  1433.     wp = wp->w_prev;
  1434. else
  1435.     wp = wp->w_next;
  1436.     }
  1437.     row = win_comp_pos();
  1438.     screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
  1439.     cmdline_row = row;
  1440.     p_ch = Rows - cmdline_row;
  1441.     update_screen(NOT_VALID);
  1442.     showmode();
  1443. }
  1444. #endif /* USE_MOUSE */
  1445. /*
  1446.  * Set new window height.
  1447.  */
  1448.     static void
  1449. win_new_height(wp, height)
  1450.     WIN     *wp;
  1451.     int     height;
  1452. {
  1453.     linenr_t lnum;
  1454.     int sline, line_size;
  1455. #define FRACTION_MULT 16384L
  1456.     if (wp->w_wrow != wp->w_prev_fraction_row && wp->w_height > 0)
  1457. wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT
  1458.     + FRACTION_MULT / 2) / (long)wp->w_height;
  1459.     wp->w_height = height;
  1460.     wp->w_skipcol = 0;
  1461.     lnum = wp->w_cursor.lnum;
  1462.     if (lnum < 1) /* can happen when starting up */
  1463. lnum = 1;
  1464.     wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT;
  1465.     line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
  1466.     sline = wp->w_wrow - line_size;
  1467.     if (sline < 0)
  1468.     {
  1469. /*
  1470.  * Cursor line would go off top of screen if w_wrow was this high.
  1471.  */
  1472. wp->w_wrow = line_size;
  1473.     }
  1474.     else
  1475.     {
  1476. while (sline > 0 && lnum > 1)
  1477.     sline -= (line_size = plines_win(wp, --lnum));
  1478. if (sline < 0)
  1479. {
  1480.     /*
  1481.      * Line we want at top would go off top of screen. Use next line
  1482.      * instead.
  1483.      */
  1484.     lnum++;
  1485.     wp->w_wrow -= line_size + sline;
  1486. }
  1487. else if (sline > 0)
  1488. {
  1489.     /* First line of file reached, use that as topline. */
  1490.     lnum = 1;
  1491.     wp->w_wrow -= sline;
  1492. }
  1493.     }
  1494.     set_topline(wp, lnum);
  1495.     if (wp == curwin)
  1496.     {
  1497. if (p_so)
  1498.     update_topline();
  1499. curs_columns(FALSE);     /* validate w_wrow */
  1500.     }
  1501.     wp->w_prev_fraction_row = wp->w_wrow;
  1502.     win_comp_scroll(wp);
  1503.     if (wp->w_redr_type < NOT_VALID)
  1504. wp->w_redr_type = NOT_VALID;
  1505.     wp->w_redr_status = TRUE;
  1506.     invalidate_botline_win(wp);
  1507. }
  1508.     void
  1509. win_comp_scroll(wp)
  1510.     WIN     *wp;
  1511. {
  1512.     wp->w_p_scroll = ((unsigned)wp->w_height >> 1);
  1513.     if (wp->w_p_scroll == 0)
  1514. wp->w_p_scroll = 1;
  1515. }
  1516. /*
  1517.  * command_height: called whenever p_ch has been changed
  1518.  */
  1519.     void
  1520. command_height(old_p_ch)
  1521.     long    old_p_ch;
  1522. {
  1523.     WIN     *wp;
  1524.     int     h;
  1525.     if (!starting)
  1526.     {
  1527. cmdline_row = Rows - p_ch;
  1528. if (p_ch > old_p_ch)     /* p_ch got bigger */
  1529. {
  1530.     for (wp = lastwin; p_ch > old_p_ch; wp = wp->w_prev)
  1531.     {
  1532. if (wp == NULL)
  1533. {
  1534.     emsg(e_noroom);
  1535.     p_ch = old_p_ch;
  1536.     break;
  1537. }
  1538. h = wp->w_height - (p_ch - old_p_ch);
  1539. if (p_wmh == 0)
  1540. {
  1541.     /* don't make current window zero lines */
  1542.     if (wp == curwin && h < 1)
  1543. h = 1;
  1544. }
  1545. else if (h < p_wmh)
  1546.     h = p_wmh;
  1547. old_p_ch += wp->w_height - h;
  1548. win_new_height(wp, h);
  1549.     }
  1550.     win_comp_pos();
  1551.     /* clear the lines added to cmdline */
  1552.     if (full_screen)
  1553. screen_fill((int)(cmdline_row), (int)Rows, 0,
  1554.    (int)Columns, ' ', ' ', 0);
  1555.     msg_row = cmdline_row;
  1556.     redraw_cmdline = TRUE;
  1557.     return;
  1558. }
  1559. if (msg_row < cmdline_row)
  1560.     msg_row = cmdline_row;
  1561. redraw_cmdline = TRUE;
  1562.     }
  1563.     win_new_height(lastwin, (int)(lastwin->w_height + old_p_ch - p_ch));
  1564. }
  1565.     void
  1566. last_status()
  1567. {
  1568.     WIN *wp;
  1569.     if (lastwin->w_status_height)
  1570.     {
  1571. /* remove status line */
  1572. if (p_ls == 0 || (p_ls == 1 && firstwin == lastwin))
  1573. {
  1574.     win_new_height(lastwin, lastwin->w_height + 1);
  1575.     lastwin->w_status_height = 0;
  1576.     comp_col();
  1577. }
  1578.     }
  1579.     else if (p_ls == 2 || (p_ls == 1 && firstwin != lastwin))
  1580.     {
  1581. /* go to first window with enough room for a win_new_height(-1) */
  1582. for (wp = lastwin; wp->w_height <= p_wmh; wp = wp->w_prev)
  1583.     if (wp == NULL)
  1584.     {
  1585. emsg(e_noroom);
  1586. return;
  1587.     }
  1588. win_new_height(wp, wp->w_height - 1);
  1589. win_comp_pos();
  1590. lastwin->w_status_height = 1;
  1591. comp_col();
  1592. update_screen(CLEAR);
  1593.     }
  1594. }
  1595. #ifdef FILE_IN_PATH
  1596. /*
  1597.  * file_name_at_cursor()
  1598.  *
  1599.  * Return the name of the file under (or to the right of) the cursor.
  1600.  *
  1601.  * get_file_name_in_path()
  1602.  *
  1603.  * Return the name of the file at (or to the right of) ptr[col].
  1604.  *
  1605.  * The p_path variable is searched if the file name does not start with '/'.
  1606.  * The string returned has been alloc'ed and should be freed by the caller.
  1607.  * NULL is returned if the file name or file is not found.
  1608.  *
  1609.  * options:
  1610.  * FNAME_MESS     give error messages
  1611.  * FNAME_EXP     expand to path
  1612.  * FNAME_HYP     check for hypertext link
  1613.  */
  1614.     char_u *
  1615. file_name_at_cursor(options, count)
  1616.     int     options;
  1617.     long    count;
  1618. {
  1619.     return get_file_name_in_path(ml_get_curline(),
  1620. curwin->w_cursor.col, options, count);
  1621. }
  1622.     char_u *
  1623. get_file_name_in_path(line, col, options, count)
  1624.     char_u  *line;
  1625.     int     col;
  1626.     int     options;
  1627.     long    count;
  1628. {
  1629.     char_u  *ptr;
  1630.     char_u  *file_name;
  1631.     int     len;
  1632.     /*
  1633.      * search forward for what could be the start of a file name
  1634.      */
  1635.     ptr = line + col;
  1636.     while (*ptr != NUL && !vim_isfilec(*ptr))
  1637. ++ptr;
  1638.     if (*ptr == NUL) /* nothing found */
  1639.     {
  1640. if (options & FNAME_MESS)
  1641.     EMSG("No file name under cursor");
  1642. return NULL;
  1643.     }
  1644.     /*
  1645.      * search backward for first char of the file name
  1646.      */
  1647.     while (ptr > line && vim_isfilec(ptr[-1]))
  1648. --ptr;
  1649.     /*
  1650.      * Go one char back to ":" before "//" even when ':' is not in 'isfname'.
  1651.      */
  1652.     if ((options & FNAME_HYP) && ptr > line && path_is_url(ptr - 1))
  1653. --ptr;
  1654.     /*
  1655.      * Search forward for the last char of the file name.
  1656.      * Also allow "://" when ':' is not in 'isfname'.
  1657.      */
  1658.     len = 0;
  1659.     while (vim_isfilec(ptr[len])
  1660.  || ((options & FNAME_HYP) && path_is_url(ptr + len)))
  1661. ++len;
  1662.     if (options & FNAME_HYP)
  1663.     {
  1664. /* For hypertext links, ignore the name of the machine.
  1665.  * Such a link looks like "type://machine/path". Only "/path" is used.
  1666.  * First search for the string "://", then for the extra '/'
  1667.  */
  1668. if ((file_name = vim_strchr(ptr, ':')) != NULL &&
  1669. ((path_is_url(file_name) == URL_SLASH &&
  1670.   (file_name = vim_strchr(file_name + 3, '/')) != NULL) ||
  1671.  (path_is_url(file_name) == URL_BACKSLASH &&
  1672.   (file_name = vim_strchr(file_name + 3, '\')) != NULL)) &&
  1673. file_name < ptr + len)
  1674. {
  1675.     len -= file_name - ptr;
  1676.     ptr = file_name;
  1677.     if (ptr[1] == '~')     /* skip '/' for /~user/path */
  1678.     {
  1679. ++ptr;
  1680. --len;
  1681.     }
  1682. }
  1683.     }
  1684.     if (!(options & FNAME_EXP))
  1685. return vim_strnsave(ptr, len);
  1686.     return find_file_in_path(ptr, len, options, count);
  1687. }
  1688. /*
  1689.  * Find the file name "ptr[len]" in the path.
  1690.  *
  1691.  * options:
  1692.  * FNAME_MESS     give error message when not found
  1693.  *
  1694.  * Uses NameBuff[]!
  1695.  *
  1696.  * Returns an allocated string for the file name.  NULL for error.
  1697.  */
  1698.     static char_u *
  1699. find_file_in_path(ptr, len, options, count)
  1700.     char_u *ptr; /* file name */
  1701.     int len; /* length of file name */
  1702.     int options;
  1703.     long count; /* use count'th matching file name */
  1704. {
  1705.     char_u save_char;
  1706.     char_u *file_name;
  1707.     char_u *curr_path = NULL;
  1708.     char_u *dir;
  1709.     int curr_path_len;
  1710.     char_u *p;
  1711.     char_u *head;
  1712.     /* copy file name into NameBuff, expanding environment variables */
  1713.     save_char = ptr[len];
  1714.     ptr[len] = NUL;
  1715.     expand_env(ptr, NameBuff, MAXPATHL);
  1716.     ptr[len] = save_char;
  1717.     if (mch_isFullName(NameBuff))
  1718.     {
  1719. /*
  1720.  * Absolute path, no need to use 'path'.
  1721.  */
  1722. if ((file_name = vim_strsave(NameBuff)) == NULL)
  1723.     return NULL;
  1724. if (mch_getperm(file_name) >= 0)
  1725.     return file_name;
  1726. if (options & FNAME_MESS)
  1727.     EMSG2("Can't find file "%s"", NameBuff);
  1728.     }
  1729.     else
  1730.     {
  1731. /*
  1732.  * Relative path, use 'path' option.
  1733.  */
  1734. if (curbuf->b_fname != NULL)
  1735. {
  1736.     curr_path = curbuf->b_fname;
  1737.     ptr = gettail(curr_path);
  1738.     curr_path_len = ptr - curr_path;
  1739. }
  1740. else
  1741.     curr_path_len = 0;
  1742. if ((file_name = alloc((int)(curr_path_len + STRLEN(p_path) +
  1743.     STRLEN(NameBuff) + 3))) == NULL)
  1744.     return NULL;
  1745. for (dir = p_path; *dir && !got_int; )
  1746. {
  1747.     len = copy_option_part(&dir, file_name, 31000, " ,");
  1748.     /* len == 0 means: use current directory */
  1749.     if (len != 0)
  1750.     {
  1751. /* Look for file relative to current file */
  1752. if (file_name[0] == '.' && curr_path_len > 0
  1753.  && (len == 1 || vim_ispathsep(file_name[1])))
  1754. {
  1755.     if (len == 1) /* just a "." */
  1756. len = 0;
  1757.     else /* "./path": move "path" */
  1758.     {
  1759. len -= 2;
  1760. mch_memmove(file_name + curr_path_len, file_name + 2,
  1761.  (size_t)len);
  1762.     }
  1763.     STRNCPY(file_name, curr_path, curr_path_len);
  1764.     len += curr_path_len;
  1765. }
  1766. if (!vim_ispathsep(file_name[len - 1]))
  1767.     file_name[len++] = PATHSEP;
  1768. file_name[len] = '';
  1769. /*
  1770.  * Handle "**" in the path: 'wildcard in path'.
  1771.  */
  1772. if (mch_has_wildcard(file_name))
  1773. {
  1774.     p = get_past_head(file_name);
  1775.     if (p == file_name)     /* no absolute file name */
  1776. p = find_file_in_wildcard_path((char_u *)"",
  1777. file_name, 0, &count);
  1778.     else    /* absolute file name, separate head */
  1779.     {
  1780. head = vim_strnsave(file_name,
  1781.    (unsigned)(p - file_name));
  1782. if (head != NULL)
  1783. {
  1784.     p = find_file_in_wildcard_path(head, p, 0, &count);
  1785.     vim_free(head);
  1786. }
  1787.     }
  1788.     if (p != NULL)
  1789.     {
  1790. vim_free(file_name);
  1791. return p;
  1792.     }
  1793.     continue;
  1794. }
  1795.     }
  1796.     STRCPY(file_name + len, NameBuff);
  1797.     /*
  1798.      * Translate names like "src/a/../b/file.c" into "src/b/file.c".
  1799.      */
  1800.     simplify_filename(file_name);
  1801.     if (mch_getperm(file_name) >= 0 && --count == 0)
  1802. return file_name;
  1803. }
  1804. if (options & FNAME_MESS)
  1805.     EMSG2("Can't find file "%s" in path", NameBuff);
  1806.     }
  1807.     /* get here when file doesn't exist */
  1808.     vim_free(file_name);
  1809.     return NULL;
  1810. }
  1811. /*
  1812.  * find_file_in_wildcard_path(): expand path recursively while searching
  1813.  *      files in path
  1814.  *
  1815.  * The syntax '**' means the whole subtree.
  1816.  * To avoid endless recursion, a counter restricts the depth to 100 levels.
  1817.  * In the following pseudo code '+/' will mean '*' followed by '/'
  1818.  *
  1819.  * in the case of 'set path=,/foo/bar/+/+/,'
  1820.  * the function call hierarchy will be
  1821.  *   find_file_in_wildcard_path("/", "foo/bar/+/+/", NameBuff, 0);
  1822.  *   find_file_in_wildcard_path("/foo/", "bar/+/+/", NameBuff, 0);
  1823.  *   find_file_in_wildcard_path("/foo/bar/", "+/+/", NameBuff, 0);
  1824.  * which in turn will call
  1825.  *   find_file_in_wildcard_path("/foo/bar/dir/", "+/", NameBuff, 1);
  1826.  * for each directory 'dir' in '/foo/bar/+'.  It's the next call,
  1827.  *   find_file_in_wildcard_path("/foo/bar/dir/dir2/", "", NameBuff, 2);
  1828.  * that will try to find the file 'NameBuff' in the given directory.
  1829.  *
  1830.  * pseudo code:
  1831.  *
  1832.  *  find_file_in_wildcard_path(path_so_far, wildcards, level)
  1833.  *  {
  1834.  *    if (level > 100)
  1835.  * return NULL;
  1836.  *
  1837.  *    file_name = path_so_far + first_segment(wildcards);
  1838.  *    rest_of_wildcards = all_but_first_segment(wildcards);
  1839.  *
  1840.  *    result = expand(file_name);
  1841.  *
  1842.  *    if (!rest_of_wildcards) {
  1843.  * foreach_path_in(result) {
  1844.  *   if (exists&readable(path + NameBuff))
  1845.  *     return path+NameBuff;
  1846.  * }
  1847.  *    } else {
  1848.  * foreach_path_in(result) {
  1849.  *   c = find_file_in_wildcard_path(path, rest_of_wildcards, level+1);
  1850.  *   if (c)
  1851.  *     return c;
  1852.  * }
  1853.  *    }
  1854.  *    if (infinite_recursion(wildcards)) {
  1855.  * foreach_path_in(result) {
  1856.  *   c = find_file_in_wildcard_path(path, wildcards, level+1);
  1857.  *   if (c)
  1858.  *     return c;
  1859.  * }
  1860.  *    }
  1861.  *    return NULL;
  1862.  *  }
  1863.  */
  1864.     static char_u *
  1865. find_file_in_wildcard_path(path_so_far, wildcards, level, countptr)
  1866.     char_u  *path_so_far;
  1867.     char_u  *wildcards;
  1868.     int     level;
  1869.     long    *countptr;
  1870. {
  1871.     char_u  *file_name;
  1872.     int     len;
  1873.     char_u  *rest_of_wildcards;
  1874.     int     nFiles = 0;
  1875.     char_u  **ppFiles;
  1876.     int     i;
  1877.     char_u  *c;
  1878.     ui_breakcheck();
  1879.     if (level > 100 || got_int)
  1880. return NULL;
  1881.     if ((file_name = alloc((int)MAXPATHL)) == NULL)
  1882. return NULL;
  1883.     STRCPY(file_name, path_so_far);
  1884.     len = STRLEN(file_name);
  1885.     if (!vim_ispathsep(file_name[len-1]))
  1886.     {
  1887. file_name[len++] = PATHSEP;
  1888. file_name[len] = '';
  1889.     }
  1890.     rest_of_wildcards = wildcards;
  1891.     if (rest_of_wildcards)
  1892.     {
  1893. if (STRNCMP(rest_of_wildcards, "**", 2) == 0)
  1894.     rest_of_wildcards++;
  1895. while (*rest_of_wildcards && !vim_ispathsep(*rest_of_wildcards))
  1896.     file_name[len++] = *rest_of_wildcards++;
  1897. /* file_name[len++] = *rest_of_wildcards++; */
  1898. rest_of_wildcards++;
  1899. file_name[len] = '';
  1900.     }
  1901.     ++expand_interactively;
  1902.     expand_wildcards(1, &file_name, &nFiles, &ppFiles, EW_FILE|EW_DIR);
  1903.     --expand_interactively;
  1904.     if (!*rest_of_wildcards)
  1905.     {
  1906. for (i = 0; i < nFiles; ++i)
  1907. {
  1908.     if (!mch_isdir(ppFiles[i]))
  1909. continue;   /* not a directory */
  1910.     STRCPY(file_name, ppFiles[i]);
  1911.     if (!vim_ispathsep(file_name[STRLEN(file_name)-1]))
  1912. STRCAT(file_name, PATHSEPSTR);
  1913.     STRCAT(file_name, NameBuff);
  1914.     if (mch_getperm(file_name) >= 0 && --*countptr == 0)
  1915.     {
  1916. FreeWild(nFiles, ppFiles);
  1917. return file_name;
  1918.     }
  1919. }
  1920.     }
  1921.     else
  1922.     {
  1923. for (i = 0; i < nFiles; ++i)
  1924. {
  1925.     if (!mch_isdir(ppFiles[i]))
  1926. continue;   /* not a directory */
  1927.     c = find_file_in_wildcard_path(ppFiles[i],
  1928. rest_of_wildcards, level+1, countptr);
  1929.     if (c)
  1930.     {
  1931. FreeWild(nFiles, ppFiles);
  1932. vim_free(file_name);
  1933. return c;
  1934.     }
  1935. }
  1936.     }
  1937.     if (STRNCMP(wildcards, "**", 2) == 0)
  1938.     {
  1939. for (i = 0; i < nFiles; ++i)
  1940. {
  1941.     if (!mch_isdir(ppFiles[i]))
  1942. continue;   /* not a directory */
  1943.     c = find_file_in_wildcard_path(ppFiles[i],
  1944. wildcards, level+1, countptr);
  1945.     if (c)
  1946.     {
  1947. FreeWild(nFiles, ppFiles);
  1948. vim_free(file_name);
  1949. return c;
  1950.     }
  1951. }
  1952.     }
  1953.     FreeWild(nFiles, ppFiles);
  1954.     vim_free(file_name);
  1955.     return NULL;
  1956. }
  1957. /*
  1958.  * Check if the "://" of a URL is at the pointer, return URL_SLASH.
  1959.  * Also check for ":\", which MS Internet Explorer accepts, return
  1960.  * URL_BACKSLASH.
  1961.  */
  1962.     static int
  1963. path_is_url(p)
  1964.     char_u  *p;
  1965. {
  1966.     if (STRNCMP(p, "://", (size_t)3) == 0)
  1967. return URL_SLASH;
  1968.     else if (STRNCMP(p, ":\\", (size_t)3) == 0)
  1969. return URL_BACKSLASH;
  1970.     return 0;
  1971. }
  1972. #endif /* FILE_IN_PATH */
  1973. /*
  1974.  * Return the minimal number of rows that is needed on the screen to display
  1975.  * the current number of windows.
  1976.  */
  1977.     int
  1978. min_rows()
  1979. {
  1980.     WIN     *wp;
  1981.     int     total;
  1982.     if (firstwin == NULL) /* not initialized yet */
  1983. return MIN_LINES;
  1984.     total = p_ch; /* count the room for the status line */
  1985.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  1986. total += p_wmh + wp->w_status_height;
  1987.     if (p_wmh == 0)
  1988. total += 1; /* at least one window should have a line! */
  1989.     return total;
  1990. }
  1991. /*
  1992.  * Return TRUE if there is only one window, not counting a help window, unless
  1993.  * it is the current window.
  1994.  */
  1995.     int
  1996. only_one_window()
  1997. {
  1998.     int     count = 0;
  1999.     WIN     *wp;
  2000.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  2001. if (!wp->w_buffer->b_help || wp == curwin)
  2002.     ++count;
  2003.     return (count <= 1);
  2004. }
  2005. /*
  2006.  * Correct the cursor line number in other windows.  Used after changing the
  2007.  * current buffer, and before applying autocommands.
  2008.  * When "do_curwin" is TRUE, also check current window.
  2009.  */
  2010.     void
  2011. check_lnums(do_curwin)
  2012.     int do_curwin;
  2013. {
  2014.     WIN *wp;
  2015.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  2016. if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf)
  2017. {
  2018.     if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count)
  2019. wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
  2020.     if (wp->w_topline > curbuf->b_ml.ml_line_count)
  2021. wp->w_topline = curbuf->b_ml.ml_line_count;
  2022. }
  2023. }