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