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