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 static int path_is_url __ARGS((char_u *p)); 13 #if defined(FEAT_WINDOWS) || defined(PROTO) 14 static void cmd_with_count __ARGS((char *cmd, char_u *bufp, size_t bufsize, long Prenum)); 15 static void win_init __ARGS((win_T *newp, win_T *oldp, int flags)); 16 static void win_init_some __ARGS((win_T *newp, win_T *oldp)); 17 static void frame_comp_pos __ARGS((frame_T *topfrp, int *row, int *col)); 18 static void frame_setheight __ARGS((frame_T *curfrp, int height)); 19 #ifdef FEAT_VERTSPLIT 20 static void frame_setwidth __ARGS((frame_T *curfrp, int width)); 21 #endif 22 static void win_exchange __ARGS((long)); 23 static void win_rotate __ARGS((int, int)); 24 static void win_totop __ARGS((int size, int flags)); 25 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)); 26 static int last_window __ARGS((void)); 27 static int close_last_window_tabpage __ARGS((win_T *win, int free_buf, tabpage_T *prev_curtab)); 28 static win_T *win_free_mem __ARGS((win_T *win, int *dirp, tabpage_T *tp)); 29 static frame_T *win_altframe __ARGS((win_T *win, tabpage_T *tp)); 30 static tabpage_T *alt_tabpage __ARGS((void)); 31 static win_T *frame2win __ARGS((frame_T *frp)); 32 static int frame_has_win __ARGS((frame_T *frp, win_T *wp)); 33 static void frame_new_height __ARGS((frame_T *topfrp, int height, int topfirst, int wfh)); 34 static int frame_fixed_height __ARGS((frame_T *frp)); 35 #ifdef FEAT_VERTSPLIT 36 static int frame_fixed_width __ARGS((frame_T *frp)); 37 static void frame_add_statusline __ARGS((frame_T *frp)); 38 static void frame_new_width __ARGS((frame_T *topfrp, int width, int leftfirst, int wfw)); 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 #endif 44 static int win_alloc_firstwin __ARGS((win_T *oldwin)); 45 static void new_frame __ARGS((win_T *wp)); 46 #if defined(FEAT_WINDOWS) || defined(PROTO) 47 static tabpage_T *alloc_tabpage __ARGS((void)); 48 static int leave_tabpage __ARGS((buf_T *new_curbuf, int trigger_leave_autocmds)); 49 static void enter_tabpage __ARGS((tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_autocmds, int trigger_leave_autocmds)); 50 static void frame_fix_height __ARGS((win_T *wp)); 51 static int frame_minheight __ARGS((frame_T *topfrp, win_T *next_curwin)); 52 static void win_enter_ext __ARGS((win_T *wp, int undo_sync, int no_curwin, int trigger_enter_autocmds, int trigger_leave_autocmds)); 53 static void win_free __ARGS((win_T *wp, tabpage_T *tp)); 54 static void frame_append __ARGS((frame_T *after, frame_T *frp)); 55 static void frame_insert __ARGS((frame_T *before, frame_T *frp)); 56 static void frame_remove __ARGS((frame_T *frp)); 57 # ifdef FEAT_VERTSPLIT 58 static void win_goto_ver __ARGS((int up, long count)); 59 static void win_goto_hor __ARGS((int left, long count)); 60 # endif 61 static void frame_add_height __ARGS((frame_T *frp, int n)); 62 static void last_status_rec __ARGS((frame_T *fr, int statusline)); 63 64 static void make_snapshot_rec __ARGS((frame_T *fr, frame_T **frp)); 65 static void clear_snapshot __ARGS((tabpage_T *tp, int idx)); 66 static void clear_snapshot_rec __ARGS((frame_T *fr)); 67 static int check_snapshot_rec __ARGS((frame_T *sn, frame_T *fr)); 68 static win_T *restore_snapshot_rec __ARGS((frame_T *sn, frame_T *fr)); 69 70 static int frame_check_height __ARGS((frame_T *topfrp, int height)); 71 #ifdef FEAT_VERTSPLIT 72 static int frame_check_width __ARGS((frame_T *topfrp, int width)); 73 #endif 74 75 #endif /* FEAT_WINDOWS */ 76 77 static win_T *win_alloc __ARGS((win_T *after, int hidden)); 78 79 #define URL_SLASH 1 /* path_is_url() has found "://" */ 80 #define URL_BACKSLASH 2 /* path_is_url() has found ":\\" */ 81 82 #define NOWIN (win_T *)-1 /* non-existing window */ 83 84 #ifdef FEAT_WINDOWS 85 # define ROWS_AVAIL (Rows - p_ch - tabline_height()) 86 #else 87 # define ROWS_AVAIL (Rows - p_ch) 88 #endif 89 90 #if defined(FEAT_WINDOWS) || defined(PROTO) 91 92 static char *m_onlyone = N_("Already only one window"); 93 94 /* 95 * all CTRL-W window commands are handled here, called from normal_cmd(). 96 */ 97 void 98 do_window(nchar, Prenum, xchar) 99 int nchar; 100 long Prenum; 101 int xchar; /* extra char from ":wincmd gx" or NUL */ 102 { 103 long Prenum1; 104 win_T *wp; 105 #if defined(FEAT_SEARCHPATH) || defined(FEAT_FIND_ID) 106 char_u *ptr; 107 linenr_T lnum = -1; 108 #endif 109 #ifdef FEAT_FIND_ID 110 int type = FIND_DEFINE; 111 int len; 112 #endif 113 char_u cbuf[40]; 114 115 if (Prenum == 0) 116 Prenum1 = 1; 117 else 118 Prenum1 = Prenum; 119 120 #ifdef FEAT_CMDWIN 121 # define CHECK_CMDWIN if (cmdwin_type != 0) { EMSG(_(e_cmdwin)); break; } 122 #else 123 # define CHECK_CMDWIN 124 #endif 125 126 switch (nchar) 127 { 128 /* split current window in two parts, horizontally */ 129 case 'S': 130 case Ctrl_S: 131 case 's': 132 CHECK_CMDWIN 133 reset_VIsual_and_resel(); /* stop Visual mode */ 134 #ifdef FEAT_QUICKFIX 135 /* When splitting the quickfix window open a new buffer in it, 136 * don't replicate the quickfix buffer. */ 137 if (bt_quickfix(curbuf)) 138 goto newwindow; 139 #endif 140 #ifdef FEAT_GUI 141 need_mouse_correct = TRUE; 142 #endif 143 (void)win_split((int)Prenum, 0); 144 break; 145 146 #ifdef FEAT_VERTSPLIT 147 /* split current window in two parts, vertically */ 148 case Ctrl_V: 149 case 'v': 150 CHECK_CMDWIN 151 reset_VIsual_and_resel(); /* stop Visual mode */ 152 # ifdef FEAT_QUICKFIX 153 /* When splitting the quickfix window open a new buffer in it, 154 * don't replicate the quickfix buffer. */ 155 if (bt_quickfix(curbuf)) 156 goto newwindow; 157 # endif 158 # ifdef FEAT_GUI 159 need_mouse_correct = TRUE; 160 # endif 161 (void)win_split((int)Prenum, WSP_VERT); 162 break; 163 #endif 164 165 /* split current window and edit alternate file */ 166 case Ctrl_HAT: 167 case '^': 168 CHECK_CMDWIN 169 reset_VIsual_and_resel(); /* stop Visual mode */ 170 cmd_with_count("split #", cbuf, sizeof(cbuf), Prenum); 171 do_cmdline_cmd(cbuf); 172 break; 173 174 /* open new window */ 175 case Ctrl_N: 176 case 'n': 177 CHECK_CMDWIN 178 reset_VIsual_and_resel(); /* stop Visual mode */ 179 #ifdef FEAT_QUICKFIX 180 newwindow: 181 #endif 182 if (Prenum) 183 /* window height */ 184 vim_snprintf((char *)cbuf, sizeof(cbuf) - 5, "%ld", Prenum); 185 else 186 cbuf[0] = NUL; 187 #if defined(FEAT_VERTSPLIT) && defined(FEAT_QUICKFIX) 188 if (nchar == 'v' || nchar == Ctrl_V) 189 STRCAT(cbuf, "v"); 190 #endif 191 STRCAT(cbuf, "new"); 192 do_cmdline_cmd(cbuf); 193 break; 194 195 /* quit current window */ 196 case Ctrl_Q: 197 case 'q': 198 reset_VIsual_and_resel(); /* stop Visual mode */ 199 cmd_with_count("quit", cbuf, sizeof(cbuf), Prenum); 200 do_cmdline_cmd(cbuf); 201 break; 202 203 /* close current window */ 204 case Ctrl_C: 205 case 'c': 206 reset_VIsual_and_resel(); /* stop Visual mode */ 207 cmd_with_count("close", cbuf, sizeof(cbuf), Prenum); 208 do_cmdline_cmd(cbuf); 209 break; 210 211 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 212 /* close preview window */ 213 case Ctrl_Z: 214 case 'z': 215 CHECK_CMDWIN 216 reset_VIsual_and_resel(); /* stop Visual mode */ 217 do_cmdline_cmd((char_u *)"pclose"); 218 break; 219 220 /* cursor to preview window */ 221 case 'P': 222 for (wp = firstwin; wp != NULL; wp = wp->w_next) 223 if (wp->w_p_pvw) 224 break; 225 if (wp == NULL) 226 EMSG(_("E441: There is no preview window")); 227 else 228 win_goto(wp); 229 break; 230 #endif 231 232 /* close all but current window */ 233 case Ctrl_O: 234 case 'o': 235 CHECK_CMDWIN 236 reset_VIsual_and_resel(); /* stop Visual mode */ 237 cmd_with_count("only", cbuf, sizeof(cbuf), Prenum); 238 do_cmdline_cmd(cbuf); 239 break; 240 241 /* cursor to next window with wrap around */ 242 case Ctrl_W: 243 case 'w': 244 /* cursor to previous window with wrap around */ 245 case 'W': 246 CHECK_CMDWIN 247 if (firstwin == lastwin && Prenum != 1) /* just one window */ 248 beep_flush(); 249 else 250 { 251 if (Prenum) /* go to specified window */ 252 { 253 for (wp = firstwin; --Prenum > 0; ) 254 { 255 if (wp->w_next == NULL) 256 break; 257 else 258 wp = wp->w_next; 259 } 260 } 261 else 262 { 263 if (nchar == 'W') /* go to previous window */ 264 { 265 wp = curwin->w_prev; 266 if (wp == NULL) 267 wp = lastwin; /* wrap around */ 268 } 269 else /* go to next window */ 270 { 271 wp = curwin->w_next; 272 if (wp == NULL) 273 wp = firstwin; /* wrap around */ 274 } 275 } 276 win_goto(wp); 277 } 278 break; 279 280 /* cursor to window below */ 281 case 'j': 282 case K_DOWN: 283 case Ctrl_J: 284 CHECK_CMDWIN 285 #ifdef FEAT_VERTSPLIT 286 win_goto_ver(FALSE, Prenum1); 287 #else 288 for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0; 289 wp = wp->w_next) 290 ; 291 win_goto(wp); 292 #endif 293 break; 294 295 /* cursor to window above */ 296 case 'k': 297 case K_UP: 298 case Ctrl_K: 299 CHECK_CMDWIN 300 #ifdef FEAT_VERTSPLIT 301 win_goto_ver(TRUE, Prenum1); 302 #else 303 for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0; 304 wp = wp->w_prev) 305 ; 306 win_goto(wp); 307 #endif 308 break; 309 310 #ifdef FEAT_VERTSPLIT 311 /* cursor to left window */ 312 case 'h': 313 case K_LEFT: 314 case Ctrl_H: 315 case K_BS: 316 CHECK_CMDWIN 317 win_goto_hor(TRUE, Prenum1); 318 break; 319 320 /* cursor to right window */ 321 case 'l': 322 case K_RIGHT: 323 case Ctrl_L: 324 CHECK_CMDWIN 325 win_goto_hor(FALSE, Prenum1); 326 break; 327 #endif 328 329 /* move window to new tab page */ 330 case 'T': 331 if (one_window()) 332 MSG(_(m_onlyone)); 333 else 334 { 335 tabpage_T *oldtab = curtab; 336 tabpage_T *newtab; 337 338 /* First create a new tab with the window, then go back to 339 * the old tab and close the window there. */ 340 wp = curwin; 341 if (win_new_tabpage((int)Prenum) == OK 342 && valid_tabpage(oldtab)) 343 { 344 newtab = curtab; 345 goto_tabpage_tp(oldtab, TRUE, TRUE); 346 if (curwin == wp) 347 win_close(curwin, FALSE); 348 if (valid_tabpage(newtab)) 349 goto_tabpage_tp(newtab, TRUE, TRUE); 350 } 351 } 352 break; 353 354 /* cursor to top-left window */ 355 case 't': 356 case Ctrl_T: 357 win_goto(firstwin); 358 break; 359 360 /* cursor to bottom-right window */ 361 case 'b': 362 case Ctrl_B: 363 win_goto(lastwin); 364 break; 365 366 /* cursor to last accessed (previous) window */ 367 case 'p': 368 case Ctrl_P: 369 if (prevwin == NULL) 370 beep_flush(); 371 else 372 win_goto(prevwin); 373 break; 374 375 /* exchange current and next window */ 376 case 'x': 377 case Ctrl_X: 378 CHECK_CMDWIN 379 win_exchange(Prenum); 380 break; 381 382 /* rotate windows downwards */ 383 case Ctrl_R: 384 case 'r': 385 CHECK_CMDWIN 386 reset_VIsual_and_resel(); /* stop Visual mode */ 387 win_rotate(FALSE, (int)Prenum1); /* downwards */ 388 break; 389 390 /* rotate windows upwards */ 391 case 'R': 392 CHECK_CMDWIN 393 reset_VIsual_and_resel(); /* stop Visual mode */ 394 win_rotate(TRUE, (int)Prenum1); /* upwards */ 395 break; 396 397 /* move window to the very top/bottom/left/right */ 398 case 'K': 399 case 'J': 400 #ifdef FEAT_VERTSPLIT 401 case 'H': 402 case 'L': 403 #endif 404 CHECK_CMDWIN 405 win_totop((int)Prenum, 406 ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0) 407 | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT)); 408 break; 409 410 /* make all windows the same height */ 411 case '=': 412 #ifdef FEAT_GUI 413 need_mouse_correct = TRUE; 414 #endif 415 win_equal(NULL, FALSE, 'b'); 416 break; 417 418 /* increase current window height */ 419 case '+': 420 #ifdef FEAT_GUI 421 need_mouse_correct = TRUE; 422 #endif 423 win_setheight(curwin->w_height + (int)Prenum1); 424 break; 425 426 /* decrease current window height */ 427 case '-': 428 #ifdef FEAT_GUI 429 need_mouse_correct = TRUE; 430 #endif 431 win_setheight(curwin->w_height - (int)Prenum1); 432 break; 433 434 /* set current window height */ 435 case Ctrl__: 436 case '_': 437 #ifdef FEAT_GUI 438 need_mouse_correct = TRUE; 439 #endif 440 win_setheight(Prenum ? (int)Prenum : 9999); 441 break; 442 443 #ifdef FEAT_VERTSPLIT 444 /* increase current window width */ 445 case '>': 446 #ifdef FEAT_GUI 447 need_mouse_correct = TRUE; 448 #endif 449 win_setwidth(curwin->w_width + (int)Prenum1); 450 break; 451 452 /* decrease current window width */ 453 case '<': 454 #ifdef FEAT_GUI 455 need_mouse_correct = TRUE; 456 #endif 457 win_setwidth(curwin->w_width - (int)Prenum1); 458 break; 459 460 /* set current window width */ 461 case '|': 462 #ifdef FEAT_GUI 463 need_mouse_correct = TRUE; 464 #endif 465 win_setwidth(Prenum != 0 ? (int)Prenum : 9999); 466 break; 467 #endif 468 469 /* jump to tag and split window if tag exists (in preview window) */ 470 #if defined(FEAT_QUICKFIX) 471 case '}': 472 CHECK_CMDWIN 473 if (Prenum) 474 g_do_tagpreview = Prenum; 475 else 476 g_do_tagpreview = p_pvh; 477 /*FALLTHROUGH*/ 478 #endif 479 case ']': 480 case Ctrl_RSB: 481 CHECK_CMDWIN 482 /* keep Visual mode, can select words to use as a tag */ 483 if (Prenum) 484 postponed_split = Prenum; 485 else 486 postponed_split = -1; 487 #ifdef FEAT_QUICKFIX 488 if (nchar != '}') 489 g_do_tagpreview = 0; 490 #endif 491 492 /* Execute the command right here, required when "wincmd ]" 493 * was used in a function. */ 494 do_nv_ident(Ctrl_RSB, NUL); 495 break; 496 497 #ifdef FEAT_SEARCHPATH 498 /* edit file name under cursor in a new window */ 499 case 'f': 500 case 'F': 501 case Ctrl_F: 502 wingotofile: 503 CHECK_CMDWIN 504 505 ptr = grab_file_name(Prenum1, &lnum); 506 if (ptr != NULL) 507 { 508 # ifdef FEAT_GUI 509 need_mouse_correct = TRUE; 510 # endif 511 setpcmark(); 512 if (win_split(0, 0) == OK) 513 { 514 RESET_BINDING(curwin); 515 (void)do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, 516 ECMD_HIDE, NULL); 517 if (nchar == 'F' && lnum >= 0) 518 { 519 curwin->w_cursor.lnum = lnum; 520 check_cursor_lnum(); 521 beginline(BL_SOL | BL_FIX); 522 } 523 } 524 vim_free(ptr); 525 } 526 break; 527 #endif 528 529 #ifdef FEAT_FIND_ID 530 /* Go to the first occurrence of the identifier under cursor along path in a 531 * new window -- webb 532 */ 533 case 'i': /* Go to any match */ 534 case Ctrl_I: 535 type = FIND_ANY; 536 /* FALLTHROUGH */ 537 case 'd': /* Go to definition, using 'define' */ 538 case Ctrl_D: 539 CHECK_CMDWIN 540 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) 541 break; 542 find_pattern_in_path(ptr, 0, len, TRUE, 543 Prenum == 0 ? TRUE : FALSE, type, 544 Prenum1, ACTION_SPLIT, (linenr_T)1, (linenr_T)MAXLNUM); 545 curwin->w_set_curswant = TRUE; 546 break; 547 #endif 548 549 case K_KENTER: 550 case CAR: 551 #if defined(FEAT_QUICKFIX) 552 /* 553 * In a quickfix window a <CR> jumps to the error under the 554 * cursor in a new window. 555 */ 556 if (bt_quickfix(curbuf)) 557 { 558 sprintf((char *)cbuf, "split +%ld%s", 559 (long)curwin->w_cursor.lnum, 560 (curwin->w_llist_ref == NULL) ? "cc" : "ll"); 561 do_cmdline_cmd(cbuf); 562 } 563 #endif 564 break; 565 566 567 /* CTRL-W g extended commands */ 568 case 'g': 569 case Ctrl_G: 570 CHECK_CMDWIN 571 #ifdef USE_ON_FLY_SCROLL 572 dont_scroll = TRUE; /* disallow scrolling here */ 573 #endif 574 ++no_mapping; 575 ++allow_keys; /* no mapping for xchar, but allow key codes */ 576 if (xchar == NUL) 577 xchar = plain_vgetc(); 578 LANGMAP_ADJUST(xchar, TRUE); 579 --no_mapping; 580 --allow_keys; 581 #ifdef FEAT_CMDL_INFO 582 (void)add_to_showcmd(xchar); 583 #endif 584 switch (xchar) 585 { 586 #if defined(FEAT_QUICKFIX) 587 case '}': 588 xchar = Ctrl_RSB; 589 if (Prenum) 590 g_do_tagpreview = Prenum; 591 else 592 g_do_tagpreview = p_pvh; 593 /*FALLTHROUGH*/ 594 #endif 595 case ']': 596 case Ctrl_RSB: 597 /* keep Visual mode, can select words to use as a tag */ 598 if (Prenum) 599 postponed_split = Prenum; 600 else 601 postponed_split = -1; 602 603 /* Execute the command right here, required when 604 * "wincmd g}" was used in a function. */ 605 do_nv_ident('g', xchar); 606 break; 607 608 #ifdef FEAT_SEARCHPATH 609 case 'f': /* CTRL-W gf: "gf" in a new tab page */ 610 case 'F': /* CTRL-W gF: "gF" in a new tab page */ 611 cmdmod.tab = tabpage_index(curtab) + 1; 612 nchar = xchar; 613 goto wingotofile; 614 #endif 615 default: 616 beep_flush(); 617 break; 618 } 619 break; 620 621 default: beep_flush(); 622 break; 623 } 624 } 625 626 /* 627 * Figure out the address type for ":wnncmd". 628 */ 629 void 630 get_wincmd_addr_type(arg, eap) 631 char_u *arg; 632 exarg_T *eap; 633 { 634 switch (*arg) 635 { 636 case 'S': 637 case Ctrl_S: 638 case 's': 639 case Ctrl_N: 640 case 'n': 641 case 'j': 642 case Ctrl_J: 643 case 'k': 644 case Ctrl_K: 645 case 'T': 646 case Ctrl_R: 647 case 'r': 648 case 'R': 649 case 'K': 650 case 'J': 651 case '+': 652 case '-': 653 case Ctrl__: 654 case '_': 655 case '|': 656 case ']': 657 case Ctrl_RSB: 658 case 'g': 659 case Ctrl_G: 660 #ifdef FEAT_VERTSPLIT 661 case Ctrl_V: 662 case 'v': 663 case 'h': 664 case Ctrl_H: 665 case 'l': 666 case Ctrl_L: 667 case 'H': 668 case 'L': 669 case '>': 670 case '<': 671 #endif 672 #if defined(FEAT_QUICKFIX) 673 case '}': 674 #endif 675 #ifdef FEAT_SEARCHPATH 676 case 'f': 677 case 'F': 678 case Ctrl_F: 679 #endif 680 #ifdef FEAT_FIND_ID 681 case 'i': 682 case Ctrl_I: 683 case 'd': 684 case Ctrl_D: 685 #endif 686 /* window size or any count */ 687 eap->addr_type = ADDR_LINES; 688 break; 689 690 case Ctrl_HAT: 691 case '^': 692 /* buffer number */ 693 eap->addr_type = ADDR_BUFFERS; 694 break; 695 696 case Ctrl_Q: 697 case 'q': 698 case Ctrl_C: 699 case 'c': 700 case Ctrl_O: 701 case 'o': 702 case Ctrl_W: 703 case 'w': 704 case 'W': 705 case 'x': 706 case Ctrl_X: 707 /* window number */ 708 eap->addr_type = ADDR_WINDOWS; 709 break; 710 711 #if defined(FEAT_QUICKFIX) 712 case Ctrl_Z: 713 case 'z': 714 case 'P': 715 #endif 716 case 't': 717 case Ctrl_T: 718 case 'b': 719 case Ctrl_B: 720 case 'p': 721 case Ctrl_P: 722 case '=': 723 case CAR: 724 /* no count */ 725 eap->addr_type = 0; 726 break; 727 } 728 } 729 730 static void 731 cmd_with_count(cmd, bufp, bufsize, Prenum) 732 char *cmd; 733 char_u *bufp; 734 size_t bufsize; 735 long Prenum; 736 { 737 size_t len = STRLEN(cmd); 738 739 STRCPY(bufp, cmd); 740 if (Prenum > 0) 741 vim_snprintf((char *)bufp + len, bufsize - len, "%ld", Prenum); 742 } 743 744 /* 745 * split the current window, implements CTRL-W s and :split 746 * 747 * "size" is the height or width for the new window, 0 to use half of current 748 * height or width. 749 * 750 * "flags": 751 * WSP_ROOM: require enough room for new window 752 * WSP_VERT: vertical split. 753 * WSP_TOP: open window at the top-left of the shell (help window). 754 * WSP_BOT: open window at the bottom-right of the shell (quickfix window). 755 * WSP_HELP: creating the help window, keep layout snapshot 756 * 757 * return FAIL for failure, OK otherwise 758 */ 759 int 760 win_split(size, flags) 761 int size; 762 int flags; 763 { 764 /* When the ":tab" modifier was used open a new tab page instead. */ 765 if (may_open_tabpage() == OK) 766 return OK; 767 768 /* Add flags from ":vertical", ":topleft" and ":botright". */ 769 flags |= cmdmod.split; 770 if ((flags & WSP_TOP) && (flags & WSP_BOT)) 771 { 772 EMSG(_("E442: Can't split topleft and botright at the same time")); 773 return FAIL; 774 } 775 776 /* When creating the help window make a snapshot of the window layout. 777 * Otherwise clear the snapshot, it's now invalid. */ 778 if (flags & WSP_HELP) 779 make_snapshot(SNAP_HELP_IDX); 780 else 781 clear_snapshot(curtab, SNAP_HELP_IDX); 782 783 return win_split_ins(size, flags, NULL, 0); 784 } 785 786 /* 787 * When "new_wp" is NULL: split the current window in two. 788 * When "new_wp" is not NULL: insert this window at the far 789 * top/left/right/bottom. 790 * return FAIL for failure, OK otherwise 791 */ 792 int 793 win_split_ins(size, flags, new_wp, dir) 794 int size; 795 int flags; 796 win_T *new_wp; 797 int dir; 798 { 799 win_T *wp = new_wp; 800 win_T *oldwin; 801 int new_size = size; 802 int i; 803 int need_status = 0; 804 int do_equal = FALSE; 805 int needed; 806 int available; 807 int oldwin_height = 0; 808 int layout; 809 frame_T *frp, *curfrp, *frp2, *prevfrp; 810 int before; 811 int minheight; 812 int wmh1; 813 814 if (flags & WSP_TOP) 815 oldwin = firstwin; 816 else if (flags & WSP_BOT) 817 oldwin = lastwin; 818 else 819 oldwin = curwin; 820 821 /* add a status line when p_ls == 1 and splitting the first window */ 822 if (lastwin == firstwin && p_ls == 1 && oldwin->w_status_height == 0) 823 { 824 if (oldwin->w_height <= p_wmh && new_wp == NULL) 825 { 826 EMSG(_(e_noroom)); 827 return FAIL; 828 } 829 need_status = STATUS_HEIGHT; 830 } 831 832 #ifdef FEAT_GUI 833 /* May be needed for the scrollbars that are going to change. */ 834 if (gui.in_use) 835 out_flush(); 836 #endif 837 838 #ifdef FEAT_VERTSPLIT 839 if (flags & WSP_VERT) 840 { 841 int wmw1; 842 int minwidth; 843 844 layout = FR_ROW; 845 846 /* 847 * Check if we are able to split the current window and compute its 848 * width. 849 */ 850 /* Current window requires at least 1 space. */ 851 wmw1 = (p_wmw == 0 ? 1 : p_wmw); 852 needed = wmw1 + 1; 853 if (flags & WSP_ROOM) 854 needed += p_wiw - wmw1; 855 if (flags & (WSP_BOT | WSP_TOP)) 856 { 857 minwidth = frame_minwidth(topframe, NOWIN); 858 available = topframe->fr_width; 859 needed += minwidth; 860 } 861 else if (p_ea) 862 { 863 minwidth = frame_minwidth(oldwin->w_frame, NOWIN); 864 prevfrp = oldwin->w_frame; 865 for (frp = oldwin->w_frame->fr_parent; frp != NULL; 866 frp = frp->fr_parent) 867 { 868 if (frp->fr_layout == FR_ROW) 869 for (frp2 = frp->fr_child; frp2 != NULL; 870 frp2 = frp2->fr_next) 871 if (frp2 != prevfrp) 872 minwidth += frame_minwidth(frp2, NOWIN); 873 prevfrp = frp; 874 } 875 available = topframe->fr_width; 876 needed += minwidth; 877 } 878 else 879 { 880 minwidth = frame_minwidth(oldwin->w_frame, NOWIN); 881 available = oldwin->w_frame->fr_width; 882 needed += minwidth; 883 } 884 if (available < needed && new_wp == NULL) 885 { 886 EMSG(_(e_noroom)); 887 return FAIL; 888 } 889 if (new_size == 0) 890 new_size = oldwin->w_width / 2; 891 if (new_size > available - minwidth - 1) 892 new_size = available - minwidth - 1; 893 if (new_size < wmw1) 894 new_size = wmw1; 895 896 /* if it doesn't fit in the current window, need win_equal() */ 897 if (oldwin->w_width - new_size - 1 < p_wmw) 898 do_equal = TRUE; 899 900 /* We don't like to take lines for the new window from a 901 * 'winfixwidth' window. Take them from a window to the left or right 902 * instead, if possible. */ 903 if (oldwin->w_p_wfw) 904 win_setwidth_win(oldwin->w_width + new_size, oldwin); 905 906 /* Only make all windows the same width if one of them (except oldwin) 907 * is wider than one of the split windows. */ 908 if (!do_equal && p_ea && size == 0 && *p_ead != 'v' 909 && oldwin->w_frame->fr_parent != NULL) 910 { 911 frp = oldwin->w_frame->fr_parent->fr_child; 912 while (frp != NULL) 913 { 914 if (frp->fr_win != oldwin && frp->fr_win != NULL 915 && (frp->fr_win->w_width > new_size 916 || frp->fr_win->w_width > oldwin->w_width 917 - new_size - 1)) 918 { 919 do_equal = TRUE; 920 break; 921 } 922 frp = frp->fr_next; 923 } 924 } 925 } 926 else 927 #endif 928 { 929 layout = FR_COL; 930 931 /* 932 * Check if we are able to split the current window and compute its 933 * height. 934 */ 935 /* Current window requires at least 1 space. */ 936 wmh1 = (p_wmh == 0 ? 1 : p_wmh); 937 needed = wmh1 + STATUS_HEIGHT; 938 if (flags & WSP_ROOM) 939 needed += p_wh - wmh1; 940 if (flags & (WSP_BOT | WSP_TOP)) 941 { 942 minheight = frame_minheight(topframe, NOWIN) + need_status; 943 available = topframe->fr_height; 944 needed += minheight; 945 } 946 else if (p_ea) 947 { 948 minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status; 949 prevfrp = oldwin->w_frame; 950 for (frp = oldwin->w_frame->fr_parent; frp != NULL; 951 frp = frp->fr_parent) 952 { 953 if (frp->fr_layout == FR_COL) 954 for (frp2 = frp->fr_child; frp2 != NULL; 955 frp2 = frp2->fr_next) 956 if (frp2 != prevfrp) 957 minheight += frame_minheight(frp2, NOWIN); 958 prevfrp = frp; 959 } 960 available = topframe->fr_height; 961 needed += minheight; 962 } 963 else 964 { 965 minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status; 966 available = oldwin->w_frame->fr_height; 967 needed += minheight; 968 } 969 if (available < needed && new_wp == NULL) 970 { 971 EMSG(_(e_noroom)); 972 return FAIL; 973 } 974 oldwin_height = oldwin->w_height; 975 if (need_status) 976 { 977 oldwin->w_status_height = STATUS_HEIGHT; 978 oldwin_height -= STATUS_HEIGHT; 979 } 980 if (new_size == 0) 981 new_size = oldwin_height / 2; 982 if (new_size > available - minheight - STATUS_HEIGHT) 983 new_size = available - minheight - STATUS_HEIGHT; 984 if (new_size < wmh1) 985 new_size = wmh1; 986 987 /* if it doesn't fit in the current window, need win_equal() */ 988 if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) 989 do_equal = TRUE; 990 991 /* We don't like to take lines for the new window from a 992 * 'winfixheight' window. Take them from a window above or below 993 * instead, if possible. */ 994 if (oldwin->w_p_wfh) 995 { 996 win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT, 997 oldwin); 998 oldwin_height = oldwin->w_height; 999 if (need_status) 1000 oldwin_height -= STATUS_HEIGHT; 1001 } 1002 1003 /* Only make all windows the same height if one of them (except oldwin) 1004 * is higher than one of the split windows. */ 1005 if (!do_equal && p_ea && size == 0 1006 #ifdef FEAT_VERTSPLIT 1007 && *p_ead != 'h' 1008 #endif 1009 && oldwin->w_frame->fr_parent != NULL) 1010 { 1011 frp = oldwin->w_frame->fr_parent->fr_child; 1012 while (frp != NULL) 1013 { 1014 if (frp->fr_win != oldwin && frp->fr_win != NULL 1015 && (frp->fr_win->w_height > new_size 1016 || frp->fr_win->w_height > oldwin_height - new_size 1017 - STATUS_HEIGHT)) 1018 { 1019 do_equal = TRUE; 1020 break; 1021 } 1022 frp = frp->fr_next; 1023 } 1024 } 1025 } 1026 1027 /* 1028 * allocate new window structure and link it in the window list 1029 */ 1030 if ((flags & WSP_TOP) == 0 1031 && ((flags & WSP_BOT) 1032 || (flags & WSP_BELOW) 1033 || (!(flags & WSP_ABOVE) 1034 && ( 1035 #ifdef FEAT_VERTSPLIT 1036 (flags & WSP_VERT) ? p_spr : 1037 #endif 1038 p_sb)))) 1039 { 1040 /* new window below/right of current one */ 1041 if (new_wp == NULL) 1042 wp = win_alloc(oldwin, FALSE); 1043 else 1044 win_append(oldwin, wp); 1045 } 1046 else 1047 { 1048 if (new_wp == NULL) 1049 wp = win_alloc(oldwin->w_prev, FALSE); 1050 else 1051 win_append(oldwin->w_prev, wp); 1052 } 1053 1054 if (new_wp == NULL) 1055 { 1056 if (wp == NULL) 1057 return FAIL; 1058 1059 new_frame(wp); 1060 if (wp->w_frame == NULL) 1061 { 1062 win_free(wp, NULL); 1063 return FAIL; 1064 } 1065 1066 /* make the contents of the new window the same as the current one */ 1067 win_init(wp, curwin, flags); 1068 } 1069 1070 /* 1071 * Reorganise the tree of frames to insert the new window. 1072 */ 1073 if (flags & (WSP_TOP | WSP_BOT)) 1074 { 1075 #ifdef FEAT_VERTSPLIT 1076 if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0) 1077 || (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0)) 1078 #else 1079 if (topframe->fr_layout == FR_COL) 1080 #endif 1081 { 1082 curfrp = topframe->fr_child; 1083 if (flags & WSP_BOT) 1084 while (curfrp->fr_next != NULL) 1085 curfrp = curfrp->fr_next; 1086 } 1087 else 1088 curfrp = topframe; 1089 before = (flags & WSP_TOP); 1090 } 1091 else 1092 { 1093 curfrp = oldwin->w_frame; 1094 if (flags & WSP_BELOW) 1095 before = FALSE; 1096 else if (flags & WSP_ABOVE) 1097 before = TRUE; 1098 else 1099 #ifdef FEAT_VERTSPLIT 1100 if (flags & WSP_VERT) 1101 before = !p_spr; 1102 else 1103 #endif 1104 before = !p_sb; 1105 } 1106 if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout) 1107 { 1108 /* Need to create a new frame in the tree to make a branch. */ 1109 frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 1110 *frp = *curfrp; 1111 curfrp->fr_layout = layout; 1112 frp->fr_parent = curfrp; 1113 frp->fr_next = NULL; 1114 frp->fr_prev = NULL; 1115 curfrp->fr_child = frp; 1116 curfrp->fr_win = NULL; 1117 curfrp = frp; 1118 if (frp->fr_win != NULL) 1119 oldwin->w_frame = frp; 1120 else 1121 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 1122 frp->fr_parent = curfrp; 1123 } 1124 1125 if (new_wp == NULL) 1126 frp = wp->w_frame; 1127 else 1128 frp = new_wp->w_frame; 1129 frp->fr_parent = curfrp->fr_parent; 1130 1131 /* Insert the new frame at the right place in the frame list. */ 1132 if (before) 1133 frame_insert(curfrp, frp); 1134 else 1135 frame_append(curfrp, frp); 1136 1137 /* Set w_fraction now so that the cursor keeps the same relative 1138 * vertical position. */ 1139 if (oldwin->w_height > 0) 1140 set_fraction(oldwin); 1141 wp->w_fraction = oldwin->w_fraction; 1142 1143 #ifdef FEAT_VERTSPLIT 1144 if (flags & WSP_VERT) 1145 { 1146 wp->w_p_scr = curwin->w_p_scr; 1147 1148 if (need_status) 1149 { 1150 win_new_height(oldwin, oldwin->w_height - 1); 1151 oldwin->w_status_height = need_status; 1152 } 1153 if (flags & (WSP_TOP | WSP_BOT)) 1154 { 1155 /* set height and row of new window to full height */ 1156 wp->w_winrow = tabline_height(); 1157 win_new_height(wp, curfrp->fr_height - (p_ls > 0)); 1158 wp->w_status_height = (p_ls > 0); 1159 } 1160 else 1161 { 1162 /* height and row of new window is same as current window */ 1163 wp->w_winrow = oldwin->w_winrow; 1164 win_new_height(wp, oldwin->w_height); 1165 wp->w_status_height = oldwin->w_status_height; 1166 } 1167 frp->fr_height = curfrp->fr_height; 1168 1169 /* "new_size" of the current window goes to the new window, use 1170 * one column for the vertical separator */ 1171 win_new_width(wp, new_size); 1172 if (before) 1173 wp->w_vsep_width = 1; 1174 else 1175 { 1176 wp->w_vsep_width = oldwin->w_vsep_width; 1177 oldwin->w_vsep_width = 1; 1178 } 1179 if (flags & (WSP_TOP | WSP_BOT)) 1180 { 1181 if (flags & WSP_BOT) 1182 frame_add_vsep(curfrp); 1183 /* Set width of neighbor frame */ 1184 frame_new_width(curfrp, curfrp->fr_width 1185 - (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP, 1186 FALSE); 1187 } 1188 else 1189 win_new_width(oldwin, oldwin->w_width - (new_size + 1)); 1190 if (before) /* new window left of current one */ 1191 { 1192 wp->w_wincol = oldwin->w_wincol; 1193 oldwin->w_wincol += new_size + 1; 1194 } 1195 else /* new window right of current one */ 1196 wp->w_wincol = oldwin->w_wincol + oldwin->w_width + 1; 1197 frame_fix_width(oldwin); 1198 frame_fix_width(wp); 1199 } 1200 else 1201 #endif 1202 { 1203 /* width and column of new window is same as current window */ 1204 #ifdef FEAT_VERTSPLIT 1205 if (flags & (WSP_TOP | WSP_BOT)) 1206 { 1207 wp->w_wincol = 0; 1208 win_new_width(wp, Columns); 1209 wp->w_vsep_width = 0; 1210 } 1211 else 1212 { 1213 wp->w_wincol = oldwin->w_wincol; 1214 win_new_width(wp, oldwin->w_width); 1215 wp->w_vsep_width = oldwin->w_vsep_width; 1216 } 1217 frp->fr_width = curfrp->fr_width; 1218 #endif 1219 1220 /* "new_size" of the current window goes to the new window, use 1221 * one row for the status line */ 1222 win_new_height(wp, new_size); 1223 if (flags & (WSP_TOP | WSP_BOT)) 1224 frame_new_height(curfrp, curfrp->fr_height 1225 - (new_size + STATUS_HEIGHT), flags & WSP_TOP, FALSE); 1226 else 1227 win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT)); 1228 if (before) /* new window above current one */ 1229 { 1230 wp->w_winrow = oldwin->w_winrow; 1231 wp->w_status_height = STATUS_HEIGHT; 1232 oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; 1233 } 1234 else /* new window below current one */ 1235 { 1236 wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; 1237 wp->w_status_height = oldwin->w_status_height; 1238 /* Don't set the status_height for oldwin yet, this might break 1239 * frame_fix_height(oldwin), therefore will be set below. */ 1240 } 1241 #ifdef FEAT_VERTSPLIT 1242 if (flags & WSP_BOT) 1243 frame_add_statusline(curfrp); 1244 #endif 1245 frame_fix_height(wp); 1246 frame_fix_height(oldwin); 1247 1248 if (!before) 1249 /* new window above current one, set the status_height after 1250 * frame_fix_height(oldwin) */ 1251 oldwin->w_status_height = STATUS_HEIGHT; 1252 } 1253 1254 if (flags & (WSP_TOP | WSP_BOT)) 1255 (void)win_comp_pos(); 1256 1257 /* 1258 * Both windows need redrawing 1259 */ 1260 redraw_win_later(wp, NOT_VALID); 1261 wp->w_redr_status = TRUE; 1262 redraw_win_later(oldwin, NOT_VALID); 1263 oldwin->w_redr_status = TRUE; 1264 1265 if (need_status) 1266 { 1267 msg_row = Rows - 1; 1268 msg_col = sc_col; 1269 msg_clr_eos_force(); /* Old command/ruler may still be there */ 1270 comp_col(); 1271 msg_row = Rows - 1; 1272 msg_col = 0; /* put position back at start of line */ 1273 } 1274 1275 /* 1276 * equalize the window sizes. 1277 */ 1278 if (do_equal || dir != 0) 1279 win_equal(wp, TRUE, 1280 #ifdef FEAT_VERTSPLIT 1281 (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') 1282 : dir == 'h' ? 'b' : 1283 #endif 1284 'v'); 1285 1286 /* Don't change the window height/width to 'winheight' / 'winwidth' if a 1287 * size was given. */ 1288 #ifdef FEAT_VERTSPLIT 1289 if (flags & WSP_VERT) 1290 { 1291 i = p_wiw; 1292 if (size != 0) 1293 p_wiw = size; 1294 1295 # ifdef FEAT_GUI 1296 /* When 'guioptions' includes 'L' or 'R' may have to add scrollbars. */ 1297 if (gui.in_use) 1298 gui_init_which_components(NULL); 1299 # endif 1300 } 1301 else 1302 #endif 1303 { 1304 i = p_wh; 1305 if (size != 0) 1306 p_wh = size; 1307 } 1308 1309 #ifdef FEAT_JUMPLIST 1310 /* Keep same changelist position in new window. */ 1311 wp->w_changelistidx = oldwin->w_changelistidx; 1312 #endif 1313 1314 /* 1315 * make the new window the current window 1316 */ 1317 win_enter(wp, FALSE); 1318 #ifdef FEAT_VERTSPLIT 1319 if (flags & WSP_VERT) 1320 p_wiw = i; 1321 else 1322 #endif 1323 p_wh = i; 1324 1325 return OK; 1326 } 1327 1328 1329 /* 1330 * Initialize window "newp" from window "oldp". 1331 * Used when splitting a window and when creating a new tab page. 1332 * The windows will both edit the same buffer. 1333 * WSP_NEWLOC may be specified in flags to prevent the location list from 1334 * being copied. 1335 */ 1336 static void 1337 win_init(newp, oldp, flags) 1338 win_T *newp; 1339 win_T *oldp; 1340 int flags UNUSED; 1341 { 1342 int i; 1343 1344 newp->w_buffer = oldp->w_buffer; 1345 #ifdef FEAT_SYN_HL 1346 newp->w_s = &(oldp->w_buffer->b_s); 1347 #endif 1348 oldp->w_buffer->b_nwindows++; 1349 newp->w_cursor = oldp->w_cursor; 1350 newp->w_valid = 0; 1351 newp->w_curswant = oldp->w_curswant; 1352 newp->w_set_curswant = oldp->w_set_curswant; 1353 newp->w_topline = oldp->w_topline; 1354 #ifdef FEAT_DIFF 1355 newp->w_topfill = oldp->w_topfill; 1356 #endif 1357 newp->w_leftcol = oldp->w_leftcol; 1358 newp->w_pcmark = oldp->w_pcmark; 1359 newp->w_prev_pcmark = oldp->w_prev_pcmark; 1360 newp->w_alt_fnum = oldp->w_alt_fnum; 1361 newp->w_wrow = oldp->w_wrow; 1362 newp->w_fraction = oldp->w_fraction; 1363 newp->w_prev_fraction_row = oldp->w_prev_fraction_row; 1364 #ifdef FEAT_JUMPLIST 1365 copy_jumplist(oldp, newp); 1366 #endif 1367 #ifdef FEAT_QUICKFIX 1368 if (flags & WSP_NEWLOC) 1369 { 1370 /* Don't copy the location list. */ 1371 newp->w_llist = NULL; 1372 newp->w_llist_ref = NULL; 1373 } 1374 else 1375 copy_loclist(oldp, newp); 1376 #endif 1377 newp->w_localdir = (oldp->w_localdir == NULL) 1378 ? NULL : vim_strsave(oldp->w_localdir); 1379 1380 /* copy tagstack and folds */ 1381 for (i = 0; i < oldp->w_tagstacklen; i++) 1382 { 1383 newp->w_tagstack[i] = oldp->w_tagstack[i]; 1384 if (newp->w_tagstack[i].tagname != NULL) 1385 newp->w_tagstack[i].tagname = 1386 vim_strsave(newp->w_tagstack[i].tagname); 1387 } 1388 newp->w_tagstackidx = oldp->w_tagstackidx; 1389 newp->w_tagstacklen = oldp->w_tagstacklen; 1390 #ifdef FEAT_FOLDING 1391 copyFoldingState(oldp, newp); 1392 #endif 1393 1394 win_init_some(newp, oldp); 1395 1396 #ifdef FEAT_SYN_HL 1397 check_colorcolumn(newp); 1398 #endif 1399 } 1400 1401 /* 1402 * Initialize window "newp" from window "old". 1403 * Only the essential things are copied. 1404 */ 1405 static void 1406 win_init_some(newp, oldp) 1407 win_T *newp; 1408 win_T *oldp; 1409 { 1410 /* Use the same argument list. */ 1411 newp->w_alist = oldp->w_alist; 1412 ++newp->w_alist->al_refcount; 1413 newp->w_arg_idx = oldp->w_arg_idx; 1414 1415 /* copy options from existing window */ 1416 win_copy_options(oldp, newp); 1417 } 1418 1419 #endif /* FEAT_WINDOWS */ 1420 1421 #if defined(FEAT_WINDOWS) || defined(PROTO) 1422 /* 1423 * Check if "win" is a pointer to an existing window. 1424 */ 1425 int 1426 win_valid(win) 1427 win_T *win; 1428 { 1429 win_T *wp; 1430 1431 if (win == NULL) 1432 return FALSE; 1433 for (wp = firstwin; wp != NULL; wp = wp->w_next) 1434 if (wp == win) 1435 return TRUE; 1436 return FALSE; 1437 } 1438 1439 /* 1440 * Return the number of windows. 1441 */ 1442 int 1443 win_count() 1444 { 1445 win_T *wp; 1446 int count = 0; 1447 1448 for (wp = firstwin; wp != NULL; wp = wp->w_next) 1449 ++count; 1450 return count; 1451 } 1452 1453 /* 1454 * Make "count" windows on the screen. 1455 * Return actual number of windows on the screen. 1456 * Must be called when there is just one window, filling the whole screen 1457 * (excluding the command line). 1458 */ 1459 int 1460 make_windows(count, vertical) 1461 int count; 1462 int vertical UNUSED; /* split windows vertically if TRUE */ 1463 { 1464 int maxcount; 1465 int todo; 1466 1467 #ifdef FEAT_VERTSPLIT 1468 if (vertical) 1469 { 1470 /* Each windows needs at least 'winminwidth' lines and a separator 1471 * column. */ 1472 maxcount = (curwin->w_width + curwin->w_vsep_width 1473 - (p_wiw - p_wmw)) / (p_wmw + 1); 1474 } 1475 else 1476 #endif 1477 { 1478 /* Each window needs at least 'winminheight' lines and a status line. */ 1479 maxcount = (curwin->w_height + curwin->w_status_height 1480 - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); 1481 } 1482 1483 if (maxcount < 2) 1484 maxcount = 2; 1485 if (count > maxcount) 1486 count = maxcount; 1487 1488 /* 1489 * add status line now, otherwise first window will be too big 1490 */ 1491 if (count > 1) 1492 last_status(TRUE); 1493 1494 #ifdef FEAT_AUTOCMD 1495 /* 1496 * Don't execute autocommands while creating the windows. Must do that 1497 * when putting the buffers in the windows. 1498 */ 1499 block_autocmds(); 1500 #endif 1501 1502 /* todo is number of windows left to create */ 1503 for (todo = count - 1; todo > 0; --todo) 1504 #ifdef FEAT_VERTSPLIT 1505 if (vertical) 1506 { 1507 if (win_split(curwin->w_width - (curwin->w_width - todo) 1508 / (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL) 1509 break; 1510 } 1511 else 1512 #endif 1513 { 1514 if (win_split(curwin->w_height - (curwin->w_height - todo 1515 * STATUS_HEIGHT) / (todo + 1) 1516 - STATUS_HEIGHT, WSP_ABOVE) == FAIL) 1517 break; 1518 } 1519 1520 #ifdef FEAT_AUTOCMD 1521 unblock_autocmds(); 1522 #endif 1523 1524 /* return actual number of windows */ 1525 return (count - todo); 1526 } 1527 1528 /* 1529 * Exchange current and next window 1530 */ 1531 static void 1532 win_exchange(Prenum) 1533 long Prenum; 1534 { 1535 frame_T *frp; 1536 frame_T *frp2; 1537 win_T *wp; 1538 win_T *wp2; 1539 int temp; 1540 1541 if (lastwin == firstwin) /* just one window */ 1542 { 1543 beep_flush(); 1544 return; 1545 } 1546 1547 #ifdef FEAT_GUI 1548 need_mouse_correct = TRUE; 1549 #endif 1550 1551 /* 1552 * find window to exchange with 1553 */ 1554 if (Prenum) 1555 { 1556 frp = curwin->w_frame->fr_parent->fr_child; 1557 while (frp != NULL && --Prenum > 0) 1558 frp = frp->fr_next; 1559 } 1560 else if (curwin->w_frame->fr_next != NULL) /* Swap with next */ 1561 frp = curwin->w_frame->fr_next; 1562 else /* Swap last window in row/col with previous */ 1563 frp = curwin->w_frame->fr_prev; 1564 1565 /* We can only exchange a window with another window, not with a frame 1566 * containing windows. */ 1567 if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin) 1568 return; 1569 wp = frp->fr_win; 1570 1571 /* 1572 * 1. remove curwin from the list. Remember after which window it was in wp2 1573 * 2. insert curwin before wp in the list 1574 * if wp != wp2 1575 * 3. remove wp from the list 1576 * 4. insert wp after wp2 1577 * 5. exchange the status line height and vsep width. 1578 */ 1579 wp2 = curwin->w_prev; 1580 frp2 = curwin->w_frame->fr_prev; 1581 if (wp->w_prev != curwin) 1582 { 1583 win_remove(curwin, NULL); 1584 frame_remove(curwin->w_frame); 1585 win_append(wp->w_prev, curwin); 1586 frame_insert(frp, curwin->w_frame); 1587 } 1588 if (wp != wp2) 1589 { 1590 win_remove(wp, NULL); 1591 frame_remove(wp->w_frame); 1592 win_append(wp2, wp); 1593 if (frp2 == NULL) 1594 frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame); 1595 else 1596 frame_append(frp2, wp->w_frame); 1597 } 1598 temp = curwin->w_status_height; 1599 curwin->w_status_height = wp->w_status_height; 1600 wp->w_status_height = temp; 1601 #ifdef FEAT_VERTSPLIT 1602 temp = curwin->w_vsep_width; 1603 curwin->w_vsep_width = wp->w_vsep_width; 1604 wp->w_vsep_width = temp; 1605 1606 /* If the windows are not in the same frame, exchange the sizes to avoid 1607 * messing up the window layout. Otherwise fix the frame sizes. */ 1608 if (curwin->w_frame->fr_parent != wp->w_frame->fr_parent) 1609 { 1610 temp = curwin->w_height; 1611 curwin->w_height = wp->w_height; 1612 wp->w_height = temp; 1613 temp = curwin->w_width; 1614 curwin->w_width = wp->w_width; 1615 wp->w_width = temp; 1616 } 1617 else 1618 { 1619 frame_fix_height(curwin); 1620 frame_fix_height(wp); 1621 frame_fix_width(curwin); 1622 frame_fix_width(wp); 1623 } 1624 #endif 1625 1626 (void)win_comp_pos(); /* recompute window positions */ 1627 1628 win_enter(wp, TRUE); 1629 redraw_later(CLEAR); 1630 } 1631 1632 /* 1633 * rotate windows: if upwards TRUE the second window becomes the first one 1634 * if upwards FALSE the first window becomes the second one 1635 */ 1636 static void 1637 win_rotate(upwards, count) 1638 int upwards; 1639 int count; 1640 { 1641 win_T *wp1; 1642 win_T *wp2; 1643 frame_T *frp; 1644 int n; 1645 1646 if (firstwin == lastwin) /* nothing to do */ 1647 { 1648 beep_flush(); 1649 return; 1650 } 1651 1652 #ifdef FEAT_GUI 1653 need_mouse_correct = TRUE; 1654 #endif 1655 1656 #ifdef FEAT_VERTSPLIT 1657 /* Check if all frames in this row/col have one window. */ 1658 for (frp = curwin->w_frame->fr_parent->fr_child; frp != NULL; 1659 frp = frp->fr_next) 1660 if (frp->fr_win == NULL) 1661 { 1662 EMSG(_("E443: Cannot rotate when another window is split")); 1663 return; 1664 } 1665 #endif 1666 1667 while (count--) 1668 { 1669 if (upwards) /* first window becomes last window */ 1670 { 1671 /* remove first window/frame from the list */ 1672 frp = curwin->w_frame->fr_parent->fr_child; 1673 wp1 = frp->fr_win; 1674 win_remove(wp1, NULL); 1675 frame_remove(frp); 1676 1677 /* find last frame and append removed window/frame after it */ 1678 for ( ; frp->fr_next != NULL; frp = frp->fr_next) 1679 ; 1680 win_append(frp->fr_win, wp1); 1681 frame_append(frp, wp1->w_frame); 1682 1683 wp2 = frp->fr_win; /* previously last window */ 1684 } 1685 else /* last window becomes first window */ 1686 { 1687 /* find last window/frame in the list and remove it */ 1688 for (frp = curwin->w_frame; frp->fr_next != NULL; 1689 frp = frp->fr_next) 1690 ; 1691 wp1 = frp->fr_win; 1692 wp2 = wp1->w_prev; /* will become last window */ 1693 win_remove(wp1, NULL); 1694 frame_remove(frp); 1695 1696 /* append the removed window/frame before the first in the list */ 1697 win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1); 1698 frame_insert(frp->fr_parent->fr_child, frp); 1699 } 1700 1701 /* exchange status height and vsep width of old and new last window */ 1702 n = wp2->w_status_height; 1703 wp2->w_status_height = wp1->w_status_height; 1704 wp1->w_status_height = n; 1705 frame_fix_height(wp1); 1706 frame_fix_height(wp2); 1707 #ifdef FEAT_VERTSPLIT 1708 n = wp2->w_vsep_width; 1709 wp2->w_vsep_width = wp1->w_vsep_width; 1710 wp1->w_vsep_width = n; 1711 frame_fix_width(wp1); 1712 frame_fix_width(wp2); 1713 #endif 1714 1715 /* recompute w_winrow and w_wincol for all windows */ 1716 (void)win_comp_pos(); 1717 } 1718 1719 redraw_later(CLEAR); 1720 } 1721 1722 /* 1723 * Move the current window to the very top/bottom/left/right of the screen. 1724 */ 1725 static void 1726 win_totop(size, flags) 1727 int size; 1728 int flags; 1729 { 1730 int dir; 1731 int height = curwin->w_height; 1732 1733 if (lastwin == firstwin) 1734 { 1735 beep_flush(); 1736 return; 1737 } 1738 1739 /* Remove the window and frame from the tree of frames. */ 1740 (void)winframe_remove(curwin, &dir, NULL); 1741 win_remove(curwin, NULL); 1742 last_status(FALSE); /* may need to remove last status line */ 1743 (void)win_comp_pos(); /* recompute window positions */ 1744 1745 /* Split a window on the desired side and put the window there. */ 1746 (void)win_split_ins(size, flags, curwin, dir); 1747 if (!(flags & WSP_VERT)) 1748 { 1749 win_setheight(height); 1750 if (p_ea) 1751 win_equal(curwin, TRUE, 'v'); 1752 } 1753 1754 #if defined(FEAT_GUI) && defined(FEAT_VERTSPLIT) 1755 /* When 'guioptions' includes 'L' or 'R' may have to remove or add 1756 * scrollbars. Have to update them anyway. */ 1757 gui_may_update_scrollbars(); 1758 #endif 1759 } 1760 1761 /* 1762 * Move window "win1" to below/right of "win2" and make "win1" the current 1763 * window. Only works within the same frame! 1764 */ 1765 void 1766 win_move_after(win1, win2) 1767 win_T *win1, *win2; 1768 { 1769 int height; 1770 1771 /* check if the arguments are reasonable */ 1772 if (win1 == win2) 1773 return; 1774 1775 /* check if there is something to do */ 1776 if (win2->w_next != win1) 1777 { 1778 /* may need move the status line/vertical separator of the last window 1779 * */ 1780 if (win1 == lastwin) 1781 { 1782 height = win1->w_prev->w_status_height; 1783 win1->w_prev->w_status_height = win1->w_status_height; 1784 win1->w_status_height = height; 1785 #ifdef FEAT_VERTSPLIT 1786 if (win1->w_prev->w_vsep_width == 1) 1787 { 1788 /* Remove the vertical separator from the last-but-one window, 1789 * add it to the last window. Adjust the frame widths. */ 1790 win1->w_prev->w_vsep_width = 0; 1791 win1->w_prev->w_frame->fr_width -= 1; 1792 win1->w_vsep_width = 1; 1793 win1->w_frame->fr_width += 1; 1794 } 1795 #endif 1796 } 1797 else if (win2 == lastwin) 1798 { 1799 height = win1->w_status_height; 1800 win1->w_status_height = win2->w_status_height; 1801 win2->w_status_height = height; 1802 #ifdef FEAT_VERTSPLIT 1803 if (win1->w_vsep_width == 1) 1804 { 1805 /* Remove the vertical separator from win1, add it to the last 1806 * window, win2. Adjust the frame widths. */ 1807 win2->w_vsep_width = 1; 1808 win2->w_frame->fr_width += 1; 1809 win1->w_vsep_width = 0; 1810 win1->w_frame->fr_width -= 1; 1811 } 1812 #endif 1813 } 1814 win_remove(win1, NULL); 1815 frame_remove(win1->w_frame); 1816 win_append(win2, win1); 1817 frame_append(win2->w_frame, win1->w_frame); 1818 1819 (void)win_comp_pos(); /* recompute w_winrow for all windows */ 1820 redraw_later(NOT_VALID); 1821 } 1822 win_enter(win1, FALSE); 1823 } 1824 1825 /* 1826 * Make all windows the same height. 1827 * 'next_curwin' will soon be the current window, make sure it has enough 1828 * rows. 1829 */ 1830 void 1831 win_equal(next_curwin, current, dir) 1832 win_T *next_curwin; /* pointer to current window to be or NULL */ 1833 int current; /* do only frame with current window */ 1834 int dir; /* 'v' for vertically, 'h' for horizontally, 1835 'b' for both, 0 for using p_ead */ 1836 { 1837 if (dir == 0) 1838 #ifdef FEAT_VERTSPLIT 1839 dir = *p_ead; 1840 #else 1841 dir = 'b'; 1842 #endif 1843 win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current, 1844 topframe, dir, 0, tabline_height(), 1845 (int)Columns, topframe->fr_height); 1846 } 1847 1848 /* 1849 * Set a frame to a new position and height, spreading the available room 1850 * equally over contained frames. 1851 * The window "next_curwin" (if not NULL) should at least get the size from 1852 * 'winheight' and 'winwidth' if possible. 1853 */ 1854 static void 1855 win_equal_rec(next_curwin, current, topfr, dir, col, row, width, height) 1856 win_T *next_curwin; /* pointer to current window to be or NULL */ 1857 int current; /* do only frame with current window */ 1858 frame_T *topfr; /* frame to set size off */ 1859 int dir; /* 'v', 'h' or 'b', see win_equal() */ 1860 int col; /* horizontal position for frame */ 1861 int row; /* vertical position for frame */ 1862 int width; /* new width of frame */ 1863 int height; /* new height of frame */ 1864 { 1865 int n, m; 1866 int extra_sep = 0; 1867 int wincount, totwincount = 0; 1868 frame_T *fr; 1869 int next_curwin_size = 0; 1870 int room = 0; 1871 int new_size; 1872 int has_next_curwin = 0; 1873 int hnc; 1874 1875 if (topfr->fr_layout == FR_LEAF) 1876 { 1877 /* Set the width/height of this frame. 1878 * Redraw when size or position changes */ 1879 if (topfr->fr_height != height || topfr->fr_win->w_winrow != row 1880 #ifdef FEAT_VERTSPLIT 1881 || topfr->fr_width != width || topfr->fr_win->w_wincol != col 1882 #endif 1883 ) 1884 { 1885 topfr->fr_win->w_winrow = row; 1886 frame_new_height(topfr, height, FALSE, FALSE); 1887 #ifdef FEAT_VERTSPLIT 1888 topfr->fr_win->w_wincol = col; 1889 frame_new_width(topfr, width, FALSE, FALSE); 1890 #endif 1891 redraw_all_later(CLEAR); 1892 } 1893 } 1894 #ifdef FEAT_VERTSPLIT 1895 else if (topfr->fr_layout == FR_ROW) 1896 { 1897 topfr->fr_width = width; 1898 topfr->fr_height = height; 1899 1900 if (dir != 'v') /* equalize frame widths */ 1901 { 1902 /* Compute the maximum number of windows horizontally in this 1903 * frame. */ 1904 n = frame_minwidth(topfr, NOWIN); 1905 /* add one for the rightmost window, it doesn't have a separator */ 1906 if (col + width == Columns) 1907 extra_sep = 1; 1908 else 1909 extra_sep = 0; 1910 totwincount = (n + extra_sep) / (p_wmw + 1); 1911 has_next_curwin = frame_has_win(topfr, next_curwin); 1912 1913 /* 1914 * Compute width for "next_curwin" window and room available for 1915 * other windows. 1916 * "m" is the minimal width when counting p_wiw for "next_curwin". 1917 */ 1918 m = frame_minwidth(topfr, next_curwin); 1919 room = width - m; 1920 if (room < 0) 1921 { 1922 next_curwin_size = p_wiw + room; 1923 room = 0; 1924 } 1925 else 1926 { 1927 next_curwin_size = -1; 1928 for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) 1929 { 1930 /* If 'winfixwidth' set keep the window width if 1931 * possible. 1932 * Watch out for this window being the next_curwin. */ 1933 if (frame_fixed_width(fr)) 1934 { 1935 n = frame_minwidth(fr, NOWIN); 1936 new_size = fr->fr_width; 1937 if (frame_has_win(fr, next_curwin)) 1938 { 1939 room += p_wiw - p_wmw; 1940 next_curwin_size = 0; 1941 if (new_size < p_wiw) 1942 new_size = p_wiw; 1943 } 1944 else 1945 /* These windows don't use up room. */ 1946 totwincount -= (n + (fr->fr_next == NULL 1947 ? extra_sep : 0)) / (p_wmw + 1); 1948 room -= new_size - n; 1949 if (room < 0) 1950 { 1951 new_size += room; 1952 room = 0; 1953 } 1954 fr->fr_newwidth = new_size; 1955 } 1956 } 1957 if (next_curwin_size == -1) 1958 { 1959 if (!has_next_curwin) 1960 next_curwin_size = 0; 1961 else if (totwincount > 1 1962 && (room + (totwincount - 2)) 1963 / (totwincount - 1) > p_wiw) 1964 { 1965 /* Can make all windows wider than 'winwidth', spread 1966 * the room equally. */ 1967 next_curwin_size = (room + p_wiw 1968 + (totwincount - 1) * p_wmw 1969 + (totwincount - 1)) / totwincount; 1970 room -= next_curwin_size - p_wiw; 1971 } 1972 else 1973 next_curwin_size = p_wiw; 1974 } 1975 } 1976 1977 if (has_next_curwin) 1978 --totwincount; /* don't count curwin */ 1979 } 1980 1981 for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) 1982 { 1983 n = m = 0; 1984 wincount = 1; 1985 if (fr->fr_next == NULL) 1986 /* last frame gets all that remains (avoid roundoff error) */ 1987 new_size = width; 1988 else if (dir == 'v') 1989 new_size = fr->fr_width; 1990 else if (frame_fixed_width(fr)) 1991 { 1992 new_size = fr->fr_newwidth; 1993 wincount = 0; /* doesn't count as a sizeable window */ 1994 } 1995 else 1996 { 1997 /* Compute the maximum number of windows horiz. in "fr". */ 1998 n = frame_minwidth(fr, NOWIN); 1999 wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) 2000 / (p_wmw + 1); 2001 m = frame_minwidth(fr, next_curwin); 2002 if (has_next_curwin) 2003 hnc = frame_has_win(fr, next_curwin); 2004 else 2005 hnc = FALSE; 2006 if (hnc) /* don't count next_curwin */ 2007 --wincount; 2008 if (totwincount == 0) 2009 new_size = room; 2010 else 2011 new_size = (wincount * room + ((unsigned)totwincount >> 1)) 2012 / totwincount; 2013 if (hnc) /* add next_curwin size */ 2014 { 2015 next_curwin_size -= p_wiw - (m - n); 2016 new_size += next_curwin_size; 2017 room -= new_size - next_curwin_size; 2018 } 2019 else 2020 room -= new_size; 2021 new_size += n; 2022 } 2023 2024 /* Skip frame that is full width when splitting or closing a 2025 * window, unless equalizing all frames. */ 2026 if (!current || dir != 'v' || topfr->fr_parent != NULL 2027 || (new_size != fr->fr_width) 2028 || frame_has_win(fr, next_curwin)) 2029 win_equal_rec(next_curwin, current, fr, dir, col, row, 2030 new_size, height); 2031 col += new_size; 2032 width -= new_size; 2033 totwincount -= wincount; 2034 } 2035 } 2036 #endif 2037 else /* topfr->fr_layout == FR_COL */ 2038 { 2039 #ifdef FEAT_VERTSPLIT 2040 topfr->fr_width = width; 2041 #endif 2042 topfr->fr_height = height; 2043 2044 if (dir != 'h') /* equalize frame heights */ 2045 { 2046 /* Compute maximum number of windows vertically in this frame. */ 2047 n = frame_minheight(topfr, NOWIN); 2048 /* add one for the bottom window if it doesn't have a statusline */ 2049 if (row + height == cmdline_row && p_ls == 0) 2050 extra_sep = 1; 2051 else 2052 extra_sep = 0; 2053 totwincount = (n + extra_sep) / (p_wmh + 1); 2054 has_next_curwin = frame_has_win(topfr, next_curwin); 2055 2056 /* 2057 * Compute height for "next_curwin" window and room available for 2058 * other windows. 2059 * "m" is the minimal height when counting p_wh for "next_curwin". 2060 */ 2061 m = frame_minheight(topfr, next_curwin); 2062 room = height - m; 2063 if (room < 0) 2064 { 2065 /* The room is less then 'winheight', use all space for the 2066 * current window. */ 2067 next_curwin_size = p_wh + room; 2068 room = 0; 2069 } 2070 else 2071 { 2072 next_curwin_size = -1; 2073 for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) 2074 { 2075 /* If 'winfixheight' set keep the window height if 2076 * possible. 2077 * Watch out for this window being the next_curwin. */ 2078 if (frame_fixed_height(fr)) 2079 { 2080 n = frame_minheight(fr, NOWIN); 2081 new_size = fr->fr_height; 2082 if (frame_has_win(fr, next_curwin)) 2083 { 2084 room += p_wh - p_wmh; 2085 next_curwin_size = 0; 2086 if (new_size < p_wh) 2087 new_size = p_wh; 2088 } 2089 else 2090 /* These windows don't use up room. */ 2091 totwincount -= (n + (fr->fr_next == NULL 2092 ? extra_sep : 0)) / (p_wmh + 1); 2093 room -= new_size - n; 2094 if (room < 0) 2095 { 2096 new_size += room; 2097 room = 0; 2098 } 2099 fr->fr_newheight = new_size; 2100 } 2101 } 2102 if (next_curwin_size == -1) 2103 { 2104 if (!has_next_curwin) 2105 next_curwin_size = 0; 2106 else if (totwincount > 1 2107 && (room + (totwincount - 2)) 2108 / (totwincount - 1) > p_wh) 2109 { 2110 /* can make all windows higher than 'winheight', 2111 * spread the room equally. */ 2112 next_curwin_size = (room + p_wh 2113 + (totwincount - 1) * p_wmh 2114 + (totwincount - 1)) / totwincount; 2115 room -= next_curwin_size - p_wh; 2116 } 2117 else 2118 next_curwin_size = p_wh; 2119 } 2120 } 2121 2122 if (has_next_curwin) 2123 --totwincount; /* don't count curwin */ 2124 } 2125 2126 for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) 2127 { 2128 n = m = 0; 2129 wincount = 1; 2130 if (fr->fr_next == NULL) 2131 /* last frame gets all that remains (avoid roundoff error) */ 2132 new_size = height; 2133 else if (dir == 'h') 2134 new_size = fr->fr_height; 2135 else if (frame_fixed_height(fr)) 2136 { 2137 new_size = fr->fr_newheight; 2138 wincount = 0; /* doesn't count as a sizeable window */ 2139 } 2140 else 2141 { 2142 /* Compute the maximum number of windows vert. in "fr". */ 2143 n = frame_minheight(fr, NOWIN); 2144 wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) 2145 / (p_wmh + 1); 2146 m = frame_minheight(fr, next_curwin); 2147 if (has_next_curwin) 2148 hnc = frame_has_win(fr, next_curwin); 2149 else 2150 hnc = FALSE; 2151 if (hnc) /* don't count next_curwin */ 2152 --wincount; 2153 if (totwincount == 0) 2154 new_size = room; 2155 else 2156 new_size = (wincount * room + ((unsigned)totwincount >> 1)) 2157 / totwincount; 2158 if (hnc) /* add next_curwin size */ 2159 { 2160 next_curwin_size -= p_wh - (m - n); 2161 new_size += next_curwin_size; 2162 room -= new_size - next_curwin_size; 2163 } 2164 else 2165 room -= new_size; 2166 new_size += n; 2167 } 2168 /* Skip frame that is full width when splitting or closing a 2169 * window, unless equalizing all frames. */ 2170 if (!current || dir != 'h' || topfr->fr_parent != NULL 2171 || (new_size != fr->fr_height) 2172 || frame_has_win(fr, next_curwin)) 2173 win_equal_rec(next_curwin, current, fr, dir, col, row, 2174 width, new_size); 2175 row += new_size; 2176 height -= new_size; 2177 totwincount -= wincount; 2178 } 2179 } 2180 } 2181 2182 /* 2183 * close all windows for buffer 'buf' 2184 */ 2185 void 2186 close_windows(buf, keep_curwin) 2187 buf_T *buf; 2188 int keep_curwin; /* don't close "curwin" */ 2189 { 2190 win_T *wp; 2191 tabpage_T *tp, *nexttp; 2192 int h = tabline_height(); 2193 2194 ++RedrawingDisabled; 2195 2196 for (wp = firstwin; wp != NULL && lastwin != firstwin; ) 2197 { 2198 if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) 2199 #ifdef FEAT_AUTOCMD 2200 && !(wp->w_closing || wp->w_buffer->b_closing) 2201 #endif 2202 ) 2203 { 2204 win_close(wp, FALSE); 2205 2206 /* Start all over, autocommands may change the window layout. */ 2207 wp = firstwin; 2208 } 2209 else 2210 wp = wp->w_next; 2211 } 2212 2213 /* Also check windows in other tab pages. */ 2214 for (tp = first_tabpage; tp != NULL; tp = nexttp) 2215 { 2216 nexttp = tp->tp_next; 2217 if (tp != curtab) 2218 for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) 2219 if (wp->w_buffer == buf 2220 #ifdef FEAT_AUTOCMD 2221 && !(wp->w_closing || wp->w_buffer->b_closing) 2222 #endif 2223 ) 2224 { 2225 win_close_othertab(wp, FALSE, tp); 2226 2227 /* Start all over, the tab page may be closed and 2228 * autocommands may change the window layout. */ 2229 nexttp = first_tabpage; 2230 break; 2231 } 2232 } 2233 2234 --RedrawingDisabled; 2235 2236 redraw_tabline = TRUE; 2237 if (h != tabline_height()) 2238 shell_new_rows(); 2239 } 2240 2241 /* 2242 * Return TRUE if the current window is the only window that exists (ignoring 2243 * "aucmd_win"). 2244 * Returns FALSE if there is a window, possibly in another tab page. 2245 */ 2246 static int 2247 last_window() 2248 { 2249 return (one_window() && first_tabpage->tp_next == NULL); 2250 } 2251 2252 /* 2253 * Return TRUE if there is only one window other than "aucmd_win" in the 2254 * current tab page. 2255 */ 2256 int 2257 one_window() 2258 { 2259 #ifdef FEAT_AUTOCMD 2260 win_T *wp; 2261 int seen_one = FALSE; 2262 2263 FOR_ALL_WINDOWS(wp) 2264 { 2265 if (wp != aucmd_win) 2266 { 2267 if (seen_one) 2268 return FALSE; 2269 seen_one = TRUE; 2270 } 2271 } 2272 return TRUE; 2273 #else 2274 return firstwin == lastwin; 2275 #endif 2276 } 2277 2278 /* 2279 * Close the possibly last window in a tab page. 2280 * Returns TRUE when the window was closed already. 2281 */ 2282 static int 2283 close_last_window_tabpage(win, free_buf, prev_curtab) 2284 win_T *win; 2285 int free_buf; 2286 tabpage_T *prev_curtab; 2287 { 2288 if (firstwin == lastwin) 2289 { 2290 #ifdef FEAT_AUTOCMD 2291 buf_T *old_curbuf = curbuf; 2292 #endif 2293 2294 /* 2295 * Closing the last window in a tab page. First go to another tab 2296 * page and then close the window and the tab page. This avoids that 2297 * curwin and curtab are invalid while we are freeing memory, they may 2298 * be used in GUI events. 2299 * Don't trigger autocommands yet, they may use wrong values, so do 2300 * that below. 2301 */ 2302 goto_tabpage_tp(alt_tabpage(), FALSE, TRUE); 2303 redraw_tabline = TRUE; 2304 2305 /* Safety check: Autocommands may have closed the window when jumping 2306 * to the other tab page. */ 2307 if (valid_tabpage(prev_curtab) && prev_curtab->tp_firstwin == win) 2308 { 2309 int h = tabline_height(); 2310 2311 win_close_othertab(win, free_buf, prev_curtab); 2312 if (h != tabline_height()) 2313 shell_new_rows(); 2314 } 2315 /* Since goto_tabpage_tp above did not trigger *Enter autocommands, do 2316 * that now. */ 2317 #ifdef FEAT_AUTOCMD 2318 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); 2319 apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); 2320 if (old_curbuf != curbuf) 2321 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 2322 #endif 2323 return TRUE; 2324 } 2325 return FALSE; 2326 } 2327 2328 /* 2329 * Close window "win". Only works for the current tab page. 2330 * If "free_buf" is TRUE related buffer may be unloaded. 2331 * 2332 * Called by :quit, :close, :xit, :wq and findtag(). 2333 * Returns FAIL when the window was not closed. 2334 */ 2335 int 2336 win_close(win, free_buf) 2337 win_T *win; 2338 int free_buf; 2339 { 2340 win_T *wp; 2341 #ifdef FEAT_AUTOCMD 2342 int other_buffer = FALSE; 2343 #endif 2344 int close_curwin = FALSE; 2345 int dir; 2346 int help_window = FALSE; 2347 tabpage_T *prev_curtab = curtab; 2348 2349 if (last_window()) 2350 { 2351 EMSG(_("E444: Cannot close last window")); 2352 return FAIL; 2353 } 2354 2355 #ifdef FEAT_AUTOCMD 2356 if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) 2357 return FAIL; /* window is already being closed */ 2358 if (win == aucmd_win) 2359 { 2360 EMSG(_("E813: Cannot close autocmd window")); 2361 return FAIL; 2362 } 2363 if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) 2364 { 2365 EMSG(_("E814: Cannot close window, only autocmd window would remain")); 2366 return FAIL; 2367 } 2368 #endif 2369 2370 /* When closing the last window in a tab page first go to another tab page 2371 * and then close the window and the tab page to avoid that curwin and 2372 * curtab are invalid while we are freeing memory. */ 2373 if (close_last_window_tabpage(win, free_buf, prev_curtab)) 2374 return FAIL; 2375 2376 /* When closing the help window, try restoring a snapshot after closing 2377 * the window. Otherwise clear the snapshot, it's now invalid. */ 2378 if (win->w_buffer != NULL && win->w_buffer->b_help) 2379 help_window = TRUE; 2380 else 2381 clear_snapshot(curtab, SNAP_HELP_IDX); 2382 2383 #ifdef FEAT_AUTOCMD 2384 if (win == curwin) 2385 { 2386 /* 2387 * Guess which window is going to be the new current window. 2388 * This may change because of the autocommands (sigh). 2389 */ 2390 wp = frame2win(win_altframe(win, NULL)); 2391 2392 /* 2393 * Be careful: If autocommands delete the window or cause this window 2394 * to be the last one left, return now. 2395 */ 2396 if (wp->w_buffer != curbuf) 2397 { 2398 other_buffer = TRUE; 2399 win->w_closing = TRUE; 2400 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); 2401 if (!win_valid(win)) 2402 return FAIL; 2403 win->w_closing = FALSE; 2404 if (last_window()) 2405 return FAIL; 2406 } 2407 win->w_closing = TRUE; 2408 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); 2409 if (!win_valid(win)) 2410 return FAIL; 2411 win->w_closing = FALSE; 2412 if (last_window()) 2413 return FAIL; 2414 # ifdef FEAT_EVAL 2415 /* autocmds may abort script processing */ 2416 if (aborting()) 2417 return FAIL; 2418 # endif 2419 } 2420 #endif 2421 2422 #ifdef FEAT_GUI 2423 /* Avoid trouble with scrollbars that are going to be deleted in 2424 * win_free(). */ 2425 if (gui.in_use) 2426 out_flush(); 2427 #endif 2428 2429 #ifdef FEAT_SYN_HL 2430 /* Free independent synblock before the buffer is freed. */ 2431 if (win->w_buffer != NULL) 2432 reset_synblock(win); 2433 #endif 2434 2435 /* 2436 * Close the link to the buffer. 2437 */ 2438 if (win->w_buffer != NULL) 2439 { 2440 #ifdef FEAT_AUTOCMD 2441 win->w_closing = TRUE; 2442 #endif 2443 close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, TRUE); 2444 #ifdef FEAT_AUTOCMD 2445 if (win_valid(win)) 2446 win->w_closing = FALSE; 2447 #endif 2448 } 2449 2450 if (only_one_window() && win_valid(win) && win->w_buffer == NULL 2451 && (last_window() || curtab != prev_curtab 2452 || close_last_window_tabpage(win, free_buf, prev_curtab))) 2453 { 2454 /* Autocommands have close all windows, quit now. Restore 2455 * curwin->w_buffer, otherwise writing viminfo may fail. */ 2456 if (curwin->w_buffer == NULL) 2457 curwin->w_buffer = curbuf; 2458 getout(0); 2459 } 2460 2461 /* Autocommands may have closed the window already, or closed the only 2462 * other window or moved to another tab page. */ 2463 else if (!win_valid(win) || last_window() || curtab != prev_curtab 2464 || close_last_window_tabpage(win, free_buf, prev_curtab)) 2465 return FAIL; 2466 2467 /* Free the memory used for the window and get the window that received 2468 * the screen space. */ 2469 wp = win_free_mem(win, &dir, NULL); 2470 2471 /* Make sure curwin isn't invalid. It can cause severe trouble when 2472 * printing an error message. For win_equal() curbuf needs to be valid 2473 * too. */ 2474 if (win == curwin) 2475 { 2476 curwin = wp; 2477 #ifdef FEAT_QUICKFIX 2478 if (wp->w_p_pvw || bt_quickfix(wp->w_buffer)) 2479 { 2480 /* 2481 * If the cursor goes to the preview or the quickfix window, try 2482 * finding another window to go to. 2483 */ 2484 for (;;) 2485 { 2486 if (wp->w_next == NULL) 2487 wp = firstwin; 2488 else 2489 wp = wp->w_next; 2490 if (wp == curwin) 2491 break; 2492 if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer)) 2493 { 2494 curwin = wp; 2495 break; 2496 } 2497 } 2498 } 2499 #endif 2500 curbuf = curwin->w_buffer; 2501 close_curwin = TRUE; 2502 } 2503 if (p_ea 2504 #ifdef FEAT_VERTSPLIT 2505 && (*p_ead == 'b' || *p_ead == dir) 2506 #endif 2507 ) 2508 win_equal(curwin, TRUE, 2509 #ifdef FEAT_VERTSPLIT 2510 dir 2511 #else 2512 0 2513 #endif 2514 ); 2515 else 2516 win_comp_pos(); 2517 if (close_curwin) 2518 { 2519 win_enter_ext(wp, FALSE, TRUE, TRUE, TRUE); 2520 #ifdef FEAT_AUTOCMD 2521 if (other_buffer) 2522 /* careful: after this wp and win may be invalid! */ 2523 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 2524 #endif 2525 } 2526 2527 /* 2528 * If last window has a status line now and we don't want one, 2529 * remove the status line. 2530 */ 2531 last_status(FALSE); 2532 2533 /* After closing the help window, try restoring the window layout from 2534 * before it was opened. */ 2535 if (help_window) 2536 restore_snapshot(SNAP_HELP_IDX, close_curwin); 2537 2538 #if defined(FEAT_GUI) && defined(FEAT_VERTSPLIT) 2539 /* When 'guioptions' includes 'L' or 'R' may have to remove scrollbars. */ 2540 if (gui.in_use && !win_hasvertsplit()) 2541 gui_init_which_components(NULL); 2542 #endif 2543 2544 redraw_all_later(NOT_VALID); 2545 return OK; 2546 } 2547 2548 /* 2549 * Close window "win" in tab page "tp", which is not the current tab page. 2550 * This may be the last window in that tab page and result in closing the tab, 2551 * thus "tp" may become invalid! 2552 * Caller must check if buffer is hidden and whether the tabline needs to be 2553 * updated. 2554 */ 2555 void 2556 win_close_othertab(win, free_buf, tp) 2557 win_T *win; 2558 int free_buf; 2559 tabpage_T *tp; 2560 { 2561 win_T *wp; 2562 int dir; 2563 tabpage_T *ptp = NULL; 2564 int free_tp = FALSE; 2565 2566 #ifdef FEAT_AUTOCMD 2567 if (win->w_closing || win->w_buffer->b_closing) 2568 return; /* window is already being closed */ 2569 #endif 2570 2571 /* Close the link to the buffer. */ 2572 close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE); 2573 2574 /* Careful: Autocommands may have closed the tab page or made it the 2575 * current tab page. */ 2576 for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) 2577 ; 2578 if (ptp == NULL || tp == curtab) 2579 return; 2580 2581 /* Autocommands may have closed the window already. */ 2582 for (wp = tp->tp_firstwin; wp != NULL && wp != win; wp = wp->w_next) 2583 ; 2584 if (wp == NULL) 2585 return; 2586 2587 /* When closing the last window in a tab page remove the tab page. */ 2588 if (tp->tp_firstwin == tp->tp_lastwin) 2589 { 2590 if (tp == first_tabpage) 2591 first_tabpage = tp->tp_next; 2592 else 2593 { 2594 for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp; 2595 ptp = ptp->tp_next) 2596 ; 2597 if (ptp == NULL) 2598 { 2599 EMSG2(_(e_intern2), "win_close_othertab()"); 2600 return; 2601 } 2602 ptp->tp_next = tp->tp_next; 2603 } 2604 free_tp = TRUE; 2605 } 2606 2607 /* Free the memory used for the window. */ 2608 win_free_mem(win, &dir, tp); 2609 2610 if (free_tp) 2611 free_tabpage(tp); 2612 } 2613 2614 /* 2615 * Free the memory used for a window. 2616 * Returns a pointer to the window that got the freed up space. 2617 */ 2618 static win_T * 2619 win_free_mem(win, dirp, tp) 2620 win_T *win; 2621 int *dirp; /* set to 'v' or 'h' for direction if 'ea' */ 2622 tabpage_T *tp; /* tab page "win" is in, NULL for current */ 2623 { 2624 frame_T *frp; 2625 win_T *wp; 2626 2627 /* Remove the window and its frame from the tree of frames. */ 2628 frp = win->w_frame; 2629 wp = winframe_remove(win, dirp, tp); 2630 vim_free(frp); 2631 win_free(win, tp); 2632 2633 /* When deleting the current window of another tab page select a new 2634 * current window. */ 2635 if (tp != NULL && win == tp->tp_curwin) 2636 tp->tp_curwin = wp; 2637 2638 return wp; 2639 } 2640 2641 #if defined(EXITFREE) || defined(PROTO) 2642 void 2643 win_free_all() 2644 { 2645 int dummy; 2646 2647 # ifdef FEAT_WINDOWS 2648 while (first_tabpage->tp_next != NULL) 2649 tabpage_close(TRUE); 2650 # endif 2651 2652 # ifdef FEAT_AUTOCMD 2653 if (aucmd_win != NULL) 2654 { 2655 (void)win_free_mem(aucmd_win, &dummy, NULL); 2656 aucmd_win = NULL; 2657 } 2658 # endif 2659 2660 while (firstwin != NULL) 2661 (void)win_free_mem(firstwin, &dummy, NULL); 2662 2663 /* No window should be used after this. Set curwin to NULL to crash 2664 * instead of using freed memory. */ 2665 curwin = NULL; 2666 } 2667 #endif 2668 2669 /* 2670 * Remove a window and its frame from the tree of frames. 2671 * Returns a pointer to the window that got the freed up space. 2672 */ 2673 win_T * 2674 winframe_remove(win, dirp, tp) 2675 win_T *win; 2676 int *dirp UNUSED; /* set to 'v' or 'h' for direction if 'ea' */ 2677 tabpage_T *tp; /* tab page "win" is in, NULL for current */ 2678 { 2679 frame_T *frp, *frp2, *frp3; 2680 frame_T *frp_close = win->w_frame; 2681 win_T *wp; 2682 2683 /* 2684 * If there is only one window there is nothing to remove. 2685 */ 2686 if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin) 2687 return NULL; 2688 2689 /* 2690 * Remove the window from its frame. 2691 */ 2692 frp2 = win_altframe(win, tp); 2693 wp = frame2win(frp2); 2694 2695 /* Remove this frame from the list of frames. */ 2696 frame_remove(frp_close); 2697 2698 #ifdef FEAT_VERTSPLIT 2699 if (frp_close->fr_parent->fr_layout == FR_COL) 2700 { 2701 #endif 2702 /* When 'winfixheight' is set, try to find another frame in the column 2703 * (as close to the closed frame as possible) to distribute the height 2704 * to. */ 2705 if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfh) 2706 { 2707 frp = frp_close->fr_prev; 2708 frp3 = frp_close->fr_next; 2709 while (frp != NULL || frp3 != NULL) 2710 { 2711 if (frp != NULL) 2712 { 2713 if (frp->fr_win != NULL && !frp->fr_win->w_p_wfh) 2714 { 2715 frp2 = frp; 2716 wp = frp->fr_win; 2717 break; 2718 } 2719 frp = frp->fr_prev; 2720 } 2721 if (frp3 != NULL) 2722 { 2723 if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfh) 2724 { 2725 frp2 = frp3; 2726 wp = frp3->fr_win; 2727 break; 2728 } 2729 frp3 = frp3->fr_next; 2730 } 2731 } 2732 } 2733 frame_new_height(frp2, frp2->fr_height + frp_close->fr_height, 2734 frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE); 2735 #ifdef FEAT_VERTSPLIT 2736 *dirp = 'v'; 2737 } 2738 else 2739 { 2740 /* When 'winfixwidth' is set, try to find another frame in the column 2741 * (as close to the closed frame as possible) to distribute the width 2742 * to. */ 2743 if (frp2->fr_win != NULL && frp2->fr_win->w_p_wfw) 2744 { 2745 frp = frp_close->fr_prev; 2746 frp3 = frp_close->fr_next; 2747 while (frp != NULL || frp3 != NULL) 2748 { 2749 if (frp != NULL) 2750 { 2751 if (frp->fr_win != NULL && !frp->fr_win->w_p_wfw) 2752 { 2753 frp2 = frp; 2754 wp = frp->fr_win; 2755 break; 2756 } 2757 frp = frp->fr_prev; 2758 } 2759 if (frp3 != NULL) 2760 { 2761 if (frp3->fr_win != NULL && !frp3->fr_win->w_p_wfw) 2762 { 2763 frp2 = frp3; 2764 wp = frp3->fr_win; 2765 break; 2766 } 2767 frp3 = frp3->fr_next; 2768 } 2769 } 2770 } 2771 frame_new_width(frp2, frp2->fr_width + frp_close->fr_width, 2772 frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE); 2773 *dirp = 'h'; 2774 } 2775 #endif 2776 2777 /* If rows/columns go to a window below/right its positions need to be 2778 * updated. Can only be done after the sizes have been updated. */ 2779 if (frp2 == frp_close->fr_next) 2780 { 2781 int row = win->w_winrow; 2782 int col = W_WINCOL(win); 2783 2784 frame_comp_pos(frp2, &row, &col); 2785 } 2786 2787 if (frp2->fr_next == NULL && frp2->fr_prev == NULL) 2788 { 2789 /* There is no other frame in this list, move its info to the parent 2790 * and remove it. */ 2791 frp2->fr_parent->fr_layout = frp2->fr_layout; 2792 frp2->fr_parent->fr_child = frp2->fr_child; 2793 for (frp = frp2->fr_child; frp != NULL; frp = frp->fr_next) 2794 frp->fr_parent = frp2->fr_parent; 2795 frp2->fr_parent->fr_win = frp2->fr_win; 2796 if (frp2->fr_win != NULL) 2797 frp2->fr_win->w_frame = frp2->fr_parent; 2798 frp = frp2->fr_parent; 2799 vim_free(frp2); 2800 2801 frp2 = frp->fr_parent; 2802 if (frp2 != NULL && frp2->fr_layout == frp->fr_layout) 2803 { 2804 /* The frame above the parent has the same layout, have to merge 2805 * the frames into this list. */ 2806 if (frp2->fr_child == frp) 2807 frp2->fr_child = frp->fr_child; 2808 frp->fr_child->fr_prev = frp->fr_prev; 2809 if (frp->fr_prev != NULL) 2810 frp->fr_prev->fr_next = frp->fr_child; 2811 for (frp3 = frp->fr_child; ; frp3 = frp3->fr_next) 2812 { 2813 frp3->fr_parent = frp2; 2814 if (frp3->fr_next == NULL) 2815 { 2816 frp3->fr_next = frp->fr_next; 2817 if (frp->fr_next != NULL) 2818 frp->fr_next->fr_prev = frp3; 2819 break; 2820 } 2821 } 2822 vim_free(frp); 2823 } 2824 } 2825 2826 return wp; 2827 } 2828 2829 /* 2830 * Find out which frame is going to get the freed up space when "win" is 2831 * closed. 2832 * if 'splitbelow'/'splitleft' the space goes to the window above/left. 2833 * if 'nosplitbelow'/'nosplitleft' the space goes to the window below/right. 2834 * This makes opening a window and closing it immediately keep the same window 2835 * layout. 2836 */ 2837 static frame_T * 2838 win_altframe(win, tp) 2839 win_T *win; 2840 tabpage_T *tp; /* tab page "win" is in, NULL for current */ 2841 { 2842 frame_T *frp; 2843 int b; 2844 2845 if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin) 2846 /* Last window in this tab page, will go to next tab page. */ 2847 return alt_tabpage()->tp_curwin->w_frame; 2848 2849 frp = win->w_frame; 2850 #ifdef FEAT_VERTSPLIT 2851 if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW) 2852 b = p_spr; 2853 else 2854 #endif 2855 b = p_sb; 2856 if ((!b && frp->fr_next != NULL) || frp->fr_prev == NULL) 2857 return frp->fr_next; 2858 return frp->fr_prev; 2859 } 2860 2861 /* 2862 * Return the tabpage that will be used if the current one is closed. 2863 */ 2864 static tabpage_T * 2865 alt_tabpage() 2866 { 2867 tabpage_T *tp; 2868 2869 /* Use the next tab page if possible. */ 2870 if (curtab->tp_next != NULL) 2871 return curtab->tp_next; 2872 2873 /* Find the last but one tab page. */ 2874 for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) 2875 ; 2876 return tp; 2877 } 2878 2879 /* 2880 * Find the left-upper window in frame "frp". 2881 */ 2882 static win_T * 2883 frame2win(frp) 2884 frame_T *frp; 2885 { 2886 while (frp->fr_win == NULL) 2887 frp = frp->fr_child; 2888 return frp->fr_win; 2889 } 2890 2891 /* 2892 * Return TRUE if frame "frp" contains window "wp". 2893 */ 2894 static int 2895 frame_has_win(frp, wp) 2896 frame_T *frp; 2897 win_T *wp; 2898 { 2899 frame_T *p; 2900 2901 if (frp->fr_layout == FR_LEAF) 2902 return frp->fr_win == wp; 2903 2904 for (p = frp->fr_child; p != NULL; p = p->fr_next) 2905 if (frame_has_win(p, wp)) 2906 return TRUE; 2907 return FALSE; 2908 } 2909 2910 /* 2911 * Set a new height for a frame. Recursively sets the height for contained 2912 * frames and windows. Caller must take care of positions. 2913 */ 2914 static void 2915 frame_new_height(topfrp, height, topfirst, wfh) 2916 frame_T *topfrp; 2917 int height; 2918 int topfirst; /* resize topmost contained frame first */ 2919 int wfh; /* obey 'winfixheight' when there is a choice; 2920 may cause the height not to be set */ 2921 { 2922 frame_T *frp; 2923 int extra_lines; 2924 int h; 2925 2926 if (topfrp->fr_win != NULL) 2927 { 2928 /* Simple case: just one window. */ 2929 win_new_height(topfrp->fr_win, 2930 height - topfrp->fr_win->w_status_height); 2931 } 2932 #ifdef FEAT_VERTSPLIT 2933 else if (topfrp->fr_layout == FR_ROW) 2934 { 2935 do 2936 { 2937 /* All frames in this row get the same new height. */ 2938 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 2939 { 2940 frame_new_height(frp, height, topfirst, wfh); 2941 if (frp->fr_height > height) 2942 { 2943 /* Could not fit the windows, make the whole row higher. */ 2944 height = frp->fr_height; 2945 break; 2946 } 2947 } 2948 } 2949 while (frp != NULL); 2950 } 2951 #endif 2952 else /* fr_layout == FR_COL */ 2953 { 2954 /* Complicated case: Resize a column of frames. Resize the bottom 2955 * frame first, frames above that when needed. */ 2956 2957 frp = topfrp->fr_child; 2958 if (wfh) 2959 /* Advance past frames with one window with 'wfh' set. */ 2960 while (frame_fixed_height(frp)) 2961 { 2962 frp = frp->fr_next; 2963 if (frp == NULL) 2964 return; /* no frame without 'wfh', give up */ 2965 } 2966 if (!topfirst) 2967 { 2968 /* Find the bottom frame of this column */ 2969 while (frp->fr_next != NULL) 2970 frp = frp->fr_next; 2971 if (wfh) 2972 /* Advance back for frames with one window with 'wfh' set. */ 2973 while (frame_fixed_height(frp)) 2974 frp = frp->fr_prev; 2975 } 2976 2977 extra_lines = height - topfrp->fr_height; 2978 if (extra_lines < 0) 2979 { 2980 /* reduce height of contained frames, bottom or top frame first */ 2981 while (frp != NULL) 2982 { 2983 h = frame_minheight(frp, NULL); 2984 if (frp->fr_height + extra_lines < h) 2985 { 2986 extra_lines += frp->fr_height - h; 2987 frame_new_height(frp, h, topfirst, wfh); 2988 } 2989 else 2990 { 2991 frame_new_height(frp, frp->fr_height + extra_lines, 2992 topfirst, wfh); 2993 break; 2994 } 2995 if (topfirst) 2996 { 2997 do 2998 frp = frp->fr_next; 2999 while (wfh && frp != NULL && frame_fixed_height(frp)); 3000 } 3001 else 3002 { 3003 do 3004 frp = frp->fr_prev; 3005 while (wfh && frp != NULL && frame_fixed_height(frp)); 3006 } 3007 /* Increase "height" if we could not reduce enough frames. */ 3008 if (frp == NULL) 3009 height -= extra_lines; 3010 } 3011 } 3012 else if (extra_lines > 0) 3013 { 3014 /* increase height of bottom or top frame */ 3015 frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh); 3016 } 3017 } 3018 topfrp->fr_height = height; 3019 } 3020 3021 /* 3022 * Return TRUE if height of frame "frp" should not be changed because of 3023 * the 'winfixheight' option. 3024 */ 3025 static int 3026 frame_fixed_height(frp) 3027 frame_T *frp; 3028 { 3029 /* frame with one window: fixed height if 'winfixheight' set. */ 3030 if (frp->fr_win != NULL) 3031 return frp->fr_win->w_p_wfh; 3032 3033 if (frp->fr_layout == FR_ROW) 3034 { 3035 /* The frame is fixed height if one of the frames in the row is fixed 3036 * height. */ 3037 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 3038 if (frame_fixed_height(frp)) 3039 return TRUE; 3040 return FALSE; 3041 } 3042 3043 /* frp->fr_layout == FR_COL: The frame is fixed height if all of the 3044 * frames in the row are fixed height. */ 3045 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 3046 if (!frame_fixed_height(frp)) 3047 return FALSE; 3048 return TRUE; 3049 } 3050 3051 #ifdef FEAT_VERTSPLIT 3052 /* 3053 * Return TRUE if width of frame "frp" should not be changed because of 3054 * the 'winfixwidth' option. 3055 */ 3056 static int 3057 frame_fixed_width(frp) 3058 frame_T *frp; 3059 { 3060 /* frame with one window: fixed width if 'winfixwidth' set. */ 3061 if (frp->fr_win != NULL) 3062 return frp->fr_win->w_p_wfw; 3063 3064 if (frp->fr_layout == FR_COL) 3065 { 3066 /* The frame is fixed width if one of the frames in the row is fixed 3067 * width. */ 3068 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 3069 if (frame_fixed_width(frp)) 3070 return TRUE; 3071 return FALSE; 3072 } 3073 3074 /* frp->fr_layout == FR_ROW: The frame is fixed width if all of the 3075 * frames in the row are fixed width. */ 3076 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 3077 if (!frame_fixed_width(frp)) 3078 return FALSE; 3079 return TRUE; 3080 } 3081 3082 /* 3083 * Add a status line to windows at the bottom of "frp". 3084 * Note: Does not check if there is room! 3085 */ 3086 static void 3087 frame_add_statusline(frp) 3088 frame_T *frp; 3089 { 3090 win_T *wp; 3091 3092 if (frp->fr_layout == FR_LEAF) 3093 { 3094 wp = frp->fr_win; 3095 if (wp->w_status_height == 0) 3096 { 3097 if (wp->w_height > 0) /* don't make it negative */ 3098 --wp->w_height; 3099 wp->w_status_height = STATUS_HEIGHT; 3100 } 3101 } 3102 else if (frp->fr_layout == FR_ROW) 3103 { 3104 /* Handle all the frames in the row. */ 3105 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 3106 frame_add_statusline(frp); 3107 } 3108 else /* frp->fr_layout == FR_COL */ 3109 { 3110 /* Only need to handle the last frame in the column. */ 3111 for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) 3112 ; 3113 frame_add_statusline(frp); 3114 } 3115 } 3116 3117 /* 3118 * Set width of a frame. Handles recursively going through contained frames. 3119 * May remove separator line for windows at the right side (for win_close()). 3120 */ 3121 static void 3122 frame_new_width(topfrp, width, leftfirst, wfw) 3123 frame_T *topfrp; 3124 int width; 3125 int leftfirst; /* resize leftmost contained frame first */ 3126 int wfw; /* obey 'winfixwidth' when there is a choice; 3127 may cause the width not to be set */ 3128 { 3129 frame_T *frp; 3130 int extra_cols; 3131 int w; 3132 win_T *wp; 3133 3134 if (topfrp->fr_layout == FR_LEAF) 3135 { 3136 /* Simple case: just one window. */ 3137 wp = topfrp->fr_win; 3138 /* Find out if there are any windows right of this one. */ 3139 for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent) 3140 if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL) 3141 break; 3142 if (frp->fr_parent == NULL) 3143 wp->w_vsep_width = 0; 3144 win_new_width(wp, width - wp->w_vsep_width); 3145 } 3146 else if (topfrp->fr_layout == FR_COL) 3147 { 3148 do 3149 { 3150 /* All frames in this column get the same new width. */ 3151 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 3152 { 3153 frame_new_width(frp, width, leftfirst, wfw); 3154 if (frp->fr_width > width) 3155 { 3156 /* Could not fit the windows, make whole column wider. */ 3157 width = frp->fr_width; 3158 break; 3159 } 3160 } 3161 } while (frp != NULL); 3162 } 3163 else /* fr_layout == FR_ROW */ 3164 { 3165 /* Complicated case: Resize a row of frames. Resize the rightmost 3166 * frame first, frames left of it when needed. */ 3167 3168 frp = topfrp->fr_child; 3169 if (wfw) 3170 /* Advance past frames with one window with 'wfw' set. */ 3171 while (frame_fixed_width(frp)) 3172 { 3173 frp = frp->fr_next; 3174 if (frp == NULL) 3175 return; /* no frame without 'wfw', give up */ 3176 } 3177 if (!leftfirst) 3178 { 3179 /* Find the rightmost frame of this row */ 3180 while (frp->fr_next != NULL) 3181 frp = frp->fr_next; 3182 if (wfw) 3183 /* Advance back for frames with one window with 'wfw' set. */ 3184 while (frame_fixed_width(frp)) 3185 frp = frp->fr_prev; 3186 } 3187 3188 extra_cols = width - topfrp->fr_width; 3189 if (extra_cols < 0) 3190 { 3191 /* reduce frame width, rightmost frame first */ 3192 while (frp != NULL) 3193 { 3194 w = frame_minwidth(frp, NULL); 3195 if (frp->fr_width + extra_cols < w) 3196 { 3197 extra_cols += frp->fr_width - w; 3198 frame_new_width(frp, w, leftfirst, wfw); 3199 } 3200 else 3201 { 3202 frame_new_width(frp, frp->fr_width + extra_cols, 3203 leftfirst, wfw); 3204 break; 3205 } 3206 if (leftfirst) 3207 { 3208 do 3209 frp = frp->fr_next; 3210 while (wfw && frp != NULL && frame_fixed_width(frp)); 3211 } 3212 else 3213 { 3214 do 3215 frp = frp->fr_prev; 3216 while (wfw && frp != NULL && frame_fixed_width(frp)); 3217 } 3218 /* Increase "width" if we could not reduce enough frames. */ 3219 if (frp == NULL) 3220 width -= extra_cols; 3221 } 3222 } 3223 else if (extra_cols > 0) 3224 { 3225 /* increase width of rightmost frame */ 3226 frame_new_width(frp, frp->fr_width + extra_cols, leftfirst, wfw); 3227 } 3228 } 3229 topfrp->fr_width = width; 3230 } 3231 3232 /* 3233 * Add the vertical separator to windows at the right side of "frp". 3234 * Note: Does not check if there is room! 3235 */ 3236 static void 3237 frame_add_vsep(frp) 3238 frame_T *frp; 3239 { 3240 win_T *wp; 3241 3242 if (frp->fr_layout == FR_LEAF) 3243 { 3244 wp = frp->fr_win; 3245 if (wp->w_vsep_width == 0) 3246 { 3247 if (wp->w_width > 0) /* don't make it negative */ 3248 --wp->w_width; 3249 wp->w_vsep_width = 1; 3250 } 3251 } 3252 else if (frp->fr_layout == FR_COL) 3253 { 3254 /* Handle all the frames in the column. */ 3255 for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) 3256 frame_add_vsep(frp); 3257 } 3258 else /* frp->fr_layout == FR_ROW */ 3259 { 3260 /* Only need to handle the last frame in the row. */ 3261 frp = frp->fr_child; 3262 while (frp->fr_next != NULL) 3263 frp = frp->fr_next; 3264 frame_add_vsep(frp); 3265 } 3266 } 3267 3268 /* 3269 * Set frame width from the window it contains. 3270 */ 3271 static void 3272 frame_fix_width(wp) 3273 win_T *wp; 3274 { 3275 wp->w_frame->fr_width = wp->w_width + wp->w_vsep_width; 3276 } 3277 #endif 3278 3279 /* 3280 * Set frame height from the window it contains. 3281 */ 3282 static void 3283 frame_fix_height(wp) 3284 win_T *wp; 3285 { 3286 wp->w_frame->fr_height = wp->w_height + wp->w_status_height; 3287 } 3288 3289 /* 3290 * Compute the minimal height for frame "topfrp". 3291 * Uses the 'winminheight' option. 3292 * When "next_curwin" isn't NULL, use p_wh for this window. 3293 * When "next_curwin" is NOWIN, don't use at least one line for the current 3294 * window. 3295 */ 3296 static int 3297 frame_minheight(topfrp, next_curwin) 3298 frame_T *topfrp; 3299 win_T *next_curwin; 3300 { 3301 frame_T *frp; 3302 int m; 3303 #ifdef FEAT_VERTSPLIT 3304 int n; 3305 #endif 3306 3307 if (topfrp->fr_win != NULL) 3308 { 3309 if (topfrp->fr_win == next_curwin) 3310 m = p_wh + topfrp->fr_win->w_status_height; 3311 else 3312 { 3313 /* window: minimal height of the window plus status line */ 3314 m = p_wmh + topfrp->fr_win->w_status_height; 3315 /* Current window is minimal one line high */ 3316 if (p_wmh == 0 && topfrp->fr_win == curwin && next_curwin == NULL) 3317 ++m; 3318 } 3319 } 3320 #ifdef FEAT_VERTSPLIT 3321 else if (topfrp->fr_layout == FR_ROW) 3322 { 3323 /* get the minimal height from each frame in this row */ 3324 m = 0; 3325 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 3326 { 3327 n = frame_minheight(frp, next_curwin); 3328 if (n > m) 3329 m = n; 3330 } 3331 } 3332 #endif 3333 else 3334 { 3335 /* Add up the minimal heights for all frames in this column. */ 3336 m = 0; 3337 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 3338 m += frame_minheight(frp, next_curwin); 3339 } 3340 3341 return m; 3342 } 3343 3344 #ifdef FEAT_VERTSPLIT 3345 /* 3346 * Compute the minimal width for frame "topfrp". 3347 * When "next_curwin" isn't NULL, use p_wiw for this window. 3348 * When "next_curwin" is NOWIN, don't use at least one column for the current 3349 * window. 3350 */ 3351 static int 3352 frame_minwidth(topfrp, next_curwin) 3353 frame_T *topfrp; 3354 win_T *next_curwin; /* use p_wh and p_wiw for next_curwin */ 3355 { 3356 frame_T *frp; 3357 int m, n; 3358 3359 if (topfrp->fr_win != NULL) 3360 { 3361 if (topfrp->fr_win == next_curwin) 3362 m = p_wiw + topfrp->fr_win->w_vsep_width; 3363 else 3364 { 3365 /* window: minimal width of the window plus separator column */ 3366 m = p_wmw + topfrp->fr_win->w_vsep_width; 3367 /* Current window is minimal one column wide */ 3368 if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) 3369 ++m; 3370 } 3371 } 3372 else if (topfrp->fr_layout == FR_COL) 3373 { 3374 /* get the minimal width from each frame in this column */ 3375 m = 0; 3376 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 3377 { 3378 n = frame_minwidth(frp, next_curwin); 3379 if (n > m) 3380 m = n; 3381 } 3382 } 3383 else 3384 { 3385 /* Add up the minimal widths for all frames in this row. */ 3386 m = 0; 3387 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 3388 m += frame_minwidth(frp, next_curwin); 3389 } 3390 3391 return m; 3392 } 3393 #endif 3394 3395 3396 /* 3397 * Try to close all windows except current one. 3398 * Buffers in the other windows become hidden if 'hidden' is set, or '!' is 3399 * used and the buffer was modified. 3400 * 3401 * Used by ":bdel" and ":only". 3402 */ 3403 void 3404 close_others(message, forceit) 3405 int message; 3406 int forceit; /* always hide all other windows */ 3407 { 3408 win_T *wp; 3409 win_T *nextwp; 3410 int r; 3411 3412 if (one_window()) 3413 { 3414 if (message 3415 #ifdef FEAT_AUTOCMD 3416 && !autocmd_busy 3417 #endif 3418 ) 3419 MSG(_(m_onlyone)); 3420 return; 3421 } 3422 3423 /* Be very careful here: autocommands may change the window layout. */ 3424 for (wp = firstwin; win_valid(wp); wp = nextwp) 3425 { 3426 nextwp = wp->w_next; 3427 if (wp != curwin) /* don't close current window */ 3428 { 3429 3430 /* Check if it's allowed to abandon this window */ 3431 r = can_abandon(wp->w_buffer, forceit); 3432 #ifdef FEAT_AUTOCMD 3433 if (!win_valid(wp)) /* autocommands messed wp up */ 3434 { 3435 nextwp = firstwin; 3436 continue; 3437 } 3438 #endif 3439 if (!r) 3440 { 3441 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) 3442 if (message && (p_confirm || cmdmod.confirm) && p_write) 3443 { 3444 dialog_changed(wp->w_buffer, FALSE); 3445 # ifdef FEAT_AUTOCMD 3446 if (!win_valid(wp)) /* autocommands messed wp up */ 3447 { 3448 nextwp = firstwin; 3449 continue; 3450 } 3451 # endif 3452 } 3453 if (bufIsChanged(wp->w_buffer)) 3454 #endif 3455 continue; 3456 } 3457 win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); 3458 } 3459 } 3460 3461 if (message && lastwin != firstwin) 3462 EMSG(_("E445: Other window contains changes")); 3463 } 3464 3465 #endif /* FEAT_WINDOWS */ 3466 3467 /* 3468 * Init the current window "curwin". 3469 * Called when a new file is being edited. 3470 */ 3471 void 3472 curwin_init() 3473 { 3474 win_init_empty(curwin); 3475 } 3476 3477 void 3478 win_init_empty(wp) 3479 win_T *wp; 3480 { 3481 redraw_win_later(wp, NOT_VALID); 3482 wp->w_lines_valid = 0; 3483 wp->w_cursor.lnum = 1; 3484 wp->w_curswant = wp->w_cursor.col = 0; 3485 #ifdef FEAT_VIRTUALEDIT 3486 wp->w_cursor.coladd = 0; 3487 #endif 3488 wp->w_pcmark.lnum = 1; /* pcmark not cleared but set to line 1 */ 3489 wp->w_pcmark.col = 0; 3490 wp->w_prev_pcmark.lnum = 0; 3491 wp->w_prev_pcmark.col = 0; 3492 wp->w_topline = 1; 3493 #ifdef FEAT_DIFF 3494 wp->w_topfill = 0; 3495 #endif 3496 wp->w_botline = 2; 3497 #ifdef FEAT_FKMAP 3498 if (wp->w_p_rl) 3499 wp->w_farsi = W_CONV + W_R_L; 3500 else 3501 wp->w_farsi = W_CONV; 3502 #endif 3503 #ifdef FEAT_SYN_HL 3504 wp->w_s = &wp->w_buffer->b_s; 3505 #endif 3506 } 3507 3508 /* 3509 * Allocate the first window and put an empty buffer in it. 3510 * Called from main(). 3511 * Return FAIL when something goes wrong (out of memory). 3512 */ 3513 int 3514 win_alloc_first() 3515 { 3516 if (win_alloc_firstwin(NULL) == FAIL) 3517 return FAIL; 3518 3519 #ifdef FEAT_WINDOWS 3520 first_tabpage = alloc_tabpage(); 3521 if (first_tabpage == NULL) 3522 return FAIL; 3523 first_tabpage->tp_topframe = topframe; 3524 curtab = first_tabpage; 3525 #endif 3526 3527 return OK; 3528 } 3529 3530 #if defined(FEAT_AUTOCMD) || defined(PROTO) 3531 /* 3532 * Init "aucmd_win". This can only be done after the first 3533 * window is fully initialized, thus it can't be in win_alloc_first(). 3534 */ 3535 void 3536 win_alloc_aucmd_win() 3537 { 3538 aucmd_win = win_alloc(NULL, TRUE); 3539 if (aucmd_win != NULL) 3540 { 3541 win_init_some(aucmd_win, curwin); 3542 RESET_BINDING(aucmd_win); 3543 new_frame(aucmd_win); 3544 } 3545 } 3546 #endif 3547 3548 /* 3549 * Allocate the first window or the first window in a new tab page. 3550 * When "oldwin" is NULL create an empty buffer for it. 3551 * When "oldwin" is not NULL copy info from it to the new window (only with 3552 * FEAT_WINDOWS). 3553 * Return FAIL when something goes wrong (out of memory). 3554 */ 3555 static int 3556 win_alloc_firstwin(oldwin) 3557 win_T *oldwin; 3558 { 3559 curwin = win_alloc(NULL, FALSE); 3560 if (oldwin == NULL) 3561 { 3562 /* Very first window, need to create an empty buffer for it and 3563 * initialize from scratch. */ 3564 curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED); 3565 if (curwin == NULL || curbuf == NULL) 3566 return FAIL; 3567 curwin->w_buffer = curbuf; 3568 #ifdef FEAT_SYN_HL 3569 curwin->w_s = &(curbuf->b_s); 3570 #endif 3571 curbuf->b_nwindows = 1; /* there is one window */ 3572 #ifdef FEAT_WINDOWS 3573 curwin->w_alist = &global_alist; 3574 #endif 3575 curwin_init(); /* init current window */ 3576 } 3577 #ifdef FEAT_WINDOWS 3578 else 3579 { 3580 /* First window in new tab page, initialize it from "oldwin". */ 3581 win_init(curwin, oldwin, 0); 3582 3583 /* We don't want cursor- and scroll-binding in the first window. */ 3584 RESET_BINDING(curwin); 3585 } 3586 #endif 3587 3588 new_frame(curwin); 3589 if (curwin->w_frame == NULL) 3590 return FAIL; 3591 topframe = curwin->w_frame; 3592 #ifdef FEAT_VERTSPLIT 3593 topframe->fr_width = Columns; 3594 #endif 3595 topframe->fr_height = Rows - p_ch; 3596 topframe->fr_win = curwin; 3597 3598 return OK; 3599 } 3600 3601 /* 3602 * Create a frame for window "wp". 3603 */ 3604 static void 3605 new_frame(win_T *wp) 3606 { 3607 frame_T *frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 3608 3609 wp->w_frame = frp; 3610 if (frp != NULL) 3611 { 3612 frp->fr_layout = FR_LEAF; 3613 frp->fr_win = wp; 3614 } 3615 } 3616 3617 /* 3618 * Initialize the window and frame size to the maximum. 3619 */ 3620 void 3621 win_init_size() 3622 { 3623 firstwin->w_height = ROWS_AVAIL; 3624 topframe->fr_height = ROWS_AVAIL; 3625 #ifdef FEAT_VERTSPLIT 3626 firstwin->w_width = Columns; 3627 topframe->fr_width = Columns; 3628 #endif 3629 } 3630 3631 #if defined(FEAT_WINDOWS) || defined(PROTO) 3632 3633 /* 3634 * Allocate a new tabpage_T and init the values. 3635 * Returns NULL when out of memory. 3636 */ 3637 static tabpage_T * 3638 alloc_tabpage() 3639 { 3640 tabpage_T *tp; 3641 # ifdef FEAT_GUI 3642 int i; 3643 # endif 3644 3645 3646 tp = (tabpage_T *)alloc_clear((unsigned)sizeof(tabpage_T)); 3647 if (tp == NULL) 3648 return NULL; 3649 3650 # ifdef FEAT_EVAL 3651 /* init t: variables */ 3652 tp->tp_vars = dict_alloc(); 3653 if (tp->tp_vars == NULL) 3654 { 3655 vim_free(tp); 3656 return NULL; 3657 } 3658 init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE); 3659 # endif 3660 3661 # ifdef FEAT_GUI 3662 for (i = 0; i < 3; i++) 3663 tp->tp_prev_which_scrollbars[i] = -1; 3664 # endif 3665 # ifdef FEAT_DIFF 3666 tp->tp_diff_invalid = TRUE; 3667 # endif 3668 tp->tp_ch_used = p_ch; 3669 3670 return tp; 3671 } 3672 3673 void 3674 free_tabpage(tp) 3675 tabpage_T *tp; 3676 { 3677 int idx; 3678 3679 # ifdef FEAT_DIFF 3680 diff_clear(tp); 3681 # endif 3682 for (idx = 0; idx < SNAP_COUNT; ++idx) 3683 clear_snapshot(tp, idx); 3684 #ifdef FEAT_EVAL 3685 vars_clear(&tp->tp_vars->dv_hashtab); /* free all t: variables */ 3686 hash_init(&tp->tp_vars->dv_hashtab); 3687 unref_var_dict(tp->tp_vars); 3688 #endif 3689 3690 #ifdef FEAT_PYTHON 3691 python_tabpage_free(tp); 3692 #endif 3693 3694 #ifdef FEAT_PYTHON3 3695 python3_tabpage_free(tp); 3696 #endif 3697 3698 vim_free(tp); 3699 } 3700 3701 /* 3702 * Create a new Tab page with one window. 3703 * It will edit the current buffer, like after ":split". 3704 * When "after" is 0 put it just after the current Tab page. 3705 * Otherwise put it just before tab page "after". 3706 * Return FAIL or OK. 3707 */ 3708 int 3709 win_new_tabpage(after) 3710 int after; 3711 { 3712 tabpage_T *tp = curtab; 3713 tabpage_T *newtp; 3714 int n; 3715 3716 newtp = alloc_tabpage(); 3717 if (newtp == NULL) 3718 return FAIL; 3719 3720 /* Remember the current windows in this Tab page. */ 3721 if (leave_tabpage(curbuf, TRUE) == FAIL) 3722 { 3723 vim_free(newtp); 3724 return FAIL; 3725 } 3726 curtab = newtp; 3727 3728 /* Create a new empty window. */ 3729 if (win_alloc_firstwin(tp->tp_curwin) == OK) 3730 { 3731 /* Make the new Tab page the new topframe. */ 3732 if (after == 1) 3733 { 3734 /* New tab page becomes the first one. */ 3735 newtp->tp_next = first_tabpage; 3736 first_tabpage = newtp; 3737 } 3738 else 3739 { 3740 if (after > 0) 3741 { 3742 /* Put new tab page before tab page "after". */ 3743 n = 2; 3744 for (tp = first_tabpage; tp->tp_next != NULL 3745 && n < after; tp = tp->tp_next) 3746 ++n; 3747 } 3748 newtp->tp_next = tp->tp_next; 3749 tp->tp_next = newtp; 3750 } 3751 win_init_size(); 3752 firstwin->w_winrow = tabline_height(); 3753 win_comp_scroll(curwin); 3754 3755 newtp->tp_topframe = topframe; 3756 last_status(FALSE); 3757 3758 #if defined(FEAT_GUI) 3759 /* When 'guioptions' includes 'L' or 'R' may have to remove or add 3760 * scrollbars. Have to update them anyway. */ 3761 gui_may_update_scrollbars(); 3762 #endif 3763 3764 redraw_all_later(CLEAR); 3765 #ifdef FEAT_AUTOCMD 3766 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); 3767 apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); 3768 #endif 3769 return OK; 3770 } 3771 3772 /* Failed, get back the previous Tab page */ 3773 enter_tabpage(curtab, curbuf, TRUE, TRUE); 3774 return FAIL; 3775 } 3776 3777 /* 3778 * Open a new tab page if ":tab cmd" was used. It will edit the same buffer, 3779 * like with ":split". 3780 * Returns OK if a new tab page was created, FAIL otherwise. 3781 */ 3782 int 3783 may_open_tabpage() 3784 { 3785 int n = (cmdmod.tab == 0) ? postponed_split_tab : cmdmod.tab; 3786 3787 if (n != 0) 3788 { 3789 cmdmod.tab = 0; /* reset it to avoid doing it twice */ 3790 postponed_split_tab = 0; 3791 return win_new_tabpage(n); 3792 } 3793 return FAIL; 3794 } 3795 3796 /* 3797 * Create up to "maxcount" tabpages with empty windows. 3798 * Returns the number of resulting tab pages. 3799 */ 3800 int 3801 make_tabpages(maxcount) 3802 int maxcount; 3803 { 3804 int count = maxcount; 3805 int todo; 3806 3807 /* Limit to 'tabpagemax' tabs. */ 3808 if (count > p_tpm) 3809 count = p_tpm; 3810 3811 #ifdef FEAT_AUTOCMD 3812 /* 3813 * Don't execute autocommands while creating the tab pages. Must do that 3814 * when putting the buffers in the windows. 3815 */ 3816 block_autocmds(); 3817 #endif 3818 3819 for (todo = count - 1; todo > 0; --todo) 3820 if (win_new_tabpage(0) == FAIL) 3821 break; 3822 3823 #ifdef FEAT_AUTOCMD 3824 unblock_autocmds(); 3825 #endif 3826 3827 /* return actual number of tab pages */ 3828 return (count - todo); 3829 } 3830 3831 /* 3832 * Return TRUE when "tpc" points to a valid tab page. 3833 */ 3834 int 3835 valid_tabpage(tpc) 3836 tabpage_T *tpc; 3837 { 3838 tabpage_T *tp; 3839 3840 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) 3841 if (tp == tpc) 3842 return TRUE; 3843 return FALSE; 3844 } 3845 3846 /* 3847 * Find tab page "n" (first one is 1). Returns NULL when not found. 3848 */ 3849 tabpage_T * 3850 find_tabpage(n) 3851 int n; 3852 { 3853 tabpage_T *tp; 3854 int i = 1; 3855 3856 for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next) 3857 ++i; 3858 return tp; 3859 } 3860 3861 /* 3862 * Get index of tab page "tp". First one has index 1. 3863 * When not found returns number of tab pages plus one. 3864 */ 3865 int 3866 tabpage_index(ftp) 3867 tabpage_T *ftp; 3868 { 3869 int i = 1; 3870 tabpage_T *tp; 3871 3872 for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next) 3873 ++i; 3874 return i; 3875 } 3876 3877 /* 3878 * Prepare for leaving the current tab page. 3879 * When autocommands change "curtab" we don't leave the tab page and return 3880 * FAIL. 3881 * Careful: When OK is returned need to get a new tab page very very soon! 3882 */ 3883 static int 3884 leave_tabpage(new_curbuf, trigger_leave_autocmds) 3885 buf_T *new_curbuf UNUSED; /* what is going to be the new curbuf, 3886 NULL if unknown */ 3887 int trigger_leave_autocmds UNUSED; 3888 { 3889 tabpage_T *tp = curtab; 3890 3891 reset_VIsual_and_resel(); /* stop Visual mode */ 3892 #ifdef FEAT_AUTOCMD 3893 if (trigger_leave_autocmds) 3894 { 3895 if (new_curbuf != curbuf) 3896 { 3897 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); 3898 if (curtab != tp) 3899 return FAIL; 3900 } 3901 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); 3902 if (curtab != tp) 3903 return FAIL; 3904 apply_autocmds(EVENT_TABLEAVE, NULL, NULL, FALSE, curbuf); 3905 if (curtab != tp) 3906 return FAIL; 3907 } 3908 #endif 3909 #if defined(FEAT_GUI) 3910 /* Remove the scrollbars. They may be added back later. */ 3911 if (gui.in_use) 3912 gui_remove_scrollbars(); 3913 #endif 3914 tp->tp_curwin = curwin; 3915 tp->tp_prevwin = prevwin; 3916 tp->tp_firstwin = firstwin; 3917 tp->tp_lastwin = lastwin; 3918 tp->tp_old_Rows = Rows; 3919 tp->tp_old_Columns = Columns; 3920 firstwin = NULL; 3921 lastwin = NULL; 3922 return OK; 3923 } 3924 3925 /* 3926 * Start using tab page "tp". 3927 * Only to be used after leave_tabpage() or freeing the current tab page. 3928 * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE. 3929 * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE. 3930 */ 3931 static void 3932 enter_tabpage(tp, old_curbuf, trigger_enter_autocmds, trigger_leave_autocmds) 3933 tabpage_T *tp; 3934 buf_T *old_curbuf UNUSED; 3935 int trigger_enter_autocmds UNUSED; 3936 int trigger_leave_autocmds UNUSED; 3937 { 3938 int old_off = tp->tp_firstwin->w_winrow; 3939 win_T *next_prevwin = tp->tp_prevwin; 3940 3941 curtab = tp; 3942 firstwin = tp->tp_firstwin; 3943 lastwin = tp->tp_lastwin; 3944 topframe = tp->tp_topframe; 3945 3946 /* We would like doing the TabEnter event first, but we don't have a 3947 * valid current window yet, which may break some commands. 3948 * This triggers autocommands, thus may make "tp" invalid. */ 3949 win_enter_ext(tp->tp_curwin, FALSE, TRUE, 3950 trigger_enter_autocmds, trigger_leave_autocmds); 3951 prevwin = next_prevwin; 3952 3953 last_status(FALSE); /* status line may appear or disappear */ 3954 (void)win_comp_pos(); /* recompute w_winrow for all windows */ 3955 must_redraw = CLEAR; /* need to redraw everything */ 3956 #ifdef FEAT_DIFF 3957 diff_need_scrollbind = TRUE; 3958 #endif 3959 3960 /* The tabpage line may have appeared or disappeared, may need to resize 3961 * the frames for that. When the Vim window was resized need to update 3962 * frame sizes too. Use the stored value of p_ch, so that it can be 3963 * different for each tab page. */ 3964 p_ch = curtab->tp_ch_used; 3965 if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow 3966 #ifdef FEAT_GUI_TABLINE 3967 && !gui_use_tabline() 3968 #endif 3969 )) 3970 shell_new_rows(); 3971 #ifdef FEAT_VERTSPLIT 3972 if (curtab->tp_old_Columns != Columns && starting == 0) 3973 shell_new_columns(); /* update window widths */ 3974 #endif 3975 3976 #if defined(FEAT_GUI) 3977 /* When 'guioptions' includes 'L' or 'R' may have to remove or add 3978 * scrollbars. Have to update them anyway. */ 3979 gui_may_update_scrollbars(); 3980 #endif 3981 3982 #ifdef FEAT_AUTOCMD 3983 /* Apply autocommands after updating the display, when 'rows' and 3984 * 'columns' have been set correctly. */ 3985 if (trigger_enter_autocmds) 3986 { 3987 apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); 3988 if (old_curbuf != curbuf) 3989 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 3990 } 3991 #endif 3992 3993 redraw_all_later(CLEAR); 3994 } 3995 3996 /* 3997 * Go to tab page "n". For ":tab N" and "Ngt". 3998 * When "n" is 9999 go to the last tab page. 3999 */ 4000 void 4001 goto_tabpage(n) 4002 int n; 4003 { 4004 tabpage_T *tp; 4005 tabpage_T *ttp; 4006 int i; 4007 4008 if (text_locked()) 4009 { 4010 /* Not allowed when editing the command line. */ 4011 #ifdef FEAT_CMDWIN 4012 if (cmdwin_type != 0) 4013 EMSG(_(e_cmdwin)); 4014 else 4015 #endif 4016 EMSG(_(e_secure)); 4017 return; 4018 } 4019 4020 /* If there is only one it can't work. */ 4021 if (first_tabpage->tp_next == NULL) 4022 { 4023 if (n > 1) 4024 beep_flush(); 4025 return; 4026 } 4027 4028 if (n == 0) 4029 { 4030 /* No count, go to next tab page, wrap around end. */ 4031 if (curtab->tp_next == NULL) 4032 tp = first_tabpage; 4033 else 4034 tp = curtab->tp_next; 4035 } 4036 else if (n < 0) 4037 { 4038 /* "gT": go to previous tab page, wrap around end. "N gT" repeats 4039 * this N times. */ 4040 ttp = curtab; 4041 for (i = n; i < 0; ++i) 4042 { 4043 for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL; 4044 tp = tp->tp_next) 4045 ; 4046 ttp = tp; 4047 } 4048 } 4049 else if (n == 9999) 4050 { 4051 /* Go to last tab page. */ 4052 for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) 4053 ; 4054 } 4055 else 4056 { 4057 /* Go to tab page "n". */ 4058 tp = find_tabpage(n); 4059 if (tp == NULL) 4060 { 4061 beep_flush(); 4062 return; 4063 } 4064 } 4065 4066 goto_tabpage_tp(tp, TRUE, TRUE); 4067 4068 #ifdef FEAT_GUI_TABLINE 4069 if (gui_use_tabline()) 4070 gui_mch_set_curtab(tabpage_index(curtab)); 4071 #endif 4072 } 4073 4074 /* 4075 * Go to tabpage "tp". 4076 * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE. 4077 * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE. 4078 * Note: doesn't update the GUI tab. 4079 */ 4080 void 4081 goto_tabpage_tp(tp, trigger_enter_autocmds, trigger_leave_autocmds) 4082 tabpage_T *tp; 4083 int trigger_enter_autocmds; 4084 int trigger_leave_autocmds; 4085 { 4086 /* Don't repeat a message in another tab page. */ 4087 set_keep_msg(NULL, 0); 4088 4089 if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer, 4090 trigger_leave_autocmds) == OK) 4091 { 4092 if (valid_tabpage(tp)) 4093 enter_tabpage(tp, curbuf, trigger_enter_autocmds, 4094 trigger_leave_autocmds); 4095 else 4096 enter_tabpage(curtab, curbuf, trigger_enter_autocmds, 4097 trigger_leave_autocmds); 4098 } 4099 } 4100 4101 /* 4102 * Enter window "wp" in tab page "tp". 4103 * Also updates the GUI tab. 4104 */ 4105 void 4106 goto_tabpage_win(tp, wp) 4107 tabpage_T *tp; 4108 win_T *wp; 4109 { 4110 goto_tabpage_tp(tp, TRUE, TRUE); 4111 if (curtab == tp && win_valid(wp)) 4112 { 4113 win_enter(wp, TRUE); 4114 # ifdef FEAT_GUI_TABLINE 4115 if (gui_use_tabline()) 4116 gui_mch_set_curtab(tabpage_index(curtab)); 4117 # endif 4118 } 4119 } 4120 4121 /* 4122 * Move the current tab page to after tab page "nr". 4123 */ 4124 void 4125 tabpage_move(nr) 4126 int nr; 4127 { 4128 int n = 1; 4129 tabpage_T *tp, *tp_dst; 4130 4131 if (first_tabpage->tp_next == NULL) 4132 return; 4133 4134 for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) 4135 ++n; 4136 4137 if (tp == curtab || (nr > 0 && tp->tp_next != NULL 4138 && tp->tp_next == curtab)) 4139 return; 4140 4141 tp_dst = tp; 4142 4143 /* Remove the current tab page from the list of tab pages. */ 4144 if (curtab == first_tabpage) 4145 first_tabpage = curtab->tp_next; 4146 else 4147 { 4148 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) 4149 if (tp->tp_next == curtab) 4150 break; 4151 if (tp == NULL) /* "cannot happen" */ 4152 return; 4153 tp->tp_next = curtab->tp_next; 4154 } 4155 4156 /* Re-insert it at the specified position. */ 4157 if (nr <= 0) 4158 { 4159 curtab->tp_next = first_tabpage; 4160 first_tabpage = curtab; 4161 } 4162 else 4163 { 4164 curtab->tp_next = tp_dst->tp_next; 4165 tp_dst->tp_next = curtab; 4166 } 4167 4168 /* Need to redraw the tabline. Tab page contents doesn't change. */ 4169 redraw_tabline = TRUE; 4170 } 4171 4172 4173 /* 4174 * Go to another window. 4175 * When jumping to another buffer, stop Visual mode. Do this before 4176 * changing windows so we can yank the selection into the '*' register. 4177 * When jumping to another window on the same buffer, adjust its cursor 4178 * position to keep the same Visual area. 4179 */ 4180 void 4181 win_goto(wp) 4182 win_T *wp; 4183 { 4184 #ifdef FEAT_CONCEAL 4185 win_T *owp = curwin; 4186 #endif 4187 4188 if (text_locked()) 4189 { 4190 beep_flush(); 4191 text_locked_msg(); 4192 return; 4193 } 4194 #ifdef FEAT_AUTOCMD 4195 if (curbuf_locked()) 4196 return; 4197 #endif 4198 4199 if (wp->w_buffer != curbuf) 4200 reset_VIsual_and_resel(); 4201 else if (VIsual_active) 4202 wp->w_cursor = curwin->w_cursor; 4203 4204 #ifdef FEAT_GUI 4205 need_mouse_correct = TRUE; 4206 #endif 4207 win_enter(wp, TRUE); 4208 4209 #ifdef FEAT_CONCEAL 4210 /* Conceal cursor line in previous window, unconceal in current window. */ 4211 if (win_valid(owp) && owp->w_p_cole > 0 && !msg_scrolled) 4212 update_single_line(owp, owp->w_cursor.lnum); 4213 if (curwin->w_p_cole > 0 && !msg_scrolled) 4214 need_cursor_line_redraw = TRUE; 4215 #endif 4216 } 4217 4218 #if defined(FEAT_PERL) || defined(PROTO) 4219 /* 4220 * Find window number "winnr" (counting top to bottom). 4221 */ 4222 win_T * 4223 win_find_nr(winnr) 4224 int winnr; 4225 { 4226 win_T *wp; 4227 4228 # ifdef FEAT_WINDOWS 4229 for (wp = firstwin; wp != NULL; wp = wp->w_next) 4230 if (--winnr == 0) 4231 break; 4232 return wp; 4233 # else 4234 return curwin; 4235 # endif 4236 } 4237 #endif 4238 4239 #if (defined(FEAT_WINDOWS) && (defined(FEAT_PYTHON) || defined(FEAT_PYTHON3))) \ 4240 || defined(PROTO) 4241 /* 4242 * Find the tabpage for window "win". 4243 */ 4244 tabpage_T * 4245 win_find_tabpage(win) 4246 win_T *win; 4247 { 4248 win_T *wp; 4249 tabpage_T *tp; 4250 4251 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) 4252 for (wp = (tp == curtab ? firstwin : tp->tp_firstwin); 4253 wp != NULL; wp = wp->w_next) 4254 if (wp == win) 4255 return tp; 4256 return NULL; 4257 } 4258 #endif 4259 4260 #ifdef FEAT_VERTSPLIT 4261 /* 4262 * Move to window above or below "count" times. 4263 */ 4264 static void 4265 win_goto_ver(up, count) 4266 int up; /* TRUE to go to win above */ 4267 long count; 4268 { 4269 frame_T *fr; 4270 frame_T *nfr; 4271 frame_T *foundfr; 4272 4273 foundfr = curwin->w_frame; 4274 while (count--) 4275 { 4276 /* 4277 * First go upwards in the tree of frames until we find a upwards or 4278 * downwards neighbor. 4279 */ 4280 fr = foundfr; 4281 for (;;) 4282 { 4283 if (fr == topframe) 4284 goto end; 4285 if (up) 4286 nfr = fr->fr_prev; 4287 else 4288 nfr = fr->fr_next; 4289 if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL) 4290 break; 4291 fr = fr->fr_parent; 4292 } 4293 4294 /* 4295 * Now go downwards to find the bottom or top frame in it. 4296 */ 4297 for (;;) 4298 { 4299 if (nfr->fr_layout == FR_LEAF) 4300 { 4301 foundfr = nfr; 4302 break; 4303 } 4304 fr = nfr->fr_child; 4305 if (nfr->fr_layout == FR_ROW) 4306 { 4307 /* Find the frame at the cursor row. */ 4308 while (fr->fr_next != NULL 4309 && frame2win(fr)->w_wincol + fr->fr_width 4310 <= curwin->w_wincol + curwin->w_wcol) 4311 fr = fr->fr_next; 4312 } 4313 if (nfr->fr_layout == FR_COL && up) 4314 while (fr->fr_next != NULL) 4315 fr = fr->fr_next; 4316 nfr = fr; 4317 } 4318 } 4319 end: 4320 if (foundfr != NULL) 4321 win_goto(foundfr->fr_win); 4322 } 4323 4324 /* 4325 * Move to left or right window. 4326 */ 4327 static void 4328 win_goto_hor(left, count) 4329 int left; /* TRUE to go to left win */ 4330 long count; 4331 { 4332 frame_T *fr; 4333 frame_T *nfr; 4334 frame_T *foundfr; 4335 4336 foundfr = curwin->w_frame; 4337 while (count--) 4338 { 4339 /* 4340 * First go upwards in the tree of frames until we find a left or 4341 * right neighbor. 4342 */ 4343 fr = foundfr; 4344 for (;;) 4345 { 4346 if (fr == topframe) 4347 goto end; 4348 if (left) 4349 nfr = fr->fr_prev; 4350 else 4351 nfr = fr->fr_next; 4352 if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL) 4353 break; 4354 fr = fr->fr_parent; 4355 } 4356 4357 /* 4358 * Now go downwards to find the leftmost or rightmost frame in it. 4359 */ 4360 for (;;) 4361 { 4362 if (nfr->fr_layout == FR_LEAF) 4363 { 4364 foundfr = nfr; 4365 break; 4366 } 4367 fr = nfr->fr_child; 4368 if (nfr->fr_layout == FR_COL) 4369 { 4370 /* Find the frame at the cursor row. */ 4371 while (fr->fr_next != NULL 4372 && frame2win(fr)->w_winrow + fr->fr_height 4373 <= curwin->w_winrow + curwin->w_wrow) 4374 fr = fr->fr_next; 4375 } 4376 if (nfr->fr_layout == FR_ROW && left) 4377 while (fr->fr_next != NULL) 4378 fr = fr->fr_next; 4379 nfr = fr; 4380 } 4381 } 4382 end: 4383 if (foundfr != NULL) 4384 win_goto(foundfr->fr_win); 4385 } 4386 #endif 4387 4388 /* 4389 * Make window "wp" the current window. 4390 */ 4391 void 4392 win_enter(wp, undo_sync) 4393 win_T *wp; 4394 int undo_sync; 4395 { 4396 win_enter_ext(wp, undo_sync, FALSE, TRUE, TRUE); 4397 } 4398 4399 /* 4400 * Make window wp the current window. 4401 * Can be called with "curwin_invalid" TRUE, which means that curwin has just 4402 * been closed and isn't valid. 4403 */ 4404 static void 4405 win_enter_ext(wp, undo_sync, curwin_invalid, trigger_enter_autocmds, trigger_leave_autocmds) 4406 win_T *wp; 4407 int undo_sync; 4408 int curwin_invalid; 4409 int trigger_enter_autocmds UNUSED; 4410 int trigger_leave_autocmds UNUSED; 4411 { 4412 #ifdef FEAT_AUTOCMD 4413 int other_buffer = FALSE; 4414 #endif 4415 4416 if (wp == curwin && !curwin_invalid) /* nothing to do */ 4417 return; 4418 4419 #ifdef FEAT_AUTOCMD 4420 if (!curwin_invalid && trigger_leave_autocmds) 4421 { 4422 /* 4423 * Be careful: If autocommands delete the window, return now. 4424 */ 4425 if (wp->w_buffer != curbuf) 4426 { 4427 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); 4428 other_buffer = TRUE; 4429 if (!win_valid(wp)) 4430 return; 4431 } 4432 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); 4433 if (!win_valid(wp)) 4434 return; 4435 # ifdef FEAT_EVAL 4436 /* autocmds may abort script processing */ 4437 if (aborting()) 4438 return; 4439 # endif 4440 } 4441 #endif 4442 4443 /* sync undo before leaving the current buffer */ 4444 if (undo_sync && curbuf != wp->w_buffer) 4445 u_sync(FALSE); 4446 4447 /* Might need to scroll the old window before switching, e.g., when the 4448 * cursor was moved. */ 4449 update_topline(); 4450 4451 /* may have to copy the buffer options when 'cpo' contains 'S' */ 4452 if (wp->w_buffer != curbuf) 4453 buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP); 4454 if (!curwin_invalid) 4455 { 4456 prevwin = curwin; /* remember for CTRL-W p */ 4457 curwin->w_redr_status = TRUE; 4458 } 4459 curwin = wp; 4460 curbuf = wp->w_buffer; 4461 check_cursor(); 4462 #ifdef FEAT_VIRTUALEDIT 4463 if (!virtual_active()) 4464 curwin->w_cursor.coladd = 0; 4465 #endif 4466 changed_line_abv_curs(); /* assume cursor position needs updating */ 4467 4468 if (curwin->w_localdir != NULL) 4469 { 4470 /* Window has a local directory: Save current directory as global 4471 * directory (unless that was done already) and change to the local 4472 * directory. */ 4473 if (globaldir == NULL) 4474 { 4475 char_u cwd[MAXPATHL]; 4476 4477 if (mch_dirname(cwd, MAXPATHL) == OK) 4478 globaldir = vim_strsave(cwd); 4479 } 4480 if (mch_chdir((char *)curwin->w_localdir) == 0) 4481 shorten_fnames(TRUE); 4482 } 4483 else if (globaldir != NULL) 4484 { 4485 /* Window doesn't have a local directory and we are not in the global 4486 * directory: Change to the global directory. */ 4487 ignored = mch_chdir((char *)globaldir); 4488 vim_free(globaldir); 4489 globaldir = NULL; 4490 shorten_fnames(TRUE); 4491 } 4492 4493 #ifdef FEAT_AUTOCMD 4494 if (trigger_enter_autocmds) 4495 { 4496 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); 4497 if (other_buffer) 4498 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 4499 } 4500 #endif 4501 4502 #ifdef FEAT_TITLE 4503 maketitle(); 4504 #endif 4505 curwin->w_redr_status = TRUE; 4506 redraw_tabline = TRUE; 4507 if (restart_edit) 4508 redraw_later(VALID); /* causes status line redraw */ 4509 4510 /* set window height to desired minimal value */ 4511 if (curwin->w_height < p_wh && !curwin->w_p_wfh) 4512 win_setheight((int)p_wh); 4513 else if (curwin->w_height == 0) 4514 win_setheight(1); 4515 4516 #ifdef FEAT_VERTSPLIT 4517 /* set window width to desired minimal value */ 4518 if (curwin->w_width < p_wiw && !curwin->w_p_wfw) 4519 win_setwidth((int)p_wiw); 4520 #endif 4521 4522 #ifdef FEAT_MOUSE 4523 setmouse(); /* in case jumped to/from help buffer */ 4524 #endif 4525 4526 /* Change directories when the 'acd' option is set. */ 4527 DO_AUTOCHDIR 4528 } 4529 4530 #endif /* FEAT_WINDOWS */ 4531 4532 #if defined(FEAT_WINDOWS) || defined(FEAT_SIGNS) || defined(PROTO) 4533 /* 4534 * Jump to the first open window that contains buffer "buf", if one exists. 4535 * Returns a pointer to the window found, otherwise NULL. 4536 */ 4537 win_T * 4538 buf_jump_open_win(buf) 4539 buf_T *buf; 4540 { 4541 win_T *wp = NULL; 4542 4543 if (curwin->w_buffer == buf) 4544 wp = curwin; 4545 # ifdef FEAT_WINDOWS 4546 else 4547 for (wp = firstwin; wp != NULL; wp = wp->w_next) 4548 if (wp->w_buffer == buf) 4549 break; 4550 if (wp != NULL) 4551 win_enter(wp, FALSE); 4552 # endif 4553 return wp; 4554 } 4555 4556 /* 4557 * Jump to the first open window in any tab page that contains buffer "buf", 4558 * if one exists. 4559 * Returns a pointer to the window found, otherwise NULL. 4560 */ 4561 win_T * 4562 buf_jump_open_tab(buf) 4563 buf_T *buf; 4564 { 4565 win_T *wp = buf_jump_open_win(buf); 4566 # ifdef FEAT_WINDOWS 4567 tabpage_T *tp; 4568 4569 if (wp != NULL) 4570 return wp; 4571 4572 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) 4573 if (tp != curtab) 4574 { 4575 for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) 4576 if (wp->w_buffer == buf) 4577 break; 4578 if (wp != NULL) 4579 { 4580 goto_tabpage_win(tp, wp); 4581 if (curwin != wp) 4582 wp = NULL; /* something went wrong */ 4583 break; 4584 } 4585 } 4586 # endif 4587 return wp; 4588 } 4589 #endif 4590 4591 /* 4592 * Allocate a window structure and link it in the window list when "hidden" is 4593 * FALSE. 4594 */ 4595 static win_T * 4596 win_alloc(after, hidden) 4597 win_T *after UNUSED; 4598 int hidden UNUSED; 4599 { 4600 win_T *new_wp; 4601 4602 /* 4603 * allocate window structure and linesizes arrays 4604 */ 4605 new_wp = (win_T *)alloc_clear((unsigned)sizeof(win_T)); 4606 if (new_wp == NULL) 4607 return NULL; 4608 4609 if (win_alloc_lines(new_wp) == FAIL) 4610 { 4611 vim_free(new_wp); 4612 return NULL; 4613 } 4614 4615 #ifdef FEAT_EVAL 4616 /* init w: variables */ 4617 new_wp->w_vars = dict_alloc(); 4618 if (new_wp->w_vars == NULL) 4619 { 4620 win_free_lsize(new_wp); 4621 vim_free(new_wp); 4622 return NULL; 4623 } 4624 init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); 4625 #endif 4626 4627 #ifdef FEAT_AUTOCMD 4628 /* Don't execute autocommands while the window is not properly 4629 * initialized yet. gui_create_scrollbar() may trigger a FocusGained 4630 * event. */ 4631 block_autocmds(); 4632 #endif 4633 /* 4634 * link the window in the window list 4635 */ 4636 #ifdef FEAT_WINDOWS 4637 if (!hidden) 4638 win_append(after, new_wp); 4639 #endif 4640 #ifdef FEAT_VERTSPLIT 4641 new_wp->w_wincol = 0; 4642 new_wp->w_width = Columns; 4643 #endif 4644 4645 /* position the display and the cursor at the top of the file. */ 4646 new_wp->w_topline = 1; 4647 #ifdef FEAT_DIFF 4648 new_wp->w_topfill = 0; 4649 #endif 4650 new_wp->w_botline = 2; 4651 new_wp->w_cursor.lnum = 1; 4652 #ifdef FEAT_SCROLLBIND 4653 new_wp->w_scbind_pos = 1; 4654 #endif 4655 4656 /* We won't calculate w_fraction until resizing the window */ 4657 new_wp->w_fraction = 0; 4658 new_wp->w_prev_fraction_row = -1; 4659 4660 #ifdef FEAT_GUI 4661 if (gui.in_use) 4662 { 4663 gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_LEFT], 4664 SBAR_LEFT, new_wp); 4665 gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_RIGHT], 4666 SBAR_RIGHT, new_wp); 4667 } 4668 #endif 4669 #ifdef FEAT_FOLDING 4670 foldInitWin(new_wp); 4671 #endif 4672 #ifdef FEAT_AUTOCMD 4673 unblock_autocmds(); 4674 #endif 4675 #ifdef FEAT_SEARCH_EXTRA 4676 new_wp->w_match_head = NULL; 4677 new_wp->w_next_match_id = 4; 4678 #endif 4679 return new_wp; 4680 } 4681 4682 #if defined(FEAT_WINDOWS) || defined(PROTO) 4683 4684 /* 4685 * Remove window 'wp' from the window list and free the structure. 4686 */ 4687 static void 4688 win_free(wp, tp) 4689 win_T *wp; 4690 tabpage_T *tp; /* tab page "win" is in, NULL for current */ 4691 { 4692 int i; 4693 buf_T *buf; 4694 wininfo_T *wip; 4695 4696 #ifdef FEAT_FOLDING 4697 clearFolding(wp); 4698 #endif 4699 4700 /* reduce the reference count to the argument list. */ 4701 alist_unlink(wp->w_alist); 4702 4703 #ifdef FEAT_AUTOCMD 4704 /* Don't execute autocommands while the window is halfway being deleted. 4705 * gui_mch_destroy_scrollbar() may trigger a FocusGained event. */ 4706 block_autocmds(); 4707 #endif 4708 4709 #ifdef FEAT_LUA 4710 lua_window_free(wp); 4711 #endif 4712 4713 #ifdef FEAT_MZSCHEME 4714 mzscheme_window_free(wp); 4715 #endif 4716 4717 #ifdef FEAT_PERL 4718 perl_win_free(wp); 4719 #endif 4720 4721 #ifdef FEAT_PYTHON 4722 python_window_free(wp); 4723 #endif 4724 4725 #ifdef FEAT_PYTHON3 4726 python3_window_free(wp); 4727 #endif 4728 4729 #ifdef FEAT_TCL 4730 tcl_window_free(wp); 4731 #endif 4732 4733 #ifdef FEAT_RUBY 4734 ruby_window_free(wp); 4735 #endif 4736 4737 clear_winopt(&wp->w_onebuf_opt); 4738 clear_winopt(&wp->w_allbuf_opt); 4739 4740 #ifdef FEAT_EVAL 4741 vars_clear(&wp->w_vars->dv_hashtab); /* free all w: variables */ 4742 hash_init(&wp->w_vars->dv_hashtab); 4743 unref_var_dict(wp->w_vars); 4744 #endif 4745 4746 if (prevwin == wp) 4747 prevwin = NULL; 4748 win_free_lsize(wp); 4749 4750 for (i = 0; i < wp->w_tagstacklen; ++i) 4751 vim_free(wp->w_tagstack[i].tagname); 4752 4753 vim_free(wp->w_localdir); 4754 4755 /* Remove the window from the b_wininfo lists, it may happen that the 4756 * freed memory is re-used for another window. */ 4757 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 4758 for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) 4759 if (wip->wi_win == wp) 4760 wip->wi_win = NULL; 4761 4762 #ifdef FEAT_SEARCH_EXTRA 4763 clear_matches(wp); 4764 #endif 4765 4766 #ifdef FEAT_JUMPLIST 4767 free_jumplist(wp); 4768 #endif 4769 4770 #ifdef FEAT_QUICKFIX 4771 qf_free_all(wp); 4772 #endif 4773 4774 #ifdef FEAT_GUI 4775 if (gui.in_use) 4776 { 4777 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]); 4778 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]); 4779 } 4780 #endif /* FEAT_GUI */ 4781 4782 #ifdef FEAT_SYN_HL 4783 vim_free(wp->w_p_cc_cols); 4784 #endif 4785 4786 #ifdef FEAT_AUTOCMD 4787 if (wp != aucmd_win) 4788 #endif 4789 win_remove(wp, tp); 4790 #ifdef FEAT_AUTOCMD 4791 if (autocmd_busy) 4792 { 4793 wp->w_next = au_pending_free_win; 4794 au_pending_free_win = wp; 4795 } 4796 else 4797 #endif 4798 vim_free(wp); 4799 4800 #ifdef FEAT_AUTOCMD 4801 unblock_autocmds(); 4802 #endif 4803 } 4804 4805 /* 4806 * Append window "wp" in the window list after window "after". 4807 */ 4808 void 4809 win_append(after, wp) 4810 win_T *after, *wp; 4811 { 4812 win_T *before; 4813 4814 if (after == NULL) /* after NULL is in front of the first */ 4815 before = firstwin; 4816 else 4817 before = after->w_next; 4818 4819 wp->w_next = before; 4820 wp->w_prev = after; 4821 if (after == NULL) 4822 firstwin = wp; 4823 else 4824 after->w_next = wp; 4825 if (before == NULL) 4826 lastwin = wp; 4827 else 4828 before->w_prev = wp; 4829 } 4830 4831 /* 4832 * Remove a window from the window list. 4833 */ 4834 void 4835 win_remove(wp, tp) 4836 win_T *wp; 4837 tabpage_T *tp; /* tab page "win" is in, NULL for current */ 4838 { 4839 if (wp->w_prev != NULL) 4840 wp->w_prev->w_next = wp->w_next; 4841 else if (tp == NULL) 4842 firstwin = wp->w_next; 4843 else 4844 tp->tp_firstwin = wp->w_next; 4845 if (wp->w_next != NULL) 4846 wp->w_next->w_prev = wp->w_prev; 4847 else if (tp == NULL) 4848 lastwin = wp->w_prev; 4849 else 4850 tp->tp_lastwin = wp->w_prev; 4851 } 4852 4853 /* 4854 * Append frame "frp" in a frame list after frame "after". 4855 */ 4856 static void 4857 frame_append(after, frp) 4858 frame_T *after, *frp; 4859 { 4860 frp->fr_next = after->fr_next; 4861 after->fr_next = frp; 4862 if (frp->fr_next != NULL) 4863 frp->fr_next->fr_prev = frp; 4864 frp->fr_prev = after; 4865 } 4866 4867 /* 4868 * Insert frame "frp" in a frame list before frame "before". 4869 */ 4870 static void 4871 frame_insert(before, frp) 4872 frame_T *before, *frp; 4873 { 4874 frp->fr_next = before; 4875 frp->fr_prev = before->fr_prev; 4876 before->fr_prev = frp; 4877 if (frp->fr_prev != NULL) 4878 frp->fr_prev->fr_next = frp; 4879 else 4880 frp->fr_parent->fr_child = frp; 4881 } 4882 4883 /* 4884 * Remove a frame from a frame list. 4885 */ 4886 static void 4887 frame_remove(frp) 4888 frame_T *frp; 4889 { 4890 if (frp->fr_prev != NULL) 4891 frp->fr_prev->fr_next = frp->fr_next; 4892 else 4893 frp->fr_parent->fr_child = frp->fr_next; 4894 if (frp->fr_next != NULL) 4895 frp->fr_next->fr_prev = frp->fr_prev; 4896 } 4897 4898 #endif /* FEAT_WINDOWS */ 4899 4900 /* 4901 * Allocate w_lines[] for window "wp". 4902 * Return FAIL for failure, OK for success. 4903 */ 4904 int 4905 win_alloc_lines(wp) 4906 win_T *wp; 4907 { 4908 wp->w_lines_valid = 0; 4909 wp->w_lines = (wline_T *)alloc_clear((unsigned)(Rows * sizeof(wline_T))); 4910 if (wp->w_lines == NULL) 4911 return FAIL; 4912 return OK; 4913 } 4914 4915 /* 4916 * free lsize arrays for a window 4917 */ 4918 void 4919 win_free_lsize(wp) 4920 win_T *wp; 4921 { 4922 /* TODO: why would wp be NULL here? */ 4923 if (wp != NULL) 4924 { 4925 vim_free(wp->w_lines); 4926 wp->w_lines = NULL; 4927 } 4928 } 4929 4930 /* 4931 * Called from win_new_shellsize() after Rows changed. 4932 * This only does the current tab page, others must be done when made active. 4933 */ 4934 void 4935 shell_new_rows() 4936 { 4937 int h = (int)ROWS_AVAIL; 4938 4939 if (firstwin == NULL) /* not initialized yet */ 4940 return; 4941 #ifdef FEAT_WINDOWS 4942 if (h < frame_minheight(topframe, NULL)) 4943 h = frame_minheight(topframe, NULL); 4944 4945 /* First try setting the heights of windows with 'winfixheight'. If 4946 * that doesn't result in the right height, forget about that option. */ 4947 frame_new_height(topframe, h, FALSE, TRUE); 4948 if (!frame_check_height(topframe, h)) 4949 frame_new_height(topframe, h, FALSE, FALSE); 4950 4951 (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ 4952 #else 4953 if (h < 1) 4954 h = 1; 4955 win_new_height(firstwin, h); 4956 #endif 4957 compute_cmdrow(); 4958 #ifdef FEAT_WINDOWS 4959 curtab->tp_ch_used = p_ch; 4960 #endif 4961 4962 #if 0 4963 /* Disabled: don't want making the screen smaller make a window larger. */ 4964 if (p_ea) 4965 win_equal(curwin, FALSE, 'v'); 4966 #endif 4967 } 4968 4969 #if defined(FEAT_VERTSPLIT) || defined(PROTO) 4970 /* 4971 * Called from win_new_shellsize() after Columns changed. 4972 */ 4973 void 4974 shell_new_columns() 4975 { 4976 if (firstwin == NULL) /* not initialized yet */ 4977 return; 4978 4979 /* First try setting the widths of windows with 'winfixwidth'. If that 4980 * doesn't result in the right width, forget about that option. */ 4981 frame_new_width(topframe, (int)Columns, FALSE, TRUE); 4982 if (!frame_check_width(topframe, Columns)) 4983 frame_new_width(topframe, (int)Columns, FALSE, FALSE); 4984 4985 (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ 4986 #if 0 4987 /* Disabled: don't want making the screen smaller make a window larger. */ 4988 if (p_ea) 4989 win_equal(curwin, FALSE, 'h'); 4990 #endif 4991 } 4992 #endif 4993 4994 #if defined(FEAT_CMDWIN) || defined(PROTO) 4995 /* 4996 * Save the size of all windows in "gap". 4997 */ 4998 void 4999 win_size_save(gap) 5000 garray_T *gap; 5001 5002 { 5003 win_T *wp; 5004 5005 ga_init2(gap, (int)sizeof(int), 1); 5006 if (ga_grow(gap, win_count() * 2) == OK) 5007 for (wp = firstwin; wp != NULL; wp = wp->w_next) 5008 { 5009 ((int *)gap->ga_data)[gap->ga_len++] = 5010 wp->w_width + wp->w_vsep_width; 5011 ((int *)gap->ga_data)[gap->ga_len++] = wp->w_height; 5012 } 5013 } 5014 5015 /* 5016 * Restore window sizes, but only if the number of windows is still the same. 5017 * Does not free the growarray. 5018 */ 5019 void 5020 win_size_restore(gap) 5021 garray_T *gap; 5022 { 5023 win_T *wp; 5024 int i, j; 5025 5026 if (win_count() * 2 == gap->ga_len) 5027 { 5028 /* The order matters, because frames contain other frames, but it's 5029 * difficult to get right. The easy way out is to do it twice. */ 5030 for (j = 0; j < 2; ++j) 5031 { 5032 i = 0; 5033 for (wp = firstwin; wp != NULL; wp = wp->w_next) 5034 { 5035 frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]); 5036 win_setheight_win(((int *)gap->ga_data)[i++], wp); 5037 } 5038 } 5039 /* recompute the window positions */ 5040 (void)win_comp_pos(); 5041 } 5042 } 5043 #endif /* FEAT_CMDWIN */ 5044 5045 #if defined(FEAT_WINDOWS) || defined(PROTO) 5046 /* 5047 * Update the position for all windows, using the width and height of the 5048 * frames. 5049 * Returns the row just after the last window. 5050 */ 5051 int 5052 win_comp_pos() 5053 { 5054 int row = tabline_height(); 5055 int col = 0; 5056 5057 frame_comp_pos(topframe, &row, &col); 5058 return row; 5059 } 5060 5061 /* 5062 * Update the position of the windows in frame "topfrp", using the width and 5063 * height of the frames. 5064 * "*row" and "*col" are the top-left position of the frame. They are updated 5065 * to the bottom-right position plus one. 5066 */ 5067 static void 5068 frame_comp_pos(topfrp, row, col) 5069 frame_T *topfrp; 5070 int *row; 5071 int *col; 5072 { 5073 win_T *wp; 5074 frame_T *frp; 5075 #ifdef FEAT_VERTSPLIT 5076 int startcol; 5077 int startrow; 5078 #endif 5079 5080 wp = topfrp->fr_win; 5081 if (wp != NULL) 5082 { 5083 if (wp->w_winrow != *row 5084 #ifdef FEAT_VERTSPLIT 5085 || wp->w_wincol != *col 5086 #endif 5087 ) 5088 { 5089 /* position changed, redraw */ 5090 wp->w_winrow = *row; 5091 #ifdef FEAT_VERTSPLIT 5092 wp->w_wincol = *col; 5093 #endif 5094 redraw_win_later(wp, NOT_VALID); 5095 wp->w_redr_status = TRUE; 5096 } 5097 *row += wp->w_height + wp->w_status_height; 5098 #ifdef FEAT_VERTSPLIT 5099 *col += wp->w_width + wp->w_vsep_width; 5100 #endif 5101 } 5102 else 5103 { 5104 #ifdef FEAT_VERTSPLIT 5105 startrow = *row; 5106 startcol = *col; 5107 #endif 5108 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 5109 { 5110 #ifdef FEAT_VERTSPLIT 5111 if (topfrp->fr_layout == FR_ROW) 5112 *row = startrow; /* all frames are at the same row */ 5113 else 5114 *col = startcol; /* all frames are at the same col */ 5115 #endif 5116 frame_comp_pos(frp, row, col); 5117 } 5118 } 5119 } 5120 5121 #endif /* FEAT_WINDOWS */ 5122 5123 /* 5124 * Set current window height and take care of repositioning other windows to 5125 * fit around it. 5126 */ 5127 void 5128 win_setheight(height) 5129 int height; 5130 { 5131 win_setheight_win(height, curwin); 5132 } 5133 5134 /* 5135 * Set the window height of window "win" and take care of repositioning other 5136 * windows to fit around it. 5137 */ 5138 void 5139 win_setheight_win(height, win) 5140 int height; 5141 win_T *win; 5142 { 5143 int row; 5144 5145 if (win == curwin) 5146 { 5147 /* Always keep current window at least one line high, even when 5148 * 'winminheight' is zero. */ 5149 #ifdef FEAT_WINDOWS 5150 if (height < p_wmh) 5151 height = p_wmh; 5152 #endif 5153 if (height == 0) 5154 height = 1; 5155 } 5156 5157 #ifdef FEAT_WINDOWS 5158 frame_setheight(win->w_frame, height + win->w_status_height); 5159 5160 /* recompute the window positions */ 5161 row = win_comp_pos(); 5162 #else 5163 if (height > topframe->fr_height) 5164 height = topframe->fr_height; 5165 win->w_height = height; 5166 row = height; 5167 #endif 5168 5169 /* 5170 * If there is extra space created between the last window and the command 5171 * line, clear it. 5172 */ 5173 if (full_screen && msg_scrolled == 0 && row < cmdline_row) 5174 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); 5175 cmdline_row = row; 5176 msg_row = row; 5177 msg_col = 0; 5178 5179 redraw_all_later(NOT_VALID); 5180 } 5181 5182 #if defined(FEAT_WINDOWS) || defined(PROTO) 5183 5184 /* 5185 * Set the height of a frame to "height" and take care that all frames and 5186 * windows inside it are resized. Also resize frames on the left and right if 5187 * the are in the same FR_ROW frame. 5188 * 5189 * Strategy: 5190 * If the frame is part of a FR_COL frame, try fitting the frame in that 5191 * frame. If that doesn't work (the FR_COL frame is too small), recursively 5192 * go to containing frames to resize them and make room. 5193 * If the frame is part of a FR_ROW frame, all frames must be resized as well. 5194 * Check for the minimal height of the FR_ROW frame. 5195 * At the top level we can also use change the command line height. 5196 */ 5197 static void 5198 frame_setheight(curfrp, height) 5199 frame_T *curfrp; 5200 int height; 5201 { 5202 int room; /* total number of lines available */ 5203 int take; /* number of lines taken from other windows */ 5204 int room_cmdline; /* lines available from cmdline */ 5205 int run; 5206 frame_T *frp; 5207 int h; 5208 int room_reserved; 5209 5210 /* If the height already is the desired value, nothing to do. */ 5211 if (curfrp->fr_height == height) 5212 return; 5213 5214 if (curfrp->fr_parent == NULL) 5215 { 5216 /* topframe: can only change the command line */ 5217 if (height > ROWS_AVAIL) 5218 height = ROWS_AVAIL; 5219 if (height > 0) 5220 frame_new_height(curfrp, height, FALSE, FALSE); 5221 } 5222 else if (curfrp->fr_parent->fr_layout == FR_ROW) 5223 { 5224 /* Row of frames: Also need to resize frames left and right of this 5225 * one. First check for the minimal height of these. */ 5226 h = frame_minheight(curfrp->fr_parent, NULL); 5227 if (height < h) 5228 height = h; 5229 frame_setheight(curfrp->fr_parent, height); 5230 } 5231 else 5232 { 5233 /* 5234 * Column of frames: try to change only frames in this column. 5235 */ 5236 #ifdef FEAT_VERTSPLIT 5237 /* 5238 * Do this twice: 5239 * 1: compute room available, if it's not enough try resizing the 5240 * containing frame. 5241 * 2: compute the room available and adjust the height to it. 5242 * Try not to reduce the height of a window with 'winfixheight' set. 5243 */ 5244 for (run = 1; run <= 2; ++run) 5245 #else 5246 for (;;) 5247 #endif 5248 { 5249 room = 0; 5250 room_reserved = 0; 5251 for (frp = curfrp->fr_parent->fr_child; frp != NULL; 5252 frp = frp->fr_next) 5253 { 5254 if (frp != curfrp 5255 && frp->fr_win != NULL 5256 && frp->fr_win->w_p_wfh) 5257 room_reserved += frp->fr_height; 5258 room += frp->fr_height; 5259 if (frp != curfrp) 5260 room -= frame_minheight(frp, NULL); 5261 } 5262 #ifdef FEAT_VERTSPLIT 5263 if (curfrp->fr_width != Columns) 5264 room_cmdline = 0; 5265 else 5266 #endif 5267 { 5268 room_cmdline = Rows - p_ch - (lastwin->w_winrow 5269 + lastwin->w_height + lastwin->w_status_height); 5270 if (room_cmdline < 0) 5271 room_cmdline = 0; 5272 } 5273 5274 if (height <= room + room_cmdline) 5275 break; 5276 #ifdef FEAT_VERTSPLIT 5277 if (run == 2 || curfrp->fr_width == Columns) 5278 #endif 5279 { 5280 if (height > room + room_cmdline) 5281 height = room + room_cmdline; 5282 break; 5283 } 5284 #ifdef FEAT_VERTSPLIT 5285 frame_setheight(curfrp->fr_parent, height 5286 + frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1); 5287 #endif 5288 /*NOTREACHED*/ 5289 } 5290 5291 /* 5292 * Compute the number of lines we will take from others frames (can be 5293 * negative!). 5294 */ 5295 take = height - curfrp->fr_height; 5296 5297 /* If there is not enough room, also reduce the height of a window 5298 * with 'winfixheight' set. */ 5299 if (height > room + room_cmdline - room_reserved) 5300 room_reserved = room + room_cmdline - height; 5301 /* If there is only a 'winfixheight' window and making the 5302 * window smaller, need to make the other window taller. */ 5303 if (take < 0 && room - curfrp->fr_height < room_reserved) 5304 room_reserved = 0; 5305 5306 if (take > 0 && room_cmdline > 0) 5307 { 5308 /* use lines from cmdline first */ 5309 if (take < room_cmdline) 5310 room_cmdline = take; 5311 take -= room_cmdline; 5312 topframe->fr_height += room_cmdline; 5313 } 5314 5315 /* 5316 * set the current frame to the new height 5317 */ 5318 frame_new_height(curfrp, height, FALSE, FALSE); 5319 5320 /* 5321 * First take lines from the frames after the current frame. If 5322 * that is not enough, takes lines from frames above the current 5323 * frame. 5324 */ 5325 for (run = 0; run < 2; ++run) 5326 { 5327 if (run == 0) 5328 frp = curfrp->fr_next; /* 1st run: start with next window */ 5329 else 5330 frp = curfrp->fr_prev; /* 2nd run: start with prev window */ 5331 while (frp != NULL && take != 0) 5332 { 5333 h = frame_minheight(frp, NULL); 5334 if (room_reserved > 0 5335 && frp->fr_win != NULL 5336 && frp->fr_win->w_p_wfh) 5337 { 5338 if (room_reserved >= frp->fr_height) 5339 room_reserved -= frp->fr_height; 5340 else 5341 { 5342 if (frp->fr_height - room_reserved > take) 5343 room_reserved = frp->fr_height - take; 5344 take -= frp->fr_height - room_reserved; 5345 frame_new_height(frp, room_reserved, FALSE, FALSE); 5346 room_reserved = 0; 5347 } 5348 } 5349 else 5350 { 5351 if (frp->fr_height - take < h) 5352 { 5353 take -= frp->fr_height - h; 5354 frame_new_height(frp, h, FALSE, FALSE); 5355 } 5356 else 5357 { 5358 frame_new_height(frp, frp->fr_height - take, 5359 FALSE, FALSE); 5360 take = 0; 5361 } 5362 } 5363 if (run == 0) 5364 frp = frp->fr_next; 5365 else 5366 frp = frp->fr_prev; 5367 } 5368 } 5369 } 5370 } 5371 5372 #if defined(FEAT_VERTSPLIT) || defined(PROTO) 5373 /* 5374 * Set current window width and take care of repositioning other windows to 5375 * fit around it. 5376 */ 5377 void 5378 win_setwidth(width) 5379 int width; 5380 { 5381 win_setwidth_win(width, curwin); 5382 } 5383 5384 void 5385 win_setwidth_win(width, wp) 5386 int width; 5387 win_T *wp; 5388 { 5389 /* Always keep current window at least one column wide, even when 5390 * 'winminwidth' is zero. */ 5391 if (wp == curwin) 5392 { 5393 if (width < p_wmw) 5394 width = p_wmw; 5395 if (width == 0) 5396 width = 1; 5397 } 5398 5399 frame_setwidth(wp->w_frame, width + wp->w_vsep_width); 5400 5401 /* recompute the window positions */ 5402 (void)win_comp_pos(); 5403 5404 redraw_all_later(NOT_VALID); 5405 } 5406 5407 /* 5408 * Set the width of a frame to "width" and take care that all frames and 5409 * windows inside it are resized. Also resize frames above and below if the 5410 * are in the same FR_ROW frame. 5411 * 5412 * Strategy is similar to frame_setheight(). 5413 */ 5414 static void 5415 frame_setwidth(curfrp, width) 5416 frame_T *curfrp; 5417 int width; 5418 { 5419 int room; /* total number of lines available */ 5420 int take; /* number of lines taken from other windows */ 5421 int run; 5422 frame_T *frp; 5423 int w; 5424 int room_reserved; 5425 5426 /* If the width already is the desired value, nothing to do. */ 5427 if (curfrp->fr_width == width) 5428 return; 5429 5430 if (curfrp->fr_parent == NULL) 5431 /* topframe: can't change width */ 5432 return; 5433 5434 if (curfrp->fr_parent->fr_layout == FR_COL) 5435 { 5436 /* Column of frames: Also need to resize frames above and below of 5437 * this one. First check for the minimal width of these. */ 5438 w = frame_minwidth(curfrp->fr_parent, NULL); 5439 if (width < w) 5440 width = w; 5441 frame_setwidth(curfrp->fr_parent, width); 5442 } 5443 else 5444 { 5445 /* 5446 * Row of frames: try to change only frames in this row. 5447 * 5448 * Do this twice: 5449 * 1: compute room available, if it's not enough try resizing the 5450 * containing frame. 5451 * 2: compute the room available and adjust the width to it. 5452 */ 5453 for (run = 1; run <= 2; ++run) 5454 { 5455 room = 0; 5456 room_reserved = 0; 5457 for (frp = curfrp->fr_parent->fr_child; frp != NULL; 5458 frp = frp->fr_next) 5459 { 5460 if (frp != curfrp 5461 && frp->fr_win != NULL 5462 && frp->fr_win->w_p_wfw) 5463 room_reserved += frp->fr_width; 5464 room += frp->fr_width; 5465 if (frp != curfrp) 5466 room -= frame_minwidth(frp, NULL); 5467 } 5468 5469 if (width <= room) 5470 break; 5471 if (run == 2 || curfrp->fr_height >= ROWS_AVAIL) 5472 { 5473 if (width > room) 5474 width = room; 5475 break; 5476 } 5477 frame_setwidth(curfrp->fr_parent, width 5478 + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1); 5479 } 5480 5481 /* 5482 * Compute the number of lines we will take from others frames (can be 5483 * negative!). 5484 */ 5485 take = width - curfrp->fr_width; 5486 5487 /* If there is not enough room, also reduce the width of a window 5488 * with 'winfixwidth' set. */ 5489 if (width > room - room_reserved) 5490 room_reserved = room - width; 5491 /* If there is only a 'winfixwidth' window and making the 5492 * window smaller, need to make the other window narrower. */ 5493 if (take < 0 && room - curfrp->fr_width < room_reserved) 5494 room_reserved = 0; 5495 5496 /* 5497 * set the current frame to the new width 5498 */ 5499 frame_new_width(curfrp, width, FALSE, FALSE); 5500 5501 /* 5502 * First take lines from the frames right of the current frame. If 5503 * that is not enough, takes lines from frames left of the current 5504 * frame. 5505 */ 5506 for (run = 0; run < 2; ++run) 5507 { 5508 if (run == 0) 5509 frp = curfrp->fr_next; /* 1st run: start with next window */ 5510 else 5511 frp = curfrp->fr_prev; /* 2nd run: start with prev window */ 5512 while (frp != NULL && take != 0) 5513 { 5514 w = frame_minwidth(frp, NULL); 5515 if (room_reserved > 0 5516 && frp->fr_win != NULL 5517 && frp->fr_win->w_p_wfw) 5518 { 5519 if (room_reserved >= frp->fr_width) 5520 room_reserved -= frp->fr_width; 5521 else 5522 { 5523 if (frp->fr_width - room_reserved > take) 5524 room_reserved = frp->fr_width - take; 5525 take -= frp->fr_width - room_reserved; 5526 frame_new_width(frp, room_reserved, FALSE, FALSE); 5527 room_reserved = 0; 5528 } 5529 } 5530 else 5531 { 5532 if (frp->fr_width - take < w) 5533 { 5534 take -= frp->fr_width - w; 5535 frame_new_width(frp, w, FALSE, FALSE); 5536 } 5537 else 5538 { 5539 frame_new_width(frp, frp->fr_width - take, 5540 FALSE, FALSE); 5541 take = 0; 5542 } 5543 } 5544 if (run == 0) 5545 frp = frp->fr_next; 5546 else 5547 frp = frp->fr_prev; 5548 } 5549 } 5550 } 5551 } 5552 #endif /* FEAT_VERTSPLIT */ 5553 5554 /* 5555 * Check 'winminheight' for a valid value. 5556 */ 5557 void 5558 win_setminheight() 5559 { 5560 int room; 5561 int first = TRUE; 5562 win_T *wp; 5563 5564 /* loop until there is a 'winminheight' that is possible */ 5565 while (p_wmh > 0) 5566 { 5567 /* TODO: handle vertical splits */ 5568 room = -p_wh; 5569 for (wp = firstwin; wp != NULL; wp = wp->w_next) 5570 room += wp->w_height - p_wmh; 5571 if (room >= 0) 5572 break; 5573 --p_wmh; 5574 if (first) 5575 { 5576 EMSG(_(e_noroom)); 5577 first = FALSE; 5578 } 5579 } 5580 } 5581 5582 #if defined(FEAT_MOUSE) || defined(PROTO) 5583 5584 /* 5585 * Status line of dragwin is dragged "offset" lines down (negative is up). 5586 */ 5587 void 5588 win_drag_status_line(dragwin, offset) 5589 win_T *dragwin; 5590 int offset; 5591 { 5592 frame_T *curfr; 5593 frame_T *fr; 5594 int room; 5595 int row; 5596 int up; /* if TRUE, drag status line up, otherwise down */ 5597 int n; 5598 5599 fr = dragwin->w_frame; 5600 curfr = fr; 5601 if (fr != topframe) /* more than one window */ 5602 { 5603 fr = fr->fr_parent; 5604 /* When the parent frame is not a column of frames, its parent should 5605 * be. */ 5606 if (fr->fr_layout != FR_COL) 5607 { 5608 curfr = fr; 5609 if (fr != topframe) /* only a row of windows, may drag statusline */ 5610 fr = fr->fr_parent; 5611 } 5612 } 5613 5614 /* If this is the last frame in a column, may want to resize the parent 5615 * frame instead (go two up to skip a row of frames). */ 5616 while (curfr != topframe && curfr->fr_next == NULL) 5617 { 5618 if (fr != topframe) 5619 fr = fr->fr_parent; 5620 curfr = fr; 5621 if (fr != topframe) 5622 fr = fr->fr_parent; 5623 } 5624 5625 if (offset < 0) /* drag up */ 5626 { 5627 up = TRUE; 5628 offset = -offset; 5629 /* sum up the room of the current frame and above it */ 5630 if (fr == curfr) 5631 { 5632 /* only one window */ 5633 room = fr->fr_height - frame_minheight(fr, NULL); 5634 } 5635 else 5636 { 5637 room = 0; 5638 for (fr = fr->fr_child; ; fr = fr->fr_next) 5639 { 5640 room += fr->fr_height - frame_minheight(fr, NULL); 5641 if (fr == curfr) 5642 break; 5643 } 5644 } 5645 fr = curfr->fr_next; /* put fr at frame that grows */ 5646 } 5647 else /* drag down */ 5648 { 5649 up = FALSE; 5650 /* 5651 * Only dragging the last status line can reduce p_ch. 5652 */ 5653 room = Rows - cmdline_row; 5654 if (curfr->fr_next == NULL) 5655 room -= 1; 5656 else 5657 room -= p_ch; 5658 if (room < 0) 5659 room = 0; 5660 /* sum up the room of frames below of the current one */ 5661 for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) 5662 room += fr->fr_height - frame_minheight(fr, NULL); 5663 fr = curfr; /* put fr at window that grows */ 5664 } 5665 5666 if (room < offset) /* Not enough room */ 5667 offset = room; /* Move as far as we can */ 5668 if (offset <= 0) 5669 return; 5670 5671 /* 5672 * Grow frame fr by "offset" lines. 5673 * Doesn't happen when dragging the last status line up. 5674 */ 5675 if (fr != NULL) 5676 frame_new_height(fr, fr->fr_height + offset, up, FALSE); 5677 5678 if (up) 5679 fr = curfr; /* current frame gets smaller */ 5680 else 5681 fr = curfr->fr_next; /* next frame gets smaller */ 5682 5683 /* 5684 * Now make the other frames smaller. 5685 */ 5686 while (fr != NULL && offset > 0) 5687 { 5688 n = frame_minheight(fr, NULL); 5689 if (fr->fr_height - offset <= n) 5690 { 5691 offset -= fr->fr_height - n; 5692 frame_new_height(fr, n, !up, FALSE); 5693 } 5694 else 5695 { 5696 frame_new_height(fr, fr->fr_height - offset, !up, FALSE); 5697 break; 5698 } 5699 if (up) 5700 fr = fr->fr_prev; 5701 else 5702 fr = fr->fr_next; 5703 } 5704 row = win_comp_pos(); 5705 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); 5706 cmdline_row = row; 5707 p_ch = Rows - cmdline_row; 5708 if (p_ch < 1) 5709 p_ch = 1; 5710 curtab->tp_ch_used = p_ch; 5711 redraw_all_later(SOME_VALID); 5712 showmode(); 5713 } 5714 5715 # if defined(FEAT_VERTSPLIT) || defined(PROTO) 5716 /* 5717 * Separator line of dragwin is dragged "offset" lines right (negative is left). 5718 */ 5719 void 5720 win_drag_vsep_line(dragwin, offset) 5721 win_T *dragwin; 5722 int offset; 5723 { 5724 frame_T *curfr; 5725 frame_T *fr; 5726 int room; 5727 int left; /* if TRUE, drag separator line left, otherwise right */ 5728 int n; 5729 5730 fr = dragwin->w_frame; 5731 if (fr == topframe) /* only one window (cannot happen?) */ 5732 return; 5733 curfr = fr; 5734 fr = fr->fr_parent; 5735 /* When the parent frame is not a row of frames, its parent should be. */ 5736 if (fr->fr_layout != FR_ROW) 5737 { 5738 if (fr == topframe) /* only a column of windows (cannot happen?) */ 5739 return; 5740 curfr = fr; 5741 fr = fr->fr_parent; 5742 } 5743 5744 /* If this is the last frame in a row, may want to resize a parent 5745 * frame instead. */ 5746 while (curfr->fr_next == NULL) 5747 { 5748 if (fr == topframe) 5749 break; 5750 curfr = fr; 5751 fr = fr->fr_parent; 5752 if (fr != topframe) 5753 { 5754 curfr = fr; 5755 fr = fr->fr_parent; 5756 } 5757 } 5758 5759 if (offset < 0) /* drag left */ 5760 { 5761 left = TRUE; 5762 offset = -offset; 5763 /* sum up the room of the current frame and left of it */ 5764 room = 0; 5765 for (fr = fr->fr_child; ; fr = fr->fr_next) 5766 { 5767 room += fr->fr_width - frame_minwidth(fr, NULL); 5768 if (fr == curfr) 5769 break; 5770 } 5771 fr = curfr->fr_next; /* put fr at frame that grows */ 5772 } 5773 else /* drag right */ 5774 { 5775 left = FALSE; 5776 /* sum up the room of frames right of the current one */ 5777 room = 0; 5778 for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) 5779 room += fr->fr_width - frame_minwidth(fr, NULL); 5780 fr = curfr; /* put fr at window that grows */ 5781 } 5782 5783 if (room < offset) /* Not enough room */ 5784 offset = room; /* Move as far as we can */ 5785 if (offset <= 0) /* No room at all, quit. */ 5786 return; 5787 if (fr == NULL) 5788 return; /* Safety check, should not happen. */ 5789 5790 /* grow frame fr by offset lines */ 5791 frame_new_width(fr, fr->fr_width + offset, left, FALSE); 5792 5793 /* shrink other frames: current and at the left or at the right */ 5794 if (left) 5795 fr = curfr; /* current frame gets smaller */ 5796 else 5797 fr = curfr->fr_next; /* next frame gets smaller */ 5798 5799 while (fr != NULL && offset > 0) 5800 { 5801 n = frame_minwidth(fr, NULL); 5802 if (fr->fr_width - offset <= n) 5803 { 5804 offset -= fr->fr_width - n; 5805 frame_new_width(fr, n, !left, FALSE); 5806 } 5807 else 5808 { 5809 frame_new_width(fr, fr->fr_width - offset, !left, FALSE); 5810 break; 5811 } 5812 if (left) 5813 fr = fr->fr_prev; 5814 else 5815 fr = fr->fr_next; 5816 } 5817 (void)win_comp_pos(); 5818 redraw_all_later(NOT_VALID); 5819 } 5820 # endif /* FEAT_VERTSPLIT */ 5821 #endif /* FEAT_MOUSE */ 5822 5823 #endif /* FEAT_WINDOWS */ 5824 5825 #define FRACTION_MULT 16384L 5826 5827 /* 5828 * Set wp->w_fraction for the current w_wrow and w_height. 5829 */ 5830 void 5831 set_fraction(wp) 5832 win_T *wp; 5833 { 5834 wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT 5835 + wp->w_height / 2) / (long)wp->w_height; 5836 } 5837 5838 /* 5839 * Set the height of a window. 5840 * This takes care of the things inside the window, not what happens to the 5841 * window position, the frame or to other windows. 5842 */ 5843 void 5844 win_new_height(wp, height) 5845 win_T *wp; 5846 int height; 5847 { 5848 linenr_T lnum; 5849 int sline, line_size; 5850 int prev_height = wp->w_height; 5851 5852 /* Don't want a negative height. Happens when splitting a tiny window. 5853 * Will equalize heights soon to fix it. */ 5854 if (height < 0) 5855 height = 0; 5856 if (wp->w_height == height) 5857 return; /* nothing to do */ 5858 5859 if (wp->w_height > 0) 5860 { 5861 if (wp == curwin) 5862 /* w_wrow needs to be valid. When setting 'laststatus' this may 5863 * call win_new_height() recursively. */ 5864 validate_cursor(); 5865 if (wp->w_height != prev_height) 5866 return; /* Recursive call already changed the size, bail out here 5867 to avoid the following to mess things up. */ 5868 if (wp->w_wrow != wp->w_prev_fraction_row) 5869 set_fraction(wp); 5870 } 5871 5872 wp->w_height = height; 5873 wp->w_skipcol = 0; 5874 5875 /* Don't change w_topline when height is zero. Don't set w_topline when 5876 * 'scrollbind' is set and this isn't the current window. */ 5877 if (height > 0 5878 #ifdef FEAT_SCROLLBIND 5879 && (!wp->w_p_scb || wp == curwin) 5880 #endif 5881 ) 5882 { 5883 /* 5884 * Find a value for w_topline that shows the cursor at the same 5885 * relative position in the window as before (more or less). 5886 */ 5887 lnum = wp->w_cursor.lnum; 5888 if (lnum < 1) /* can happen when starting up */ 5889 lnum = 1; 5890 wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L 5891 + FRACTION_MULT / 2) / FRACTION_MULT; 5892 line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; 5893 sline = wp->w_wrow - line_size; 5894 5895 if (sline >= 0) 5896 { 5897 /* Make sure the whole cursor line is visible, if possible. */ 5898 int rows = plines_win(wp, lnum, FALSE); 5899 5900 if (sline > wp->w_height - rows) 5901 { 5902 sline = wp->w_height - rows; 5903 wp->w_wrow -= rows - line_size; 5904 } 5905 } 5906 5907 if (sline < 0) 5908 { 5909 /* 5910 * Cursor line would go off top of screen if w_wrow was this high. 5911 * Make cursor line the first line in the window. If not enough 5912 * room use w_skipcol; 5913 */ 5914 wp->w_wrow = line_size; 5915 if (wp->w_wrow >= wp->w_height 5916 && (W_WIDTH(wp) - win_col_off(wp)) > 0) 5917 { 5918 wp->w_skipcol += W_WIDTH(wp) - win_col_off(wp); 5919 --wp->w_wrow; 5920 while (wp->w_wrow >= wp->w_height) 5921 { 5922 wp->w_skipcol += W_WIDTH(wp) - win_col_off(wp) 5923 + win_col_off2(wp); 5924 --wp->w_wrow; 5925 } 5926 } 5927 set_topline(wp, lnum); 5928 } 5929 else if (sline > 0) 5930 { 5931 while (sline > 0 && lnum > 1) 5932 { 5933 #ifdef FEAT_FOLDING 5934 hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL); 5935 if (lnum == 1) 5936 { 5937 /* first line in buffer is folded */ 5938 line_size = 1; 5939 --sline; 5940 break; 5941 } 5942 #endif 5943 --lnum; 5944 #ifdef FEAT_DIFF 5945 if (lnum == wp->w_topline) 5946 line_size = plines_win_nofill(wp, lnum, TRUE) 5947 + wp->w_topfill; 5948 else 5949 #endif 5950 line_size = plines_win(wp, lnum, TRUE); 5951 sline -= line_size; 5952 } 5953 5954 if (sline < 0) 5955 { 5956 /* 5957 * Line we want at top would go off top of screen. Use next 5958 * line instead. 5959 */ 5960 #ifdef FEAT_FOLDING 5961 hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL); 5962 #endif 5963 lnum++; 5964 wp->w_wrow -= line_size + sline; 5965 } 5966 else if (sline > 0) 5967 { 5968 /* First line of file reached, use that as topline. */ 5969 lnum = 1; 5970 wp->w_wrow -= sline; 5971 } 5972 5973 set_topline(wp, lnum); 5974 } 5975 } 5976 5977 if (wp == curwin) 5978 { 5979 if (p_so) 5980 update_topline(); 5981 curs_columns(FALSE); /* validate w_wrow */ 5982 } 5983 if (prev_height > 0) 5984 wp->w_prev_fraction_row = wp->w_wrow; 5985 5986 win_comp_scroll(wp); 5987 redraw_win_later(wp, SOME_VALID); 5988 #ifdef FEAT_WINDOWS 5989 wp->w_redr_status = TRUE; 5990 #endif 5991 invalidate_botline_win(wp); 5992 } 5993 5994 #ifdef FEAT_VERTSPLIT 5995 /* 5996 * Set the width of a window. 5997 */ 5998 void 5999 win_new_width(wp, width) 6000 win_T *wp; 6001 int width; 6002 { 6003 wp->w_width = width; 6004 wp->w_lines_valid = 0; 6005 changed_line_abv_curs_win(wp); 6006 invalidate_botline_win(wp); 6007 if (wp == curwin) 6008 { 6009 update_topline(); 6010 curs_columns(TRUE); /* validate w_wrow */ 6011 } 6012 redraw_win_later(wp, NOT_VALID); 6013 wp->w_redr_status = TRUE; 6014 } 6015 #endif 6016 6017 void 6018 win_comp_scroll(wp) 6019 win_T *wp; 6020 { 6021 wp->w_p_scr = ((unsigned)wp->w_height >> 1); 6022 if (wp->w_p_scr == 0) 6023 wp->w_p_scr = 1; 6024 } 6025 6026 /* 6027 * command_height: called whenever p_ch has been changed 6028 */ 6029 void 6030 command_height() 6031 { 6032 #ifdef FEAT_WINDOWS 6033 int h; 6034 frame_T *frp; 6035 int old_p_ch = curtab->tp_ch_used; 6036 6037 /* Use the value of p_ch that we remembered. This is needed for when the 6038 * GUI starts up, we can't be sure in what order things happen. And when 6039 * p_ch was changed in another tab page. */ 6040 curtab->tp_ch_used = p_ch; 6041 6042 /* Find bottom frame with width of screen. */ 6043 frp = lastwin->w_frame; 6044 # ifdef FEAT_VERTSPLIT 6045 while (frp->fr_width != Columns && frp->fr_parent != NULL) 6046 frp = frp->fr_parent; 6047 # endif 6048 6049 /* Avoid changing the height of a window with 'winfixheight' set. */ 6050 while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF 6051 && frp->fr_win->w_p_wfh) 6052 frp = frp->fr_prev; 6053 6054 if (starting != NO_SCREEN) 6055 { 6056 cmdline_row = Rows - p_ch; 6057 6058 if (p_ch > old_p_ch) /* p_ch got bigger */ 6059 { 6060 while (p_ch > old_p_ch) 6061 { 6062 if (frp == NULL) 6063 { 6064 EMSG(_(e_noroom)); 6065 p_ch = old_p_ch; 6066 curtab->tp_ch_used = p_ch; 6067 cmdline_row = Rows - p_ch; 6068 break; 6069 } 6070 h = frp->fr_height - frame_minheight(frp, NULL); 6071 if (h > p_ch - old_p_ch) 6072 h = p_ch - old_p_ch; 6073 old_p_ch += h; 6074 frame_add_height(frp, -h); 6075 frp = frp->fr_prev; 6076 } 6077 6078 /* Recompute window positions. */ 6079 (void)win_comp_pos(); 6080 6081 /* clear the lines added to cmdline */ 6082 if (full_screen) 6083 screen_fill((int)(cmdline_row), (int)Rows, 0, 6084 (int)Columns, ' ', ' ', 0); 6085 msg_row = cmdline_row; 6086 redraw_cmdline = TRUE; 6087 return; 6088 } 6089 6090 if (msg_row < cmdline_row) 6091 msg_row = cmdline_row; 6092 redraw_cmdline = TRUE; 6093 } 6094 frame_add_height(frp, (int)(old_p_ch - p_ch)); 6095 6096 /* Recompute window positions. */ 6097 if (frp != lastwin->w_frame) 6098 (void)win_comp_pos(); 6099 #else 6100 cmdline_row = Rows - p_ch; 6101 win_setheight(cmdline_row); 6102 #endif 6103 } 6104 6105 #if defined(FEAT_WINDOWS) || defined(PROTO) 6106 /* 6107 * Resize frame "frp" to be "n" lines higher (negative for less high). 6108 * Also resize the frames it is contained in. 6109 */ 6110 static void 6111 frame_add_height(frp, n) 6112 frame_T *frp; 6113 int n; 6114 { 6115 frame_new_height(frp, frp->fr_height + n, FALSE, FALSE); 6116 for (;;) 6117 { 6118 frp = frp->fr_parent; 6119 if (frp == NULL) 6120 break; 6121 frp->fr_height += n; 6122 } 6123 } 6124 6125 /* 6126 * Add or remove a status line for the bottom window(s), according to the 6127 * value of 'laststatus'. 6128 */ 6129 void 6130 last_status(morewin) 6131 int morewin; /* pretend there are two or more windows */ 6132 { 6133 /* Don't make a difference between horizontal or vertical split. */ 6134 last_status_rec(topframe, (p_ls == 2 6135 || (p_ls == 1 && (morewin || lastwin != firstwin)))); 6136 } 6137 6138 static void 6139 last_status_rec(fr, statusline) 6140 frame_T *fr; 6141 int statusline; 6142 { 6143 frame_T *fp; 6144 win_T *wp; 6145 6146 if (fr->fr_layout == FR_LEAF) 6147 { 6148 wp = fr->fr_win; 6149 if (wp->w_status_height != 0 && !statusline) 6150 { 6151 /* remove status line */ 6152 win_new_height(wp, wp->w_height + 1); 6153 wp->w_status_height = 0; 6154 comp_col(); 6155 } 6156 else if (wp->w_status_height == 0 && statusline) 6157 { 6158 /* Find a frame to take a line from. */ 6159 fp = fr; 6160 while (fp->fr_height <= frame_minheight(fp, NULL)) 6161 { 6162 if (fp == topframe) 6163 { 6164 EMSG(_(e_noroom)); 6165 return; 6166 } 6167 /* In a column of frames: go to frame above. If already at 6168 * the top or in a row of frames: go to parent. */ 6169 if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) 6170 fp = fp->fr_prev; 6171 else 6172 fp = fp->fr_parent; 6173 } 6174 wp->w_status_height = 1; 6175 if (fp != fr) 6176 { 6177 frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE); 6178 frame_fix_height(wp); 6179 (void)win_comp_pos(); 6180 } 6181 else 6182 win_new_height(wp, wp->w_height - 1); 6183 comp_col(); 6184 redraw_all_later(SOME_VALID); 6185 } 6186 } 6187 #ifdef FEAT_VERTSPLIT 6188 else if (fr->fr_layout == FR_ROW) 6189 { 6190 /* vertically split windows, set status line for each one */ 6191 for (fp = fr->fr_child; fp != NULL; fp = fp->fr_next) 6192 last_status_rec(fp, statusline); 6193 } 6194 #endif 6195 else 6196 { 6197 /* horizontally split window, set status line for last one */ 6198 for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) 6199 ; 6200 last_status_rec(fp, statusline); 6201 } 6202 } 6203 6204 /* 6205 * Return the number of lines used by the tab page line. 6206 */ 6207 int 6208 tabline_height() 6209 { 6210 #ifdef FEAT_GUI_TABLINE 6211 /* When the GUI has the tabline then this always returns zero. */ 6212 if (gui_use_tabline()) 6213 return 0; 6214 #endif 6215 switch (p_stal) 6216 { 6217 case 0: return 0; 6218 case 1: return (first_tabpage->tp_next == NULL) ? 0 : 1; 6219 } 6220 return 1; 6221 } 6222 6223 #endif /* FEAT_WINDOWS */ 6224 6225 #if defined(FEAT_SEARCHPATH) || defined(PROTO) 6226 /* 6227 * Get the file name at the cursor. 6228 * If Visual mode is active, use the selected text if it's in one line. 6229 * Returns the name in allocated memory, NULL for failure. 6230 */ 6231 char_u * 6232 grab_file_name(count, file_lnum) 6233 long count; 6234 linenr_T *file_lnum; 6235 { 6236 int options = FNAME_MESS|FNAME_EXP|FNAME_REL|FNAME_UNESC; 6237 6238 if (VIsual_active) 6239 { 6240 int len; 6241 char_u *ptr; 6242 6243 if (get_visual_text(NULL, &ptr, &len) == FAIL) 6244 return NULL; 6245 return find_file_name_in_path(ptr, len, options, 6246 count, curbuf->b_ffname); 6247 } 6248 return file_name_at_cursor(options | FNAME_HYP, count, file_lnum); 6249 6250 } 6251 6252 /* 6253 * Return the file name under or after the cursor. 6254 * 6255 * The 'path' option is searched if the file name is not absolute. 6256 * The string returned has been alloc'ed and should be freed by the caller. 6257 * NULL is returned if the file name or file is not found. 6258 * 6259 * options: 6260 * FNAME_MESS give error messages 6261 * FNAME_EXP expand to path 6262 * FNAME_HYP check for hypertext link 6263 * FNAME_INCL apply "includeexpr" 6264 */ 6265 char_u * 6266 file_name_at_cursor(options, count, file_lnum) 6267 int options; 6268 long count; 6269 linenr_T *file_lnum; 6270 { 6271 return file_name_in_line(ml_get_curline(), 6272 curwin->w_cursor.col, options, count, curbuf->b_ffname, 6273 file_lnum); 6274 } 6275 6276 /* 6277 * Return the name of the file under or after ptr[col]. 6278 * Otherwise like file_name_at_cursor(). 6279 */ 6280 char_u * 6281 file_name_in_line(line, col, options, count, rel_fname, file_lnum) 6282 char_u *line; 6283 int col; 6284 int options; 6285 long count; 6286 char_u *rel_fname; /* file we are searching relative to */ 6287 linenr_T *file_lnum; /* line number after the file name */ 6288 { 6289 char_u *ptr; 6290 int len; 6291 6292 /* 6293 * search forward for what could be the start of a file name 6294 */ 6295 ptr = line + col; 6296 while (*ptr != NUL && !vim_isfilec(*ptr)) 6297 mb_ptr_adv(ptr); 6298 if (*ptr == NUL) /* nothing found */ 6299 { 6300 if (options & FNAME_MESS) 6301 EMSG(_("E446: No file name under cursor")); 6302 return NULL; 6303 } 6304 6305 /* 6306 * Search backward for first char of the file name. 6307 * Go one char back to ":" before "//" even when ':' is not in 'isfname'. 6308 */ 6309 while (ptr > line) 6310 { 6311 #ifdef FEAT_MBYTE 6312 if (has_mbyte && (len = (*mb_head_off)(line, ptr - 1)) > 0) 6313 ptr -= len + 1; 6314 else 6315 #endif 6316 if (vim_isfilec(ptr[-1]) 6317 || ((options & FNAME_HYP) && path_is_url(ptr - 1))) 6318 --ptr; 6319 else 6320 break; 6321 } 6322 6323 /* 6324 * Search forward for the last char of the file name. 6325 * Also allow "://" when ':' is not in 'isfname'. 6326 */ 6327 len = 0; 6328 while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') 6329 || ((options & FNAME_HYP) && path_is_url(ptr + len))) 6330 { 6331 if (ptr[len] == '\\') 6332 /* Skip over the "\" in "\ ". */ 6333 ++len; 6334 #ifdef FEAT_MBYTE 6335 if (has_mbyte) 6336 len += (*mb_ptr2len)(ptr + len); 6337 else 6338 #endif 6339 ++len; 6340 } 6341 6342 /* 6343 * If there is trailing punctuation, remove it. 6344 * But don't remove "..", could be a directory name. 6345 */ 6346 if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL 6347 && ptr[len - 2] != '.') 6348 --len; 6349 6350 if (file_lnum != NULL) 6351 { 6352 char_u *p; 6353 6354 /* Get the number after the file name and a separator character */ 6355 p = ptr + len; 6356 p = skipwhite(p); 6357 if (*p != NUL) 6358 { 6359 if (!isdigit(*p)) 6360 ++p; /* skip the separator */ 6361 p = skipwhite(p); 6362 if (isdigit(*p)) 6363 *file_lnum = (int)getdigits(&p); 6364 } 6365 } 6366 6367 return find_file_name_in_path(ptr, len, options, count, rel_fname); 6368 } 6369 6370 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6371 static char_u *eval_includeexpr __ARGS((char_u *ptr, int len)); 6372 6373 static char_u * 6374 eval_includeexpr(ptr, len) 6375 char_u *ptr; 6376 int len; 6377 { 6378 char_u *res; 6379 6380 set_vim_var_string(VV_FNAME, ptr, len); 6381 res = eval_to_string_safe(curbuf->b_p_inex, NULL, 6382 was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL)); 6383 set_vim_var_string(VV_FNAME, NULL, 0); 6384 return res; 6385 } 6386 #endif 6387 6388 /* 6389 * Return the name of the file ptr[len] in 'path'. 6390 * Otherwise like file_name_at_cursor(). 6391 */ 6392 char_u * 6393 find_file_name_in_path(ptr, len, options, count, rel_fname) 6394 char_u *ptr; 6395 int len; 6396 int options; 6397 long count; 6398 char_u *rel_fname; /* file we are searching relative to */ 6399 { 6400 char_u *file_name; 6401 int c; 6402 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6403 char_u *tofree = NULL; 6404 6405 if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) 6406 { 6407 tofree = eval_includeexpr(ptr, len); 6408 if (tofree != NULL) 6409 { 6410 ptr = tofree; 6411 len = (int)STRLEN(ptr); 6412 } 6413 } 6414 # endif 6415 6416 if (options & FNAME_EXP) 6417 { 6418 file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, 6419 TRUE, rel_fname); 6420 6421 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6422 /* 6423 * If the file could not be found in a normal way, try applying 6424 * 'includeexpr' (unless done already). 6425 */ 6426 if (file_name == NULL 6427 && !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) 6428 { 6429 tofree = eval_includeexpr(ptr, len); 6430 if (tofree != NULL) 6431 { 6432 ptr = tofree; 6433 len = (int)STRLEN(ptr); 6434 file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, 6435 TRUE, rel_fname); 6436 } 6437 } 6438 # endif 6439 if (file_name == NULL && (options & FNAME_MESS)) 6440 { 6441 c = ptr[len]; 6442 ptr[len] = NUL; 6443 EMSG2(_("E447: Can't find file \"%s\" in path"), ptr); 6444 ptr[len] = c; 6445 } 6446 6447 /* Repeat finding the file "count" times. This matters when it 6448 * appears several times in the path. */ 6449 while (file_name != NULL && --count > 0) 6450 { 6451 vim_free(file_name); 6452 file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname); 6453 } 6454 } 6455 else 6456 file_name = vim_strnsave(ptr, len); 6457 6458 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6459 vim_free(tofree); 6460 # endif 6461 6462 return file_name; 6463 } 6464 #endif /* FEAT_SEARCHPATH */ 6465 6466 /* 6467 * Check if the "://" of a URL is at the pointer, return URL_SLASH. 6468 * Also check for ":\\", which MS Internet Explorer accepts, return 6469 * URL_BACKSLASH. 6470 */ 6471 static int 6472 path_is_url(p) 6473 char_u *p; 6474 { 6475 if (STRNCMP(p, "://", (size_t)3) == 0) 6476 return URL_SLASH; 6477 else if (STRNCMP(p, ":\\\\", (size_t)3) == 0) 6478 return URL_BACKSLASH; 6479 return 0; 6480 } 6481 6482 /* 6483 * Check if "fname" starts with "name://". Return URL_SLASH if it does. 6484 * Return URL_BACKSLASH for "name:\\". 6485 * Return zero otherwise. 6486 */ 6487 int 6488 path_with_url(fname) 6489 char_u *fname; 6490 { 6491 char_u *p; 6492 6493 for (p = fname; isalpha(*p); ++p) 6494 ; 6495 return path_is_url(p); 6496 } 6497 6498 /* 6499 * Return TRUE if "name" is a full (absolute) path name or URL. 6500 */ 6501 int 6502 vim_isAbsName(name) 6503 char_u *name; 6504 { 6505 return (path_with_url(name) != 0 || mch_isFullName(name)); 6506 } 6507 6508 /* 6509 * Get absolute file name into buffer "buf[len]". 6510 * 6511 * return FAIL for failure, OK otherwise 6512 */ 6513 int 6514 vim_FullName(fname, buf, len, force) 6515 char_u *fname, *buf; 6516 int len; 6517 int force; /* force expansion even when already absolute */ 6518 { 6519 int retval = OK; 6520 int url; 6521 6522 *buf = NUL; 6523 if (fname == NULL) 6524 return FAIL; 6525 6526 url = path_with_url(fname); 6527 if (!url) 6528 retval = mch_FullName(fname, buf, len, force); 6529 if (url || retval == FAIL) 6530 { 6531 /* something failed; use the file name (truncate when too long) */ 6532 vim_strncpy(buf, fname, len - 1); 6533 } 6534 #if defined(MACOS_CLASSIC) || defined(OS2) || defined(MSDOS) || defined(MSWIN) 6535 slash_adjust(buf); 6536 #endif 6537 return retval; 6538 } 6539 6540 /* 6541 * Return the minimal number of rows that is needed on the screen to display 6542 * the current number of windows. 6543 */ 6544 int 6545 min_rows() 6546 { 6547 int total; 6548 #ifdef FEAT_WINDOWS 6549 tabpage_T *tp; 6550 int n; 6551 #endif 6552 6553 if (firstwin == NULL) /* not initialized yet */ 6554 return MIN_LINES; 6555 6556 #ifdef FEAT_WINDOWS 6557 total = 0; 6558 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) 6559 { 6560 n = frame_minheight(tp->tp_topframe, NULL); 6561 if (total < n) 6562 total = n; 6563 } 6564 total += tabline_height(); 6565 #else 6566 total = 1; /* at least one window should have a line! */ 6567 #endif 6568 total += 1; /* count the room for the command line */ 6569 return total; 6570 } 6571 6572 /* 6573 * Return TRUE if there is only one window (in the current tab page), not 6574 * counting a help or preview window, unless it is the current window. 6575 * Does not count "aucmd_win". 6576 */ 6577 int 6578 only_one_window() 6579 { 6580 #ifdef FEAT_WINDOWS 6581 int count = 0; 6582 win_T *wp; 6583 6584 /* If there is another tab page there always is another window. */ 6585 if (first_tabpage->tp_next != NULL) 6586 return FALSE; 6587 6588 for (wp = firstwin; wp != NULL; wp = wp->w_next) 6589 if (wp->w_buffer != NULL 6590 && (!((wp->w_buffer->b_help && !curbuf->b_help) 6591 # ifdef FEAT_QUICKFIX 6592 || wp->w_p_pvw 6593 # endif 6594 ) || wp == curwin) 6595 # ifdef FEAT_AUTOCMD 6596 && wp != aucmd_win 6597 # endif 6598 ) 6599 ++count; 6600 return (count <= 1); 6601 #else 6602 return TRUE; 6603 #endif 6604 } 6605 6606 #if defined(FEAT_WINDOWS) || defined(FEAT_AUTOCMD) || defined(PROTO) 6607 /* 6608 * Correct the cursor line number in other windows. Used after changing the 6609 * current buffer, and before applying autocommands. 6610 * When "do_curwin" is TRUE, also check current window. 6611 */ 6612 void 6613 check_lnums(do_curwin) 6614 int do_curwin; 6615 { 6616 win_T *wp; 6617 6618 #ifdef FEAT_WINDOWS 6619 tabpage_T *tp; 6620 6621 FOR_ALL_TAB_WINDOWS(tp, wp) 6622 if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) 6623 #else 6624 wp = curwin; 6625 if (do_curwin) 6626 #endif 6627 { 6628 if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count) 6629 wp->w_cursor.lnum = curbuf->b_ml.ml_line_count; 6630 if (wp->w_topline > curbuf->b_ml.ml_line_count) 6631 wp->w_topline = curbuf->b_ml.ml_line_count; 6632 } 6633 } 6634 #endif 6635 6636 #if defined(FEAT_WINDOWS) || defined(PROTO) 6637 6638 /* 6639 * A snapshot of the window sizes, to restore them after closing the help 6640 * window. 6641 * Only these fields are used: 6642 * fr_layout 6643 * fr_width 6644 * fr_height 6645 * fr_next 6646 * fr_child 6647 * fr_win (only valid for the old curwin, NULL otherwise) 6648 */ 6649 6650 /* 6651 * Create a snapshot of the current frame sizes. 6652 */ 6653 void 6654 make_snapshot(idx) 6655 int idx; 6656 { 6657 clear_snapshot(curtab, idx); 6658 make_snapshot_rec(topframe, &curtab->tp_snapshot[idx]); 6659 } 6660 6661 static void 6662 make_snapshot_rec(fr, frp) 6663 frame_T *fr; 6664 frame_T **frp; 6665 { 6666 *frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 6667 if (*frp == NULL) 6668 return; 6669 (*frp)->fr_layout = fr->fr_layout; 6670 # ifdef FEAT_VERTSPLIT 6671 (*frp)->fr_width = fr->fr_width; 6672 # endif 6673 (*frp)->fr_height = fr->fr_height; 6674 if (fr->fr_next != NULL) 6675 make_snapshot_rec(fr->fr_next, &((*frp)->fr_next)); 6676 if (fr->fr_child != NULL) 6677 make_snapshot_rec(fr->fr_child, &((*frp)->fr_child)); 6678 if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin) 6679 (*frp)->fr_win = curwin; 6680 } 6681 6682 /* 6683 * Remove any existing snapshot. 6684 */ 6685 static void 6686 clear_snapshot(tp, idx) 6687 tabpage_T *tp; 6688 int idx; 6689 { 6690 clear_snapshot_rec(tp->tp_snapshot[idx]); 6691 tp->tp_snapshot[idx] = NULL; 6692 } 6693 6694 static void 6695 clear_snapshot_rec(fr) 6696 frame_T *fr; 6697 { 6698 if (fr != NULL) 6699 { 6700 clear_snapshot_rec(fr->fr_next); 6701 clear_snapshot_rec(fr->fr_child); 6702 vim_free(fr); 6703 } 6704 } 6705 6706 /* 6707 * Restore a previously created snapshot, if there is any. 6708 * This is only done if the screen size didn't change and the window layout is 6709 * still the same. 6710 */ 6711 void 6712 restore_snapshot(idx, close_curwin) 6713 int idx; 6714 int close_curwin; /* closing current window */ 6715 { 6716 win_T *wp; 6717 6718 if (curtab->tp_snapshot[idx] != NULL 6719 # ifdef FEAT_VERTSPLIT 6720 && curtab->tp_snapshot[idx]->fr_width == topframe->fr_width 6721 # endif 6722 && curtab->tp_snapshot[idx]->fr_height == topframe->fr_height 6723 && check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK) 6724 { 6725 wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe); 6726 win_comp_pos(); 6727 if (wp != NULL && close_curwin) 6728 win_goto(wp); 6729 redraw_all_later(CLEAR); 6730 } 6731 clear_snapshot(curtab, idx); 6732 } 6733 6734 /* 6735 * Check if frames "sn" and "fr" have the same layout, same following frames 6736 * and same children. 6737 */ 6738 static int 6739 check_snapshot_rec(sn, fr) 6740 frame_T *sn; 6741 frame_T *fr; 6742 { 6743 if (sn->fr_layout != fr->fr_layout 6744 || (sn->fr_next == NULL) != (fr->fr_next == NULL) 6745 || (sn->fr_child == NULL) != (fr->fr_child == NULL) 6746 || (sn->fr_next != NULL 6747 && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) 6748 || (sn->fr_child != NULL 6749 && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)) 6750 return FAIL; 6751 return OK; 6752 } 6753 6754 /* 6755 * Copy the size of snapshot frame "sn" to frame "fr". Do the same for all 6756 * following frames and children. 6757 * Returns a pointer to the old current window, or NULL. 6758 */ 6759 static win_T * 6760 restore_snapshot_rec(sn, fr) 6761 frame_T *sn; 6762 frame_T *fr; 6763 { 6764 win_T *wp = NULL; 6765 win_T *wp2; 6766 6767 fr->fr_height = sn->fr_height; 6768 # ifdef FEAT_VERTSPLIT 6769 fr->fr_width = sn->fr_width; 6770 # endif 6771 if (fr->fr_layout == FR_LEAF) 6772 { 6773 frame_new_height(fr, fr->fr_height, FALSE, FALSE); 6774 # ifdef FEAT_VERTSPLIT 6775 frame_new_width(fr, fr->fr_width, FALSE, FALSE); 6776 # endif 6777 wp = sn->fr_win; 6778 } 6779 if (sn->fr_next != NULL) 6780 { 6781 wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next); 6782 if (wp2 != NULL) 6783 wp = wp2; 6784 } 6785 if (sn->fr_child != NULL) 6786 { 6787 wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child); 6788 if (wp2 != NULL) 6789 wp = wp2; 6790 } 6791 return wp; 6792 } 6793 6794 #endif 6795 6796 #if defined(FEAT_EVAL) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \ 6797 || defined(PROTO) 6798 /* 6799 * Set "win" to be the curwin and "tp" to be the current tab page. 6800 * restore_win() MUST be called to undo, also when FAIL is returned. 6801 * No autocommands will be executed until restore_win() is called. 6802 * When "no_display" is TRUE the display won't be affected, no redraw is 6803 * triggered, another tabpage access is limited. 6804 * Returns FAIL if switching to "win" failed. 6805 */ 6806 int 6807 switch_win(save_curwin, save_curtab, win, tp, no_display) 6808 win_T **save_curwin UNUSED; 6809 tabpage_T **save_curtab UNUSED; 6810 win_T *win UNUSED; 6811 tabpage_T *tp UNUSED; 6812 int no_display UNUSED; 6813 { 6814 # ifdef FEAT_AUTOCMD 6815 block_autocmds(); 6816 # endif 6817 # ifdef FEAT_WINDOWS 6818 *save_curwin = curwin; 6819 if (tp != NULL) 6820 { 6821 *save_curtab = curtab; 6822 if (no_display) 6823 { 6824 curtab->tp_firstwin = firstwin; 6825 curtab->tp_lastwin = lastwin; 6826 curtab = tp; 6827 firstwin = curtab->tp_firstwin; 6828 lastwin = curtab->tp_lastwin; 6829 } 6830 else 6831 goto_tabpage_tp(tp, FALSE, FALSE); 6832 } 6833 if (!win_valid(win)) 6834 return FAIL; 6835 curwin = win; 6836 curbuf = curwin->w_buffer; 6837 # endif 6838 return OK; 6839 } 6840 6841 /* 6842 * Restore current tabpage and window saved by switch_win(), if still valid. 6843 * When "no_display" is TRUE the display won't be affected, no redraw is 6844 * triggered. 6845 */ 6846 void 6847 restore_win(save_curwin, save_curtab, no_display) 6848 win_T *save_curwin UNUSED; 6849 tabpage_T *save_curtab UNUSED; 6850 int no_display UNUSED; 6851 { 6852 # ifdef FEAT_WINDOWS 6853 if (save_curtab != NULL && valid_tabpage(save_curtab)) 6854 { 6855 if (no_display) 6856 { 6857 curtab->tp_firstwin = firstwin; 6858 curtab->tp_lastwin = lastwin; 6859 curtab = save_curtab; 6860 firstwin = curtab->tp_firstwin; 6861 lastwin = curtab->tp_lastwin; 6862 } 6863 else 6864 goto_tabpage_tp(save_curtab, FALSE, FALSE); 6865 } 6866 if (win_valid(save_curwin)) 6867 { 6868 curwin = save_curwin; 6869 curbuf = curwin->w_buffer; 6870 } 6871 # endif 6872 # ifdef FEAT_AUTOCMD 6873 unblock_autocmds(); 6874 # endif 6875 } 6876 6877 /* 6878 * Make "buf" the current buffer. restore_buffer() MUST be called to undo. 6879 * No autocommands will be executed. Use aucmd_prepbuf() if there are any. 6880 */ 6881 void 6882 switch_buffer(save_curbuf, buf) 6883 buf_T *buf; 6884 buf_T **save_curbuf; 6885 { 6886 # ifdef FEAT_AUTOCMD 6887 block_autocmds(); 6888 # endif 6889 *save_curbuf = curbuf; 6890 --curbuf->b_nwindows; 6891 curbuf = buf; 6892 curwin->w_buffer = buf; 6893 ++curbuf->b_nwindows; 6894 } 6895 6896 /* 6897 * Restore the current buffer after using switch_buffer(). 6898 */ 6899 void 6900 restore_buffer(save_curbuf) 6901 buf_T *save_curbuf; 6902 { 6903 # ifdef FEAT_AUTOCMD 6904 unblock_autocmds(); 6905 # endif 6906 /* Check for valid buffer, just in case. */ 6907 if (buf_valid(save_curbuf)) 6908 { 6909 --curbuf->b_nwindows; 6910 curwin->w_buffer = save_curbuf; 6911 curbuf = save_curbuf; 6912 ++curbuf->b_nwindows; 6913 } 6914 } 6915 #endif 6916 6917 #if (defined(FEAT_GUI) && defined(FEAT_VERTSPLIT)) || defined(PROTO) 6918 /* 6919 * Return TRUE if there is any vertically split window. 6920 */ 6921 int 6922 win_hasvertsplit() 6923 { 6924 frame_T *fr; 6925 6926 if (topframe->fr_layout == FR_ROW) 6927 return TRUE; 6928 6929 if (topframe->fr_layout == FR_COL) 6930 for (fr = topframe->fr_child; fr != NULL; fr = fr->fr_next) 6931 if (fr->fr_layout == FR_ROW) 6932 return TRUE; 6933 6934 return FALSE; 6935 } 6936 #endif 6937 6938 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) 6939 /* 6940 * Add match to the match list of window 'wp'. The pattern 'pat' will be 6941 * highlighted with the group 'grp' with priority 'prio'. 6942 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1). 6943 * If no particular ID is desired, -1 must be specified for 'id'. 6944 * Return ID of added match, -1 on failure. 6945 */ 6946 int 6947 match_add(wp, grp, pat, prio, id, pos_list, conceal_char) 6948 win_T *wp; 6949 char_u *grp; 6950 char_u *pat; 6951 int prio; 6952 int id; 6953 list_T *pos_list; 6954 char_u *conceal_char UNUSED; /* pointer to conceal replacement char */ 6955 { 6956 matchitem_T *cur; 6957 matchitem_T *prev; 6958 matchitem_T *m; 6959 int hlg_id; 6960 regprog_T *regprog = NULL; 6961 int rtype = SOME_VALID; 6962 6963 if (*grp == NUL || (pat != NULL && *pat == NUL)) 6964 return -1; 6965 if (id < -1 || id == 0) 6966 { 6967 EMSGN("E799: Invalid ID: %ld (must be greater than or equal to 1)", id); 6968 return -1; 6969 } 6970 if (id != -1) 6971 { 6972 cur = wp->w_match_head; 6973 while (cur != NULL) 6974 { 6975 if (cur->id == id) 6976 { 6977 EMSGN("E801: ID already taken: %ld", id); 6978 return -1; 6979 } 6980 cur = cur->next; 6981 } 6982 } 6983 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) 6984 { 6985 EMSG2(_(e_nogroup), grp); 6986 return -1; 6987 } 6988 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) 6989 { 6990 EMSG2(_(e_invarg2), pat); 6991 return -1; 6992 } 6993 6994 /* Find available match ID. */ 6995 while (id == -1) 6996 { 6997 cur = wp->w_match_head; 6998 while (cur != NULL && cur->id != wp->w_next_match_id) 6999 cur = cur->next; 7000 if (cur == NULL) 7001 id = wp->w_next_match_id; 7002 wp->w_next_match_id++; 7003 } 7004 7005 /* Build new match. */ 7006 m = (matchitem_T *)alloc_clear(sizeof(matchitem_T)); 7007 m->id = id; 7008 m->priority = prio; 7009 m->pattern = pat == NULL ? NULL : vim_strsave(pat); 7010 m->hlg_id = hlg_id; 7011 m->match.regprog = regprog; 7012 m->match.rmm_ic = FALSE; 7013 m->match.rmm_maxcol = 0; 7014 #ifdef FEAT_CONCEAL 7015 m->conceal_char = 0; 7016 if (conceal_char != NULL) 7017 m->conceal_char = (*mb_ptr2char)(conceal_char); 7018 #endif 7019 7020 /* Set up position matches */ 7021 if (pos_list != NULL) 7022 { 7023 linenr_T toplnum = 0; 7024 linenr_T botlnum = 0; 7025 listitem_T *li; 7026 int i; 7027 7028 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; 7029 i++, li = li->li_next) 7030 { 7031 linenr_T lnum = 0; 7032 colnr_T col = 0; 7033 int len = 1; 7034 list_T *subl; 7035 listitem_T *subli; 7036 int error = FALSE; 7037 7038 if (li->li_tv.v_type == VAR_LIST) 7039 { 7040 subl = li->li_tv.vval.v_list; 7041 if (subl == NULL) 7042 goto fail; 7043 subli = subl->lv_first; 7044 if (subli == NULL) 7045 goto fail; 7046 lnum = get_tv_number_chk(&subli->li_tv, &error); 7047 if (error == TRUE) 7048 goto fail; 7049 if (lnum == 0) 7050 { 7051 --i; 7052 continue; 7053 } 7054 m->pos.pos[i].lnum = lnum; 7055 subli = subli->li_next; 7056 if (subli != NULL) 7057 { 7058 col = get_tv_number_chk(&subli->li_tv, &error); 7059 if (error == TRUE) 7060 goto fail; 7061 subli = subli->li_next; 7062 if (subli != NULL) 7063 { 7064 len = get_tv_number_chk(&subli->li_tv, &error); 7065 if (error == TRUE) 7066 goto fail; 7067 } 7068 } 7069 m->pos.pos[i].col = col; 7070 m->pos.pos[i].len = len; 7071 } 7072 else if (li->li_tv.v_type == VAR_NUMBER) 7073 { 7074 if (li->li_tv.vval.v_number == 0) 7075 { 7076 --i; 7077 continue; 7078 } 7079 m->pos.pos[i].lnum = li->li_tv.vval.v_number; 7080 m->pos.pos[i].col = 0; 7081 m->pos.pos[i].len = 0; 7082 } 7083 else 7084 { 7085 EMSG(_("List or number required")); 7086 goto fail; 7087 } 7088 if (toplnum == 0 || lnum < toplnum) 7089 toplnum = lnum; 7090 if (botlnum == 0 || lnum >= botlnum) 7091 botlnum = lnum + 1; 7092 } 7093 7094 /* Calculate top and bottom lines for redrawing area */ 7095 if (toplnum != 0) 7096 { 7097 if (wp->w_buffer->b_mod_set) 7098 { 7099 if (wp->w_buffer->b_mod_top > toplnum) 7100 wp->w_buffer->b_mod_top = toplnum; 7101 if (wp->w_buffer->b_mod_bot < botlnum) 7102 wp->w_buffer->b_mod_bot = botlnum; 7103 } 7104 else 7105 { 7106 wp->w_buffer->b_mod_set = TRUE; 7107 wp->w_buffer->b_mod_top = toplnum; 7108 wp->w_buffer->b_mod_bot = botlnum; 7109 wp->w_buffer->b_mod_xlines = 0; 7110 } 7111 m->pos.toplnum = toplnum; 7112 m->pos.botlnum = botlnum; 7113 rtype = VALID; 7114 } 7115 } 7116 7117 /* Insert new match. The match list is in ascending order with regard to 7118 * the match priorities. */ 7119 cur = wp->w_match_head; 7120 prev = cur; 7121 while (cur != NULL && prio >= cur->priority) 7122 { 7123 prev = cur; 7124 cur = cur->next; 7125 } 7126 if (cur == prev) 7127 wp->w_match_head = m; 7128 else 7129 prev->next = m; 7130 m->next = cur; 7131 7132 redraw_later(rtype); 7133 return id; 7134 7135 fail: 7136 vim_free(m); 7137 return -1; 7138 } 7139 7140 /* 7141 * Delete match with ID 'id' in the match list of window 'wp'. 7142 * Print error messages if 'perr' is TRUE. 7143 */ 7144 int 7145 match_delete(wp, id, perr) 7146 win_T *wp; 7147 int id; 7148 int perr; 7149 { 7150 matchitem_T *cur = wp->w_match_head; 7151 matchitem_T *prev = cur; 7152 int rtype = SOME_VALID; 7153 7154 if (id < 1) 7155 { 7156 if (perr == TRUE) 7157 EMSGN("E802: Invalid ID: %ld (must be greater than or equal to 1)", 7158 id); 7159 return -1; 7160 } 7161 while (cur != NULL && cur->id != id) 7162 { 7163 prev = cur; 7164 cur = cur->next; 7165 } 7166 if (cur == NULL) 7167 { 7168 if (perr == TRUE) 7169 EMSGN("E803: ID not found: %ld", id); 7170 return -1; 7171 } 7172 if (cur == prev) 7173 wp->w_match_head = cur->next; 7174 else 7175 prev->next = cur->next; 7176 vim_regfree(cur->match.regprog); 7177 vim_free(cur->pattern); 7178 if (cur->pos.toplnum != 0) 7179 { 7180 if (wp->w_buffer->b_mod_set) 7181 { 7182 if (wp->w_buffer->b_mod_top > cur->pos.toplnum) 7183 wp->w_buffer->b_mod_top = cur->pos.toplnum; 7184 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) 7185 wp->w_buffer->b_mod_bot = cur->pos.botlnum; 7186 } 7187 else 7188 { 7189 wp->w_buffer->b_mod_set = TRUE; 7190 wp->w_buffer->b_mod_top = cur->pos.toplnum; 7191 wp->w_buffer->b_mod_bot = cur->pos.botlnum; 7192 wp->w_buffer->b_mod_xlines = 0; 7193 } 7194 rtype = VALID; 7195 } 7196 vim_free(cur); 7197 redraw_later(rtype); 7198 return 0; 7199 } 7200 7201 /* 7202 * Delete all matches in the match list of window 'wp'. 7203 */ 7204 void 7205 clear_matches(wp) 7206 win_T *wp; 7207 { 7208 matchitem_T *m; 7209 7210 while (wp->w_match_head != NULL) 7211 { 7212 m = wp->w_match_head->next; 7213 vim_regfree(wp->w_match_head->match.regprog); 7214 vim_free(wp->w_match_head->pattern); 7215 vim_free(wp->w_match_head); 7216 wp->w_match_head = m; 7217 } 7218 redraw_later(SOME_VALID); 7219 } 7220 7221 /* 7222 * Get match from ID 'id' in window 'wp'. 7223 * Return NULL if match not found. 7224 */ 7225 matchitem_T * 7226 get_match(wp, id) 7227 win_T *wp; 7228 int id; 7229 { 7230 matchitem_T *cur = wp->w_match_head; 7231 7232 while (cur != NULL && cur->id != id) 7233 cur = cur->next; 7234 return cur; 7235 } 7236 #endif 7237 7238 #if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) || defined(PROTO) 7239 int 7240 get_win_number(win_T *wp, win_T *first_win) 7241 { 7242 int i = 1; 7243 win_T *w; 7244 7245 for (w = first_win; w != NULL && w != wp; w = W_NEXT(w)) 7246 ++i; 7247 7248 if (w == NULL) 7249 return 0; 7250 else 7251 return i; 7252 } 7253 7254 int 7255 get_tab_number(tabpage_T *tp UNUSED) 7256 { 7257 int i = 1; 7258 # ifdef FEAT_WINDOWS 7259 tabpage_T *t; 7260 7261 for (t = first_tabpage; t != NULL && t != tp; t = t->tp_next) 7262 ++i; 7263 7264 if (t == NULL) 7265 return 0; 7266 else 7267 # endif 7268 return i; 7269 } 7270 #endif 7271 7272 #if defined(FEAT_WINDOWS) || defined(PROTO) 7273 /* 7274 * Return TRUE if "topfrp" and its children are at the right height. 7275 */ 7276 static int 7277 frame_check_height(topfrp, height) 7278 frame_T *topfrp; 7279 int height; 7280 { 7281 frame_T *frp; 7282 7283 if (topfrp->fr_height != height) 7284 return FALSE; 7285 7286 if (topfrp->fr_layout == FR_ROW) 7287 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 7288 if (frp->fr_height != height) 7289 return FALSE; 7290 7291 return TRUE; 7292 } 7293 #endif 7294 7295 #if defined(FEAT_VERTSPLIT) || defined(PROTO) 7296 /* 7297 * Return TRUE if "topfrp" and its children are at the right width. 7298 */ 7299 static int 7300 frame_check_width(topfrp, width) 7301 frame_T *topfrp; 7302 int width; 7303 { 7304 frame_T *frp; 7305 7306 if (topfrp->fr_width != width) 7307 return FALSE; 7308 7309 if (topfrp->fr_layout == FR_COL) 7310 for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) 7311 if (frp->fr_width != width) 7312 return FALSE; 7313 7314 return TRUE; 7315 } 7316 #endif 7317 7318