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