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