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