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