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