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 * See README.txt for an overview of the Vim source code. 8 */ 9 10 #include "vim.h" 11 12 #ifdef HAVE_FCNTL_H 13 # include <fcntl.h> /* for chdir() */ 14 #endif 15 16 static int path_is_url __ARGS((char_u *p)); 17 #if defined(FEAT_WINDOWS) || defined(PROTO) 18 static int win_split_ins __ARGS((int size, int flags, win_T *newwin, int dir)); 19 static int win_comp_pos __ARGS((void)); 20 static void frame_comp_pos __ARGS((frame_T *topfrp, int *row, int *col)); 21 static void frame_setheight __ARGS((frame_T *curfrp, int height)); 22 #ifdef FEAT_VERTSPLIT 23 static void frame_setwidth __ARGS((frame_T *curfrp, int width)); 24 #endif 25 static void win_exchange __ARGS((long)); 26 static void win_rotate __ARGS((int, int)); 27 static void win_totop __ARGS((int size, int flags)); 28 static void win_equal_rec __ARGS((win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height)); 29 static win_T *win_free_mem __ARGS((win_T *win, int *dirp)); 30 static win_T *winframe_remove __ARGS((win_T *win, int *dirp)); 31 static frame_T *win_altframe __ARGS((win_T *win)); 32 static win_T *frame2win __ARGS((frame_T *frp)); 33 static int frame_has_win __ARGS((frame_T *frp, win_T *wp)); 34 static void frame_new_height __ARGS((frame_T *topfrp, int height, int topfirst, int wfh)); 35 static int frame_fixed_height __ARGS((frame_T *frp)); 36 #ifdef FEAT_VERTSPLIT 37 static void frame_add_statusline __ARGS((frame_T *frp)); 38 static void frame_new_width __ARGS((frame_T *topfrp, int width, int leftfirst)); 39 static void frame_add_vsep __ARGS((frame_T *frp)); 40 static int frame_minwidth __ARGS((frame_T *topfrp, win_T *next_curwin)); 41 static void frame_fix_width __ARGS((win_T *wp)); 42 #endif 43 static void frame_fix_height __ARGS((win_T *wp)); 44 static int frame_minheight __ARGS((frame_T *topfrp, win_T *next_curwin)); 45 static void win_enter_ext __ARGS((win_T *wp, int undo_sync, int no_curwin)); 46 static void win_free __ARGS((win_T *wp)); 47 static void win_append __ARGS((win_T *, win_T *)); 48 static void win_remove __ARGS((win_T *)); 49 static void frame_append __ARGS((frame_T *after, frame_T *frp)); 50 static void frame_insert __ARGS((frame_T *before, frame_T *frp)); 51 static void frame_remove __ARGS((frame_T *frp)); 52 #ifdef FEAT_VERTSPLIT 53 static void win_new_width __ARGS((win_T *wp, int width)); 54 static void win_goto_ver __ARGS((int up, long count)); 55 static void win_goto_hor __ARGS((int left, long count)); 56 #endif 57 static void frame_add_height __ARGS((frame_T *frp, int n)); 58 static void last_status_rec __ARGS((frame_T *fr, int statusline)); 59 60 static void make_snapshot __ARGS((void)); 61 static void make_snapshot_rec __ARGS((frame_T *fr, frame_T **frp)); 62 static void clear_snapshot __ARGS((void)); 63 static void clear_snapshot_rec __ARGS((frame_T *fr)); 64 static void restore_snapshot __ARGS((int close_curwin)); 65 static int check_snapshot_rec __ARGS((frame_T *sn, frame_T *fr)); 66 static win_T *restore_snapshot_rec __ARGS((frame_T *sn, frame_T *fr)); 67 68 #endif /* FEAT_WINDOWS */ 69 static win_T *win_alloc __ARGS((win_T *after)); 70 static void win_new_height __ARGS((win_T *, int)); 71 72 #define URL_SLASH 1 /* path_is_url() has found "://" */ 73 #define URL_BACKSLASH 2 /* path_is_url() has found ":\\" */ 74 75 #define NOWIN (win_T *)-1 /* non-exisiting window */ 76 77 #ifdef FEAT_WINDOWS 78 static long p_ch_used = 1L; /* value of 'cmdheight' when frame 79 size was set */ 80 #endif 81 82 #if defined(FEAT_WINDOWS) || defined(PROTO) 83 /* 84 * all CTRL-W window commands are handled here, called from normal_cmd(). 85 */ 86 void 87 do_window(nchar, Prenum, xchar) 88 int nchar; 89 long Prenum; 90 int xchar; /* extra char from ":wincmd gx" or NUL */ 91 { 92 long Prenum1; 93 win_T *wp; 94 #if defined(FEAT_SEARCHPATH) || defined(FEAT_FIND_ID) 95 char_u *ptr; 96 #endif 97 #ifdef FEAT_FIND_ID 98 int type = FIND_DEFINE; 99 int len; 100 #endif 101 char_u cbuf[40]; 102 103 if (Prenum == 0) 104 Prenum1 = 1; 105 else 106 Prenum1 = Prenum; 107 108 #ifdef FEAT_CMDWIN 109 # define CHECK_CMDWIN if (cmdwin_type != 0) { EMSG(_(e_cmdwin)); break; } 110 #else 111 # define CHECK_CMDWIN 112 #endif 113 114 switch (nchar) 115 { 116 /* split current window in two parts, horizontally */ 117 case 'S': 118 case Ctrl_S: 119 case 's': 120 CHECK_CMDWIN 121 #ifdef FEAT_VISUAL 122 reset_VIsual_and_resel(); /* stop Visual mode */ 123 #endif 124 #ifdef FEAT_GUI 125 need_mouse_correct = TRUE; 126 #endif 127 win_split((int)Prenum, 0); 128 break; 129 130 #ifdef FEAT_VERTSPLIT 131 /* split current window in two parts, vertically */ 132 case Ctrl_V: 133 case 'v': 134 CHECK_CMDWIN 135 #ifdef FEAT_VISUAL 136 reset_VIsual_and_resel(); /* stop Visual mode */ 137 #endif 138 #ifdef FEAT_GUI 139 need_mouse_correct = TRUE; 140 #endif 141 win_split((int)Prenum, WSP_VERT); 142 break; 143 #endif 144 145 /* split current window and edit alternate file */ 146 case Ctrl_HAT: 147 case '^': 148 CHECK_CMDWIN 149 #ifdef FEAT_VISUAL 150 reset_VIsual_and_resel(); /* stop Visual mode */ 151 #endif 152 STRCPY(cbuf, "split #"); 153 if (Prenum) 154 sprintf((char *)cbuf + 7, "%ld", Prenum); 155 do_cmdline_cmd(cbuf); 156 break; 157 158 /* open new window */ 159 case Ctrl_N: 160 case 'n': 161 CHECK_CMDWIN 162 #ifdef FEAT_VISUAL 163 reset_VIsual_and_resel(); /* stop Visual mode */ 164 #endif 165 if (Prenum) 166 sprintf((char *)cbuf, "%ld", Prenum); /* window height */ 167 else 168 cbuf[0] = NUL; 169 STRCAT(cbuf, "new"); 170 do_cmdline_cmd(cbuf); 171 break; 172 173 /* quit current window */ 174 case Ctrl_Q: 175 case 'q': 176 #ifdef FEAT_VISUAL 177 reset_VIsual_and_resel(); /* stop Visual mode */ 178 #endif 179 do_cmdline_cmd((char_u *)"quit"); 180 break; 181 182 /* close current window */ 183 case Ctrl_C: 184 case 'c': 185 #ifdef FEAT_VISUAL 186 reset_VIsual_and_resel(); /* stop Visual mode */ 187 #endif 188 do_cmdline_cmd((char_u *)"close"); 189 break; 190 191 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 192 /* close preview window */ 193 case Ctrl_Z: 194 case 'z': 195 CHECK_CMDWIN 196 #ifdef FEAT_VISUAL 197 reset_VIsual_and_resel(); /* stop Visual mode */ 198 #endif 199 do_cmdline_cmd((char_u *)"pclose"); 200 break; 201 202 /* cursor to preview window */ 203 case 'P': 204 for (wp = firstwin; wp != NULL; wp = wp->w_next) 205 if (wp->w_p_pvw) 206 break; 207 if (wp == NULL) 208 EMSG(_("E441: There is no preview window")); 209 else 210 win_goto(wp); 211 break; 212 #endif 213 214 /* close all but current window */ 215 case Ctrl_O: 216 case 'o': 217 CHECK_CMDWIN 218 #ifdef FEAT_VISUAL 219 reset_VIsual_and_resel(); /* stop Visual mode */ 220 #endif 221 do_cmdline_cmd((char_u *)"only"); 222 break; 223 224 /* cursor to next window with wrap around */ 225 case Ctrl_W: 226 case 'w': 227 /* cursor to previous window with wrap around */ 228 case 'W': 229 CHECK_CMDWIN 230 if (lastwin == firstwin && Prenum != 1) /* just one window */ 231 beep_flush(); 232 else 233 { 234 if (Prenum) /* go to specified window */ 235 { 236 for (wp = firstwin; --Prenum > 0; ) 237 { 238 if (wp->w_next == NULL) 239 break; 240 else 241 wp = wp->w_next; 242 } 243 } 244 else 245 { 246 if (nchar == 'W') /* go to previous window */ 247 { 248 wp = curwin->w_prev; 249 if (wp == NULL) 250 wp = lastwin; /* wrap around */ 251 } 252 else /* go to next window */ 253 { 254 wp = curwin->w_next; 255 if (wp == NULL) 256 wp = firstwin; /* wrap around */ 257 } 258 } 259 win_goto(wp); 260 } 261 break; 262 263 /* cursor to window below */ 264 case 'j': 265 case K_DOWN: 266 case Ctrl_J: 267 CHECK_CMDWIN 268 #ifdef FEAT_VERTSPLIT 269 win_goto_ver(FALSE, Prenum1); 270 #else 271 for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0; 272 wp = wp->w_next) 273 ; 274 win_goto(wp); 275 #endif 276 break; 277 278 /* cursor to window above */ 279 case 'k': 280 case K_UP: 281 case Ctrl_K: 282 CHECK_CMDWIN 283 #ifdef FEAT_VERTSPLIT 284 win_goto_ver(TRUE, Prenum1); 285 #else 286 for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0; 287 wp = wp->w_prev) 288 ; 289 win_goto(wp); 290 #endif 291 break; 292 293 #ifdef FEAT_VERTSPLIT 294 /* cursor to left window */ 295 case 'h': 296 case K_LEFT: 297 case Ctrl_H: 298 case K_BS: 299 CHECK_CMDWIN 300 win_goto_hor(TRUE, Prenum1); 301 break; 302 303 /* cursor to right window */ 304 case 'l': 305 case K_RIGHT: 306 case Ctrl_L: 307 CHECK_CMDWIN 308 win_goto_hor(FALSE, Prenum1); 309 break; 310 #endif 311 312 /* cursor to top-left window */ 313 case 't': 314 case Ctrl_T: 315 win_goto(firstwin); 316 break; 317 318 /* cursor to bottom-right window */ 319 case 'b': 320 case Ctrl_B: 321 win_goto(lastwin); 322 break; 323 324 /* cursor to last accessed (previous) window */ 325 case 'p': 326 case Ctrl_P: 327 if (prevwin == NULL) 328 beep_flush(); 329 else 330 win_goto(prevwin); 331 break; 332 333 /* exchange current and next window */ 334 case 'x': 335 case Ctrl_X: 336 CHECK_CMDWIN 337 win_exchange(Prenum); 338 break; 339 340 /* rotate windows downwards */ 341 case Ctrl_R: 342 case 'r': 343 CHECK_CMDWIN 344 #ifdef FEAT_VISUAL 345 reset_VIsual_and_resel(); /* stop Visual mode */ 346 #endif 347 win_rotate(FALSE, (int)Prenum1); /* downwards */ 348 break; 349 350 /* rotate windows upwards */ 351 case 'R': 352 CHECK_CMDWIN 353 #ifdef FEAT_VISUAL 354 reset_VIsual_and_resel(); /* stop Visual mode */ 355 #endif 356 win_rotate(TRUE, (int)Prenum1); /* upwards */ 357 break; 358 359 /* move window to the very top/bottom/left/right */ 360 case 'K': 361 case 'J': 362 #ifdef FEAT_VERTSPLIT 363 case 'H': 364 case 'L': 365 #endif 366 CHECK_CMDWIN 367 win_totop((int)Prenum, 368 ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0) 369 | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT)); 370 break; 371 372 /* make all windows the same height */ 373 case '=': 374 #ifdef FEAT_GUI 375 need_mouse_correct = TRUE; 376 #endif 377 win_equal(NULL, FALSE, 'b'); 378 break; 379 380 /* increase current window height */ 381 case '+': 382 #ifdef FEAT_GUI 383 need_mouse_correct = TRUE; 384 #endif 385 win_setheight(curwin->w_height + (int)Prenum1); 386 break; 387 388 /* decrease current window height */ 389 case '-': 390 #ifdef FEAT_GUI 391 need_mouse_correct = TRUE; 392 #endif 393 win_setheight(curwin->w_height - (int)Prenum1); 394 break; 395 396 /* set current window height */ 397 case Ctrl__: 398 case '_': 399 #ifdef FEAT_GUI 400 need_mouse_correct = TRUE; 401 #endif 402 win_setheight(Prenum ? (int)Prenum : 9999); 403 break; 404 405 #ifdef FEAT_VERTSPLIT 406 /* increase current window width */ 407 case '>': 408 #ifdef FEAT_GUI 409 need_mouse_correct = TRUE; 410 #endif 411 win_setwidth(curwin->w_width + (int)Prenum1); 412 break; 413 414 /* decrease current window width */ 415 case '<': 416 #ifdef FEAT_GUI 417 need_mouse_correct = TRUE; 418 #endif 419 win_setwidth(curwin->w_width - (int)Prenum1); 420 break; 421 422 /* set current window width */ 423 case '|': 424 #ifdef FEAT_GUI 425 need_mouse_correct = TRUE; 426 #endif 427 win_setwidth(Prenum != 0 ? (int)Prenum : 9999); 428 break; 429 #endif 430 431 /* jump to tag and split window if tag exists (in preview window) */ 432 #if defined(FEAT_QUICKFIX) 433 case '}': 434 CHECK_CMDWIN 435 if (Prenum) 436 g_do_tagpreview = Prenum; 437 else 438 g_do_tagpreview = p_pvh; 439 /*FALLTHROUGH*/ 440 #endif 441 case ']': 442 case Ctrl_RSB: 443 CHECK_CMDWIN 444 #ifdef FEAT_VISUAL 445 reset_VIsual_and_resel(); /* stop Visual mode */ 446 #endif 447 if (Prenum) 448 postponed_split = Prenum; 449 else 450 postponed_split = -1; 451 452 /* Execute the command right here, required when 453 * "wincmd ]" was used in a function. */ 454 do_nv_ident(Ctrl_RSB, NUL); 455 break; 456 457 #ifdef FEAT_SEARCHPATH 458 /* edit file name under cursor in a new window */ 459 case 'f': 460 case Ctrl_F: 461 CHECK_CMDWIN 462 463 ptr = grab_file_name(Prenum1); 464 if (ptr != NULL) 465 { 466 #ifdef FEAT_GUI 467 need_mouse_correct = TRUE; 468 #endif 469 setpcmark(); 470 if (win_split(0, 0) == OK) 471 { 472 # ifdef FEAT_SCROLLBIND 473 curwin->w_p_scb = FALSE; 474 # endif 475 (void)do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, 476 ECMD_HIDE); 477 } 478 vim_free(ptr); 479 } 480 break; 481 #endif 482 483 #ifdef FEAT_FIND_ID 484 /* Go to the first occurence of the identifier under cursor along path in a 485 * new window -- webb 486 */ 487 case 'i': /* Go to any match */ 488 case Ctrl_I: 489 type = FIND_ANY; 490 /* FALLTHROUGH */ 491 case 'd': /* Go to definition, using 'define' */ 492 case Ctrl_D: 493 CHECK_CMDWIN 494 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) 495 break; 496 find_pattern_in_path(ptr, 0, len, TRUE, 497 Prenum == 0 ? TRUE : FALSE, type, 498 Prenum1, ACTION_SPLIT, (linenr_T)1, (linenr_T)MAXLNUM); 499 curwin->w_set_curswant = TRUE; 500 break; 501 #endif 502 503 case K_KENTER: 504 case CAR: 505 #if defined(FEAT_QUICKFIX) 506 /* 507 * In a quickfix window a <CR> jumps to the error under the 508 * cursor in a new window. 509 */ 510 if (bt_quickfix(curbuf)) 511 { 512 sprintf((char *)cbuf, "split +%ldcc", 513 (long)curwin->w_cursor.lnum); 514 do_cmdline_cmd(cbuf); 515 } 516 #endif 517 break; 518 519 520 /* CTRL-W g extended commands */ 521 case 'g': 522 case Ctrl_G: 523 CHECK_CMDWIN 524 #ifdef USE_ON_FLY_SCROLL 525 dont_scroll = TRUE; /* disallow scrolling here */ 526 #endif 527 ++no_mapping; 528 ++allow_keys; /* no mapping for xchar, but allow key codes */ 529 if (xchar == NUL) 530 xchar = safe_vgetc(); 531 #ifdef FEAT_LANGMAP 532 LANGMAP_ADJUST(xchar, TRUE); 533 #endif 534 --no_mapping; 535 --allow_keys; 536 #ifdef FEAT_CMDL_INFO 537 (void)add_to_showcmd(xchar); 538 #endif 539 switch (xchar) 540 { 541 #if defined(FEAT_QUICKFIX) 542 case '}': 543 xchar = Ctrl_RSB; 544 if (Prenum) 545 g_do_tagpreview = Prenum; 546 else 547 g_do_tagpreview = p_pvh; 548 /*FALLTHROUGH*/ 549 #endif 550 case ']': 551 case Ctrl_RSB: 552 #ifdef FEAT_VISUAL 553 reset_VIsual_and_resel(); /* stop Visual mode */ 554 #endif 555 if (Prenum) 556 postponed_split = Prenum; 557 else 558 postponed_split = -1; 559 560 /* Execute the command right here, required when 561 * "wincmd g}" was used in a function. */ 562 do_nv_ident('g', xchar); 563 break; 564 565 default: 566 beep_flush(); 567 break; 568 } 569 break; 570 571 default: beep_flush(); 572 break; 573 } 574 } 575 576 /* 577 * split the current window, implements CTRL-W s and :split 578 * 579 * "size" is the height or width for the new window, 0 to use half of current 580 * height or width. 581 * 582 * "flags": 583 * WSP_ROOM: require enough room for new window 584 * WSP_VERT: vertical split. 585 * WSP_TOP: open window at the top-left of the shell (help window). 586 * WSP_BOT: open window at the bottom-right of the shell (quickfix window). 587 * WSP_HELP: creating the help window, keep layout snapshot 588 * 589 * return FAIL for failure, OK otherwise 590 */ 591 int 592 win_split(size, flags) 593 int size; 594 int flags; 595 { 596 /* Add flags from ":vertical", ":topleft" and ":botright". */ 597 flags |= cmdmod.split; 598 if ((flags & WSP_TOP) && (flags & WSP_BOT)) 599 { 600 EMSG(_("E442: Can't split topleft and botright at the same time")); 601 return FAIL; 602 } 603 604 /* When creating the help window make a snapshot of the window layout. 605 * Otherwise clear the snapshot, it's now invalid. */ 606 if (flags & WSP_HELP) 607 make_snapshot(); 608 else 609 clear_snapshot(); 610 611 return win_split_ins(size, flags, NULL, 0); 612 } 613 614 /* 615 * When "newwin" is NULL: split a window in two. 616 * When "newwin" is not NULL: insert this window at the far 617 * top/left/right/bottom. 618 * return FAIL for failure, OK otherwise 619 */ 620 static int 621 win_split_ins(size, flags, newwin, dir) 622 int size; 623 int flags; 624 win_T *newwin; 625 int dir; 626 { 627 win_T *wp = newwin; 628 win_T *oldwin; 629 int new_size = size; 630 int i; 631 int need_status = 0; 632 int do_equal = FALSE; 633 int needed; 634 int available; 635 int oldwin_height = 0; 636 int layout; 637 frame_T *frp, *curfrp; 638 int before; 639 640 if (flags & WSP_TOP) 641 oldwin = firstwin; 642 else if (flags & WSP_BOT) 643 oldwin = lastwin; 644 else 645 oldwin = curwin; 646 647 /* add a status line when p_ls == 1 and splitting the first window */ 648 if (lastwin == firstwin && p_ls == 1 && oldwin->w_status_height == 0) 649 { 650 if (oldwin->w_height <= p_wmh && newwin == NULL) 651 { 652 EMSG(_(e_noroom)); 653 return FAIL; 654 } 655 need_status = STATUS_HEIGHT; 656 } 657 658 #ifdef FEAT_VERTSPLIT 659 if (flags & WSP_VERT) 660 { 661 layout = FR_ROW; 662 do_equal = (p_ea && new_size == 0 && *p_ead != 'v'); 663 664 /* 665 * Check if we are able to split the current window and compute its 666 * width. 667 */ 668 needed = p_wmw + 1; 669 if (flags & WSP_ROOM) 670 needed += p_wiw - p_wmw; 671 if (p_ea || (flags & (WSP_BOT | WSP_TOP))) 672 { 673 available = topframe->fr_width; 674 needed += frame_minwidth(topframe, NULL); 675 } 676 else 677 available = oldwin->w_width; 678 if (available < needed && newwin == NULL) 679 { 680 EMSG(_(e_noroom)); 681 return FAIL; 682 } 683 if (new_size == 0) 684 new_size = oldwin->w_width / 2; 685 if (new_size > oldwin->w_width - p_wmw - 1) 686 new_size = oldwin->w_width - p_wmw - 1; 687 if (new_size < p_wmw) 688 new_size = p_wmw; 689 690 /* if it doesn't fit in the current window, need win_equal() */ 691 if (oldwin->w_width - new_size - 1 < p_wmw) 692 do_equal = TRUE; 693 } 694 else 695 #endif 696 { 697 layout = FR_COL; 698 do_equal = (p_ea && new_size == 0 699 #ifdef FEAT_VERTSPLIT 700 && *p_ead != 'h' 701 #endif 702 ); 703 704 /* 705 * Check if we are able to split the current window and compute its 706 * height. 707 */ 708 needed = p_wmh + STATUS_HEIGHT + need_status; 709 if (flags & WSP_ROOM) 710 needed += p_wh - p_wmh; 711 if (p_ea || (flags & (WSP_BOT | WSP_TOP))) 712 { 713 available = topframe->fr_height; 714 needed += frame_minheight(topframe, NULL); 715 } 716 else 717 { 718 available = oldwin->w_height; 719 needed += p_wmh; 720 } 721 if (available < needed && newwin == NULL) 722 { 723 EMSG(_(e_noroom)); 724 return FAIL; 725 } 726 oldwin_height = oldwin->w_height; 727 if (need_status) 728 { 729 oldwin->w_status_height = STATUS_HEIGHT; 730 oldwin_height -= STATUS_HEIGHT; 731 } 732 if (new_size == 0) 733 new_size = oldwin_height / 2; 734 735 if (new_size > oldwin_height - p_wmh - STATUS_HEIGHT) 736 new_size = oldwin_height - p_wmh - STATUS_HEIGHT; 737 if (new_size < p_wmh) 738 new_size = p_wmh; 739 740 /* if it doesn't fit in the current window, need win_equal() */ 741 if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) 742 do_equal = TRUE; 743 744 /* We don't like to take lines for the new window from a 745 * 'winfixheight' window. Take them from a window above or below 746 * instead, if possible. */ 747 if (oldwin->w_p_wfh) 748 { 749 win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT, 750 oldwin); 751 oldwin_height = oldwin->w_height; 752 if (need_status) 753 oldwin_height -= STATUS_HEIGHT; 754 } 755 } 756 757 /* 758 * allocate new window structure and link it in the window list 759 */ 760 if ((flags & WSP_TOP) == 0 761 && ((flags & WSP_BOT) 762 || (flags & WSP_BELOW) 763 || (!(flags & WSP_ABOVE) 764 && ( 765 #ifdef FEAT_VERTSPLIT 766 (flags & WSP_VERT) ? p_spr : 767 #endif 768 p_sb)))) 769 { 770 /* new window below/right of current one */ 771 if (newwin == NULL) 772 wp = win_alloc(oldwin); 773 else 774 win_append(oldwin, wp); 775 } 776 else 777 { 778 if (newwin == NULL) 779 wp = win_alloc(oldwin->w_prev); 780 else 781 win_append(oldwin->w_prev, wp); 782 } 783 784 if (newwin == NULL) 785 { 786 if (wp == NULL) 787 return FAIL; 788 789 /* 790 * make the contents of the new window the same as the current one 791 */ 792 wp->w_buffer = curbuf; 793 curbuf->b_nwindows++; 794 wp->w_cursor = curwin->w_cursor; 795 wp->w_valid = 0; 796 wp->w_curswant = curwin->w_curswant; 797 wp->w_set_curswant = curwin->w_set_curswant; 798 wp->w_topline = curwin->w_topline; 799 #ifdef FEAT_DIFF 800 wp->w_topfill = curwin->w_topfill; 801 #endif 802 wp->w_leftcol = curwin->w_leftcol; 803 wp->w_pcmark = curwin->w_pcmark; 804 wp->w_prev_pcmark = curwin->w_prev_pcmark; 805 wp->w_alt_fnum = curwin->w_alt_fnum; 806 wp->w_fraction = curwin->w_fraction; 807 wp->w_prev_fraction_row = curwin->w_prev_fraction_row; 808 #ifdef FEAT_JUMPLIST 809 copy_jumplist(curwin, wp); 810 #endif 811 if (curwin->w_localdir != NULL) 812 wp->w_localdir = vim_strsave(curwin->w_localdir); 813 814 /* Use the same argument list. */ 815 wp->w_alist = curwin->w_alist; 816 ++wp->w_alist->al_refcount; 817 wp->w_arg_idx = curwin->w_arg_idx; 818 819 /* 820 * copy tagstack and options from existing window 821 */ 822 for (i = 0; i < curwin->w_tagstacklen; i++) 823 { 824 wp->w_tagstack[i] = curwin->w_tagstack[i]; 825 if (wp->w_tagstack[i].tagname != NULL) 826 wp->w_tagstack[i].tagname = 827 vim_strsave(wp->w_tagstack[i].tagname); 828 } 829 wp->w_tagstackidx = curwin->w_tagstackidx; 830 wp->w_tagstacklen = curwin->w_tagstacklen; 831 win_copy_options(curwin, wp); 832 #ifdef FEAT_FOLDING 833 copyFoldingState(curwin, wp); 834 #endif 835 } 836 837 /* 838 * Reorganise the tree of frames to insert the new window. 839 */ 840 if (flags & (WSP_TOP | WSP_BOT)) 841 { 842 #ifdef FEAT_VERTSPLIT 843 if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0) 844 || (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0)) 845 #else 846 if (topframe->fr_layout == FR_COL) 847 #endif 848 { 849 curfrp = topframe->fr_child; 850 if (flags & WSP_BOT) 851 while (curfrp->fr_next != NULL) 852 curfrp = curfrp->fr_next; 853 } 854 else 855 curfrp = topframe; 856 before = (flags & WSP_TOP); 857 } 858 else 859 { 860 curfrp = oldwin->w_frame; 861 if (flags & WSP_BELOW) 862 before = FALSE; 863 else if (flags & WSP_ABOVE) 864 before = TRUE; 865 else 866 #ifdef FEAT_VERTSPLIT 867 if (flags & WSP_VERT) 868 before = !p_spr; 869 else 870 #endif 871 before = !p_sb; 872 } 873 if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout) 874 { 875 /* Need to create a new frame in the tree to make a branch. */ 876 frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 877 *frp = *curfrp; 878 curfrp->fr_layout = layout; 879 frp->fr_parent = curfrp; 880 frp->fr_next = NULL; 881 frp->fr_prev = NULL; 882 curfrp->fr_child = frp; 883 curfrp->fr_win = NULL; 884 curfrp = frp; 885 if (frp->fr_win != NULL) 886 oldwin->w_frame = frp; 887 else 888 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 889 frp->fr_parent = curfrp; 890 } 891 892 if (newwin == NULL) 893 { 894 /* Create a frame for the new window. */ 895 frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 896 frp->fr_layout = FR_LEAF; 897 frp->fr_win = wp; 898 wp->w_frame = frp; 899 } 900 else 901 frp = newwin->w_frame; 902 frp->fr_parent = curfrp->fr_parent; 903 904 /* Insert the new frame at the right place in the frame list. */ 905 if (before) 906 frame_insert(curfrp, frp); 907 else 908 frame_append(curfrp, frp); 909 910 #ifdef FEAT_VERTSPLIT 911 if (flags & WSP_VERT) 912 { 913 wp->w_p_scr = curwin->w_p_scr; 914 if (need_status) 915 { 916 --oldwin->w_height; 917 oldwin->w_status_height = need_status; 918 } 919 if (flags & (WSP_TOP | WSP_BOT)) 920 { 921 /* set height and row of new window to full height */ 922 wp->w_winrow = 0; 923 wp->w_height = curfrp->fr_height - (p_ls > 0); 924 wp->w_status_height = (p_ls > 0); 925 } 926 else 927 { 928 /* height and row of new window is same as current window */ 929 wp->w_winrow = oldwin->w_winrow; 930 wp->w_height = oldwin->w_height; 931 wp->w_status_height = oldwin->w_status_height; 932 } 933 frp->fr_height = curfrp->fr_height; 934 935 /* "new_size" of the current window goes to the new window, use 936 * one column for the vertical separator */ 937 wp->w_width = new_size; 938 if (before) 939 wp->w_vsep_width = 1; 940 else 941 { 942 wp->w_vsep_width = oldwin->w_vsep_width; 943 oldwin->w_vsep_width = 1; 944 } 945 if (flags & (WSP_TOP | WSP_BOT)) 946 { 947 if (flags & WSP_BOT) 948 frame_add_vsep(curfrp); 949 /* Set width of neighbor frame */ 950 frame_new_width(curfrp, curfrp->fr_width 951 - (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP); 952 } 953 else 954 oldwin->w_width -= new_size + 1; 955 if (before) /* new window left of current one */ 956 { 957 wp->w_wincol = oldwin->w_wincol; 958 oldwin->w_wincol += new_size + 1; 959 } 960 else /* new window right of current one */ 961 wp->w_wincol = oldwin->w_wincol + oldwin->w_width + 1; 962 frame_fix_width(oldwin); 963 frame_fix_width(wp); 964 } 965 else 966 #endif 967 { 968 /* width and column of new window is same as current window */ 969 #ifdef FEAT_VERTSPLIT 970 if (flags & (WSP_TOP | WSP_BOT)) 971 { 972 wp->w_wincol = 0; 973 wp->w_width = Columns; 974 wp->w_vsep_width = 0; 975 } 976 else 977 { 978 wp->w_wincol = oldwin->w_wincol; 979 wp->w_width = oldwin->w_width; 980 wp->w_vsep_width = oldwin->w_vsep_width; 981 } 982 frp->fr_width = curfrp->fr_width; 983 #endif 984 985 /* "new_size" of the current window goes to the new window, use 986 * one row for the status line */ 987 win_new_height(wp, new_size); 988 if (flags & (WSP_TOP | WSP_BOT)) 989 frame_new_height(curfrp, curfrp->fr_height 990 - (new_size + STATUS_HEIGHT), flags & WSP_TOP, FALSE); 991 else 992 win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT)); 993 if (before) /* new window above current one */ 994 { 995 wp->w_winrow = oldwin->w_winrow; 996 wp->w_status_height = STATUS_HEIGHT; 997 oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; 998 } 999 else /* new window below current one */ 1000 { 1001 wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; 1002 wp->w_status_height = oldwin->w_status_height; 1003 oldwin->w_status_height = STATUS_HEIGHT; 1004 } 1005 #ifdef FEAT_VERTSPLIT 1006 if (flags & WSP_BOT) 1007 frame_add_statusline(curfrp); 1008 #endif 1009 frame_fix_height(wp); 1010 frame_fix_height(oldwin); 1011 } 1012 1013 if (flags & (WSP_TOP | WSP_BOT)) 1014 (void)win_comp_pos(); 1015 1016 /* 1017 * Both windows need redrawing 1018 */ 1019 redraw_win_later(wp, NOT_VALID); 1020 wp->w_redr_status = TRUE; 1021 redraw_win_later(oldwin, NOT_VALID); 1022 oldwin->w_redr_status = TRUE; 1023 1024 if (need_status) 1025 { 1026 msg_row = Rows - 1; 1027 msg_col = sc_col; 1028 msg_clr_eos_force(); /* Old command/ruler may still be there */ 1029 comp_col(); 1030 msg_row = Rows - 1; 1031 msg_col = 0; /* put position back at start of line */ 1032 } 1033 1034 /* 1035 * make the new window the current window and redraw 1036 */ 1037 if (do_equal || dir != 0) 1038 win_equal(wp, TRUE, 1039 #ifdef FEAT_VERTSPLIT 1040 (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') 1041 : dir == 'h' ? 'b' : 1042 #endif 1043 'v'); 1044 1045 /* Don't change the window height/width to 'winheight' / 'winwidth' if a 1046 * size was given. */ 1047 #ifdef FEAT_VERTSPLIT 1048 if (flags & WSP_VERT) 1049 { 1050 i = p_wiw; 1051 if (size != 0) 1052 p_wiw = size; 1053 1054 # ifdef FEAT_GUI 1055 /* When 'guioptions' includes 'L' or 'R' may have to add scrollbars. */ 1056 if (gui.in_use) 1057 gui_init_which_components(NULL); 1058 # endif 1059 } 1060 else 1061 #endif 1062 { 1063 i = p_wh; 1064 if (size != 0) 1065 p_wh = size; 1066 } 1067 win_enter(wp, FALSE); 1068 #ifdef FEAT_VERTSPLIT 1069 if (flags & WSP_VERT) 1070 p_wiw = i; 1071 else 1072 #endif 1073 p_wh = i; 1074 1075 return OK; 1076 } 1077 1078 #endif /* FEAT_WINDOWS */ 1079 1080 #if defined(FEAT_WINDOWS) || defined(PROTO) 1081 /* 1082 * Check if "win" is a pointer to an existing window. 1083 */ 1084 int 1085 win_valid(win) 1086 win_T *win; 1087 { 1088 win_T *wp; 1089 1090 if (win == NULL) 1091 return FALSE; 1092 for (wp = firstwin; wp != NULL; wp = wp->w_next) 1093 if (wp == win) 1094 return TRUE; 1095 return FALSE; 1096 } 1097 1098 /* 1099 * Return the number of windows. 1100 */ 1101 int 1102 win_count() 1103 { 1104 win_T *wp; 1105 int count = 0; 1106 1107 for (wp = firstwin; wp != NULL; wp = wp->w_next) 1108 ++count; 1109 return count; 1110 } 1111 1112 /* 1113 * Make "count" windows on the screen. 1114 * Return actual number of windows on the screen. 1115 * Must be called when there is just one window, filling the whole screen 1116 * (excluding the command line). 1117 */ 1118 /*ARGSUSED*/ 1119 int 1120 make_windows(count, vertical) 1121 int count; 1122 int vertical; /* split windows vertically if TRUE */ 1123 { 1124 int maxcount; 1125 int todo; 1126 1127 #ifdef FEAT_VERTSPLIT 1128 if (vertical) 1129 { 1130 /* Each windows needs at least 'winminwidth' lines and a separator 1131 * column. */ 1132 maxcount = (curwin->w_width + curwin->w_vsep_width 1133 - (p_wiw - p_wmw)) / (p_wmw + 1); 1134 } 1135 else 1136 #endif 1137 { 1138 /* Each window needs at least 'winminheight' lines and a status line. */ 1139 maxcount = (curwin->w_height + curwin->w_status_height 1140 - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); 1141 } 1142 1143 if (maxcount < 2) 1144 maxcount = 2; 1145 if (count > maxcount) 1146 count = maxcount; 1147 1148 /* 1149 * add status line now, otherwise first window will be too big 1150 */ 1151 if (count > 1) 1152 last_status(TRUE); 1153 1154 #ifdef FEAT_AUTOCMD 1155 /* 1156 * Don't execute autocommands while creating the windows. Must do that 1157 * when putting the buffers in the windows. 1158 */ 1159 ++autocmd_block; 1160 #endif 1161 1162 /* todo is number of windows left to create */ 1163 for (todo = count - 1; todo > 0; --todo) 1164 #ifdef FEAT_VERTSPLIT 1165 if (vertical) 1166 { 1167 if (win_split(curwin->w_width - (curwin->w_width - todo) 1168 / (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL) 1169 break; 1170 } 1171 else 1172 #endif 1173 { 1174 if (win_split(curwin->w_height - (curwin->w_height - todo 1175 * STATUS_HEIGHT) / (todo + 1) 1176 - STATUS_HEIGHT, WSP_ABOVE) == FAIL) 1177 break; 1178 } 1179 1180 #ifdef FEAT_AUTOCMD 1181 --autocmd_block; 1182 #endif 1183 1184 /* return actual number of windows */ 1185 return (count - todo); 1186 } 1187 1188 /* 1189 * Exchange current and next window 1190 */ 1191 static void 1192 win_exchange(Prenum) 1193 long Prenum; 1194 { 1195 frame_T *frp; 1196 frame_T *frp2; 1197 win_T *wp; 1198 win_T *wp2; 1199 int temp; 1200 1201 if (lastwin == firstwin) /* just one window */ 1202 { 1203 beep_flush(); 1204 return; 1205 } 1206 1207 #ifdef FEAT_GUI 1208 need_mouse_correct = TRUE; 1209 #endif 1210 1211 /* 1212 * find window to exchange with 1213 */ 1214 if (Prenum) 1215 { 1216 frp = curwin->w_frame->fr_parent->fr_child; 1217 while (frp != NULL && --Prenum > 0) 1218 frp = frp->fr_next; 1219 } 1220 else if (curwin->w_frame->fr_next != NULL) /* Swap with next */ 1221 frp = curwin->w_frame->fr_next; 1222 else /* Swap last window in row/col with previous */ 1223 frp = curwin->w_frame->fr_prev; 1224 1225 /* We can only exchange a window with another window, not with a frame 1226 * containing windows. */ 1227 if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin) 1228 return; 1229 wp = frp->fr_win; 1230 1231 /* 1232 * 1. remove curwin from the list. Remember after which window it was in wp2 1233 * 2. insert curwin before wp in the list 1234 * if wp != wp2 1235 * 3. remove wp from the list 1236 * 4. insert wp after wp2 1237 * 5. exchange the status line height and vsep width. 1238 */ 1239 wp2 = curwin->w_prev; 1240 frp2 = curwin->w_frame->fr_prev; 1241 if (wp->w_prev != curwin) 1242 { 1243 win_remove(curwin); 1244 frame_remove(curwin->w_frame); 1245 win_append(wp->w_prev, curwin); 1246 frame_insert(frp, curwin->w_frame); 1247 } 1248 if (wp != wp2) 1249 { 1250 win_remove(wp); 1251 frame_remove(wp->w_frame); 1252 win_append(wp2, wp); 1253 if (frp2 == NULL) 1254 frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame); 1255 else 1256 frame_append(frp2, wp->w_frame); 1257 } 1258 temp = curwin->w_status_height; 1259 curwin->w_status_height = wp->w_status_height; 1260 wp->w_status_height = temp; 1261 #ifdef FEAT_VERTSPLIT 1262 temp = curwin->w_vsep_width; 1263 curwin->w_vsep_width = wp->w_vsep_width; 1264 wp->w_vsep_width = temp; 1265 1266 /* If the windows are not in the same frame, exchange the sizes to avoid 1267 * messing up the window layout. Otherwise fix the frame sizes. */ 1268 if (curwin->w_frame->fr_parent != wp->w_frame->fr_parent) 1269 { 1270 temp = curwin->w_height; 1271 curwin->w_height = wp->w_height; 1272 wp->w_height = temp; 1273 temp = curwin->w_width; 1274 curwin->w_width = wp->w_width; 1275 wp->w_width = temp; 1276 } 1277 else 1278 { 1279 frame_fix_height(curwin); 1280 frame_fix_height(wp); 1281 frame_fix_width(curwin); 1282 frame_fix_width(wp); 1283 } 1284 #endif 1285 1286 (void)win_comp_pos(); /* recompute window positions */ 1287 1288 win_enter(wp, TRUE); 1289 redraw_later(CLEAR); 1290 } 1291 1292 /* 1293 * rotate windows: if upwards TRUE the second window becomes the first one 1294 * if upwards FALSE the first window becomes the second one 1295 */ 1296 static void 1297 win_rotate(upwards, count) 1298 int upwards; 1299 int count; 1300 { 1301 win_T *wp1; 1302 win_T *wp2; 1303 frame_T *frp; 1304 int n; 1305 1306 if (firstwin == lastwin) /* nothing to do */ 1307 { 1308 beep_flush(); 1309 return; 1310 } 1311 1312 #ifdef FEAT_GUI 1313 need_mouse_correct = TRUE; 1314 #endif 1315 1316 #ifdef FEAT_VERTSPLIT 1317 /* Check if all frames in this row/col have one window. */ 1318 for (frp = curwin->w_frame->fr_parent->fr_child; frp != NULL; 1319 frp = frp->fr_next) 1320 if (frp->fr_win == NULL) 1321 { 1322 EMSG(_("E443: Cannot rotate when another window is split")); 1323 return; 1324 } 1325 #endif 1326 1327 while (count--) 1328 { 1329 if (upwards) /* first window becomes last window */ 1330 { 1331 /* remove first window/frame from the list */ 1332 frp = curwin->w_frame->fr_parent->fr_child; 1333 wp1 = frp->fr_win; 1334 win_remove(wp1); 1335 frame_remove(frp); 1336 1337 /* find last frame and append removed window/frame after it */ 1338 for ( ; frp->fr_next != NULL; frp = frp->fr_next) 1339 ; 1340 win_append(frp->fr_win, wp1); 1341 frame_append(frp, wp1->w_frame); 1342 1343 wp2 = frp->fr_win; /* previously last window */ 1344 } 1345 else /* last window becomes first window */ 1346 { 1347 /* find last window/frame in the list and remove it */ 1348 for (frp = curwin->w_frame; frp->fr_next != NULL; 1349 frp = frp->fr_next) 1350 ; 1351 wp1 = frp->fr_win; 1352 wp2 = wp1->w_prev; /* will become last window */ 1353 win_remove(wp1); 1354 frame_remove(frp); 1355 1356 /* append the removed window/frame before the first in the list */ 1357 win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1); 1358 frame_insert(frp->fr_parent->fr_child, frp); 1359 } 1360 1361 /* exchange status height and vsep width of old and new last window */ 1362 n = wp2->w_status_height; 1363 wp2->w_status_height = wp1->w_status_height; 1364 wp1->w_status_height = n; 1365 frame_fix_height(wp1); 1366 frame_fix_height(wp2); 1367 #ifdef FEAT_VERTSPLIT 1368 n = wp2->w_vsep_width; 1369 wp2->w_vsep_width = wp1->w_vsep_width; 1370 wp1->w_vsep_width = n; 1371 frame_fix_width(wp1); 1372 frame_fix_width(wp2); 1373 #endif 1374 1375 /* recompute w_winrow and w_wincol for all windows */ 1376 (void)win_comp_pos(); 1377 } 1378 1379 redraw_later(CLEAR); 1380 } 1381 1382 /* 1383 * Move the current window to the very top/bottom/left/right of the screen. 1384 */ 1385 static void 1386 win_totop(size, flags) 1387 int size; 1388 int flags; 1389 { 1390 int dir; 1391 int height = curwin->w_height; 1392 1393 if (lastwin == firstwin) 1394 { 1395 beep_flush(); 1396 return; 1397 } 1398 1399 /* Remove the window and frame from the tree of frames. */ 1400 (void)winframe_remove(curwin, &dir); 1401 win_remove(curwin); 1402 last_status(FALSE); /* may need to remove last status line */ 1403 (void)win_comp_pos(); /* recompute window positions */ 1404 1405 /* Split a window on the desired side and put the window there. */ 1406 (void)win_split_ins(size, flags, curwin, dir); 1407 if (!(flags & WSP_VERT)) 1408 { 1409 win_setheight(height); 1410 if (p_ea) 1411 win_equal(curwin, TRUE, 'v'); 1412 } 1413 1414 #if defined(FEAT_GUI) && defined(FEAT_VERTSPLIT) 1415 /* When 'guioptions' includes 'L' or 'R' may have to remove or add 1416 * scrollbars. Have to update them anyway. */ 1417 if (gui.in_use) 1418 { 1419 out_flush(); 1420 gui_init_which_components(NULL); 1421 gui_update_scrollbars(TRUE); 1422 } 1423 need_mouse_correct = TRUE; 1424 #endif 1425 1426 } 1427 1428 /* 1429 * Move window "win1" to below/right of "win2" and make "win1" the current 1430 * window. Only works within the same frame! 1431 */ 1432 void 1433 win_move_after(win1, win2) 1434 win_T *win1, *win2; 1435 { 1436 int height; 1437 1438 /* check if the arguments are reasonable */ 1439 if (win1 == win2) 1440 return; 1441 1442 /* check if there is something to do */ 1443 if (win2->w_next != win1) 1444 { 1445 /* may need move the status line/vertical separator of the last window 1446 * */ 1447 if (win1 == lastwin) 1448 { 1449 height = win1->w_prev->w_status_height; 1450 win1->w_prev->w_status_height = win1->w_status_height; 1451 win1->w_status_height = height; 1452 #ifdef FEAT_VERTSPLIT 1453 win1->w_prev->w_vsep_width = 0; 1454 win1->w_vsep_width = 1; 1455 #endif 1456 } 1457 else if (win2 == lastwin) 1458 { 1459 height = win1->w_status_height; 1460 win1->w_status_height = win2->w_status_height; 1461 win2->w_status_height = height; 1462 #ifdef FEAT_VERTSPLIT 1463 win2->w_vsep_width = 1; 1464 win1->w_vsep_width = 0; 1465 #endif 1466 } 1467 win_remove(win1); 1468 frame_remove(win1->w_frame); 1469 win_append(win2, win1); 1470 frame_append(win2->w_frame, win1->w_frame); 1471 1472 (void)win_comp_pos(); /* recompute w_winrow for all windows */ 1473 redraw_later(NOT_VALID); 1474 } 1475 win_enter(win1, FALSE); 1476 } 1477 1478 /* 1479 * Make all windows the same height. 1480 * 'next_curwin' will soon be the current window, make sure it has enough 1481 * rows. 1482 */ 1483 void 1484 win_equal(next_curwin, current, dir) 1485 win_T *next_curwin; /* pointer to current window to be or NULL */ 1486 int current; /* do only frame with current window */ 1487 int dir; /* 'v' for vertically, 'h' for horizontally, 1488 'b' for both, 0 for using p_ead */ 1489 { 1490 if (dir == 0) 1491 #ifdef FEAT_VERTSPLIT 1492 dir = *p_ead; 1493 #else 1494 dir = 'b'; 1495 #endif 1496 win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current, 1497 topframe, dir, 0, 0, (int)Columns, topframe->fr_height); 1498 } 1499 1500 /* 1501 * Set a frame to a new position and height, spreading the available room 1502 * equally over contained frames. 1503 * The window "next_curwin" (if not NULL) should at least get the size from 1504 * 'winheight' and 'winwidth' if possible. 1505 */ 1506 static void 1507 win_equal_rec(next_curwin, current, topfr, dir, col, row, width, height) 1508 win_T *next_curwin; /* pointer to current window to be or NULL */ 1509 int current; /* do only frame with current window */ 1510 frame_T *topfr; /* frame to set size off */ 1511 int dir; /* 'v', 'h' or 'b', see win_equal() */ 1512 int col; /* horizontal position for frame */ 1513 int row; /* vertical position for frame */ 1514 int width; /* new width of frame */ 1515 int height; /* new height of frame */ 1516 { 1517 int n, m; 1518 int extra_sep = 0; 1519 int wincount, totwincount = 0; 1520 frame_T *fr; 1521 int next_curwin_size = 0; 1522 int room = 0; 1523 int new_size; 1524 int has_next_curwin = 0; 1525 int hnc; 1526 1527 if (topfr->fr_layout == FR_LEAF) 1528 { 1529 /* Set the width/height of this frame. 1530 * Redraw when size or position changes */ 1531 if (topfr->fr_height != height || topfr->fr_win->w_winrow != row 1532 #ifdef FEAT_VERTSPLIT 1533 || topfr->fr_width != width || topfr->fr_win->w_wincol != col 1534 #endif 1535 ) 1536 { 1537 topfr->fr_win->w_winrow = row; 1538 frame_new_height(topfr, height, FALSE, FALSE); 1539 #ifdef FEAT_VERTSPLIT 1540 topfr->fr_win->w_wincol = col; 1541 frame_new_width(topfr, width, FALSE); 1542 #endif 1543 redraw_all_later(CLEAR); 1544 } 1545 } 1546 #ifdef FEAT_VERTSPLIT 1547 else if (topfr->fr_layout == FR_ROW) 1548 { 1549 topfr->fr_width = width; 1550 topfr->fr_height = height; 1551 1552 if (dir != 'v') /* equalize frame widths */ 1553 { 1554 /* Compute the maximum number of windows horizontally in this 1555 * frame. */ 1556 n = frame_minwidth(topfr, NOWIN); 1557 /* add one for the rightmost window, it doesn't have a separator */ 1558 if (col + width == Columns) 1559 extra_sep = 1; 1560 else 1561 extra_sep = 0; 1562 totwincount = (n + extra_sep) / (p_wmw + 1); 1563 1564 /* Compute room available for windows other than "next_curwin" */ 1565 m = frame_minwidth(topfr, next_curwin); 1566 room = width - m; 1567 if (room < 0) 1568 { 1569 next_curwin_size = p_wiw + room; 1570 room = 0; 1571 } 1572 else if (n == m) /* doesn't contain curwin */ 1573 next_curwin_size = 0; 1574 else 1575 { 1576 next_curwin_size = (room + p_wiw + (totwincount - 1) * p_wmw 1577 + (totwincount - 1)) / totwincount; 1578 if (next_curwin_size > p_wiw) 1579 room -= next_curwin_size - p_wiw; 1580 else 1581 next_curwin_size = p_wiw; 1582 } 1583 if (n != m) 1584 --totwincount; /* don't count curwin */ 1585 } 1586 1587 for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) 1588 { 1589 n = m = 0; 1590 wincount = 1; 1591 if (fr->fr_next == NULL) 1592 /* last frame gets all that remains (avoid roundoff error) */ 1593 new_size = width; 1594 else if (dir == 'v') 1595 new_size = fr->fr_width; 1596 else 1597 { 1598 /* Compute the maximum number of windows horiz. in "fr". */ 1599 n = frame_minwidth(fr, NOWIN); 1600 wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) 1601 / (p_wmw + 1); 1602 m = frame_minwidth(fr, next_curwin); 1603 if (n != m) /* don't count next_curwin */ 1604 --wincount; 1605 new_size = (wincount * room + ((unsigned)totwincount >> 1)) 1606 / totwincount; 1607 if (n != m) /* add next_curwin size */ 1608 { 1609 next_curwin_size -= p_wiw - (m - n); 1610 new_size += next_curwin_size; 1611 } 1612 } 1613 1614 /* Skip frame that is full height when splitting or closing a 1615 * window, unless equalizing all frames. */ 1616 if (!current || dir != 'v' || topfr->fr_parent != NULL 1617 || (new_size != fr->fr_width) 1618 || frame_has_win(fr, next_curwin)) 1619 win_equal_rec(next_curwin, current, fr, dir, col, row, 1620 new_size + n, height); 1621 col += new_size + n; 1622 width -= new_size + n; 1623 if (n != m) /* contains curwin */ 1624 room -= new_size - next_curwin_size; 1625 else 1626 room -= new_size; 1627 totwincount -= wincount; 1628 } 1629 } 1630 #endif 1631 else /* topfr->fr_layout == FR_COL */ 1632 { 1633 #ifdef FEAT_VERTSPLIT 1634 topfr->fr_width = width; 1635 #endif 1636 topfr->fr_height = height; 1637 1638 if (dir != 'h') /* equalize frame heights */ 1639 { 1640 /* Compute maximum number of windows vertically in this frame. */ 1641 n = frame_minheight(topfr, NOWIN); 1642 /* add one for the bottom window if it doesn't have a statusline */ 1643 if (row + height == cmdline_row && p_ls == 0) 1644 extra_sep = 1; 1645 else 1646 extra_sep = 0; 1647 totwincount = (n + extra_sep) / (p_wmh + 1); 1648 has_next_curwin = frame_has_win(topfr, next_curwin); 1649 1650 /* 1651 * Compute height for "next_curwin" window and room available for 1652 * other windows. 1653 * "m" is the minimal height when counting p_wh for "next_curwin". 1654 */ 1655 m = frame_minheight(topfr, next_curwin); 1656 room = height - m; 1657 if (room < 0) 1658 { 1659 /* The room is less then 'winheight', use all space for the 1660 * current window. */ 1661 next_curwin_size = p_wh + room; 1662 room = 0; 1663 } 1664 else 1665 { 1666 next_curwin_size = -1; 1667 for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) 1668 { 1669 /* If 'winfixheight' set keep the window height if 1670 * possible. 1671 * Watch out for this window being the next_curwin. */ 1672 if (frame_fixed_height(fr)) 1673 { 1674 n = frame_minheight(fr, NOWIN); 1675 new_size = fr->fr_height; 1676 if (frame_has_win(fr, next_curwin)) 1677 { 1678 room += p_wh - p_wmh; 1679 next_curwin_size = 0; 1680 if (new_size < p_wh) 1681 new_size = p_wh; 1682 } 1683 else 1684 /* These windows don't use up room. */ 1685 totwincount -= (n + (fr->fr_next == NULL 1686 ? extra_sep : 0)) / (p_wmh + 1); 1687 room -= new_size - n; 1688 if (room < 0) 1689 { 1690 new_size += room; 1691 room = 0; 1692 } 1693 fr->fr_newheight = new_size; 1694 } 1695 } 1696 if (next_curwin_size == -1) 1697 { 1698 if (!has_next_curwin) 1699 next_curwin_size = 0; 1700 else if (totwincount > 1 1701 && (room + (totwincount - 2)) 1702 / (totwincount - 1) > p_wh) 1703 { 1704 next_curwin_size = (room + p_wh + totwincount * p_wmh 1705 + (totwincount - 1)) / totwincount; 1706 room -= next_curwin_size - p_wh; 1707 } 1708 else 1709 next_curwin_size = p_wh; 1710 } 1711 } 1712 1713 if (has_next_curwin) 1714 --totwincount; /* don't count curwin */ 1715 } 1716 1717 for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) 1718 { 1719 n = m = 0; 1720 wincount = 1; 1721 if (fr->fr_next == NULL) 1722 /* last frame gets all that remains (avoid roundoff error) */ 1723 new_size = height; 1724 else if (dir == 'h') 1725 new_size = fr->fr_height; 1726 else if (frame_fixed_height(fr)) 1727 { 1728 new_size = fr->fr_newheight; 1729 wincount = 0; /* doesn't count as a sizeable window */ 1730 } 1731 else 1732 { 1733 /* Compute the maximum number of windows vert. in "fr". */ 1734 n = frame_minheight(fr, NOWIN); 1735 wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) 1736 / (p_wmh + 1); 1737 m = frame_minheight(fr, next_curwin); 1738 if (has_next_curwin) 1739 hnc = frame_has_win(fr, next_curwin); 1740 else 1741 hnc = FALSE; 1742 if (hnc) /* don't count next_curwin */ 1743 --wincount; 1744 if (totwincount == 0) 1745 new_size = room; 1746 else 1747 new_size = (wincount * room + ((unsigned)totwincount >> 1)) 1748 / totwincount; 1749 if (hnc) /* add next_curwin size */ 1750 { 1751 next_curwin_size -= p_wh - (m - n); 1752 new_size += next_curwin_size; 1753 room -= new_size - next_curwin_size; 1754 } 1755 else 1756 room -= new_size; 1757 new_size += n; 1758 } 1759 /* Skip frame that is full width when splitting or closing a 1760 * window, unless equalizing all frames. */ 1761 if (!current || dir != 'h' || topfr->fr_parent != NULL 1762 || (new_size != fr->fr_height) 1763 || frame_has_win(fr, next_curwin)) 1764 win_equal_rec(next_curwin, current, fr, dir, col, row, 1765 width, new_size); 1766 row += new_size; 1767 height -= new_size; 1768 totwincount -= wincount; 1769 } 1770 } 1771 } 1772 1773 /* 1774 * close all windows for buffer 'buf' 1775 */ 1776 void 1777 close_windows(buf) 1778 buf_T *buf; 1779 { 1780 win_T *win; 1781 1782 ++RedrawingDisabled; 1783 for (win = firstwin; win != NULL && lastwin != firstwin; ) 1784 { 1785 if (win->w_buffer == buf) 1786 { 1787 win_close(win, FALSE); 1788 win = firstwin; /* go back to the start */ 1789 } 1790 else 1791 win = win->w_next; 1792 } 1793 --RedrawingDisabled; 1794 } 1795 1796 /* 1797 * close window "win" 1798 * If "free_buf" is TRUE related buffer may be unloaded. 1799 * 1800 * called by :quit, :close, :xit, :wq and findtag() 1801 */ 1802 void 1803 win_close(win, free_buf) 1804 win_T *win; 1805 int free_buf; 1806 { 1807 win_T *wp; 1808 #ifdef FEAT_AUTOCMD 1809 int other_buffer = FALSE; 1810 #endif 1811 int close_curwin = FALSE; 1812 int dir; 1813 int help_window = FALSE; 1814 1815 if (lastwin == firstwin) 1816 { 1817 EMSG(_("E444: Cannot close last window")); 1818 return; 1819 } 1820 1821 /* When closing the help window, try restoring a snapshot after closing 1822 * the window. Otherwise clear the snapshot, it's now invalid. */ 1823 if (win->w_buffer->b_help) 1824 help_window = TRUE; 1825 else 1826 clear_snapshot(); 1827 1828 #ifdef FEAT_AUTOCMD 1829 if (win == curwin) 1830 { 1831 /* 1832 * Guess which window is going to be the new current window. 1833 * This may change because of the autocommands (sigh). 1834 */ 1835 wp = frame2win(win_altframe(win)); 1836 1837 /* 1838 * Be careful: If autocommands delete the window, return now. 1839 */ 1840 if (wp->w_buffer != curbuf) 1841 { 1842 other_buffer = TRUE; 1843 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); 1844 if (!win_valid(win) || firstwin == lastwin) 1845 return; 1846 } 1847 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); 1848 if (!win_valid(win) || firstwin == lastwin) 1849 return; 1850 # ifdef FEAT_EVAL 1851 /* autocmds may abort script processing */ 1852 if (aborting()) 1853 return; 1854 # endif 1855 } 1856 #endif 1857 1858 /* 1859 * Close the link to the buffer. 1860 */ 1861 close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0); 1862 /* Autocommands may have closed the window already, or closed the only 1863 * other window. */ 1864 if (!win_valid(win) || firstwin == lastwin) 1865 return; 1866 1867 /* Free the memory used for the window. */ 1868 wp = win_free_mem(win, &dir); 1869 1870 /* Make sure curwin isn't invalid. It can cause severe trouble when 1871 * printing an error message. For win_equal() curbuf needs to be valid 1872 * too. */ 1873 if (win == curwin) 1874 { 1875 curwin = wp; 1876 #ifdef FEAT_QUICKFIX 1877 if (wp->w_p_pvw || bt_quickfix(wp->w_buffer)) 1878 { 1879 /* 1880 * The cursor goes to the preview or the quickfix window, try 1881 * finding another window to go to. 1882 */ 1883 for (;;) 1884 { 1885 if (wp->w_next == NULL) 1886 wp = firstwin; 1887 else 1888 wp = wp->w_next; 1889 if (wp == curwin) 1890 break; 1891 if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer)) 1892 { 1893 curwin = wp; 1894 break; 1895 } 1896 } 1897 } 1898 #endif 1899 curbuf = curwin->w_buffer; 1900 close_curwin = TRUE; 1901 } 1902 if (p_ea 1903 #ifdef FEAT_VERTSPLIT 1904 && (*p_ead == 'b' || *p_ead == dir) 1905 #endif 1906 ) 1907 win_equal(curwin, TRUE, 1908 #ifdef FEAT_VERTSPLIT 1909 dir 1910 #else 1911 0 1912 #endif 1913 ); 1914 else 1915 win_comp_pos(); 1916 if (close_curwin) 1917 { 1918 win_enter_ext(wp, FALSE, TRUE); 1919 #ifdef FEAT_AUTOCMD 1920 if (other_buffer) 1921 /* careful: after this wp and win may be invalid! */ 1922 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 1923 #endif 1924 } 1925 1926 /* 1927 * if last window has a status line now and we don't want one, 1928 * remove the status line 1929 */ 1930 last_status(FALSE); 1931 1932 /* After closing the help window, try restoring the window layout from 1933 * before it was opened. */ 1934 if (help_window) 1935 restore_snapshot(close_curwin); 1936 1937 #if defined(FEAT_GUI) && defined(FEAT_VERTSPLIT) 1938 /* When 'guioptions' includes 'L' or 'R' may have to remove scrollbars. */ 1939 if (gui.in_use && !win_hasvertsplit()) 1940 gui_init_which_components(NULL); 1941 #endif 1942 1943 redraw_all_later(NOT_VALID); 1944 } 1945 1946 /* 1947 * Free the memory used for a window. 1948 * Returns a pointer to the window that got the freed up space. 1949 */ 1950 static win_T * 1951 win_free_mem(win, dirp) 1952 win_T *win; 1953 int *dirp; /* set to 'v' or 'h' for direction if 'ea' */ 1954 { 1955 frame_T *frp; 1956 win_T *wp; 1957 1958 #ifdef FEAT_FOLDING 1959 clearFolding(win); 1960 #endif 1961 1962 /* reduce the reference count to the argument list. */ 1963 alist_unlink(win->w_alist); 1964 1965 /* remove the window and its frame from the tree of frames. */ 1966 frp = win->w_frame; 1967 wp = winframe_remove(win, dirp); 1968 vim_free(frp); 1969 win_free(win); 1970 1971 return wp; 1972 } 1973 1974 #if defined(EXITFREE) || defined(PROTO) 1975 void 1976 win_free_all() 1977 { 1978 int dummy; 1979 1980 while (firstwin != NULL) 1981 (void)win_free_mem(firstwin, &dummy); 1982 } 1983 #endif 1984 1985 /* 1986 * Remove a window and its frame from the tree of frames. 1987 * Returns a pointer to the window that got the freed up space. 1988 */ 1989 /*ARGSUSED*/ 1990 static win_T * 1991 winframe_remove(win, dirp) 1992 win_T *win; 1993 int *dirp; /* set to 'v' or 'h' for direction if 'ea' */ 1994 { 1995 frame_T *frp, *frp2, *frp3; 1996 frame_T *frp_close = win->w_frame; 1997 win_T *wp; 1998 int old_height = 0; 1999 2000 /* 2001 * Remove the window from its frame. 2002 */ 2003 frp2 = win_altframe(win); 2004 if (frp2 == NULL) 2005 return NULL; /* deleted the last frame */ 2006 2007 wp = frame2win(frp2); 2008 2009 /* Remove this frame from the list of frames. */ 2010 frame_remove(frp_close); 2011 2012 #ifdef FEAT_VERTSPLIT 2013 if (frp_close->fr_parent->fr_layout == FR_COL) 2014 { 2015 #endif 2016 /* When 'winfixheight' is set, remember its old size and restore 2017 * it later (it's a simplistic solution...). Don't do this if the 2018 * window will occupy the full height of the screen. */ 2019 if (frp2->fr_win != NULL 2020 && (frp2->fr_next != NULL || frp2->fr_prev != NULL) 2021 && frp2->fr_win->w_p_wfh) 2022 old_height = frp2->fr_win->w_height; 2023 frame_new_height(frp2, frp2->fr_height + frp_close->fr_height, 2024 frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE); 2025 if (old_height != 0) 2026 win_setheight_win(old_height, frp2->fr_win); 2027 #ifdef FEAT_VERTSPLIT 2028 *dirp = 'v'; 2029 } 2030 else 2031 { 2032 frame_new_width(frp2, frp2->fr_width + frp_close->fr_width, 2033 frp2 == frp_close->fr_next ? TRUE : FALSE); 2034 *dirp = 'h'; 2035 } 2036 #endif 2037 2038 /* If rows/columns go to a window below/right its positions need to be 2039 * updated. Can only be done after the sizes have been updated. */ 2040 if (frp2 == frp_close->fr_next) 2041 { 2042 int row = win->w_winrow; 2043 int col = W_WINCOL(win); 2044 2045 frame_comp_pos(frp2, &row, &col); 2046 } 2047 2048 if (frp2->fr_next == NULL && frp2->fr_prev == NULL) 2049 { 2050 /* There is no other frame in this list, move its info to the parent 2051 * and remove it. */ 2052 frp2->fr_parent->fr_layout = frp2->fr_layout; 2053 frp2->fr_parent->fr_child = frp2->fr_child; 2054 for (frp = frp2->fr_child; frp != NULL; frp = frp->fr_next) 2055 frp->fr_parent = frp2->fr_parent; 2056 frp2->fr_parent->fr_win = frp2->fr_win; 2057 if (frp2->fr_win != NULL) 2058 frp2->fr_win->w_frame = frp2->fr_parent; 2059 frp = frp2->fr_parent; 2060 vim_free(frp2); 2061 2062 frp2 = frp->fr_parent; 2063 if (frp2 != NULL && frp2->fr_layout == frp->fr_layout) 2064 { 2065 /* The frame above the parent has the same layout, have to merge 2066 * the frames into this list. */ 2067 if (frp2->fr_child == frp) 2068 frp2->fr_child = frp->fr_child; 2069 frp->fr_child->fr_prev = frp->fr_prev; 2070 if (frp->fr_prev != NULL) 2071 frp->fr_prev->fr_next = frp->fr_child; 2072 for (frp3 = frp->fr_child; ; frp3 = frp3->fr_next) 2073 { 2074 frp3->fr_parent = frp2; 2075 if (frp3->fr_next == NULL) 2076 { 2077 frp3->fr_next = frp->fr_next; 2078 if (frp->fr_next != NULL) 2079 frp->fr_next->fr_prev = frp3; 2080 break; 2081 } 2082 } 2083 vim_free(frp); 2084 } 2085 } 2086 2087 return wp; 2088 } 2089 2090 /* 2091 * Find out which frame is going to get the freed up space when "win" is 2092 * closed. 2093 * if 'splitbelow'/'splitleft' the space goes to the window above/left. 2094 * if 'nosplitbelow'/'nosplitleft' the space goes to the window below/right. 2095 * This makes opening a window and closing it immediately keep the same window 2096 * layout. 2097 */ 2098 static frame_T * 2099 win_altframe(win) 2100 win_T *win; 2101 { 2102 frame_T *frp; 2103 int b; 2104 2105 frp = win->w_frame; 2106 #ifdef FEAT_VERTSPLIT 2107 if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW) 2108 b = p_spr; 2109 else 2110 #endif 2111 b = p_sb; 2112 if ((!b && frp->fr_next != NULL) || frp->fr_prev == NULL) 2113 return frp->fr_next; 2114 return frp->fr_prev; 2115 } 2116 2117 /* 2118 * Find the left-upper window in frame "frp". 2119 */ 2120 static win_T * 2121 frame2win(frp) 2122 frame_T *frp; 2123 { 2124 while (frp->fr_win == NULL) 2125 frp = frp->fr_child; 2126 return frp->fr_win; 2127 } 2128 2129 /* 2130 * Return TRUE if frame "frp" contains window "wp". 2131 */ 2132 static int 2133 frame_has_win(frp, wp) 2134 frame_T *frp; 2135 win_T *wp; 2136 { 2137 frame_T *p; 2138 2139 if (frp->fr_layout == FR_LEAF) 2140 return frp->fr_win == wp; 2141 2142 for (p = frp->fr_child; p != NULL; p = p->fr_next) 2143 if (frame_has_win(p, wp)) 2144 return TRUE; 2145 return FALSE; 2146 } 2147 2148 /* 2149 * Set a new height for a frame. Recursively sets the height for contained 2150 * frames and windows. Caller must take care of positions. 2151 */ 2152 static void 2153 frame_new_height(topfrp, height, topfirst, wfh) 2154 frame_T *topfrp; 2155 int height; 2156 int topfirst; /* resize topmost contained frame first */ 2157 int wfh; /* obey 'winfixheight' when there is a choice; 2158 may cause the height not to be set */ 2159 { 2160 frame_T *frp; 2161 int extra_lines; 2162 int h; 2163 2164 if (topfrp->fr_win != NULL) 2165 { 2166 /* Simple case: just one window. */ 2167 win_new_height(topfrp->fr_win, 2168 height - topfrp->fr_win->w_status_height); 2169 } 2170 #ifdef FEAT_VERTSPLIT 2171 else if (topfrp->fr_layout == FR_ROW) 2172 { 2173 do 2174 { 2175 /* All frames in this row get the same new height. */ 2176 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 2177 { 2178 frame_new_height(frp, height, topfirst, wfh); 2179 if (frp->fr_height > height) 2180 { 2181 /* Could not fit the windows, make the whole row higher. */ 2182 height = frp->fr_height; 2183 break; 2184 } 2185 } 2186 } 2187 while (frp != NULL); 2188 } 2189 #endif 2190 else 2191 { 2192 /* Complicated case: Resize a column of frames. Resize the bottom 2193 * frame first, frames above that when needed. */ 2194 2195 frp = topfrp->fr_child; 2196 if (wfh) 2197 /* Advance past frames with one window with 'wfh' set. */ 2198 while (frame_fixed_height(frp)) 2199 { 2200 frp = frp->fr_next; 2201 if (frp == NULL) 2202 return; /* no frame without 'wfh', give up */ 2203 } 2204 if (!topfirst) 2205 { 2206 /* Find the bottom frame of this column */ 2207 while (frp->fr_next != NULL) 2208 frp = frp->fr_next; 2209 if (wfh) 2210 /* Advance back for frames with one window with 'wfh' set. */ 2211 while (frame_fixed_height(frp)) 2212 frp = frp->fr_prev; 2213 } 2214 2215 extra_lines = height - topfrp->fr_height; 2216 if (extra_lines < 0) 2217 { 2218 /* reduce height of contained frames, bottom or top frame first */ 2219 while (frp != NULL) 2220 { 2221 h = frame_minheight(frp, NULL); 2222 if (frp->fr_height + extra_lines < h) 2223 { 2224 extra_lines += frp->fr_height - h; 2225 frame_new_height(frp, h, topfirst, wfh); 2226 } 2227 else 2228 { 2229 frame_new_height(frp, frp->fr_height + extra_lines, 2230 topfirst, wfh); 2231 break; 2232 } 2233 if (topfirst) 2234 { 2235 do 2236 frp = frp->fr_next; 2237 while (wfh && frp != NULL && frame_fixed_height(frp)); 2238 } 2239 else 2240 { 2241 do 2242 frp = frp->fr_prev; 2243 while (wfh && frp != NULL && frame_fixed_height(frp)); 2244 } 2245 /* Increase "height" if we could not reduce enough frames. */ 2246 if (frp == NULL) 2247 height -= extra_lines; 2248 } 2249 } 2250 else if (extra_lines > 0) 2251 { 2252 /* increase height of bottom or top frame */ 2253 frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh); 2254 } 2255 } 2256 topfrp->fr_height = height; 2257 } 2258 2259 /* 2260 * Return TRUE if height of frame "frp" should not be changed because of 2261 * the 'winfixheight' option. 2262 */ 2263 static int 2264 frame_fixed_height(frp) 2265 frame_T *frp; 2266 { 2267 /* frame with one window: fixed height if 'winfixheight' set. */ 2268 if (frp->fr_win != NULL) 2269 return frp->fr_win->w_p_wfh; 2270 2271 if (frp->fr_layout == FR_ROW) 2272 { 2273 /* The frame is fixed height if one of the frames in the row is fixed 2274 * height. */ 2275 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 2276 if (frame_fixed_height(frp)) 2277 return TRUE; 2278 return FALSE; 2279 } 2280 2281 /* frp->fr_layout == FR_COL: The frame is fixed height if all of the 2282 * frames in the row are fixed height. */ 2283 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 2284 if (!frame_fixed_height(frp)) 2285 return FALSE; 2286 return TRUE; 2287 } 2288 2289 #ifdef FEAT_VERTSPLIT 2290 /* 2291 * Add a status line to windows at the bottom of "frp". 2292 * Note: Does not check if there is room! 2293 */ 2294 static void 2295 frame_add_statusline(frp) 2296 frame_T *frp; 2297 { 2298 win_T *wp; 2299 2300 if (frp->fr_layout == FR_LEAF) 2301 { 2302 wp = frp->fr_win; 2303 if (wp->w_status_height == 0) 2304 { 2305 if (wp->w_height > 0) /* don't make it negative */ 2306 --wp->w_height; 2307 wp->w_status_height = STATUS_HEIGHT; 2308 } 2309 } 2310 else if (frp->fr_layout == FR_ROW) 2311 { 2312 /* Handle all the frames in the row. */ 2313 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 2314 frame_add_statusline(frp); 2315 } 2316 else /* frp->fr_layout == FR_COL */ 2317 { 2318 /* Only need to handle the last frame in the column. */ 2319 for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) 2320 ; 2321 frame_add_statusline(frp); 2322 } 2323 } 2324 2325 /* 2326 * Set width of a frame. Handles recursively going through contained frames. 2327 * May remove separator line for windows at the right side (for win_close()). 2328 */ 2329 static void 2330 frame_new_width(topfrp, width, leftfirst) 2331 frame_T *topfrp; 2332 int width; 2333 int leftfirst; /* resize leftmost contained frame first */ 2334 { 2335 frame_T *frp; 2336 int extra_cols; 2337 int w; 2338 win_T *wp; 2339 2340 if (topfrp->fr_layout == FR_LEAF) 2341 { 2342 /* Simple case: just one window. */ 2343 wp = topfrp->fr_win; 2344 /* Find out if there are any windows right of this one. */ 2345 for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent) 2346 if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL) 2347 break; 2348 if (frp->fr_parent == NULL) 2349 wp->w_vsep_width = 0; 2350 win_new_width(wp, width - wp->w_vsep_width); 2351 } 2352 else if (topfrp->fr_layout == FR_COL) 2353 { 2354 /* All frames in this column get the same new width. */ 2355 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 2356 frame_new_width(frp, width, leftfirst); 2357 } 2358 else /* fr_layout == FR_ROW */ 2359 { 2360 /* Complicated case: Resize a row of frames. Resize the rightmost 2361 * frame first, frames left of it when needed. */ 2362 2363 /* Find the rightmost frame of this row */ 2364 frp = topfrp->fr_child; 2365 if (!leftfirst) 2366 while (frp->fr_next != NULL) 2367 frp = frp->fr_next; 2368 2369 extra_cols = width - topfrp->fr_width; 2370 if (extra_cols < 0) 2371 { 2372 /* reduce frame width, rightmost frame first */ 2373 while (frp != NULL) 2374 { 2375 w = frame_minwidth(frp, NULL); 2376 if (frp->fr_width + extra_cols < w) 2377 { 2378 extra_cols += frp->fr_width - w; 2379 frame_new_width(frp, w, leftfirst); 2380 } 2381 else 2382 { 2383 frame_new_width(frp, frp->fr_width + extra_cols, leftfirst); 2384 break; 2385 } 2386 if (leftfirst) 2387 frp = frp->fr_next; 2388 else 2389 frp = frp->fr_prev; 2390 } 2391 } 2392 else if (extra_cols > 0) 2393 { 2394 /* increase width of rightmost frame */ 2395 frame_new_width(frp, frp->fr_width + extra_cols, leftfirst); 2396 } 2397 } 2398 topfrp->fr_width = width; 2399 } 2400 2401 /* 2402 * Add the vertical separator to windows at the right side of "frp". 2403 * Note: Does not check if there is room! 2404 */ 2405 static void 2406 frame_add_vsep(frp) 2407 frame_T *frp; 2408 { 2409 win_T *wp; 2410 2411 if (frp->fr_layout == FR_LEAF) 2412 { 2413 wp = frp->fr_win; 2414 if (wp->w_vsep_width == 0) 2415 { 2416 if (wp->w_width > 0) /* don't make it negative */ 2417 --wp->w_width; 2418 wp->w_vsep_width = 1; 2419 } 2420 } 2421 else if (frp->fr_layout == FR_COL) 2422 { 2423 /* Handle all the frames in the column. */ 2424 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 2425 frame_add_vsep(frp); 2426 } 2427 else /* frp->fr_layout == FR_ROW */ 2428 { 2429 /* Only need to handle the last frame in the row. */ 2430 frp = frp->fr_child; 2431 while (frp->fr_next != NULL) 2432 frp = frp->fr_next; 2433 frame_add_vsep(frp); 2434 } 2435 } 2436 2437 /* 2438 * Set frame width from the window it contains. 2439 */ 2440 static void 2441 frame_fix_width(wp) 2442 win_T *wp; 2443 { 2444 wp->w_frame->fr_width = wp->w_width + wp->w_vsep_width; 2445 } 2446 #endif 2447 2448 /* 2449 * Set frame height from the window it contains. 2450 */ 2451 static void 2452 frame_fix_height(wp) 2453 win_T *wp; 2454 { 2455 wp->w_frame->fr_height = wp->w_height + wp->w_status_height; 2456 } 2457 2458 /* 2459 * Compute the minimal height for frame "topfrp". 2460 * Uses the 'winminheight' option. 2461 * When "next_curwin" isn't NULL, use p_wh for this window. 2462 * When "next_curwin" is NOWIN, don't use at least one line for the current 2463 * window. 2464 */ 2465 static int 2466 frame_minheight(topfrp, next_curwin) 2467 frame_T *topfrp; 2468 win_T *next_curwin; 2469 { 2470 frame_T *frp; 2471 int m; 2472 #ifdef FEAT_VERTSPLIT 2473 int n; 2474 #endif 2475 2476 if (topfrp->fr_win != NULL) 2477 { 2478 if (topfrp->fr_win == next_curwin) 2479 m = p_wh + topfrp->fr_win->w_status_height; 2480 else 2481 { 2482 /* window: minimal height of the window plus status line */ 2483 m = p_wmh + topfrp->fr_win->w_status_height; 2484 /* Current window is minimal one line high */ 2485 if (p_wmh == 0 && topfrp->fr_win == curwin && next_curwin == NULL) 2486 ++m; 2487 } 2488 } 2489 #ifdef FEAT_VERTSPLIT 2490 else if (topfrp->fr_layout == FR_ROW) 2491 { 2492 /* get the minimal height from each frame in this row */ 2493 m = 0; 2494 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 2495 { 2496 n = frame_minheight(frp, next_curwin); 2497 if (n > m) 2498 m = n; 2499 } 2500 } 2501 #endif 2502 else 2503 { 2504 /* Add up the minimal heights for all frames in this column. */ 2505 m = 0; 2506 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 2507 m += frame_minheight(frp, next_curwin); 2508 } 2509 2510 return m; 2511 } 2512 2513 #ifdef FEAT_VERTSPLIT 2514 /* 2515 * Compute the minimal width for frame "topfrp". 2516 * When "next_curwin" isn't NULL, use p_wiw for this window. 2517 * When "next_curwin" is NOWIN, don't use at least one column for the current 2518 * window. 2519 */ 2520 static int 2521 frame_minwidth(topfrp, next_curwin) 2522 frame_T *topfrp; 2523 win_T *next_curwin; /* use p_wh and p_wiw for next_curwin */ 2524 { 2525 frame_T *frp; 2526 int m, n; 2527 2528 if (topfrp->fr_win != NULL) 2529 { 2530 if (topfrp->fr_win == next_curwin) 2531 m = p_wiw + topfrp->fr_win->w_vsep_width; 2532 else 2533 { 2534 /* window: minimal width of the window plus separator column */ 2535 m = p_wmw + topfrp->fr_win->w_vsep_width; 2536 /* Current window is minimal one column wide */ 2537 if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) 2538 ++m; 2539 } 2540 } 2541 else if (topfrp->fr_layout == FR_COL) 2542 { 2543 /* get the minimal width from each frame in this column */ 2544 m = 0; 2545 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 2546 { 2547 n = frame_minwidth(frp, next_curwin); 2548 if (n > m) 2549 m = n; 2550 } 2551 } 2552 else 2553 { 2554 /* Add up the minimal widths for all frames in this row. */ 2555 m = 0; 2556 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 2557 m += frame_minwidth(frp, next_curwin); 2558 } 2559 2560 return m; 2561 } 2562 #endif 2563 2564 2565 /* 2566 * Try to close all windows except current one. 2567 * Buffers in the other windows become hidden if 'hidden' is set, or '!' is 2568 * used and the buffer was modified. 2569 * 2570 * Used by ":bdel" and ":only". 2571 */ 2572 void 2573 close_others(message, forceit) 2574 int message; 2575 int forceit; /* always hide all other windows */ 2576 { 2577 win_T *wp; 2578 win_T *nextwp; 2579 int r; 2580 2581 if (lastwin == firstwin) 2582 { 2583 if (message 2584 #ifdef FEAT_AUTOCMD 2585 && !autocmd_busy 2586 #endif 2587 ) 2588 MSG(_("Already only one window")); 2589 return; 2590 } 2591 2592 /* Be very careful here: autocommands may change the window layout. */ 2593 for (wp = firstwin; win_valid(wp); wp = nextwp) 2594 { 2595 nextwp = wp->w_next; 2596 if (wp != curwin) /* don't close current window */ 2597 { 2598 2599 /* Check if it's allowed to abandon this window */ 2600 r = can_abandon(wp->w_buffer, forceit); 2601 #ifdef FEAT_AUTOCMD 2602 if (!win_valid(wp)) /* autocommands messed wp up */ 2603 { 2604 nextwp = firstwin; 2605 continue; 2606 } 2607 #endif 2608 if (!r) 2609 { 2610 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) 2611 if (message && (p_confirm || cmdmod.confirm) && p_write) 2612 { 2613 dialog_changed(wp->w_buffer, FALSE); 2614 # ifdef FEAT_AUTOCMD 2615 if (!win_valid(wp)) /* autocommands messed wp up */ 2616 { 2617 nextwp = firstwin; 2618 continue; 2619 } 2620 # endif 2621 } 2622 if (bufIsChanged(wp->w_buffer)) 2623 #endif 2624 continue; 2625 } 2626 win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); 2627 } 2628 } 2629 2630 /* 2631 * If current window has a status line and we don't want one, 2632 * remove the status line. 2633 */ 2634 if (lastwin != firstwin) 2635 EMSG(_("E445: Other window contains changes")); 2636 } 2637 2638 #endif /* FEAT_WINDOWS */ 2639 2640 /* 2641 * init the cursor in the window 2642 * 2643 * called when a new file is being edited 2644 */ 2645 void 2646 win_init(wp) 2647 win_T *wp; 2648 { 2649 redraw_win_later(wp, NOT_VALID); 2650 wp->w_lines_valid = 0; 2651 wp->w_cursor.lnum = 1; 2652 wp->w_curswant = wp->w_cursor.col = 0; 2653 #ifdef FEAT_VIRTUALEDIT 2654 wp->w_cursor.coladd = 0; 2655 #endif 2656 wp->w_pcmark.lnum = 1; /* pcmark not cleared but set to line 1 */ 2657 wp->w_pcmark.col = 0; 2658 wp->w_prev_pcmark.lnum = 0; 2659 wp->w_prev_pcmark.col = 0; 2660 wp->w_topline = 1; 2661 #ifdef FEAT_DIFF 2662 wp->w_topfill = 0; 2663 #endif 2664 wp->w_botline = 2; 2665 #ifdef FEAT_FKMAP 2666 if (curwin->w_p_rl) 2667 wp->w_farsi = W_CONV + W_R_L; 2668 else 2669 wp->w_farsi = W_CONV; 2670 #endif 2671 } 2672 2673 /* 2674 * Allocate the first window and put an empty buffer in it. 2675 * Called from main(). 2676 * When this fails we can't do anything: exit. 2677 */ 2678 void 2679 win_alloc_first() 2680 { 2681 curwin = win_alloc(NULL); 2682 curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED); 2683 if (curwin == NULL || curbuf == NULL) 2684 mch_exit(0); 2685 curwin->w_buffer = curbuf; 2686 curbuf->b_nwindows = 1; /* there is one window */ 2687 #ifdef FEAT_WINDOWS 2688 curwin->w_alist = &global_alist; 2689 #endif 2690 win_init(curwin); /* init current window */ 2691 2692 topframe = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 2693 if (topframe == NULL) 2694 mch_exit(0); 2695 topframe->fr_layout = FR_LEAF; 2696 #ifdef FEAT_VERTSPLIT 2697 topframe->fr_width = Columns; 2698 #endif 2699 topframe->fr_height = Rows - p_ch; 2700 #ifdef FEAT_WINDOWS 2701 p_ch_used = p_ch; 2702 #endif 2703 topframe->fr_win = curwin; 2704 curwin->w_frame = topframe; 2705 } 2706 2707 #if defined(FEAT_WINDOWS) || defined(PROTO) 2708 2709 /* 2710 * Go to another window. 2711 * When jumping to another buffer, stop Visual mode. Do this before 2712 * changing windows so we can yank the selection into the '*' register. 2713 * When jumping to another window on the same buffer, adjust its cursor 2714 * position to keep the same Visual area. 2715 */ 2716 void 2717 win_goto(wp) 2718 win_T *wp; 2719 { 2720 #ifdef FEAT_CMDWIN 2721 if (cmdwin_type != 0) 2722 { 2723 beep_flush(); 2724 return; 2725 } 2726 #endif 2727 #ifdef FEAT_VISUAL 2728 if (wp->w_buffer != curbuf) 2729 reset_VIsual_and_resel(); 2730 else if (VIsual_active) 2731 wp->w_cursor = curwin->w_cursor; 2732 #endif 2733 2734 #ifdef FEAT_GUI 2735 need_mouse_correct = TRUE; 2736 #endif 2737 win_enter(wp, TRUE); 2738 } 2739 2740 #if defined(FEAT_PERL) || defined(PROTO) 2741 /* 2742 * Find window number "winnr" (counting top to bottom). 2743 */ 2744 win_T * 2745 win_find_nr(winnr) 2746 int winnr; 2747 { 2748 win_T *wp; 2749 2750 # ifdef FEAT_WINDOWS 2751 for (wp = firstwin; wp != NULL; wp = wp->w_next) 2752 if (--winnr == 0) 2753 break; 2754 return wp; 2755 # else 2756 return curwin; 2757 # endif 2758 } 2759 #endif 2760 2761 #ifdef FEAT_VERTSPLIT 2762 /* 2763 * Move to window above or below "count" times. 2764 */ 2765 static void 2766 win_goto_ver(up, count) 2767 int up; /* TRUE to go to win above */ 2768 long count; 2769 { 2770 frame_T *fr; 2771 frame_T *nfr; 2772 frame_T *foundfr; 2773 2774 foundfr = curwin->w_frame; 2775 while (count--) 2776 { 2777 /* 2778 * First go upwards in the tree of frames until we find a upwards or 2779 * downwards neighbor. 2780 */ 2781 fr = foundfr; 2782 for (;;) 2783 { 2784 if (fr == topframe) 2785 goto end; 2786 if (up) 2787 nfr = fr->fr_prev; 2788 else 2789 nfr = fr->fr_next; 2790 if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL) 2791 break; 2792 fr = fr->fr_parent; 2793 } 2794 2795 /* 2796 * Now go downwards to find the bottom or top frame in it. 2797 */ 2798 for (;;) 2799 { 2800 if (nfr->fr_layout == FR_LEAF) 2801 { 2802 foundfr = nfr; 2803 break; 2804 } 2805 fr = nfr->fr_child; 2806 if (nfr->fr_layout == FR_ROW) 2807 { 2808 /* Find the frame at the cursor row. */ 2809 while (fr->fr_next != NULL 2810 && frame2win(fr)->w_wincol + fr->fr_width 2811 <= curwin->w_wincol + curwin->w_wcol) 2812 fr = fr->fr_next; 2813 } 2814 if (nfr->fr_layout == FR_COL && up) 2815 while (fr->fr_next != NULL) 2816 fr = fr->fr_next; 2817 nfr = fr; 2818 } 2819 } 2820 end: 2821 if (foundfr != NULL) 2822 win_goto(foundfr->fr_win); 2823 } 2824 2825 /* 2826 * Move to left or right window. 2827 */ 2828 static void 2829 win_goto_hor(left, count) 2830 int left; /* TRUE to go to left win */ 2831 long count; 2832 { 2833 frame_T *fr; 2834 frame_T *nfr; 2835 frame_T *foundfr; 2836 2837 foundfr = curwin->w_frame; 2838 while (count--) 2839 { 2840 /* 2841 * First go upwards in the tree of frames until we find a left or 2842 * right neighbor. 2843 */ 2844 fr = foundfr; 2845 for (;;) 2846 { 2847 if (fr == topframe) 2848 goto end; 2849 if (left) 2850 nfr = fr->fr_prev; 2851 else 2852 nfr = fr->fr_next; 2853 if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL) 2854 break; 2855 fr = fr->fr_parent; 2856 } 2857 2858 /* 2859 * Now go downwards to find the leftmost or rightmost frame in it. 2860 */ 2861 for (;;) 2862 { 2863 if (nfr->fr_layout == FR_LEAF) 2864 { 2865 foundfr = nfr; 2866 break; 2867 } 2868 fr = nfr->fr_child; 2869 if (nfr->fr_layout == FR_COL) 2870 { 2871 /* Find the frame at the cursor row. */ 2872 while (fr->fr_next != NULL 2873 && frame2win(fr)->w_winrow + fr->fr_height 2874 <= curwin->w_winrow + curwin->w_wrow) 2875 fr = fr->fr_next; 2876 } 2877 if (nfr->fr_layout == FR_ROW && left) 2878 while (fr->fr_next != NULL) 2879 fr = fr->fr_next; 2880 nfr = fr; 2881 } 2882 } 2883 end: 2884 if (foundfr != NULL) 2885 win_goto(foundfr->fr_win); 2886 } 2887 #endif 2888 2889 /* 2890 * Make window "wp" the current window. 2891 */ 2892 void 2893 win_enter(wp, undo_sync) 2894 win_T *wp; 2895 int undo_sync; 2896 { 2897 win_enter_ext(wp, undo_sync, FALSE); 2898 } 2899 2900 /* 2901 * Make window wp the current window. 2902 * Can be called with "curwin_invalid" TRUE, which means that curwin has just 2903 * been closed and isn't valid. 2904 */ 2905 static void 2906 win_enter_ext(wp, undo_sync, curwin_invalid) 2907 win_T *wp; 2908 int undo_sync; 2909 int curwin_invalid; 2910 { 2911 #ifdef FEAT_AUTOCMD 2912 int other_buffer = FALSE; 2913 #endif 2914 2915 if (wp == curwin && !curwin_invalid) /* nothing to do */ 2916 return; 2917 2918 #ifdef FEAT_AUTOCMD 2919 if (!curwin_invalid) 2920 { 2921 /* 2922 * Be careful: If autocommands delete the window, return now. 2923 */ 2924 if (wp->w_buffer != curbuf) 2925 { 2926 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); 2927 other_buffer = TRUE; 2928 if (!win_valid(wp)) 2929 return; 2930 } 2931 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); 2932 if (!win_valid(wp)) 2933 return; 2934 # ifdef FEAT_EVAL 2935 /* autocmds may abort script processing */ 2936 if (aborting()) 2937 return; 2938 # endif 2939 } 2940 #endif 2941 2942 /* sync undo before leaving the current buffer */ 2943 if (undo_sync && curbuf != wp->w_buffer) 2944 u_sync(); 2945 /* may have to copy the buffer options when 'cpo' contains 'S' */ 2946 if (wp->w_buffer != curbuf) 2947 buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP); 2948 if (!curwin_invalid) 2949 { 2950 prevwin = curwin; /* remember for CTRL-W p */ 2951 curwin->w_redr_status = TRUE; 2952 } 2953 curwin = wp; 2954 curbuf = wp->w_buffer; 2955 check_cursor(); 2956 #ifdef FEAT_VIRTUALEDIT 2957 if (!virtual_active()) 2958 curwin->w_cursor.coladd = 0; 2959 #endif 2960 changed_line_abv_curs(); /* assume cursor position needs updating */ 2961 2962 if (curwin->w_localdir != NULL) 2963 { 2964 /* Window has a local directory: Save current directory as global 2965 * directory (unless that was done already) and change to the local 2966 * directory. */ 2967 if (globaldir == NULL) 2968 { 2969 char_u cwd[MAXPATHL]; 2970 2971 if (mch_dirname(cwd, MAXPATHL) == OK) 2972 globaldir = vim_strsave(cwd); 2973 } 2974 mch_chdir((char *)curwin->w_localdir); 2975 shorten_fnames(TRUE); 2976 } 2977 else if (globaldir != NULL) 2978 { 2979 /* Window doesn't have a local directory and we are not in the global 2980 * directory: Change to the global directory. */ 2981 mch_chdir((char *)globaldir); 2982 vim_free(globaldir); 2983 globaldir = NULL; 2984 shorten_fnames(TRUE); 2985 } 2986 2987 #ifdef FEAT_AUTOCMD 2988 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); 2989 if (other_buffer) 2990 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 2991 #endif 2992 2993 #ifdef FEAT_TITLE 2994 maketitle(); 2995 #endif 2996 curwin->w_redr_status = TRUE; 2997 if (restart_edit) 2998 redraw_later(VALID); /* causes status line redraw */ 2999 3000 /* set window height to desired minimal value */ 3001 if (curwin->w_height < p_wh && !curwin->w_p_wfh) 3002 win_setheight((int)p_wh); 3003 else if (curwin->w_height == 0) 3004 win_setheight(1); 3005 3006 #ifdef FEAT_VERTSPLIT 3007 /* set window width to desired minimal value */ 3008 if (curwin->w_width < p_wiw) 3009 win_setwidth((int)p_wiw); 3010 #endif 3011 3012 #ifdef FEAT_MOUSE 3013 setmouse(); /* in case jumped to/from help buffer */ 3014 #endif 3015 3016 #if defined(FEAT_NETBEANS_INTG) || defined(FEAT_SUN_WORKSHOP) 3017 /* Change directories when the acd option is set on and after 3018 * switching windows. */ 3019 if (p_acd && curbuf->b_ffname != NULL 3020 && vim_chdirfile(curbuf->b_ffname) == OK) 3021 shorten_fnames(TRUE); 3022 #endif 3023 } 3024 3025 #endif /* FEAT_WINDOWS */ 3026 3027 #if defined(FEAT_WINDOWS) || defined(FEAT_SIGNS) || defined(PROTO) 3028 /* 3029 * Jump to the first open window that contains buffer buf if one exists 3030 * TODO: Alternatively jump to last open window? Dependent from 'splitbelow'? 3031 * Returns pointer to window if it exists, otherwise NULL. 3032 */ 3033 win_T * 3034 buf_jump_open_win(buf) 3035 buf_T *buf; 3036 { 3037 # ifdef FEAT_WINDOWS 3038 win_T *wp; 3039 3040 for (wp = firstwin; wp; wp = wp->w_next) 3041 if (wp->w_buffer == buf) 3042 break; 3043 if (wp != NULL) 3044 win_enter(wp, FALSE); 3045 return wp; 3046 # else 3047 if (curwin->w_buffer == buf) 3048 return curwin; 3049 return NULL; 3050 # endif 3051 } 3052 #endif 3053 3054 /* 3055 * allocate a window structure and link it in the window list 3056 */ 3057 /*ARGSUSED*/ 3058 static win_T * 3059 win_alloc(after) 3060 win_T *after; 3061 { 3062 win_T *newwin; 3063 3064 /* 3065 * allocate window structure and linesizes arrays 3066 */ 3067 newwin = (win_T *)alloc_clear((unsigned)sizeof(win_T)); 3068 if (newwin != NULL && win_alloc_lines(newwin) == FAIL) 3069 { 3070 vim_free(newwin); 3071 newwin = NULL; 3072 } 3073 3074 if (newwin != NULL) 3075 { 3076 /* 3077 * link the window in the window list 3078 */ 3079 #ifdef FEAT_WINDOWS 3080 win_append(after, newwin); 3081 #endif 3082 #ifdef FEAT_VERTSPLIT 3083 newwin->w_wincol = 0; 3084 newwin->w_width = Columns; 3085 #endif 3086 3087 /* position the display and the cursor at the top of the file. */ 3088 newwin->w_topline = 1; 3089 #ifdef FEAT_DIFF 3090 newwin->w_topfill = 0; 3091 #endif 3092 newwin->w_botline = 2; 3093 newwin->w_cursor.lnum = 1; 3094 #ifdef FEAT_SCROLLBIND 3095 newwin->w_scbind_pos = 1; 3096 #endif 3097 3098 /* We won't calculate w_fraction until resizing the window */ 3099 newwin->w_fraction = 0; 3100 newwin->w_prev_fraction_row = -1; 3101 3102 #ifdef FEAT_GUI 3103 if (gui.in_use) 3104 { 3105 out_flush(); 3106 gui_create_scrollbar(&newwin->w_scrollbars[SBAR_LEFT], 3107 SBAR_LEFT, newwin); 3108 gui_create_scrollbar(&newwin->w_scrollbars[SBAR_RIGHT], 3109 SBAR_RIGHT, newwin); 3110 } 3111 #endif 3112 #ifdef FEAT_EVAL 3113 /* init w: variables */ 3114 init_var_dict(&newwin->w_vars, &newwin->w_winvar); 3115 #endif 3116 #ifdef FEAT_FOLDING 3117 foldInitWin(newwin); 3118 #endif 3119 } 3120 return newwin; 3121 } 3122 3123 #if defined(FEAT_WINDOWS) || defined(PROTO) 3124 3125 /* 3126 * remove window 'wp' from the window list and free the structure 3127 */ 3128 static void 3129 win_free(wp) 3130 win_T *wp; 3131 { 3132 int i; 3133 3134 #ifdef FEAT_MZSCHEME 3135 mzscheme_window_free(wp); 3136 #endif 3137 3138 #ifdef FEAT_PERL 3139 perl_win_free(wp); 3140 #endif 3141 3142 #ifdef FEAT_PYTHON 3143 python_window_free(wp); 3144 #endif 3145 3146 #ifdef FEAT_TCL 3147 tcl_window_free(wp); 3148 #endif 3149 3150 #ifdef FEAT_RUBY 3151 ruby_window_free(wp); 3152 #endif 3153 3154 clear_winopt(&wp->w_onebuf_opt); 3155 clear_winopt(&wp->w_allbuf_opt); 3156 3157 #ifdef FEAT_EVAL 3158 vars_clear(&wp->w_vars.dv_hashtab); /* free all w: variables */ 3159 #endif 3160 3161 if (prevwin == wp) 3162 prevwin = NULL; 3163 win_free_lsize(wp); 3164 3165 for (i = 0; i < wp->w_tagstacklen; ++i) 3166 vim_free(wp->w_tagstack[i].tagname); 3167 3168 vim_free(wp->w_localdir); 3169 #ifdef FEAT_SEARCH_EXTRA 3170 vim_free(wp->w_match.regprog); 3171 #endif 3172 #ifdef FEAT_JUMPLIST 3173 free_jumplist(wp); 3174 #endif 3175 3176 #ifdef FEAT_GUI 3177 if (gui.in_use) 3178 { 3179 out_flush(); 3180 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]); 3181 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]); 3182 } 3183 #endif /* FEAT_GUI */ 3184 3185 win_remove(wp); 3186 vim_free(wp); 3187 } 3188 3189 /* 3190 * Append window "wp" in the window list after window "after". 3191 */ 3192 static void 3193 win_append(after, wp) 3194 win_T *after, *wp; 3195 { 3196 win_T *before; 3197 3198 if (after == NULL) /* after NULL is in front of the first */ 3199 before = firstwin; 3200 else 3201 before = after->w_next; 3202 3203 wp->w_next = before; 3204 wp->w_prev = after; 3205 if (after == NULL) 3206 firstwin = wp; 3207 else 3208 after->w_next = wp; 3209 if (before == NULL) 3210 lastwin = wp; 3211 else 3212 before->w_prev = wp; 3213 } 3214 3215 /* 3216 * Remove a window from the window list. 3217 */ 3218 static void 3219 win_remove(wp) 3220 win_T *wp; 3221 { 3222 if (wp->w_prev != NULL) 3223 wp->w_prev->w_next = wp->w_next; 3224 else 3225 firstwin = wp->w_next; 3226 if (wp->w_next != NULL) 3227 wp->w_next->w_prev = wp->w_prev; 3228 else 3229 lastwin = wp->w_prev; 3230 } 3231 3232 /* 3233 * Append frame "frp" in a frame list after frame "after". 3234 */ 3235 static void 3236 frame_append(after, frp) 3237 frame_T *after, *frp; 3238 { 3239 frp->fr_next = after->fr_next; 3240 after->fr_next = frp; 3241 if (frp->fr_next != NULL) 3242 frp->fr_next->fr_prev = frp; 3243 frp->fr_prev = after; 3244 } 3245 3246 /* 3247 * Insert frame "frp" in a frame list before frame "before". 3248 */ 3249 static void 3250 frame_insert(before, frp) 3251 frame_T *before, *frp; 3252 { 3253 frp->fr_next = before; 3254 frp->fr_prev = before->fr_prev; 3255 before->fr_prev = frp; 3256 if (frp->fr_prev != NULL) 3257 frp->fr_prev->fr_next = frp; 3258 else 3259 frp->fr_parent->fr_child = frp; 3260 } 3261 3262 /* 3263 * Remove a frame from a frame list. 3264 */ 3265 static void 3266 frame_remove(frp) 3267 frame_T *frp; 3268 { 3269 if (frp->fr_prev != NULL) 3270 frp->fr_prev->fr_next = frp->fr_next; 3271 else 3272 frp->fr_parent->fr_child = frp->fr_next; 3273 if (frp->fr_next != NULL) 3274 frp->fr_next->fr_prev = frp->fr_prev; 3275 } 3276 3277 #endif /* FEAT_WINDOWS */ 3278 3279 /* 3280 * Allocate w_lines[] for window "wp". 3281 * Return FAIL for failure, OK for success. 3282 */ 3283 int 3284 win_alloc_lines(wp) 3285 win_T *wp; 3286 { 3287 wp->w_lines_valid = 0; 3288 wp->w_lines = (wline_T *)alloc((unsigned)(Rows * sizeof(wline_T))); 3289 if (wp->w_lines == NULL) 3290 return FAIL; 3291 return OK; 3292 } 3293 3294 /* 3295 * free lsize arrays for a window 3296 */ 3297 void 3298 win_free_lsize(wp) 3299 win_T *wp; 3300 { 3301 vim_free(wp->w_lines); 3302 wp->w_lines = NULL; 3303 } 3304 3305 /* 3306 * Called from win_new_shellsize() after Rows changed. 3307 */ 3308 void 3309 shell_new_rows() 3310 { 3311 int h = (int)(Rows - p_ch); 3312 3313 if (firstwin == NULL) /* not initialized yet */ 3314 return; 3315 #ifdef FEAT_WINDOWS 3316 if (h < frame_minheight(topframe, NULL)) 3317 h = frame_minheight(topframe, NULL); 3318 /* First try setting the heights of windows without 'winfixheight'. If 3319 * that doesn't result in the right height, forget about that option. */ 3320 frame_new_height(topframe, h, FALSE, TRUE); 3321 if (topframe->fr_height != h) 3322 frame_new_height(topframe, h, FALSE, FALSE); 3323 3324 (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ 3325 #else 3326 if (h < 1) 3327 h = 1; 3328 win_new_height(firstwin, h); 3329 #endif 3330 compute_cmdrow(); 3331 #ifdef FEAT_WINDOWS 3332 p_ch_used = p_ch; 3333 #endif 3334 3335 #if 0 3336 /* Disabled: don't want making the screen smaller make a window larger. */ 3337 if (p_ea) 3338 win_equal(curwin, FALSE, 'v'); 3339 #endif 3340 } 3341 3342 #if defined(FEAT_VERTSPLIT) || defined(PROTO) 3343 /* 3344 * Called from win_new_shellsize() after Columns changed. 3345 */ 3346 void 3347 shell_new_columns() 3348 { 3349 if (firstwin == NULL) /* not initialized yet */ 3350 return; 3351 frame_new_width(topframe, (int)Columns, FALSE); 3352 (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ 3353 #if 0 3354 /* Disabled: don't want making the screen smaller make a window larger. */ 3355 if (p_ea) 3356 win_equal(curwin, FALSE, 'h'); 3357 #endif 3358 } 3359 #endif 3360 3361 #if defined(FEAT_CMDWIN) || defined(PROTO) 3362 /* 3363 * Save the size of all windows in "gap". 3364 */ 3365 void 3366 win_size_save(gap) 3367 garray_T *gap; 3368 3369 { 3370 win_T *wp; 3371 3372 ga_init2(gap, (int)sizeof(int), 1); 3373 if (ga_grow(gap, win_count() * 2) == OK) 3374 for (wp = firstwin; wp != NULL; wp = wp->w_next) 3375 { 3376 ((int *)gap->ga_data)[gap->ga_len++] = 3377 wp->w_width + wp->w_vsep_width; 3378 ((int *)gap->ga_data)[gap->ga_len++] = wp->w_height; 3379 } 3380 } 3381 3382 /* 3383 * Restore window sizes, but only if the number of windows is still the same. 3384 * Does not free the growarray. 3385 */ 3386 void 3387 win_size_restore(gap) 3388 garray_T *gap; 3389 { 3390 win_T *wp; 3391 int i; 3392 3393 if (win_count() * 2 == gap->ga_len) 3394 { 3395 i = 0; 3396 for (wp = firstwin; wp != NULL; wp = wp->w_next) 3397 { 3398 frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]); 3399 win_setheight_win(((int *)gap->ga_data)[i++], wp); 3400 } 3401 /* recompute the window positions */ 3402 (void)win_comp_pos(); 3403 } 3404 } 3405 #endif /* FEAT_CMDWIN */ 3406 3407 #if defined(FEAT_WINDOWS) || defined(PROTO) 3408 /* 3409 * Update the position for all windows, using the width and height of the 3410 * frames. 3411 * Returns the row just after the last window. 3412 */ 3413 static int 3414 win_comp_pos() 3415 { 3416 int row = 0; 3417 int col = 0; 3418 3419 frame_comp_pos(topframe, &row, &col); 3420 return row; 3421 } 3422 3423 /* 3424 * Update the position of the windows in frame "topfrp", using the width and 3425 * height of the frames. 3426 * "*row" and "*col" are the top-left position of the frame. They are updated 3427 * to the bottom-right position plus one. 3428 */ 3429 static void 3430 frame_comp_pos(topfrp, row, col) 3431 frame_T *topfrp; 3432 int *row; 3433 int *col; 3434 { 3435 win_T *wp; 3436 frame_T *frp; 3437 #ifdef FEAT_VERTSPLIT 3438 int startcol; 3439 int startrow; 3440 #endif 3441 3442 wp = topfrp->fr_win; 3443 if (wp != NULL) 3444 { 3445 if (wp->w_winrow != *row 3446 #ifdef FEAT_VERTSPLIT 3447 || wp->w_wincol != *col 3448 #endif 3449 ) 3450 { 3451 /* position changed, redraw */ 3452 wp->w_winrow = *row; 3453 #ifdef FEAT_VERTSPLIT 3454 wp->w_wincol = *col; 3455 #endif 3456 redraw_win_later(wp, NOT_VALID); 3457 wp->w_redr_status = TRUE; 3458 } 3459 *row += wp->w_height + wp->w_status_height; 3460 #ifdef FEAT_VERTSPLIT 3461 *col += wp->w_width + wp->w_vsep_width; 3462 #endif 3463 } 3464 else 3465 { 3466 #ifdef FEAT_VERTSPLIT 3467 startrow = *row; 3468 startcol = *col; 3469 #endif 3470 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 3471 { 3472 #ifdef FEAT_VERTSPLIT 3473 if (topfrp->fr_layout == FR_ROW) 3474 *row = startrow; /* all frames are at the same row */ 3475 else 3476 *col = startcol; /* all frames are at the same col */ 3477 #endif 3478 frame_comp_pos(frp, row, col); 3479 } 3480 } 3481 } 3482 3483 #endif /* FEAT_WINDOWS */ 3484 3485 /* 3486 * Set current window height and take care of repositioning other windows to 3487 * fit around it. 3488 */ 3489 void 3490 win_setheight(height) 3491 int height; 3492 { 3493 win_setheight_win(height, curwin); 3494 } 3495 3496 /* 3497 * Set the window height of window "win" and take care of repositioning other 3498 * windows to fit around it. 3499 */ 3500 void 3501 win_setheight_win(height, win) 3502 int height; 3503 win_T *win; 3504 { 3505 int row; 3506 3507 if (win == curwin) 3508 { 3509 /* Always keep current window at least one line high, even when 3510 * 'winminheight' is zero. */ 3511 #ifdef FEAT_WINDOWS 3512 if (height < p_wmh) 3513 height = p_wmh; 3514 #endif 3515 if (height == 0) 3516 height = 1; 3517 } 3518 3519 #ifdef FEAT_WINDOWS 3520 frame_setheight(win->w_frame, height + win->w_status_height); 3521 3522 /* recompute the window positions */ 3523 row = win_comp_pos(); 3524 #else 3525 if (height > topframe->fr_height) 3526 height = topframe->fr_height; 3527 win->w_height = height; 3528 row = height; 3529 #endif 3530 3531 /* 3532 * If there is extra space created between the last window and the command 3533 * line, clear it. 3534 */ 3535 if (full_screen && msg_scrolled == 0 && row < cmdline_row) 3536 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); 3537 cmdline_row = row; 3538 msg_row = row; 3539 msg_col = 0; 3540 3541 redraw_all_later(NOT_VALID); 3542 } 3543 3544 #if defined(FEAT_WINDOWS) || defined(PROTO) 3545 3546 /* 3547 * Set the height of a frame to "height" and take care that all frames and 3548 * windows inside it are resized. Also resize frames on the left and right if 3549 * the are in the same FR_ROW frame. 3550 * 3551 * Strategy: 3552 * If the frame is part of a FR_COL frame, try fitting the frame in that 3553 * frame. If that doesn't work (the FR_COL frame is too small), recursively 3554 * go to containing frames to resize them and make room. 3555 * If the frame is part of a FR_ROW frame, all frames must be resized as well. 3556 * Check for the minimal height of the FR_ROW frame. 3557 * At the top level we can also use change the command line height. 3558 */ 3559 static void 3560 frame_setheight(curfrp, height) 3561 frame_T *curfrp; 3562 int height; 3563 { 3564 int room; /* total number of lines available */ 3565 int take; /* number of lines taken from other windows */ 3566 int room_cmdline; /* lines available from cmdline */ 3567 int run; 3568 frame_T *frp; 3569 int h; 3570 int room_reserved; 3571 3572 /* If the height already is the desired value, nothing to do. */ 3573 if (curfrp->fr_height == height) 3574 return; 3575 3576 if (curfrp->fr_parent == NULL) 3577 { 3578 /* topframe: can only change the command line */ 3579 if (height > Rows - p_ch) 3580 height = Rows - p_ch; 3581 if (height > 0) 3582 frame_new_height(curfrp, height, FALSE, FALSE); 3583 } 3584 else if (curfrp->fr_parent->fr_layout == FR_ROW) 3585 { 3586 /* Row of frames: Also need to resize frames left and right of this 3587 * one. First check for the minimal height of these. */ 3588 h = frame_minheight(curfrp->fr_parent, NULL); 3589 if (height < h) 3590 height = h; 3591 frame_setheight(curfrp->fr_parent, height); 3592 } 3593 else 3594 { 3595 /* 3596 * Column of frames: try to change only frames in this column. 3597 */ 3598 #ifdef FEAT_VERTSPLIT 3599 /* 3600 * Do this twice: 3601 * 1: compute room available, if it's not enough try resizing the 3602 * containing frame. 3603 * 2: compute the room available and adjust the height to it. 3604 * Try not to reduce the height of a window with 'winfixheight' set. 3605 */ 3606 for (run = 1; run <= 2; ++run) 3607 #else 3608 for (;;) 3609 #endif 3610 { 3611 room = 0; 3612 room_reserved = 0; 3613 for (frp = curfrp->fr_parent->fr_child; frp != NULL; 3614 frp = frp->fr_next) 3615 { 3616 if (frp != curfrp 3617 && frp->fr_win != NULL 3618 && frp->fr_win->w_p_wfh) 3619 room_reserved += frp->fr_height; 3620 room += frp->fr_height; 3621 if (frp != curfrp) 3622 room -= frame_minheight(frp, NULL); 3623 } 3624 #ifdef FEAT_VERTSPLIT 3625 if (curfrp->fr_width != Columns) 3626 room_cmdline = 0; 3627 else 3628 #endif 3629 { 3630 room_cmdline = Rows - p_ch - (lastwin->w_winrow 3631 + lastwin->w_height + lastwin->w_status_height); 3632 if (room_cmdline < 0) 3633 room_cmdline = 0; 3634 } 3635 3636 if (height <= room + room_cmdline) 3637 break; 3638 #ifdef FEAT_VERTSPLIT 3639 if (run == 2 || curfrp->fr_width == Columns) 3640 #endif 3641 { 3642 if (height > room + room_cmdline) 3643 height = room + room_cmdline; 3644 break; 3645 } 3646 #ifdef FEAT_VERTSPLIT 3647 frame_setheight(curfrp->fr_parent, height 3648 + frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1); 3649 #endif 3650 /*NOTREACHED*/ 3651 } 3652 3653 /* 3654 * Compute the number of lines we will take from others frames (can be 3655 * negative!). 3656 */ 3657 take = height - curfrp->fr_height; 3658 3659 /* If there is not enough room, also reduce the height of a window 3660 * with 'winfixheight' set. */ 3661 if (height > room + room_cmdline - room_reserved) 3662 room_reserved = room + room_cmdline - height; 3663 /* If there is only a 'winfixheight' window and making the 3664 * window smaller, need to make the other window taller. */ 3665 if (take < 0 && room - curfrp->fr_height < room_reserved) 3666 room_reserved = 0; 3667 3668 if (take > 0 && room_cmdline > 0) 3669 { 3670 /* use lines from cmdline first */ 3671 if (take < room_cmdline) 3672 room_cmdline = take; 3673 take -= room_cmdline; 3674 topframe->fr_height += room_cmdline; 3675 } 3676 3677 /* 3678 * set the current frame to the new height 3679 */ 3680 frame_new_height(curfrp, height, FALSE, FALSE); 3681 3682 /* 3683 * First take lines from the frames after the current frame. If 3684 * that is not enough, takes lines from frames above the current 3685 * frame. 3686 */ 3687 for (run = 0; run < 2; ++run) 3688 { 3689 if (run == 0) 3690 frp = curfrp->fr_next; /* 1st run: start with next window */ 3691 else 3692 frp = curfrp->fr_prev; /* 2nd run: start with prev window */ 3693 while (frp != NULL && take != 0) 3694 { 3695 h = frame_minheight(frp, NULL); 3696 if (room_reserved > 0 3697 && frp->fr_win != NULL 3698 && frp->fr_win->w_p_wfh) 3699 { 3700 if (room_reserved >= frp->fr_height) 3701 room_reserved -= frp->fr_height; 3702 else 3703 { 3704 if (frp->fr_height - room_reserved > take) 3705 room_reserved = frp->fr_height - take; 3706 take -= frp->fr_height - room_reserved; 3707 frame_new_height(frp, room_reserved, FALSE, FALSE); 3708 room_reserved = 0; 3709 } 3710 } 3711 else 3712 { 3713 if (frp->fr_height - take < h) 3714 { 3715 take -= frp->fr_height - h; 3716 frame_new_height(frp, h, FALSE, FALSE); 3717 } 3718 else 3719 { 3720 frame_new_height(frp, frp->fr_height - take, 3721 FALSE, FALSE); 3722 take = 0; 3723 } 3724 } 3725 if (run == 0) 3726 frp = frp->fr_next; 3727 else 3728 frp = frp->fr_prev; 3729 } 3730 } 3731 } 3732 } 3733 3734 #if defined(FEAT_VERTSPLIT) || defined(PROTO) 3735 /* 3736 * Set current window width and take care of repositioning other windows to 3737 * fit around it. 3738 */ 3739 void 3740 win_setwidth(width) 3741 int width; 3742 { 3743 win_setwidth_win(width, curwin); 3744 } 3745 3746 void 3747 win_setwidth_win(width, wp) 3748 int width; 3749 win_T *wp; 3750 { 3751 /* Always keep current window at least one column wide, even when 3752 * 'winminwidth' is zero. */ 3753 if (wp == curwin) 3754 { 3755 if (width < p_wmw) 3756 width = p_wmw; 3757 if (width == 0) 3758 width = 1; 3759 } 3760 3761 frame_setwidth(wp->w_frame, width + wp->w_vsep_width); 3762 3763 /* recompute the window positions */ 3764 (void)win_comp_pos(); 3765 3766 redraw_all_later(NOT_VALID); 3767 } 3768 3769 /* 3770 * Set the width of a frame to "width" and take care that all frames and 3771 * windows inside it are resized. Also resize frames above and below if the 3772 * are in the same FR_ROW frame. 3773 * 3774 * Strategy is similar to frame_setheight(). 3775 */ 3776 static void 3777 frame_setwidth(curfrp, width) 3778 frame_T *curfrp; 3779 int width; 3780 { 3781 int room; /* total number of lines available */ 3782 int take; /* number of lines taken from other windows */ 3783 int run; 3784 frame_T *frp; 3785 int w; 3786 3787 /* If the width already is the desired value, nothing to do. */ 3788 if (curfrp->fr_width == width) 3789 return; 3790 3791 if (curfrp->fr_parent == NULL) 3792 /* topframe: can't change width */ 3793 return; 3794 3795 if (curfrp->fr_parent->fr_layout == FR_COL) 3796 { 3797 /* Column of frames: Also need to resize frames above and below of 3798 * this one. First check for the minimal width of these. */ 3799 w = frame_minwidth(curfrp->fr_parent, NULL); 3800 if (width < w) 3801 width = w; 3802 frame_setwidth(curfrp->fr_parent, width); 3803 } 3804 else 3805 { 3806 /* 3807 * Row of frames: try to change only frames in this row. 3808 * 3809 * Do this twice: 3810 * 1: compute room available, if it's not enough try resizing the 3811 * containing frame. 3812 * 2: compute the room available and adjust the width to it. 3813 */ 3814 for (run = 1; run <= 2; ++run) 3815 { 3816 room = 0; 3817 for (frp = curfrp->fr_parent->fr_child; frp != NULL; 3818 frp = frp->fr_next) 3819 { 3820 room += frp->fr_width; 3821 if (frp != curfrp) 3822 room -= frame_minwidth(frp, NULL); 3823 } 3824 3825 if (width <= room) 3826 break; 3827 if (run == 2 || curfrp->fr_height >= Rows - p_ch) 3828 { 3829 if (width > room) 3830 width = room; 3831 break; 3832 } 3833 frame_setwidth(curfrp->fr_parent, width 3834 + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1); 3835 } 3836 3837 3838 /* 3839 * Compute the number of lines we will take from others frames (can be 3840 * negative!). 3841 */ 3842 take = width - curfrp->fr_width; 3843 3844 /* 3845 * set the current frame to the new width 3846 */ 3847 frame_new_width(curfrp, width, FALSE); 3848 3849 /* 3850 * First take lines from the frames right of the current frame. If 3851 * that is not enough, takes lines from frames left of the current 3852 * frame. 3853 */ 3854 for (run = 0; run < 2; ++run) 3855 { 3856 if (run == 0) 3857 frp = curfrp->fr_next; /* 1st run: start with next window */ 3858 else 3859 frp = curfrp->fr_prev; /* 2nd run: start with prev window */ 3860 while (frp != NULL && take != 0) 3861 { 3862 w = frame_minwidth(frp, NULL); 3863 if (frp->fr_width - take < w) 3864 { 3865 take -= frp->fr_width - w; 3866 frame_new_width(frp, w, FALSE); 3867 } 3868 else 3869 { 3870 frame_new_width(frp, frp->fr_width - take, FALSE); 3871 take = 0; 3872 } 3873 if (run == 0) 3874 frp = frp->fr_next; 3875 else 3876 frp = frp->fr_prev; 3877 } 3878 } 3879 } 3880 } 3881 #endif /* FEAT_VERTSPLIT */ 3882 3883 /* 3884 * Check 'winminheight' for a valid value. 3885 */ 3886 void 3887 win_setminheight() 3888 { 3889 int room; 3890 int first = TRUE; 3891 win_T *wp; 3892 3893 /* loop until there is a 'winminheight' that is possible */ 3894 while (p_wmh > 0) 3895 { 3896 /* TODO: handle vertical splits */ 3897 room = -p_wh; 3898 for (wp = firstwin; wp != NULL; wp = wp->w_next) 3899 room += wp->w_height - p_wmh; 3900 if (room >= 0) 3901 break; 3902 --p_wmh; 3903 if (first) 3904 { 3905 EMSG(_(e_noroom)); 3906 first = FALSE; 3907 } 3908 } 3909 } 3910 3911 #ifdef FEAT_MOUSE 3912 3913 /* 3914 * Status line of dragwin is dragged "offset" lines down (negative is up). 3915 */ 3916 void 3917 win_drag_status_line(dragwin, offset) 3918 win_T *dragwin; 3919 int offset; 3920 { 3921 frame_T *curfr; 3922 frame_T *fr; 3923 int room; 3924 int row; 3925 int up; /* if TRUE, drag status line up, otherwise down */ 3926 int n; 3927 3928 fr = dragwin->w_frame; 3929 curfr = fr; 3930 if (fr != topframe) /* more than one window */ 3931 { 3932 fr = fr->fr_parent; 3933 /* When the parent frame is not a column of frames, its parent should 3934 * be. */ 3935 if (fr->fr_layout != FR_COL) 3936 { 3937 curfr = fr; 3938 if (fr != topframe) /* only a row of windows, may drag statusline */ 3939 fr = fr->fr_parent; 3940 } 3941 } 3942 3943 /* If this is the last frame in a column, may want to resize the parent 3944 * frame instead (go two up to skip a row of frames). */ 3945 while (curfr != topframe && curfr->fr_next == NULL) 3946 { 3947 if (fr != topframe) 3948 fr = fr->fr_parent; 3949 curfr = fr; 3950 if (fr != topframe) 3951 fr = fr->fr_parent; 3952 } 3953 3954 if (offset < 0) /* drag up */ 3955 { 3956 up = TRUE; 3957 offset = -offset; 3958 /* sum up the room of the current frame and above it */ 3959 if (fr == curfr) 3960 { 3961 /* only one window */ 3962 room = fr->fr_height - frame_minheight(fr, NULL); 3963 } 3964 else 3965 { 3966 room = 0; 3967 for (fr = fr->fr_child; ; fr = fr->fr_next) 3968 { 3969 room += fr->fr_height - frame_minheight(fr, NULL); 3970 if (fr == curfr) 3971 break; 3972 } 3973 } 3974 fr = curfr->fr_next; /* put fr at frame that grows */ 3975 } 3976 else /* drag down */ 3977 { 3978 up = FALSE; 3979 /* 3980 * Only dragging the last status line can reduce p_ch. 3981 */ 3982 room = Rows - cmdline_row; 3983 if (curfr->fr_next == NULL) 3984 room -= 1; 3985 else 3986 room -= p_ch; 3987 if (room < 0) 3988 room = 0; 3989 /* sum up the room of frames below of the current one */ 3990 for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) 3991 room += fr->fr_height - frame_minheight(fr, NULL); 3992 fr = curfr; /* put fr at window that grows */ 3993 } 3994 3995 if (room < offset) /* Not enough room */ 3996 offset = room; /* Move as far as we can */ 3997 if (offset <= 0) 3998 return; 3999 4000 /* 4001 * Grow frame fr by "offset" lines. 4002 * Doesn't happen when dragging the last status line up. 4003 */ 4004 if (fr != NULL) 4005 frame_new_height(fr, fr->fr_height + offset, up, FALSE); 4006 4007 if (up) 4008 fr = curfr; /* current frame gets smaller */ 4009 else 4010 fr = curfr->fr_next; /* next frame gets smaller */ 4011 4012 /* 4013 * Now make the other frames smaller. 4014 */ 4015 while (fr != NULL && offset > 0) 4016 { 4017 n = frame_minheight(fr, NULL); 4018 if (fr->fr_height - offset <= n) 4019 { 4020 offset -= fr->fr_height - n; 4021 frame_new_height(fr, n, !up, FALSE); 4022 } 4023 else 4024 { 4025 frame_new_height(fr, fr->fr_height - offset, !up, FALSE); 4026 break; 4027 } 4028 if (up) 4029 fr = fr->fr_prev; 4030 else 4031 fr = fr->fr_next; 4032 } 4033 row = win_comp_pos(); 4034 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); 4035 cmdline_row = row; 4036 p_ch = Rows - cmdline_row; 4037 if (p_ch < 1) 4038 p_ch = 1; 4039 redraw_all_later(NOT_VALID); 4040 showmode(); 4041 } 4042 4043 #ifdef FEAT_VERTSPLIT 4044 /* 4045 * Separator line of dragwin is dragged "offset" lines right (negative is left). 4046 */ 4047 void 4048 win_drag_vsep_line(dragwin, offset) 4049 win_T *dragwin; 4050 int offset; 4051 { 4052 frame_T *curfr; 4053 frame_T *fr; 4054 int room; 4055 int left; /* if TRUE, drag separator line left, otherwise right */ 4056 int n; 4057 4058 fr = dragwin->w_frame; 4059 if (fr == topframe) /* only one window (cannot happe?) */ 4060 return; 4061 curfr = fr; 4062 fr = fr->fr_parent; 4063 /* When the parent frame is not a row of frames, its parent should be. */ 4064 if (fr->fr_layout != FR_ROW) 4065 { 4066 if (fr == topframe) /* only a column of windows (cannot happen?) */ 4067 return; 4068 curfr = fr; 4069 fr = fr->fr_parent; 4070 } 4071 4072 /* If this is the last frame in a row, may want to resize a parent 4073 * frame instead. */ 4074 while (curfr->fr_next == NULL) 4075 { 4076 if (fr == topframe) 4077 break; 4078 curfr = fr; 4079 fr = fr->fr_parent; 4080 if (fr != topframe) 4081 { 4082 curfr = fr; 4083 fr = fr->fr_parent; 4084 } 4085 } 4086 4087 if (offset < 0) /* drag left */ 4088 { 4089 left = TRUE; 4090 offset = -offset; 4091 /* sum up the room of the current frame and left of it */ 4092 room = 0; 4093 for (fr = fr->fr_child; ; fr = fr->fr_next) 4094 { 4095 room += fr->fr_width - frame_minwidth(fr, NULL); 4096 if (fr == curfr) 4097 break; 4098 } 4099 fr = curfr->fr_next; /* put fr at frame that grows */ 4100 } 4101 else /* drag right */ 4102 { 4103 left = FALSE; 4104 /* sum up the room of frames right of the current one */ 4105 room = 0; 4106 for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) 4107 room += fr->fr_width - frame_minwidth(fr, NULL); 4108 fr = curfr; /* put fr at window that grows */ 4109 } 4110 4111 if (room < offset) /* Not enough room */ 4112 offset = room; /* Move as far as we can */ 4113 if (offset <= 0) /* No room at all, quit. */ 4114 return; 4115 4116 /* grow frame fr by offset lines */ 4117 frame_new_width(fr, fr->fr_width + offset, left); 4118 4119 /* shrink other frames: current and at the left or at the right */ 4120 if (left) 4121 fr = curfr; /* current frame gets smaller */ 4122 else 4123 fr = curfr->fr_next; /* next frame gets smaller */ 4124 4125 while (fr != NULL && offset > 0) 4126 { 4127 n = frame_minwidth(fr, NULL); 4128 if (fr->fr_width - offset <= n) 4129 { 4130 offset -= fr->fr_width - n; 4131 frame_new_width(fr, n, !left); 4132 } 4133 else 4134 { 4135 frame_new_width(fr, fr->fr_width - offset, !left); 4136 break; 4137 } 4138 if (left) 4139 fr = fr->fr_prev; 4140 else 4141 fr = fr->fr_next; 4142 } 4143 (void)win_comp_pos(); 4144 redraw_all_later(NOT_VALID); 4145 } 4146 #endif /* FEAT_VERTSPLIT */ 4147 #endif /* FEAT_MOUSE */ 4148 4149 #endif /* FEAT_WINDOWS */ 4150 4151 /* 4152 * Set the height of a window. 4153 * This takes care of the things inside the window, not what happens to the 4154 * window position, the frame or to other windows. 4155 */ 4156 static void 4157 win_new_height(wp, height) 4158 win_T *wp; 4159 int height; 4160 { 4161 linenr_T lnum; 4162 linenr_T bot; 4163 int sline, line_size; 4164 int space; 4165 int did_below = FALSE; 4166 #define FRACTION_MULT 16384L 4167 4168 /* Don't want a negative height. Happens when splitting a tiny window. 4169 * Will equalize heights soon to fix it. */ 4170 if (height < 0) 4171 height = 0; 4172 4173 if (wp->w_wrow != wp->w_prev_fraction_row && wp->w_height > 0) 4174 wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT 4175 + FRACTION_MULT / 2) / (long)wp->w_height; 4176 4177 wp->w_height = height; 4178 wp->w_skipcol = 0; 4179 4180 /* Don't change w_topline when height is zero. Don't set w_topline when 4181 * 'scrollbind' is set and this isn't the current window. */ 4182 if (height > 0 4183 #ifdef FEAT_SCROLLBIND 4184 && (!wp->w_p_scb || wp == curwin) 4185 #endif 4186 ) 4187 { 4188 /* 4189 * Find a value for w_topline that shows the cursor at the same 4190 * relative position in the window as before (more or less). 4191 */ 4192 lnum = wp->w_cursor.lnum; 4193 if (lnum < 1) /* can happen when starting up */ 4194 lnum = 1; 4195 wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; 4196 line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; 4197 sline = wp->w_wrow - line_size; 4198 if (sline < 0) 4199 { 4200 /* 4201 * Cursor line would go off top of screen if w_wrow was this high. 4202 */ 4203 wp->w_wrow = line_size; 4204 } 4205 else 4206 { 4207 space = height; 4208 while (lnum > 1) 4209 { 4210 space -= line_size; 4211 if (space > 0 && sline <= 0 && !did_below) 4212 { 4213 /* Try to use "~" lines below the text to avoid that text 4214 * is above the window while there are empty lines. 4215 * Subtract the rows below the cursor from "space" and 4216 * give the rest to "sline". */ 4217 did_below = TRUE; 4218 bot = wp->w_cursor.lnum; 4219 while (space > 0) 4220 { 4221 if (wp->w_buffer->b_ml.ml_line_count - bot >= space) 4222 space = 0; 4223 else 4224 { 4225 #ifdef FEAT_FOLDING 4226 hasFoldingWin(wp, bot, NULL, &bot, TRUE, NULL); 4227 #endif 4228 if (bot >= wp->w_buffer->b_ml.ml_line_count) 4229 break; 4230 ++bot; 4231 space -= plines_win(wp, bot, TRUE); 4232 } 4233 } 4234 if (bot == wp->w_buffer->b_ml.ml_line_count && space > 0) 4235 sline += space; 4236 } 4237 if (sline <= 0) 4238 break; 4239 4240 #ifdef FEAT_FOLDING 4241 hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL); 4242 if (lnum == 1) 4243 { 4244 /* first line in buffer is folded */ 4245 line_size = 1; 4246 --sline; 4247 break; 4248 } 4249 #endif 4250 --lnum; 4251 #ifdef FEAT_DIFF 4252 if (lnum == wp->w_topline) 4253 line_size = plines_win_nofill(wp, lnum, TRUE) 4254 + wp->w_topfill; 4255 else 4256 #endif 4257 line_size = plines_win(wp, lnum, TRUE); 4258 sline -= line_size; 4259 } 4260 4261 if (sline < 0) 4262 { 4263 /* 4264 * Line we want at top would go off top of screen. Use next 4265 * line instead. 4266 */ 4267 #ifdef FEAT_FOLDING 4268 hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL); 4269 #endif 4270 lnum++; 4271 wp->w_wrow -= line_size + sline; 4272 } 4273 else if (sline > 0) 4274 { 4275 /* First line of file reached, use that as topline. */ 4276 lnum = 1; 4277 wp->w_wrow -= sline; 4278 } 4279 } 4280 set_topline(wp, lnum); 4281 } 4282 4283 if (wp == curwin) 4284 { 4285 if (p_so) 4286 update_topline(); 4287 curs_columns(FALSE); /* validate w_wrow */ 4288 } 4289 wp->w_prev_fraction_row = wp->w_wrow; 4290 4291 win_comp_scroll(wp); 4292 redraw_win_later(wp, NOT_VALID); 4293 #ifdef FEAT_WINDOWS 4294 wp->w_redr_status = TRUE; 4295 #endif 4296 invalidate_botline_win(wp); 4297 } 4298 4299 #ifdef FEAT_VERTSPLIT 4300 /* 4301 * Set the width of a window. 4302 */ 4303 static void 4304 win_new_width(wp, width) 4305 win_T *wp; 4306 int width; 4307 { 4308 wp->w_width = width; 4309 wp->w_lines_valid = 0; 4310 changed_line_abv_curs_win(wp); 4311 invalidate_botline_win(wp); 4312 if (wp == curwin) 4313 { 4314 update_topline(); 4315 curs_columns(TRUE); /* validate w_wrow */ 4316 } 4317 redraw_win_later(wp, NOT_VALID); 4318 wp->w_redr_status = TRUE; 4319 } 4320 #endif 4321 4322 void 4323 win_comp_scroll(wp) 4324 win_T *wp; 4325 { 4326 wp->w_p_scr = ((unsigned)wp->w_height >> 1); 4327 if (wp->w_p_scr == 0) 4328 wp->w_p_scr = 1; 4329 } 4330 4331 /* 4332 * command_height: called whenever p_ch has been changed 4333 */ 4334 void 4335 command_height(old_p_ch) 4336 long old_p_ch; 4337 { 4338 #ifdef FEAT_WINDOWS 4339 int h; 4340 frame_T *frp; 4341 4342 /* When passed a negative value use the value of p_ch that we remembered. 4343 * This is needed for when the GUI starts up, we can't be sure in what 4344 * order things happen. */ 4345 if (old_p_ch < 0) 4346 old_p_ch = p_ch_used; 4347 p_ch_used = p_ch; 4348 4349 /* Find bottom frame with width of screen. */ 4350 frp = lastwin->w_frame; 4351 # ifdef FEAT_VERTSPLIT 4352 while (frp->fr_width != Columns && frp->fr_parent != NULL) 4353 frp = frp->fr_parent; 4354 # endif 4355 4356 /* Avoid changing the height of a window with 'winfixheight' set. */ 4357 while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF 4358 && frp->fr_win->w_p_wfh) 4359 frp = frp->fr_prev; 4360 4361 if (starting != NO_SCREEN) 4362 { 4363 cmdline_row = Rows - p_ch; 4364 4365 if (p_ch > old_p_ch) /* p_ch got bigger */ 4366 { 4367 while (p_ch > old_p_ch) 4368 { 4369 if (frp == NULL) 4370 { 4371 EMSG(_(e_noroom)); 4372 p_ch = old_p_ch; 4373 cmdline_row = Rows - p_ch; 4374 break; 4375 } 4376 h = frp->fr_height - frame_minheight(frp, NULL); 4377 if (h > p_ch - old_p_ch) 4378 h = p_ch - old_p_ch; 4379 old_p_ch += h; 4380 frame_add_height(frp, -h); 4381 frp = frp->fr_prev; 4382 } 4383 4384 /* Recompute window positions. */ 4385 (void)win_comp_pos(); 4386 4387 /* clear the lines added to cmdline */ 4388 if (full_screen) 4389 screen_fill((int)(cmdline_row), (int)Rows, 0, 4390 (int)Columns, ' ', ' ', 0); 4391 msg_row = cmdline_row; 4392 redraw_cmdline = TRUE; 4393 return; 4394 } 4395 4396 if (msg_row < cmdline_row) 4397 msg_row = cmdline_row; 4398 redraw_cmdline = TRUE; 4399 } 4400 frame_add_height(frp, (int)(old_p_ch - p_ch)); 4401 4402 /* Recompute window positions. */ 4403 if (frp != lastwin->w_frame) 4404 (void)win_comp_pos(); 4405 #else 4406 win_setheight((int)(firstwin->w_height + old_p_ch - p_ch)); 4407 cmdline_row = Rows - p_ch; 4408 #endif 4409 } 4410 4411 #if defined(FEAT_WINDOWS) || defined(PROTO) 4412 /* 4413 * Resize frame "frp" to be "n" lines higher (negative for less high). 4414 * Also resize the frames it is contained in. 4415 */ 4416 static void 4417 frame_add_height(frp, n) 4418 frame_T *frp; 4419 int n; 4420 { 4421 frame_new_height(frp, frp->fr_height + n, FALSE, FALSE); 4422 for (;;) 4423 { 4424 frp = frp->fr_parent; 4425 if (frp == NULL) 4426 break; 4427 frp->fr_height += n; 4428 } 4429 } 4430 4431 /* 4432 * Add or remove a status line for the bottom window(s), according to the 4433 * value of 'laststatus'. 4434 */ 4435 void 4436 last_status(morewin) 4437 int morewin; /* pretend there are two or more windows */ 4438 { 4439 /* Don't make a difference between horizontal or vertical split. */ 4440 last_status_rec(topframe, (p_ls == 2 4441 || (p_ls == 1 && (morewin || lastwin != firstwin)))); 4442 } 4443 4444 static void 4445 last_status_rec(fr, statusline) 4446 frame_T *fr; 4447 int statusline; 4448 { 4449 frame_T *fp; 4450 win_T *wp; 4451 4452 if (fr->fr_layout == FR_LEAF) 4453 { 4454 wp = fr->fr_win; 4455 if (wp->w_status_height != 0 && !statusline) 4456 { 4457 /* remove status line */ 4458 win_new_height(wp, wp->w_height + 1); 4459 wp->w_status_height = 0; 4460 comp_col(); 4461 } 4462 else if (wp->w_status_height == 0 && statusline) 4463 { 4464 /* Find a frame to take a line from. */ 4465 fp = fr; 4466 while (fp->fr_height <= frame_minheight(fp, NULL)) 4467 { 4468 if (fp == topframe) 4469 { 4470 EMSG(_(e_noroom)); 4471 return; 4472 } 4473 /* In a column of frames: go to frame above. If already at 4474 * the top or in a row of frames: go to parent. */ 4475 if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) 4476 fp = fp->fr_prev; 4477 else 4478 fp = fp->fr_parent; 4479 } 4480 wp->w_status_height = 1; 4481 if (fp != fr) 4482 { 4483 frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE); 4484 frame_fix_height(wp); 4485 (void)win_comp_pos(); 4486 } 4487 else 4488 win_new_height(wp, wp->w_height - 1); 4489 comp_col(); 4490 redraw_all_later(NOT_VALID); 4491 } 4492 } 4493 #ifdef FEAT_VERTSPLIT 4494 else if (fr->fr_layout == FR_ROW) 4495 { 4496 /* vertically split windows, set status line for each one */ 4497 for (fp = fr->fr_child; fp != NULL; fp = fp->fr_next) 4498 last_status_rec(fp, statusline); 4499 } 4500 #endif 4501 else 4502 { 4503 /* horizontally split window, set status line for last one */ 4504 for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) 4505 ; 4506 last_status_rec(fp, statusline); 4507 } 4508 } 4509 4510 #endif /* FEAT_WINDOWS */ 4511 4512 #if defined(FEAT_SEARCHPATH) || defined(PROTO) 4513 /* 4514 * Get the file name at the cursor. 4515 * If Visual mode is active, use the selected text if it's in one line. 4516 * Returns the name in allocated memory, NULL for failure. 4517 */ 4518 char_u * 4519 grab_file_name(count) 4520 long count; 4521 { 4522 # ifdef FEAT_VISUAL 4523 if (VIsual_active) 4524 { 4525 int len; 4526 char_u *ptr; 4527 4528 if (get_visual_text(NULL, &ptr, &len) == FAIL) 4529 return NULL; 4530 return find_file_name_in_path(ptr, len, 4531 FNAME_MESS|FNAME_EXP|FNAME_REL, count, curbuf->b_ffname); 4532 } 4533 # endif 4534 return file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP|FNAME_REL, count); 4535 } 4536 4537 /* 4538 * Return the file name under or after the cursor. 4539 * 4540 * The 'path' option is searched if the file name is not absolute. 4541 * The string returned has been alloc'ed and should be freed by the caller. 4542 * NULL is returned if the file name or file is not found. 4543 * 4544 * options: 4545 * FNAME_MESS give error messages 4546 * FNAME_EXP expand to path 4547 * FNAME_HYP check for hypertext link 4548 * FNAME_INCL apply "includeexpr" 4549 */ 4550 char_u * 4551 file_name_at_cursor(options, count) 4552 int options; 4553 long count; 4554 { 4555 return file_name_in_line(ml_get_curline(), 4556 curwin->w_cursor.col, options, count, curbuf->b_ffname); 4557 } 4558 4559 /* 4560 * Return the name of the file under or after ptr[col]. 4561 * Otherwise like file_name_at_cursor(). 4562 */ 4563 char_u * 4564 file_name_in_line(line, col, options, count, rel_fname) 4565 char_u *line; 4566 int col; 4567 int options; 4568 long count; 4569 char_u *rel_fname; /* file we are searching relative to */ 4570 { 4571 char_u *ptr; 4572 int len; 4573 4574 /* 4575 * search forward for what could be the start of a file name 4576 */ 4577 ptr = line + col; 4578 while (*ptr != NUL && !vim_isfilec(*ptr)) 4579 mb_ptr_adv(ptr); 4580 if (*ptr == NUL) /* nothing found */ 4581 { 4582 if (options & FNAME_MESS) 4583 EMSG(_("E446: No file name under cursor")); 4584 return NULL; 4585 } 4586 4587 /* 4588 * Search backward for first char of the file name. 4589 * Go one char back to ":" before "//" even when ':' is not in 'isfname'. 4590 */ 4591 while (ptr > line) 4592 { 4593 #ifdef FEAT_MBYTE 4594 if (has_mbyte && (len = (*mb_head_off)(line, ptr - 1)) > 0) 4595 ptr -= len + 1; 4596 else 4597 #endif 4598 if (vim_isfilec(ptr[-1]) 4599 || ((options & FNAME_HYP) && path_is_url(ptr - 1))) 4600 --ptr; 4601 else 4602 break; 4603 } 4604 4605 /* 4606 * Search forward for the last char of the file name. 4607 * Also allow "://" when ':' is not in 'isfname'. 4608 */ 4609 len = 0; 4610 while (vim_isfilec(ptr[len]) 4611 || ((options & FNAME_HYP) && path_is_url(ptr + len))) 4612 #ifdef FEAT_MBYTE 4613 if (has_mbyte) 4614 len += (*mb_ptr2len)(ptr + len); 4615 else 4616 #endif 4617 ++len; 4618 4619 /* 4620 * If there is trailing punctuation, remove it. 4621 * But don't remove "..", could be a directory name. 4622 */ 4623 if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL 4624 && ptr[len - 2] != '.') 4625 --len; 4626 4627 return find_file_name_in_path(ptr, len, options, count, rel_fname); 4628 } 4629 4630 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 4631 static char_u *eval_includeexpr __ARGS((char_u *ptr, int len)); 4632 4633 static char_u * 4634 eval_includeexpr(ptr, len) 4635 char_u *ptr; 4636 int len; 4637 { 4638 char_u *res; 4639 4640 set_vim_var_string(VV_FNAME, ptr, len); 4641 res = eval_to_string_safe(curbuf->b_p_inex, NULL); 4642 set_vim_var_string(VV_FNAME, NULL, 0); 4643 return res; 4644 } 4645 #endif 4646 4647 /* 4648 * Return the name of the file ptr[len] in 'path'. 4649 * Otherwise like file_name_at_cursor(). 4650 */ 4651 char_u * 4652 find_file_name_in_path(ptr, len, options, count, rel_fname) 4653 char_u *ptr; 4654 int len; 4655 int options; 4656 long count; 4657 char_u *rel_fname; /* file we are searching relative to */ 4658 { 4659 char_u *file_name; 4660 int c; 4661 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 4662 char_u *tofree = NULL; 4663 4664 if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) 4665 { 4666 tofree = eval_includeexpr(ptr, len); 4667 if (tofree != NULL) 4668 { 4669 ptr = tofree; 4670 len = (int)STRLEN(ptr); 4671 } 4672 } 4673 # endif 4674 4675 if (options & FNAME_EXP) 4676 { 4677 file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, 4678 TRUE, rel_fname); 4679 4680 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 4681 /* 4682 * If the file could not be found in a normal way, try applying 4683 * 'includeexpr' (unless done already). 4684 */ 4685 if (file_name == NULL 4686 && !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) 4687 { 4688 tofree = eval_includeexpr(ptr, len); 4689 if (tofree != NULL) 4690 { 4691 ptr = tofree; 4692 len = (int)STRLEN(ptr); 4693 file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, 4694 TRUE, rel_fname); 4695 } 4696 } 4697 # endif 4698 if (file_name == NULL && (options & FNAME_MESS)) 4699 { 4700 c = ptr[len]; 4701 ptr[len] = NUL; 4702 EMSG2(_("E447: Can't find file \"%s\" in path"), ptr); 4703 ptr[len] = c; 4704 } 4705 4706 /* Repeat finding the file "count" times. This matters when it 4707 * appears several times in the path. */ 4708 while (file_name != NULL && --count > 0) 4709 { 4710 vim_free(file_name); 4711 file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname); 4712 } 4713 } 4714 else 4715 file_name = vim_strnsave(ptr, len); 4716 4717 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 4718 vim_free(tofree); 4719 # endif 4720 4721 return file_name; 4722 } 4723 #endif /* FEAT_SEARCHPATH */ 4724 4725 /* 4726 * Check if the "://" of a URL is at the pointer, return URL_SLASH. 4727 * Also check for ":\\", which MS Internet Explorer accepts, return 4728 * URL_BACKSLASH. 4729 */ 4730 static int 4731 path_is_url(p) 4732 char_u *p; 4733 { 4734 if (STRNCMP(p, "://", (size_t)3) == 0) 4735 return URL_SLASH; 4736 else if (STRNCMP(p, ":\\\\", (size_t)3) == 0) 4737 return URL_BACKSLASH; 4738 return 0; 4739 } 4740 4741 /* 4742 * Check if "fname" starts with "name://". Return URL_SLASH if it does. 4743 * Return URL_BACKSLASH for "name:\\". 4744 * Return zero otherwise. 4745 */ 4746 int 4747 path_with_url(fname) 4748 char_u *fname; 4749 { 4750 char_u *p; 4751 4752 for (p = fname; isalpha(*p); ++p) 4753 ; 4754 return path_is_url(p); 4755 } 4756 4757 /* 4758 * Return TRUE if "name" is a full (absolute) path name or URL. 4759 */ 4760 int 4761 vim_isAbsName(name) 4762 char_u *name; 4763 { 4764 return (path_with_url(name) != 0 || mch_isFullName(name)); 4765 } 4766 4767 /* 4768 * Get absolute file name into buffer "buf[len]". 4769 * 4770 * return FAIL for failure, OK otherwise 4771 */ 4772 int 4773 vim_FullName(fname, buf, len, force) 4774 char_u *fname, *buf; 4775 int len; 4776 int force; /* force expansion even when already absolute */ 4777 { 4778 int retval = OK; 4779 int url; 4780 4781 *buf = NUL; 4782 if (fname == NULL) 4783 return FAIL; 4784 4785 url = path_with_url(fname); 4786 if (!url) 4787 retval = mch_FullName(fname, buf, len, force); 4788 if (url || retval == FAIL) 4789 { 4790 /* something failed; use the file name (truncate when too long) */ 4791 vim_strncpy(buf, fname, len - 1); 4792 } 4793 #if defined(MACOS_CLASSIC) || defined(OS2) || defined(MSDOS) || defined(MSWIN) 4794 slash_adjust(buf); 4795 #endif 4796 return retval; 4797 } 4798 4799 /* 4800 * Return the minimal number of rows that is needed on the screen to display 4801 * the current number of windows. 4802 */ 4803 int 4804 min_rows() 4805 { 4806 int total; 4807 4808 if (firstwin == NULL) /* not initialized yet */ 4809 return MIN_LINES; 4810 4811 total = 1; /* count the room for the command line */ 4812 #ifdef FEAT_WINDOWS 4813 total += frame_minheight(topframe, NULL); 4814 #else 4815 total += 1; /* at least one window should have a line! */ 4816 #endif 4817 return total; 4818 } 4819 4820 /* 4821 * Return TRUE if there is only one window, not counting a help or preview 4822 * window, unless it is the current window. 4823 */ 4824 int 4825 only_one_window() 4826 { 4827 #ifdef FEAT_WINDOWS 4828 int count = 0; 4829 win_T *wp; 4830 4831 for (wp = firstwin; wp != NULL; wp = wp->w_next) 4832 if (!((wp->w_buffer->b_help && !curbuf->b_help) 4833 # ifdef FEAT_QUICKFIX 4834 || wp->w_p_pvw 4835 # endif 4836 ) || wp == curwin) 4837 ++count; 4838 return (count <= 1); 4839 #else 4840 return TRUE; 4841 #endif 4842 } 4843 4844 #if defined(FEAT_WINDOWS) || defined(FEAT_AUTOCMD) || defined(PROTO) 4845 /* 4846 * Correct the cursor line number in other windows. Used after changing the 4847 * current buffer, and before applying autocommands. 4848 * When "do_curwin" is TRUE, also check current window. 4849 */ 4850 void 4851 check_lnums(do_curwin) 4852 int do_curwin; 4853 { 4854 win_T *wp; 4855 4856 #ifdef FEAT_WINDOWS 4857 for (wp = firstwin; wp != NULL; wp = wp->w_next) 4858 if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) 4859 #else 4860 wp = curwin; 4861 if (do_curwin) 4862 #endif 4863 { 4864 if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count) 4865 wp->w_cursor.lnum = curbuf->b_ml.ml_line_count; 4866 if (wp->w_topline > curbuf->b_ml.ml_line_count) 4867 wp->w_topline = curbuf->b_ml.ml_line_count; 4868 } 4869 } 4870 #endif 4871 4872 #if defined(FEAT_WINDOWS) || defined(PROTO) 4873 4874 /* 4875 * A snapshot of the window sizes, to restore them after closing the help 4876 * window. 4877 * Only these fields are used: 4878 * fr_layout 4879 * fr_width 4880 * fr_height 4881 * fr_next 4882 * fr_child 4883 * fr_win (only valid for the old curwin, NULL otherwise) 4884 */ 4885 static frame_T *snapshot = NULL; 4886 4887 /* 4888 * Create a snapshot of the current frame sizes. 4889 */ 4890 static void 4891 make_snapshot() 4892 { 4893 clear_snapshot(); 4894 make_snapshot_rec(topframe, &snapshot); 4895 } 4896 4897 static void 4898 make_snapshot_rec(fr, frp) 4899 frame_T *fr; 4900 frame_T **frp; 4901 { 4902 *frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 4903 if (*frp == NULL) 4904 return; 4905 (*frp)->fr_layout = fr->fr_layout; 4906 # ifdef FEAT_VERTSPLIT 4907 (*frp)->fr_width = fr->fr_width; 4908 # endif 4909 (*frp)->fr_height = fr->fr_height; 4910 if (fr->fr_next != NULL) 4911 make_snapshot_rec(fr->fr_next, &((*frp)->fr_next)); 4912 if (fr->fr_child != NULL) 4913 make_snapshot_rec(fr->fr_child, &((*frp)->fr_child)); 4914 if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin) 4915 (*frp)->fr_win = curwin; 4916 } 4917 4918 /* 4919 * Remove any existing snapshot. 4920 */ 4921 static void 4922 clear_snapshot() 4923 { 4924 clear_snapshot_rec(snapshot); 4925 snapshot = NULL; 4926 } 4927 4928 static void 4929 clear_snapshot_rec(fr) 4930 frame_T *fr; 4931 { 4932 if (fr != NULL) 4933 { 4934 clear_snapshot_rec(fr->fr_next); 4935 clear_snapshot_rec(fr->fr_child); 4936 vim_free(fr); 4937 } 4938 } 4939 4940 /* 4941 * Restore a previously created snapshot, if there is any. 4942 * This is only done if the screen size didn't change and the window layout is 4943 * still the same. 4944 */ 4945 static void 4946 restore_snapshot(close_curwin) 4947 int close_curwin; /* closing current window */ 4948 { 4949 win_T *wp; 4950 4951 if (snapshot != NULL 4952 # ifdef FEAT_VERTSPLIT 4953 && snapshot->fr_width == topframe->fr_width 4954 # endif 4955 && snapshot->fr_height == topframe->fr_height 4956 && check_snapshot_rec(snapshot, topframe) == OK) 4957 { 4958 wp = restore_snapshot_rec(snapshot, topframe); 4959 win_comp_pos(); 4960 if (wp != NULL && close_curwin) 4961 win_goto(wp); 4962 redraw_all_later(CLEAR); 4963 } 4964 clear_snapshot(); 4965 } 4966 4967 /* 4968 * Check if frames "sn" and "fr" have the same layout, same following frames 4969 * and same children. 4970 */ 4971 static int 4972 check_snapshot_rec(sn, fr) 4973 frame_T *sn; 4974 frame_T *fr; 4975 { 4976 if (sn->fr_layout != fr->fr_layout 4977 || (sn->fr_next == NULL) != (fr->fr_next == NULL) 4978 || (sn->fr_child == NULL) != (fr->fr_child == NULL) 4979 || (sn->fr_next != NULL 4980 && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) 4981 || (sn->fr_child != NULL 4982 && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)) 4983 return FAIL; 4984 return OK; 4985 } 4986 4987 /* 4988 * Copy the size of snapshot frame "sn" to frame "fr". Do the same for all 4989 * following frames and children. 4990 * Returns a pointer to the old current window, or NULL. 4991 */ 4992 static win_T * 4993 restore_snapshot_rec(sn, fr) 4994 frame_T *sn; 4995 frame_T *fr; 4996 { 4997 win_T *wp = NULL; 4998 win_T *wp2; 4999 5000 fr->fr_height = sn->fr_height; 5001 # ifdef FEAT_VERTSPLIT 5002 fr->fr_width = sn->fr_width; 5003 # endif 5004 if (fr->fr_layout == FR_LEAF) 5005 { 5006 frame_new_height(fr, fr->fr_height, FALSE, FALSE); 5007 # ifdef FEAT_VERTSPLIT 5008 frame_new_width(fr, fr->fr_width, FALSE); 5009 # endif 5010 wp = sn->fr_win; 5011 } 5012 if (sn->fr_next != NULL) 5013 { 5014 wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next); 5015 if (wp2 != NULL) 5016 wp = wp2; 5017 } 5018 if (sn->fr_child != NULL) 5019 { 5020 wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child); 5021 if (wp2 != NULL) 5022 wp = wp2; 5023 } 5024 return wp; 5025 } 5026 5027 #endif 5028 5029 #if (defined(FEAT_GUI) && defined(FEAT_VERTSPLIT)) || defined(PROTO) 5030 /* 5031 * Return TRUE if there is any vertically split window. 5032 */ 5033 int 5034 win_hasvertsplit() 5035 { 5036 frame_T *fr; 5037 5038 if (topframe->fr_layout == FR_ROW) 5039 return TRUE; 5040 5041 if (topframe->fr_layout == FR_COL) 5042 for (fr = topframe->fr_child; fr != NULL; fr = fr->fr_next) 5043 if (fr->fr_layout == FR_ROW) 5044 return TRUE; 5045 5046 return FALSE; 5047 } 5048 #endif 5049