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