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