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 EMSGN(_("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_later(CLEAR); 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_later(CLEAR); 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(CLEAR); 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(CLEAR); 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 must_redraw = CLEAR; /* need to redraw everything */ 3943 #ifdef FEAT_DIFF 3944 diff_need_scrollbind = TRUE; 3945 #endif 3946 3947 /* The tabpage line may have appeared or disappeared, may need to resize 3948 * the frames for that. When the Vim window was resized need to update 3949 * frame sizes too. Use the stored value of p_ch, so that it can be 3950 * different for each tab page. */ 3951 p_ch = curtab->tp_ch_used; 3952 if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow 3953 #ifdef FEAT_GUI_TABLINE 3954 && !gui_use_tabline() 3955 #endif 3956 )) 3957 shell_new_rows(); 3958 if (curtab->tp_old_Columns != Columns && starting == 0) 3959 shell_new_columns(); /* update window widths */ 3960 3961 #if defined(FEAT_GUI) 3962 /* When 'guioptions' includes 'L' or 'R' may have to remove or add 3963 * scrollbars. Have to update them anyway. */ 3964 gui_may_update_scrollbars(); 3965 #endif 3966 3967 /* Apply autocommands after updating the display, when 'rows' and 3968 * 'columns' have been set correctly. */ 3969 if (trigger_enter_autocmds) 3970 { 3971 apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); 3972 if (old_curbuf != curbuf) 3973 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 3974 } 3975 3976 redraw_all_later(CLEAR); 3977 } 3978 3979 /* 3980 * Go to tab page "n". For ":tab N" and "Ngt". 3981 * When "n" is 9999 go to the last tab page. 3982 */ 3983 void 3984 goto_tabpage(int n) 3985 { 3986 tabpage_T *tp; 3987 tabpage_T *ttp; 3988 int i; 3989 3990 if (text_locked()) 3991 { 3992 /* Not allowed when editing the command line. */ 3993 text_locked_msg(); 3994 return; 3995 } 3996 3997 /* If there is only one it can't work. */ 3998 if (first_tabpage->tp_next == NULL) 3999 { 4000 if (n > 1) 4001 beep_flush(); 4002 return; 4003 } 4004 4005 if (n == 0) 4006 { 4007 /* No count, go to next tab page, wrap around end. */ 4008 if (curtab->tp_next == NULL) 4009 tp = first_tabpage; 4010 else 4011 tp = curtab->tp_next; 4012 } 4013 else if (n < 0) 4014 { 4015 /* "gT": go to previous tab page, wrap around end. "N gT" repeats 4016 * this N times. */ 4017 ttp = curtab; 4018 for (i = n; i < 0; ++i) 4019 { 4020 for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL; 4021 tp = tp->tp_next) 4022 ; 4023 ttp = tp; 4024 } 4025 } 4026 else if (n == 9999) 4027 { 4028 /* Go to last tab page. */ 4029 for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) 4030 ; 4031 } 4032 else 4033 { 4034 /* Go to tab page "n". */ 4035 tp = find_tabpage(n); 4036 if (tp == NULL) 4037 { 4038 beep_flush(); 4039 return; 4040 } 4041 } 4042 4043 goto_tabpage_tp(tp, TRUE, TRUE); 4044 4045 #ifdef FEAT_GUI_TABLINE 4046 if (gui_use_tabline()) 4047 gui_mch_set_curtab(tabpage_index(curtab)); 4048 #endif 4049 } 4050 4051 /* 4052 * Go to tabpage "tp". 4053 * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE. 4054 * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE. 4055 * Note: doesn't update the GUI tab. 4056 */ 4057 void 4058 goto_tabpage_tp( 4059 tabpage_T *tp, 4060 int trigger_enter_autocmds, 4061 int trigger_leave_autocmds) 4062 { 4063 /* Don't repeat a message in another tab page. */ 4064 set_keep_msg(NULL, 0); 4065 4066 if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer, 4067 trigger_leave_autocmds) == OK) 4068 { 4069 if (valid_tabpage(tp)) 4070 enter_tabpage(tp, curbuf, trigger_enter_autocmds, 4071 trigger_leave_autocmds); 4072 else 4073 enter_tabpage(curtab, curbuf, trigger_enter_autocmds, 4074 trigger_leave_autocmds); 4075 } 4076 } 4077 4078 /* 4079 * Enter window "wp" in tab page "tp". 4080 * Also updates the GUI tab. 4081 */ 4082 void 4083 goto_tabpage_win(tabpage_T *tp, win_T *wp) 4084 { 4085 goto_tabpage_tp(tp, TRUE, TRUE); 4086 if (curtab == tp && win_valid(wp)) 4087 { 4088 win_enter(wp, TRUE); 4089 # ifdef FEAT_GUI_TABLINE 4090 if (gui_use_tabline()) 4091 gui_mch_set_curtab(tabpage_index(curtab)); 4092 # endif 4093 } 4094 } 4095 4096 /* 4097 * Move the current tab page to after tab page "nr". 4098 */ 4099 void 4100 tabpage_move(int nr) 4101 { 4102 int n = 1; 4103 tabpage_T *tp, *tp_dst; 4104 4105 if (first_tabpage->tp_next == NULL) 4106 return; 4107 4108 for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) 4109 ++n; 4110 4111 if (tp == curtab || (nr > 0 && tp->tp_next != NULL 4112 && tp->tp_next == curtab)) 4113 return; 4114 4115 tp_dst = tp; 4116 4117 /* Remove the current tab page from the list of tab pages. */ 4118 if (curtab == first_tabpage) 4119 first_tabpage = curtab->tp_next; 4120 else 4121 { 4122 FOR_ALL_TABPAGES(tp) 4123 if (tp->tp_next == curtab) 4124 break; 4125 if (tp == NULL) /* "cannot happen" */ 4126 return; 4127 tp->tp_next = curtab->tp_next; 4128 } 4129 4130 /* Re-insert it at the specified position. */ 4131 if (nr <= 0) 4132 { 4133 curtab->tp_next = first_tabpage; 4134 first_tabpage = curtab; 4135 } 4136 else 4137 { 4138 curtab->tp_next = tp_dst->tp_next; 4139 tp_dst->tp_next = curtab; 4140 } 4141 4142 /* Need to redraw the tabline. Tab page contents doesn't change. */ 4143 redraw_tabline = TRUE; 4144 } 4145 4146 4147 /* 4148 * Go to another window. 4149 * When jumping to another buffer, stop Visual mode. Do this before 4150 * changing windows so we can yank the selection into the '*' register. 4151 * When jumping to another window on the same buffer, adjust its cursor 4152 * position to keep the same Visual area. 4153 */ 4154 void 4155 win_goto(win_T *wp) 4156 { 4157 #ifdef FEAT_CONCEAL 4158 win_T *owp = curwin; 4159 #endif 4160 4161 if (text_locked()) 4162 { 4163 beep_flush(); 4164 text_locked_msg(); 4165 return; 4166 } 4167 if (curbuf_locked()) 4168 return; 4169 4170 if (wp->w_buffer != curbuf) 4171 reset_VIsual_and_resel(); 4172 else if (VIsual_active) 4173 wp->w_cursor = curwin->w_cursor; 4174 4175 #ifdef FEAT_GUI 4176 need_mouse_correct = TRUE; 4177 #endif 4178 win_enter(wp, TRUE); 4179 4180 #ifdef FEAT_CONCEAL 4181 /* Conceal cursor line in previous window, unconceal in current window. */ 4182 if (win_valid(owp) && owp->w_p_cole > 0 && !msg_scrolled) 4183 update_single_line(owp, owp->w_cursor.lnum); 4184 if (curwin->w_p_cole > 0 && !msg_scrolled) 4185 need_cursor_line_redraw = TRUE; 4186 #endif 4187 } 4188 4189 #if defined(FEAT_PERL) || defined(PROTO) 4190 /* 4191 * Find window number "winnr" (counting top to bottom). 4192 */ 4193 win_T * 4194 win_find_nr(int winnr) 4195 { 4196 win_T *wp; 4197 4198 FOR_ALL_WINDOWS(wp) 4199 if (--winnr == 0) 4200 break; 4201 return wp; 4202 } 4203 #endif 4204 4205 #if ((defined(FEAT_PYTHON) || defined(FEAT_PYTHON3))) || defined(PROTO) 4206 /* 4207 * Find the tabpage for window "win". 4208 */ 4209 tabpage_T * 4210 win_find_tabpage(win_T *win) 4211 { 4212 win_T *wp; 4213 tabpage_T *tp; 4214 4215 FOR_ALL_TAB_WINDOWS(tp, wp) 4216 if (wp == win) 4217 return tp; 4218 return NULL; 4219 } 4220 #endif 4221 4222 /* 4223 * Move to window above or below "count" times. 4224 */ 4225 static void 4226 win_goto_ver( 4227 int up, /* TRUE to go to win above */ 4228 long count) 4229 { 4230 frame_T *fr; 4231 frame_T *nfr; 4232 frame_T *foundfr; 4233 4234 foundfr = curwin->w_frame; 4235 while (count--) 4236 { 4237 /* 4238 * First go upwards in the tree of frames until we find a upwards or 4239 * downwards neighbor. 4240 */ 4241 fr = foundfr; 4242 for (;;) 4243 { 4244 if (fr == topframe) 4245 goto end; 4246 if (up) 4247 nfr = fr->fr_prev; 4248 else 4249 nfr = fr->fr_next; 4250 if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL) 4251 break; 4252 fr = fr->fr_parent; 4253 } 4254 4255 /* 4256 * Now go downwards to find the bottom or top frame in it. 4257 */ 4258 for (;;) 4259 { 4260 if (nfr->fr_layout == FR_LEAF) 4261 { 4262 foundfr = nfr; 4263 break; 4264 } 4265 fr = nfr->fr_child; 4266 if (nfr->fr_layout == FR_ROW) 4267 { 4268 /* Find the frame at the cursor row. */ 4269 while (fr->fr_next != NULL 4270 && frame2win(fr)->w_wincol + fr->fr_width 4271 <= curwin->w_wincol + curwin->w_wcol) 4272 fr = fr->fr_next; 4273 } 4274 if (nfr->fr_layout == FR_COL && up) 4275 while (fr->fr_next != NULL) 4276 fr = fr->fr_next; 4277 nfr = fr; 4278 } 4279 } 4280 end: 4281 if (foundfr != NULL) 4282 win_goto(foundfr->fr_win); 4283 } 4284 4285 /* 4286 * Move to left or right window. 4287 */ 4288 static void 4289 win_goto_hor( 4290 int left, /* TRUE to go to left win */ 4291 long count) 4292 { 4293 frame_T *fr; 4294 frame_T *nfr; 4295 frame_T *foundfr; 4296 4297 foundfr = curwin->w_frame; 4298 while (count--) 4299 { 4300 /* 4301 * First go upwards in the tree of frames until we find a left or 4302 * right neighbor. 4303 */ 4304 fr = foundfr; 4305 for (;;) 4306 { 4307 if (fr == topframe) 4308 goto end; 4309 if (left) 4310 nfr = fr->fr_prev; 4311 else 4312 nfr = fr->fr_next; 4313 if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL) 4314 break; 4315 fr = fr->fr_parent; 4316 } 4317 4318 /* 4319 * Now go downwards to find the leftmost or rightmost frame in it. 4320 */ 4321 for (;;) 4322 { 4323 if (nfr->fr_layout == FR_LEAF) 4324 { 4325 foundfr = nfr; 4326 break; 4327 } 4328 fr = nfr->fr_child; 4329 if (nfr->fr_layout == FR_COL) 4330 { 4331 /* Find the frame at the cursor row. */ 4332 while (fr->fr_next != NULL 4333 && frame2win(fr)->w_winrow + fr->fr_height 4334 <= curwin->w_winrow + curwin->w_wrow) 4335 fr = fr->fr_next; 4336 } 4337 if (nfr->fr_layout == FR_ROW && left) 4338 while (fr->fr_next != NULL) 4339 fr = fr->fr_next; 4340 nfr = fr; 4341 } 4342 } 4343 end: 4344 if (foundfr != NULL) 4345 win_goto(foundfr->fr_win); 4346 } 4347 4348 /* 4349 * Make window "wp" the current window. 4350 */ 4351 void 4352 win_enter(win_T *wp, int undo_sync) 4353 { 4354 win_enter_ext(wp, undo_sync, FALSE, FALSE, TRUE, TRUE); 4355 } 4356 4357 /* 4358 * Make window wp the current window. 4359 * Can be called with "curwin_invalid" TRUE, which means that curwin has just 4360 * been closed and isn't valid. 4361 */ 4362 static void 4363 win_enter_ext( 4364 win_T *wp, 4365 int undo_sync, 4366 int curwin_invalid, 4367 int trigger_new_autocmds, 4368 int trigger_enter_autocmds, 4369 int trigger_leave_autocmds) 4370 { 4371 int other_buffer = FALSE; 4372 4373 if (wp == curwin && !curwin_invalid) /* nothing to do */ 4374 return; 4375 4376 #ifdef FEAT_JOB_CHANNEL 4377 if (!curwin_invalid) 4378 leaving_window(curwin); 4379 #endif 4380 4381 if (!curwin_invalid && trigger_leave_autocmds) 4382 { 4383 /* 4384 * Be careful: If autocommands delete the window, return now. 4385 */ 4386 if (wp->w_buffer != curbuf) 4387 { 4388 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); 4389 other_buffer = TRUE; 4390 if (!win_valid(wp)) 4391 return; 4392 } 4393 apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); 4394 if (!win_valid(wp)) 4395 return; 4396 #ifdef FEAT_EVAL 4397 /* autocmds may abort script processing */ 4398 if (aborting()) 4399 return; 4400 #endif 4401 } 4402 4403 /* sync undo before leaving the current buffer */ 4404 if (undo_sync && curbuf != wp->w_buffer) 4405 u_sync(FALSE); 4406 4407 /* Might need to scroll the old window before switching, e.g., when the 4408 * cursor was moved. */ 4409 update_topline(); 4410 4411 /* may have to copy the buffer options when 'cpo' contains 'S' */ 4412 if (wp->w_buffer != curbuf) 4413 buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP); 4414 if (!curwin_invalid) 4415 { 4416 prevwin = curwin; /* remember for CTRL-W p */ 4417 curwin->w_redr_status = TRUE; 4418 } 4419 curwin = wp; 4420 curbuf = wp->w_buffer; 4421 check_cursor(); 4422 #ifdef FEAT_VIRTUALEDIT 4423 if (!virtual_active()) 4424 curwin->w_cursor.coladd = 0; 4425 #endif 4426 changed_line_abv_curs(); /* assume cursor position needs updating */ 4427 4428 if (curwin->w_localdir != NULL) 4429 { 4430 /* Window has a local directory: Save current directory as global 4431 * directory (unless that was done already) and change to the local 4432 * directory. */ 4433 if (globaldir == NULL) 4434 { 4435 char_u cwd[MAXPATHL]; 4436 4437 if (mch_dirname(cwd, MAXPATHL) == OK) 4438 globaldir = vim_strsave(cwd); 4439 } 4440 if (mch_chdir((char *)curwin->w_localdir) == 0) 4441 shorten_fnames(TRUE); 4442 } 4443 else if (globaldir != NULL) 4444 { 4445 /* Window doesn't have a local directory and we are not in the global 4446 * directory: Change to the global directory. */ 4447 vim_ignored = mch_chdir((char *)globaldir); 4448 VIM_CLEAR(globaldir); 4449 shorten_fnames(TRUE); 4450 } 4451 4452 #ifdef FEAT_JOB_CHANNEL 4453 entering_window(curwin); 4454 #endif 4455 if (trigger_new_autocmds) 4456 apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf); 4457 if (trigger_enter_autocmds) 4458 { 4459 apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); 4460 if (other_buffer) 4461 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 4462 } 4463 4464 #ifdef FEAT_TITLE 4465 maketitle(); 4466 #endif 4467 curwin->w_redr_status = TRUE; 4468 redraw_tabline = TRUE; 4469 if (restart_edit) 4470 redraw_later(VALID); /* causes status line redraw */ 4471 4472 /* set window height to desired minimal value */ 4473 if (curwin->w_height < p_wh && !curwin->w_p_wfh) 4474 win_setheight((int)p_wh); 4475 else if (curwin->w_height == 0) 4476 win_setheight(1); 4477 4478 /* set window width to desired minimal value */ 4479 if (curwin->w_width < p_wiw && !curwin->w_p_wfw) 4480 win_setwidth((int)p_wiw); 4481 4482 #ifdef FEAT_MOUSE 4483 setmouse(); /* in case jumped to/from help buffer */ 4484 #endif 4485 4486 /* Change directories when the 'acd' option is set. */ 4487 DO_AUTOCHDIR; 4488 } 4489 4490 4491 /* 4492 * Jump to the first open window that contains buffer "buf", if one exists. 4493 * Returns a pointer to the window found, otherwise NULL. 4494 */ 4495 win_T * 4496 buf_jump_open_win(buf_T *buf) 4497 { 4498 win_T *wp = NULL; 4499 4500 if (curwin->w_buffer == buf) 4501 wp = curwin; 4502 else 4503 FOR_ALL_WINDOWS(wp) 4504 if (wp->w_buffer == buf) 4505 break; 4506 if (wp != NULL) 4507 win_enter(wp, FALSE); 4508 return wp; 4509 } 4510 4511 /* 4512 * Jump to the first open window in any tab page that contains buffer "buf", 4513 * if one exists. 4514 * Returns a pointer to the window found, otherwise NULL. 4515 */ 4516 win_T * 4517 buf_jump_open_tab(buf_T *buf) 4518 { 4519 win_T *wp = buf_jump_open_win(buf); 4520 tabpage_T *tp; 4521 4522 if (wp != NULL) 4523 return wp; 4524 4525 FOR_ALL_TABPAGES(tp) 4526 if (tp != curtab) 4527 { 4528 for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) 4529 if (wp->w_buffer == buf) 4530 break; 4531 if (wp != NULL) 4532 { 4533 goto_tabpage_win(tp, wp); 4534 if (curwin != wp) 4535 wp = NULL; /* something went wrong */ 4536 break; 4537 } 4538 } 4539 return wp; 4540 } 4541 4542 static int last_win_id = LOWEST_WIN_ID - 1; 4543 4544 /* 4545 * Allocate a window structure and link it in the window list when "hidden" is 4546 * FALSE. 4547 */ 4548 static win_T * 4549 win_alloc(win_T *after UNUSED, int hidden UNUSED) 4550 { 4551 win_T *new_wp; 4552 4553 /* 4554 * allocate window structure and linesizes arrays 4555 */ 4556 new_wp = (win_T *)alloc_clear((unsigned)sizeof(win_T)); 4557 if (new_wp == NULL) 4558 return NULL; 4559 4560 if (win_alloc_lines(new_wp) == FAIL) 4561 { 4562 vim_free(new_wp); 4563 return NULL; 4564 } 4565 4566 new_wp->w_id = ++last_win_id; 4567 4568 #ifdef FEAT_EVAL 4569 /* init w: variables */ 4570 new_wp->w_vars = dict_alloc(); 4571 if (new_wp->w_vars == NULL) 4572 { 4573 win_free_lsize(new_wp); 4574 vim_free(new_wp); 4575 return NULL; 4576 } 4577 init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); 4578 #endif 4579 4580 /* Don't execute autocommands while the window is not properly 4581 * initialized yet. gui_create_scrollbar() may trigger a FocusGained 4582 * event. */ 4583 block_autocmds(); 4584 4585 /* 4586 * link the window in the window list 4587 */ 4588 if (!hidden) 4589 win_append(after, new_wp); 4590 new_wp->w_wincol = 0; 4591 new_wp->w_width = Columns; 4592 4593 /* position the display and the cursor at the top of the file. */ 4594 new_wp->w_topline = 1; 4595 #ifdef FEAT_DIFF 4596 new_wp->w_topfill = 0; 4597 #endif 4598 new_wp->w_botline = 2; 4599 new_wp->w_cursor.lnum = 1; 4600 new_wp->w_scbind_pos = 1; 4601 4602 /* We won't calculate w_fraction until resizing the window */ 4603 new_wp->w_fraction = 0; 4604 new_wp->w_prev_fraction_row = -1; 4605 4606 #ifdef FEAT_GUI 4607 if (gui.in_use) 4608 { 4609 gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_LEFT], 4610 SBAR_LEFT, new_wp); 4611 gui_create_scrollbar(&new_wp->w_scrollbars[SBAR_RIGHT], 4612 SBAR_RIGHT, new_wp); 4613 } 4614 #endif 4615 #ifdef FEAT_FOLDING 4616 foldInitWin(new_wp); 4617 #endif 4618 unblock_autocmds(); 4619 #ifdef FEAT_SEARCH_EXTRA 4620 new_wp->w_match_head = NULL; 4621 new_wp->w_next_match_id = 4; 4622 #endif 4623 return new_wp; 4624 } 4625 4626 /* 4627 * Remove window 'wp' from the window list and free the structure. 4628 */ 4629 static void 4630 win_free( 4631 win_T *wp, 4632 tabpage_T *tp) /* tab page "win" is in, NULL for current */ 4633 { 4634 int i; 4635 buf_T *buf; 4636 wininfo_T *wip; 4637 4638 #ifdef FEAT_FOLDING 4639 clearFolding(wp); 4640 #endif 4641 4642 /* reduce the reference count to the argument list. */ 4643 alist_unlink(wp->w_alist); 4644 4645 /* Don't execute autocommands while the window is halfway being deleted. 4646 * gui_mch_destroy_scrollbar() may trigger a FocusGained event. */ 4647 block_autocmds(); 4648 4649 #ifdef FEAT_LUA 4650 lua_window_free(wp); 4651 #endif 4652 4653 #ifdef FEAT_MZSCHEME 4654 mzscheme_window_free(wp); 4655 #endif 4656 4657 #ifdef FEAT_PERL 4658 perl_win_free(wp); 4659 #endif 4660 4661 #ifdef FEAT_PYTHON 4662 python_window_free(wp); 4663 #endif 4664 4665 #ifdef FEAT_PYTHON3 4666 python3_window_free(wp); 4667 #endif 4668 4669 #ifdef FEAT_TCL 4670 tcl_window_free(wp); 4671 #endif 4672 4673 #ifdef FEAT_RUBY 4674 ruby_window_free(wp); 4675 #endif 4676 4677 clear_winopt(&wp->w_onebuf_opt); 4678 clear_winopt(&wp->w_allbuf_opt); 4679 4680 #ifdef FEAT_EVAL 4681 vars_clear(&wp->w_vars->dv_hashtab); /* free all w: variables */ 4682 hash_init(&wp->w_vars->dv_hashtab); 4683 unref_var_dict(wp->w_vars); 4684 #endif 4685 4686 { 4687 tabpage_T *ttp; 4688 4689 if (prevwin == wp) 4690 prevwin = NULL; 4691 FOR_ALL_TABPAGES(ttp) 4692 if (ttp->tp_prevwin == wp) 4693 ttp->tp_prevwin = NULL; 4694 } 4695 win_free_lsize(wp); 4696 4697 for (i = 0; i < wp->w_tagstacklen; ++i) 4698 vim_free(wp->w_tagstack[i].tagname); 4699 4700 vim_free(wp->w_localdir); 4701 4702 /* Remove the window from the b_wininfo lists, it may happen that the 4703 * freed memory is re-used for another window. */ 4704 FOR_ALL_BUFFERS(buf) 4705 for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) 4706 if (wip->wi_win == wp) 4707 wip->wi_win = NULL; 4708 4709 #ifdef FEAT_SEARCH_EXTRA 4710 clear_matches(wp); 4711 #endif 4712 4713 #ifdef FEAT_JUMPLIST 4714 free_jumplist(wp); 4715 #endif 4716 4717 #ifdef FEAT_QUICKFIX 4718 qf_free_all(wp); 4719 #endif 4720 4721 #ifdef FEAT_GUI 4722 if (gui.in_use) 4723 { 4724 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_LEFT]); 4725 gui_mch_destroy_scrollbar(&wp->w_scrollbars[SBAR_RIGHT]); 4726 } 4727 #endif /* FEAT_GUI */ 4728 4729 #ifdef FEAT_MENU 4730 remove_winbar(wp); 4731 #endif 4732 4733 #ifdef FEAT_SYN_HL 4734 vim_free(wp->w_p_cc_cols); 4735 #endif 4736 4737 if (wp != aucmd_win) 4738 win_remove(wp, tp); 4739 if (autocmd_busy) 4740 { 4741 wp->w_next = au_pending_free_win; 4742 au_pending_free_win = wp; 4743 } 4744 else 4745 vim_free(wp); 4746 4747 unblock_autocmds(); 4748 } 4749 4750 /* 4751 * Append window "wp" in the window list after window "after". 4752 */ 4753 void 4754 win_append(win_T *after, win_T *wp) 4755 { 4756 win_T *before; 4757 4758 if (after == NULL) /* after NULL is in front of the first */ 4759 before = firstwin; 4760 else 4761 before = after->w_next; 4762 4763 wp->w_next = before; 4764 wp->w_prev = after; 4765 if (after == NULL) 4766 firstwin = wp; 4767 else 4768 after->w_next = wp; 4769 if (before == NULL) 4770 lastwin = wp; 4771 else 4772 before->w_prev = wp; 4773 } 4774 4775 /* 4776 * Remove a window from the window list. 4777 */ 4778 void 4779 win_remove( 4780 win_T *wp, 4781 tabpage_T *tp) /* tab page "win" is in, NULL for current */ 4782 { 4783 if (wp->w_prev != NULL) 4784 wp->w_prev->w_next = wp->w_next; 4785 else if (tp == NULL) 4786 firstwin = curtab->tp_firstwin = wp->w_next; 4787 else 4788 tp->tp_firstwin = wp->w_next; 4789 4790 if (wp->w_next != NULL) 4791 wp->w_next->w_prev = wp->w_prev; 4792 else if (tp == NULL) 4793 lastwin = curtab->tp_lastwin = wp->w_prev; 4794 else 4795 tp->tp_lastwin = wp->w_prev; 4796 } 4797 4798 /* 4799 * Append frame "frp" in a frame list after frame "after". 4800 */ 4801 static void 4802 frame_append(frame_T *after, frame_T *frp) 4803 { 4804 frp->fr_next = after->fr_next; 4805 after->fr_next = frp; 4806 if (frp->fr_next != NULL) 4807 frp->fr_next->fr_prev = frp; 4808 frp->fr_prev = after; 4809 } 4810 4811 /* 4812 * Insert frame "frp" in a frame list before frame "before". 4813 */ 4814 static void 4815 frame_insert(frame_T *before, frame_T *frp) 4816 { 4817 frp->fr_next = before; 4818 frp->fr_prev = before->fr_prev; 4819 before->fr_prev = frp; 4820 if (frp->fr_prev != NULL) 4821 frp->fr_prev->fr_next = frp; 4822 else 4823 frp->fr_parent->fr_child = frp; 4824 } 4825 4826 /* 4827 * Remove a frame from a frame list. 4828 */ 4829 static void 4830 frame_remove(frame_T *frp) 4831 { 4832 if (frp->fr_prev != NULL) 4833 frp->fr_prev->fr_next = frp->fr_next; 4834 else 4835 { 4836 frp->fr_parent->fr_child = frp->fr_next; 4837 /* special case: topframe->fr_child == frp */ 4838 if (topframe->fr_child == frp) 4839 topframe->fr_child = frp->fr_next; 4840 } 4841 if (frp->fr_next != NULL) 4842 frp->fr_next->fr_prev = frp->fr_prev; 4843 } 4844 4845 /* 4846 * Allocate w_lines[] for window "wp". 4847 * Return FAIL for failure, OK for success. 4848 */ 4849 int 4850 win_alloc_lines(win_T *wp) 4851 { 4852 wp->w_lines_valid = 0; 4853 wp->w_lines = (wline_T *)alloc_clear((unsigned)(Rows * sizeof(wline_T))); 4854 if (wp->w_lines == NULL) 4855 return FAIL; 4856 return OK; 4857 } 4858 4859 /* 4860 * free lsize arrays for a window 4861 */ 4862 void 4863 win_free_lsize(win_T *wp) 4864 { 4865 /* TODO: why would wp be NULL here? */ 4866 if (wp != NULL) 4867 VIM_CLEAR(wp->w_lines); 4868 } 4869 4870 /* 4871 * Called from win_new_shellsize() after Rows changed. 4872 * This only does the current tab page, others must be done when made active. 4873 */ 4874 void 4875 shell_new_rows(void) 4876 { 4877 int h = (int)ROWS_AVAIL; 4878 4879 if (firstwin == NULL) /* not initialized yet */ 4880 return; 4881 if (h < frame_minheight(topframe, NULL)) 4882 h = frame_minheight(topframe, NULL); 4883 4884 /* First try setting the heights of windows with 'winfixheight'. If 4885 * that doesn't result in the right height, forget about that option. */ 4886 frame_new_height(topframe, h, FALSE, TRUE); 4887 if (!frame_check_height(topframe, h)) 4888 frame_new_height(topframe, h, FALSE, FALSE); 4889 4890 (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ 4891 compute_cmdrow(); 4892 curtab->tp_ch_used = p_ch; 4893 4894 #if 0 4895 /* Disabled: don't want making the screen smaller make a window larger. */ 4896 if (p_ea) 4897 win_equal(curwin, FALSE, 'v'); 4898 #endif 4899 } 4900 4901 /* 4902 * Called from win_new_shellsize() after Columns changed. 4903 */ 4904 void 4905 shell_new_columns(void) 4906 { 4907 if (firstwin == NULL) /* not initialized yet */ 4908 return; 4909 4910 /* First try setting the widths of windows with 'winfixwidth'. If that 4911 * doesn't result in the right width, forget about that option. */ 4912 frame_new_width(topframe, (int)Columns, FALSE, TRUE); 4913 if (!frame_check_width(topframe, Columns)) 4914 frame_new_width(topframe, (int)Columns, FALSE, FALSE); 4915 4916 (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ 4917 #if 0 4918 /* Disabled: don't want making the screen smaller make a window larger. */ 4919 if (p_ea) 4920 win_equal(curwin, FALSE, 'h'); 4921 #endif 4922 } 4923 4924 #if defined(FEAT_CMDWIN) || defined(PROTO) 4925 /* 4926 * Save the size of all windows in "gap". 4927 */ 4928 void 4929 win_size_save(garray_T *gap) 4930 4931 { 4932 win_T *wp; 4933 4934 ga_init2(gap, (int)sizeof(int), 1); 4935 if (ga_grow(gap, win_count() * 2) == OK) 4936 FOR_ALL_WINDOWS(wp) 4937 { 4938 ((int *)gap->ga_data)[gap->ga_len++] = 4939 wp->w_width + wp->w_vsep_width; 4940 ((int *)gap->ga_data)[gap->ga_len++] = wp->w_height; 4941 } 4942 } 4943 4944 /* 4945 * Restore window sizes, but only if the number of windows is still the same. 4946 * Does not free the growarray. 4947 */ 4948 void 4949 win_size_restore(garray_T *gap) 4950 { 4951 win_T *wp; 4952 int i, j; 4953 4954 if (win_count() * 2 == gap->ga_len) 4955 { 4956 /* The order matters, because frames contain other frames, but it's 4957 * difficult to get right. The easy way out is to do it twice. */ 4958 for (j = 0; j < 2; ++j) 4959 { 4960 i = 0; 4961 FOR_ALL_WINDOWS(wp) 4962 { 4963 frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]); 4964 win_setheight_win(((int *)gap->ga_data)[i++], wp); 4965 } 4966 } 4967 /* recompute the window positions */ 4968 (void)win_comp_pos(); 4969 } 4970 } 4971 #endif /* FEAT_CMDWIN */ 4972 4973 /* 4974 * Update the position for all windows, using the width and height of the 4975 * frames. 4976 * Returns the row just after the last window. 4977 */ 4978 int 4979 win_comp_pos(void) 4980 { 4981 int row = tabline_height(); 4982 int col = 0; 4983 4984 frame_comp_pos(topframe, &row, &col); 4985 return row; 4986 } 4987 4988 /* 4989 * Update the position of the windows in frame "topfrp", using the width and 4990 * height of the frames. 4991 * "*row" and "*col" are the top-left position of the frame. They are updated 4992 * to the bottom-right position plus one. 4993 */ 4994 static void 4995 frame_comp_pos(frame_T *topfrp, int *row, int *col) 4996 { 4997 win_T *wp; 4998 frame_T *frp; 4999 int startcol; 5000 int startrow; 5001 int h; 5002 5003 wp = topfrp->fr_win; 5004 if (wp != NULL) 5005 { 5006 if (wp->w_winrow != *row || wp->w_wincol != *col) 5007 { 5008 /* position changed, redraw */ 5009 wp->w_winrow = *row; 5010 wp->w_wincol = *col; 5011 redraw_win_later(wp, NOT_VALID); 5012 wp->w_redr_status = TRUE; 5013 } 5014 /* WinBar will not show if the window height is zero */ 5015 h = VISIBLE_HEIGHT(wp) + wp->w_status_height; 5016 *row += h > topfrp->fr_height ? topfrp->fr_height : h; 5017 *col += wp->w_width + wp->w_vsep_width; 5018 } 5019 else 5020 { 5021 startrow = *row; 5022 startcol = *col; 5023 FOR_ALL_FRAMES(frp, topfrp->fr_child) 5024 { 5025 if (topfrp->fr_layout == FR_ROW) 5026 *row = startrow; /* all frames are at the same row */ 5027 else 5028 *col = startcol; /* all frames are at the same col */ 5029 frame_comp_pos(frp, row, col); 5030 } 5031 } 5032 } 5033 5034 /* 5035 * Set current window height and take care of repositioning other windows to 5036 * fit around it. 5037 */ 5038 void 5039 win_setheight(int height) 5040 { 5041 win_setheight_win(height, curwin); 5042 } 5043 5044 /* 5045 * Set the window height of window "win" and take care of repositioning other 5046 * windows to fit around it. 5047 */ 5048 void 5049 win_setheight_win(int height, win_T *win) 5050 { 5051 int row; 5052 5053 if (win == curwin) 5054 { 5055 /* Always keep current window at least one line high, even when 5056 * 'winminheight' is zero. */ 5057 if (height < p_wmh) 5058 height = p_wmh; 5059 if (height == 0) 5060 height = 1; 5061 height += WINBAR_HEIGHT(curwin); 5062 } 5063 5064 frame_setheight(win->w_frame, height + win->w_status_height); 5065 5066 /* recompute the window positions */ 5067 row = win_comp_pos(); 5068 5069 /* 5070 * If there is extra space created between the last window and the command 5071 * line, clear it. 5072 */ 5073 if (full_screen && msg_scrolled == 0 && row < cmdline_row) 5074 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); 5075 cmdline_row = row; 5076 msg_row = row; 5077 msg_col = 0; 5078 5079 redraw_all_later(NOT_VALID); 5080 } 5081 5082 /* 5083 * Set the height of a frame to "height" and take care that all frames and 5084 * windows inside it are resized. Also resize frames on the left and right if 5085 * the are in the same FR_ROW frame. 5086 * 5087 * Strategy: 5088 * If the frame is part of a FR_COL frame, try fitting the frame in that 5089 * frame. If that doesn't work (the FR_COL frame is too small), recursively 5090 * go to containing frames to resize them and make room. 5091 * If the frame is part of a FR_ROW frame, all frames must be resized as well. 5092 * Check for the minimal height of the FR_ROW frame. 5093 * At the top level we can also use change the command line height. 5094 */ 5095 static void 5096 frame_setheight(frame_T *curfrp, int height) 5097 { 5098 int room; /* total number of lines available */ 5099 int take; /* number of lines taken from other windows */ 5100 int room_cmdline; /* lines available from cmdline */ 5101 int run; 5102 frame_T *frp; 5103 int h; 5104 int room_reserved; 5105 5106 /* If the height already is the desired value, nothing to do. */ 5107 if (curfrp->fr_height == height) 5108 return; 5109 5110 if (curfrp->fr_parent == NULL) 5111 { 5112 /* topframe: can only change the command line */ 5113 if (height > ROWS_AVAIL) 5114 height = ROWS_AVAIL; 5115 if (height > 0) 5116 frame_new_height(curfrp, height, FALSE, FALSE); 5117 } 5118 else if (curfrp->fr_parent->fr_layout == FR_ROW) 5119 { 5120 /* Row of frames: Also need to resize frames left and right of this 5121 * one. First check for the minimal height of these. */ 5122 h = frame_minheight(curfrp->fr_parent, NULL); 5123 if (height < h) 5124 height = h; 5125 frame_setheight(curfrp->fr_parent, height); 5126 } 5127 else 5128 { 5129 /* 5130 * Column of frames: try to change only frames in this column. 5131 */ 5132 /* 5133 * Do this twice: 5134 * 1: compute room available, if it's not enough try resizing the 5135 * containing frame. 5136 * 2: compute the room available and adjust the height to it. 5137 * Try not to reduce the height of a window with 'winfixheight' set. 5138 */ 5139 for (run = 1; run <= 2; ++run) 5140 { 5141 room = 0; 5142 room_reserved = 0; 5143 FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) 5144 { 5145 if (frp != curfrp 5146 && frp->fr_win != NULL 5147 && frp->fr_win->w_p_wfh) 5148 room_reserved += frp->fr_height; 5149 room += frp->fr_height; 5150 if (frp != curfrp) 5151 room -= frame_minheight(frp, NULL); 5152 } 5153 if (curfrp->fr_width != Columns) 5154 room_cmdline = 0; 5155 else 5156 { 5157 room_cmdline = Rows - p_ch - (lastwin->w_winrow 5158 + VISIBLE_HEIGHT(lastwin) 5159 + lastwin->w_status_height); 5160 if (room_cmdline < 0) 5161 room_cmdline = 0; 5162 } 5163 5164 if (height <= room + room_cmdline) 5165 break; 5166 if (run == 2 || curfrp->fr_width == Columns) 5167 { 5168 if (height > room + room_cmdline) 5169 height = room + room_cmdline; 5170 break; 5171 } 5172 frame_setheight(curfrp->fr_parent, height 5173 + frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1); 5174 } 5175 5176 /* 5177 * Compute the number of lines we will take from others frames (can be 5178 * negative!). 5179 */ 5180 take = height - curfrp->fr_height; 5181 5182 /* If there is not enough room, also reduce the height of a window 5183 * with 'winfixheight' set. */ 5184 if (height > room + room_cmdline - room_reserved) 5185 room_reserved = room + room_cmdline - height; 5186 /* If there is only a 'winfixheight' window and making the 5187 * window smaller, need to make the other window taller. */ 5188 if (take < 0 && room - curfrp->fr_height < room_reserved) 5189 room_reserved = 0; 5190 5191 if (take > 0 && room_cmdline > 0) 5192 { 5193 /* use lines from cmdline first */ 5194 if (take < room_cmdline) 5195 room_cmdline = take; 5196 take -= room_cmdline; 5197 topframe->fr_height += room_cmdline; 5198 } 5199 5200 /* 5201 * set the current frame to the new height 5202 */ 5203 frame_new_height(curfrp, height, FALSE, FALSE); 5204 5205 /* 5206 * First take lines from the frames after the current frame. If 5207 * that is not enough, takes lines from frames above the current 5208 * frame. 5209 */ 5210 for (run = 0; run < 2; ++run) 5211 { 5212 if (run == 0) 5213 frp = curfrp->fr_next; /* 1st run: start with next window */ 5214 else 5215 frp = curfrp->fr_prev; /* 2nd run: start with prev window */ 5216 while (frp != NULL && take != 0) 5217 { 5218 h = frame_minheight(frp, NULL); 5219 if (room_reserved > 0 5220 && frp->fr_win != NULL 5221 && frp->fr_win->w_p_wfh) 5222 { 5223 if (room_reserved >= frp->fr_height) 5224 room_reserved -= frp->fr_height; 5225 else 5226 { 5227 if (frp->fr_height - room_reserved > take) 5228 room_reserved = frp->fr_height - take; 5229 take -= frp->fr_height - room_reserved; 5230 frame_new_height(frp, room_reserved, FALSE, FALSE); 5231 room_reserved = 0; 5232 } 5233 } 5234 else 5235 { 5236 if (frp->fr_height - take < h) 5237 { 5238 take -= frp->fr_height - h; 5239 frame_new_height(frp, h, FALSE, FALSE); 5240 } 5241 else 5242 { 5243 frame_new_height(frp, frp->fr_height - take, 5244 FALSE, FALSE); 5245 take = 0; 5246 } 5247 } 5248 if (run == 0) 5249 frp = frp->fr_next; 5250 else 5251 frp = frp->fr_prev; 5252 } 5253 } 5254 } 5255 } 5256 5257 /* 5258 * Set current window width and take care of repositioning other windows to 5259 * fit around it. 5260 */ 5261 void 5262 win_setwidth(int width) 5263 { 5264 win_setwidth_win(width, curwin); 5265 } 5266 5267 void 5268 win_setwidth_win(int width, win_T *wp) 5269 { 5270 /* Always keep current window at least one column wide, even when 5271 * 'winminwidth' is zero. */ 5272 if (wp == curwin) 5273 { 5274 if (width < p_wmw) 5275 width = p_wmw; 5276 if (width == 0) 5277 width = 1; 5278 } 5279 5280 frame_setwidth(wp->w_frame, width + wp->w_vsep_width); 5281 5282 /* recompute the window positions */ 5283 (void)win_comp_pos(); 5284 5285 redraw_all_later(NOT_VALID); 5286 } 5287 5288 /* 5289 * Set the width of a frame to "width" and take care that all frames and 5290 * windows inside it are resized. Also resize frames above and below if the 5291 * are in the same FR_ROW frame. 5292 * 5293 * Strategy is similar to frame_setheight(). 5294 */ 5295 static void 5296 frame_setwidth(frame_T *curfrp, int width) 5297 { 5298 int room; /* total number of lines available */ 5299 int take; /* number of lines taken from other windows */ 5300 int run; 5301 frame_T *frp; 5302 int w; 5303 int room_reserved; 5304 5305 /* If the width already is the desired value, nothing to do. */ 5306 if (curfrp->fr_width == width) 5307 return; 5308 5309 if (curfrp->fr_parent == NULL) 5310 /* topframe: can't change width */ 5311 return; 5312 5313 if (curfrp->fr_parent->fr_layout == FR_COL) 5314 { 5315 /* Column of frames: Also need to resize frames above and below of 5316 * this one. First check for the minimal width of these. */ 5317 w = frame_minwidth(curfrp->fr_parent, NULL); 5318 if (width < w) 5319 width = w; 5320 frame_setwidth(curfrp->fr_parent, width); 5321 } 5322 else 5323 { 5324 /* 5325 * Row of frames: try to change only frames in this row. 5326 * 5327 * Do this twice: 5328 * 1: compute room available, if it's not enough try resizing the 5329 * containing frame. 5330 * 2: compute the room available and adjust the width to it. 5331 */ 5332 for (run = 1; run <= 2; ++run) 5333 { 5334 room = 0; 5335 room_reserved = 0; 5336 FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) 5337 { 5338 if (frp != curfrp 5339 && frp->fr_win != NULL 5340 && frp->fr_win->w_p_wfw) 5341 room_reserved += frp->fr_width; 5342 room += frp->fr_width; 5343 if (frp != curfrp) 5344 room -= frame_minwidth(frp, NULL); 5345 } 5346 5347 if (width <= room) 5348 break; 5349 if (run == 2 || curfrp->fr_height >= ROWS_AVAIL) 5350 { 5351 if (width > room) 5352 width = room; 5353 break; 5354 } 5355 frame_setwidth(curfrp->fr_parent, width 5356 + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1); 5357 } 5358 5359 /* 5360 * Compute the number of lines we will take from others frames (can be 5361 * negative!). 5362 */ 5363 take = width - curfrp->fr_width; 5364 5365 /* If there is not enough room, also reduce the width of a window 5366 * with 'winfixwidth' set. */ 5367 if (width > room - room_reserved) 5368 room_reserved = room - width; 5369 /* If there is only a 'winfixwidth' window and making the 5370 * window smaller, need to make the other window narrower. */ 5371 if (take < 0 && room - curfrp->fr_width < room_reserved) 5372 room_reserved = 0; 5373 5374 /* 5375 * set the current frame to the new width 5376 */ 5377 frame_new_width(curfrp, width, FALSE, FALSE); 5378 5379 /* 5380 * First take lines from the frames right of the current frame. If 5381 * that is not enough, takes lines from frames left of the current 5382 * frame. 5383 */ 5384 for (run = 0; run < 2; ++run) 5385 { 5386 if (run == 0) 5387 frp = curfrp->fr_next; /* 1st run: start with next window */ 5388 else 5389 frp = curfrp->fr_prev; /* 2nd run: start with prev window */ 5390 while (frp != NULL && take != 0) 5391 { 5392 w = frame_minwidth(frp, NULL); 5393 if (room_reserved > 0 5394 && frp->fr_win != NULL 5395 && frp->fr_win->w_p_wfw) 5396 { 5397 if (room_reserved >= frp->fr_width) 5398 room_reserved -= frp->fr_width; 5399 else 5400 { 5401 if (frp->fr_width - room_reserved > take) 5402 room_reserved = frp->fr_width - take; 5403 take -= frp->fr_width - room_reserved; 5404 frame_new_width(frp, room_reserved, FALSE, FALSE); 5405 room_reserved = 0; 5406 } 5407 } 5408 else 5409 { 5410 if (frp->fr_width - take < w) 5411 { 5412 take -= frp->fr_width - w; 5413 frame_new_width(frp, w, FALSE, FALSE); 5414 } 5415 else 5416 { 5417 frame_new_width(frp, frp->fr_width - take, 5418 FALSE, FALSE); 5419 take = 0; 5420 } 5421 } 5422 if (run == 0) 5423 frp = frp->fr_next; 5424 else 5425 frp = frp->fr_prev; 5426 } 5427 } 5428 } 5429 } 5430 5431 /* 5432 * Check 'winminheight' for a valid value and reduce it if needed. 5433 */ 5434 void 5435 win_setminheight(void) 5436 { 5437 int room; 5438 int needed; 5439 int first = TRUE; 5440 5441 // loop until there is a 'winminheight' that is possible 5442 while (p_wmh > 0) 5443 { 5444 room = Rows - p_ch; 5445 needed = frame_minheight(topframe, NULL); 5446 if (room >= needed) 5447 break; 5448 --p_wmh; 5449 if (first) 5450 { 5451 EMSG(_(e_noroom)); 5452 first = FALSE; 5453 } 5454 } 5455 } 5456 5457 /* 5458 * Check 'winminwidth' for a valid value and reduce it if needed. 5459 */ 5460 void 5461 win_setminwidth(void) 5462 { 5463 int room; 5464 int needed; 5465 int first = TRUE; 5466 5467 // loop until there is a 'winminheight' that is possible 5468 while (p_wmw > 0) 5469 { 5470 room = Columns; 5471 needed = frame_minwidth(topframe, NULL); 5472 if (room >= needed) 5473 break; 5474 --p_wmw; 5475 if (first) 5476 { 5477 EMSG(_(e_noroom)); 5478 first = FALSE; 5479 } 5480 } 5481 } 5482 5483 #if defined(FEAT_MOUSE) || defined(PROTO) 5484 5485 /* 5486 * Status line of dragwin is dragged "offset" lines down (negative is up). 5487 */ 5488 void 5489 win_drag_status_line(win_T *dragwin, int offset) 5490 { 5491 frame_T *curfr; 5492 frame_T *fr; 5493 int room; 5494 int row; 5495 int up; /* if TRUE, drag status line up, otherwise down */ 5496 int n; 5497 5498 fr = dragwin->w_frame; 5499 curfr = fr; 5500 if (fr != topframe) /* more than one window */ 5501 { 5502 fr = fr->fr_parent; 5503 /* When the parent frame is not a column of frames, its parent should 5504 * be. */ 5505 if (fr->fr_layout != FR_COL) 5506 { 5507 curfr = fr; 5508 if (fr != topframe) /* only a row of windows, may drag statusline */ 5509 fr = fr->fr_parent; 5510 } 5511 } 5512 5513 /* If this is the last frame in a column, may want to resize the parent 5514 * frame instead (go two up to skip a row of frames). */ 5515 while (curfr != topframe && curfr->fr_next == NULL) 5516 { 5517 if (fr != topframe) 5518 fr = fr->fr_parent; 5519 curfr = fr; 5520 if (fr != topframe) 5521 fr = fr->fr_parent; 5522 } 5523 5524 if (offset < 0) /* drag up */ 5525 { 5526 up = TRUE; 5527 offset = -offset; 5528 /* sum up the room of the current frame and above it */ 5529 if (fr == curfr) 5530 { 5531 /* only one window */ 5532 room = fr->fr_height - frame_minheight(fr, NULL); 5533 } 5534 else 5535 { 5536 room = 0; 5537 for (fr = fr->fr_child; ; fr = fr->fr_next) 5538 { 5539 room += fr->fr_height - frame_minheight(fr, NULL); 5540 if (fr == curfr) 5541 break; 5542 } 5543 } 5544 fr = curfr->fr_next; /* put fr at frame that grows */ 5545 } 5546 else /* drag down */ 5547 { 5548 up = FALSE; 5549 /* 5550 * Only dragging the last status line can reduce p_ch. 5551 */ 5552 room = Rows - cmdline_row; 5553 if (curfr->fr_next == NULL) 5554 room -= 1; 5555 else 5556 room -= p_ch; 5557 if (room < 0) 5558 room = 0; 5559 /* sum up the room of frames below of the current one */ 5560 FOR_ALL_FRAMES(fr, curfr->fr_next) 5561 room += fr->fr_height - frame_minheight(fr, NULL); 5562 fr = curfr; /* put fr at window that grows */ 5563 } 5564 5565 if (room < offset) /* Not enough room */ 5566 offset = room; /* Move as far as we can */ 5567 if (offset <= 0) 5568 return; 5569 5570 /* 5571 * Grow frame fr by "offset" lines. 5572 * Doesn't happen when dragging the last status line up. 5573 */ 5574 if (fr != NULL) 5575 frame_new_height(fr, fr->fr_height + offset, up, FALSE); 5576 5577 if (up) 5578 fr = curfr; /* current frame gets smaller */ 5579 else 5580 fr = curfr->fr_next; /* next frame gets smaller */ 5581 5582 /* 5583 * Now make the other frames smaller. 5584 */ 5585 while (fr != NULL && offset > 0) 5586 { 5587 n = frame_minheight(fr, NULL); 5588 if (fr->fr_height - offset <= n) 5589 { 5590 offset -= fr->fr_height - n; 5591 frame_new_height(fr, n, !up, FALSE); 5592 } 5593 else 5594 { 5595 frame_new_height(fr, fr->fr_height - offset, !up, FALSE); 5596 break; 5597 } 5598 if (up) 5599 fr = fr->fr_prev; 5600 else 5601 fr = fr->fr_next; 5602 } 5603 row = win_comp_pos(); 5604 screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); 5605 cmdline_row = row; 5606 p_ch = Rows - cmdline_row; 5607 if (p_ch < 1) 5608 p_ch = 1; 5609 curtab->tp_ch_used = p_ch; 5610 redraw_all_later(SOME_VALID); 5611 showmode(); 5612 } 5613 5614 /* 5615 * Separator line of dragwin is dragged "offset" lines right (negative is left). 5616 */ 5617 void 5618 win_drag_vsep_line(win_T *dragwin, int offset) 5619 { 5620 frame_T *curfr; 5621 frame_T *fr; 5622 int room; 5623 int left; /* if TRUE, drag separator line left, otherwise right */ 5624 int n; 5625 5626 fr = dragwin->w_frame; 5627 if (fr == topframe) /* only one window (cannot happen?) */ 5628 return; 5629 curfr = fr; 5630 fr = fr->fr_parent; 5631 /* When the parent frame is not a row of frames, its parent should be. */ 5632 if (fr->fr_layout != FR_ROW) 5633 { 5634 if (fr == topframe) /* only a column of windows (cannot happen?) */ 5635 return; 5636 curfr = fr; 5637 fr = fr->fr_parent; 5638 } 5639 5640 /* If this is the last frame in a row, may want to resize a parent 5641 * frame instead. */ 5642 while (curfr->fr_next == NULL) 5643 { 5644 if (fr == topframe) 5645 break; 5646 curfr = fr; 5647 fr = fr->fr_parent; 5648 if (fr != topframe) 5649 { 5650 curfr = fr; 5651 fr = fr->fr_parent; 5652 } 5653 } 5654 5655 if (offset < 0) /* drag left */ 5656 { 5657 left = TRUE; 5658 offset = -offset; 5659 /* sum up the room of the current frame and left of it */ 5660 room = 0; 5661 for (fr = fr->fr_child; ; fr = fr->fr_next) 5662 { 5663 room += fr->fr_width - frame_minwidth(fr, NULL); 5664 if (fr == curfr) 5665 break; 5666 } 5667 fr = curfr->fr_next; /* put fr at frame that grows */ 5668 } 5669 else /* drag right */ 5670 { 5671 left = FALSE; 5672 /* sum up the room of frames right of the current one */ 5673 room = 0; 5674 FOR_ALL_FRAMES(fr, curfr->fr_next) 5675 room += fr->fr_width - frame_minwidth(fr, NULL); 5676 fr = curfr; /* put fr at window that grows */ 5677 } 5678 5679 if (room < offset) /* Not enough room */ 5680 offset = room; /* Move as far as we can */ 5681 if (offset <= 0) /* No room at all, quit. */ 5682 return; 5683 if (fr == NULL) 5684 return; /* Safety check, should not happen. */ 5685 5686 /* grow frame fr by offset lines */ 5687 frame_new_width(fr, fr->fr_width + offset, left, FALSE); 5688 5689 /* shrink other frames: current and at the left or at the right */ 5690 if (left) 5691 fr = curfr; /* current frame gets smaller */ 5692 else 5693 fr = curfr->fr_next; /* next frame gets smaller */ 5694 5695 while (fr != NULL && offset > 0) 5696 { 5697 n = frame_minwidth(fr, NULL); 5698 if (fr->fr_width - offset <= n) 5699 { 5700 offset -= fr->fr_width - n; 5701 frame_new_width(fr, n, !left, FALSE); 5702 } 5703 else 5704 { 5705 frame_new_width(fr, fr->fr_width - offset, !left, FALSE); 5706 break; 5707 } 5708 if (left) 5709 fr = fr->fr_prev; 5710 else 5711 fr = fr->fr_next; 5712 } 5713 (void)win_comp_pos(); 5714 redraw_all_later(NOT_VALID); 5715 } 5716 #endif /* FEAT_MOUSE */ 5717 5718 #define FRACTION_MULT 16384L 5719 5720 /* 5721 * Set wp->w_fraction for the current w_wrow and w_height. 5722 * Has no effect when the window is less than two lines. 5723 */ 5724 void 5725 set_fraction(win_T *wp) 5726 { 5727 if (wp->w_height > 1) 5728 wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT 5729 + wp->w_height / 2) / (long)wp->w_height; 5730 } 5731 5732 /* 5733 * Set the height of a window. 5734 * "height" excludes any window toolbar. 5735 * This takes care of the things inside the window, not what happens to the 5736 * window position, the frame or to other windows. 5737 */ 5738 void 5739 win_new_height(win_T *wp, int height) 5740 { 5741 int prev_height = wp->w_height; 5742 5743 /* Don't want a negative height. Happens when splitting a tiny window. 5744 * Will equalize heights soon to fix it. */ 5745 if (height < 0) 5746 height = 0; 5747 if (wp->w_height == height) 5748 return; /* nothing to do */ 5749 5750 if (wp->w_height > 0) 5751 { 5752 if (wp == curwin) 5753 /* w_wrow needs to be valid. When setting 'laststatus' this may 5754 * call win_new_height() recursively. */ 5755 validate_cursor(); 5756 if (wp->w_height != prev_height) 5757 return; /* Recursive call already changed the size, bail out here 5758 to avoid the following to mess things up. */ 5759 if (wp->w_wrow != wp->w_prev_fraction_row) 5760 set_fraction(wp); 5761 } 5762 5763 wp->w_height = height; 5764 wp->w_skipcol = 0; 5765 5766 /* There is no point in adjusting the scroll position when exiting. Some 5767 * values might be invalid. */ 5768 if (!exiting) 5769 scroll_to_fraction(wp, prev_height); 5770 } 5771 5772 void 5773 scroll_to_fraction(win_T *wp, int prev_height) 5774 { 5775 linenr_T lnum; 5776 int sline, line_size; 5777 int height = wp->w_height; 5778 5779 /* Don't change w_topline when height is zero. Don't set w_topline when 5780 * 'scrollbind' is set and this isn't the current window. */ 5781 if (height > 0 && (!wp->w_p_scb || wp == curwin)) 5782 { 5783 /* 5784 * Find a value for w_topline that shows the cursor at the same 5785 * relative position in the window as before (more or less). 5786 */ 5787 lnum = wp->w_cursor.lnum; 5788 if (lnum < 1) /* can happen when starting up */ 5789 lnum = 1; 5790 wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L 5791 + FRACTION_MULT / 2) / FRACTION_MULT; 5792 line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; 5793 sline = wp->w_wrow - line_size; 5794 5795 if (sline >= 0) 5796 { 5797 /* Make sure the whole cursor line is visible, if possible. */ 5798 int rows = plines_win(wp, lnum, FALSE); 5799 5800 if (sline > wp->w_height - rows) 5801 { 5802 sline = wp->w_height - rows; 5803 wp->w_wrow -= rows - line_size; 5804 } 5805 } 5806 5807 if (sline < 0) 5808 { 5809 /* 5810 * Cursor line would go off top of screen if w_wrow was this high. 5811 * Make cursor line the first line in the window. If not enough 5812 * room use w_skipcol; 5813 */ 5814 wp->w_wrow = line_size; 5815 if (wp->w_wrow >= wp->w_height 5816 && (wp->w_width - win_col_off(wp)) > 0) 5817 { 5818 wp->w_skipcol += wp->w_width - win_col_off(wp); 5819 --wp->w_wrow; 5820 while (wp->w_wrow >= wp->w_height) 5821 { 5822 wp->w_skipcol += wp->w_width - win_col_off(wp) 5823 + win_col_off2(wp); 5824 --wp->w_wrow; 5825 } 5826 } 5827 set_topline(wp, lnum); 5828 } 5829 else if (sline > 0) 5830 { 5831 while (sline > 0 && lnum > 1) 5832 { 5833 #ifdef FEAT_FOLDING 5834 hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL); 5835 if (lnum == 1) 5836 { 5837 /* first line in buffer is folded */ 5838 line_size = 1; 5839 --sline; 5840 break; 5841 } 5842 #endif 5843 --lnum; 5844 #ifdef FEAT_DIFF 5845 if (lnum == wp->w_topline) 5846 line_size = plines_win_nofill(wp, lnum, TRUE) 5847 + wp->w_topfill; 5848 else 5849 #endif 5850 line_size = plines_win(wp, lnum, TRUE); 5851 sline -= line_size; 5852 } 5853 5854 if (sline < 0) 5855 { 5856 /* 5857 * Line we want at top would go off top of screen. Use next 5858 * line instead. 5859 */ 5860 #ifdef FEAT_FOLDING 5861 hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL); 5862 #endif 5863 lnum++; 5864 wp->w_wrow -= line_size + sline; 5865 } 5866 else if (sline > 0) 5867 { 5868 /* First line of file reached, use that as topline. */ 5869 lnum = 1; 5870 wp->w_wrow -= sline; 5871 } 5872 5873 set_topline(wp, lnum); 5874 } 5875 } 5876 5877 if (wp == curwin) 5878 { 5879 if (p_so) 5880 update_topline(); 5881 curs_columns(FALSE); /* validate w_wrow */ 5882 } 5883 if (prev_height > 0) 5884 wp->w_prev_fraction_row = wp->w_wrow; 5885 5886 win_comp_scroll(wp); 5887 redraw_win_later(wp, SOME_VALID); 5888 wp->w_redr_status = TRUE; 5889 invalidate_botline_win(wp); 5890 } 5891 5892 /* 5893 * Set the width of a window. 5894 */ 5895 void 5896 win_new_width(win_T *wp, int width) 5897 { 5898 wp->w_width = width; 5899 wp->w_lines_valid = 0; 5900 changed_line_abv_curs_win(wp); 5901 invalidate_botline_win(wp); 5902 if (wp == curwin) 5903 { 5904 update_topline(); 5905 curs_columns(TRUE); /* validate w_wrow */ 5906 } 5907 redraw_win_later(wp, NOT_VALID); 5908 wp->w_redr_status = TRUE; 5909 } 5910 5911 void 5912 win_comp_scroll(win_T *wp) 5913 { 5914 wp->w_p_scr = ((unsigned)wp->w_height >> 1); 5915 if (wp->w_p_scr == 0) 5916 wp->w_p_scr = 1; 5917 } 5918 5919 /* 5920 * command_height: called whenever p_ch has been changed 5921 */ 5922 void 5923 command_height(void) 5924 { 5925 int h; 5926 frame_T *frp; 5927 int old_p_ch = curtab->tp_ch_used; 5928 5929 /* Use the value of p_ch that we remembered. This is needed for when the 5930 * GUI starts up, we can't be sure in what order things happen. And when 5931 * p_ch was changed in another tab page. */ 5932 curtab->tp_ch_used = p_ch; 5933 5934 /* Find bottom frame with width of screen. */ 5935 frp = lastwin->w_frame; 5936 while (frp->fr_width != Columns && frp->fr_parent != NULL) 5937 frp = frp->fr_parent; 5938 5939 /* Avoid changing the height of a window with 'winfixheight' set. */ 5940 while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF 5941 && frp->fr_win->w_p_wfh) 5942 frp = frp->fr_prev; 5943 5944 if (starting != NO_SCREEN) 5945 { 5946 cmdline_row = Rows - p_ch; 5947 5948 if (p_ch > old_p_ch) /* p_ch got bigger */ 5949 { 5950 while (p_ch > old_p_ch) 5951 { 5952 if (frp == NULL) 5953 { 5954 EMSG(_(e_noroom)); 5955 p_ch = old_p_ch; 5956 curtab->tp_ch_used = p_ch; 5957 cmdline_row = Rows - p_ch; 5958 break; 5959 } 5960 h = frp->fr_height - frame_minheight(frp, NULL); 5961 if (h > p_ch - old_p_ch) 5962 h = p_ch - old_p_ch; 5963 old_p_ch += h; 5964 frame_add_height(frp, -h); 5965 frp = frp->fr_prev; 5966 } 5967 5968 /* Recompute window positions. */ 5969 (void)win_comp_pos(); 5970 5971 /* clear the lines added to cmdline */ 5972 if (full_screen) 5973 screen_fill((int)(cmdline_row), (int)Rows, 0, 5974 (int)Columns, ' ', ' ', 0); 5975 msg_row = cmdline_row; 5976 redraw_cmdline = TRUE; 5977 return; 5978 } 5979 5980 if (msg_row < cmdline_row) 5981 msg_row = cmdline_row; 5982 redraw_cmdline = TRUE; 5983 } 5984 frame_add_height(frp, (int)(old_p_ch - p_ch)); 5985 5986 /* Recompute window positions. */ 5987 if (frp != lastwin->w_frame) 5988 (void)win_comp_pos(); 5989 } 5990 5991 /* 5992 * Resize frame "frp" to be "n" lines higher (negative for less high). 5993 * Also resize the frames it is contained in. 5994 */ 5995 static void 5996 frame_add_height(frame_T *frp, int n) 5997 { 5998 frame_new_height(frp, frp->fr_height + n, FALSE, FALSE); 5999 for (;;) 6000 { 6001 frp = frp->fr_parent; 6002 if (frp == NULL) 6003 break; 6004 frp->fr_height += n; 6005 } 6006 } 6007 6008 /* 6009 * Add or remove a status line for the bottom window(s), according to the 6010 * value of 'laststatus'. 6011 */ 6012 void 6013 last_status( 6014 int morewin) /* pretend there are two or more windows */ 6015 { 6016 /* Don't make a difference between horizontal or vertical split. */ 6017 last_status_rec(topframe, (p_ls == 2 6018 || (p_ls == 1 && (morewin || !ONE_WINDOW)))); 6019 } 6020 6021 static void 6022 last_status_rec(frame_T *fr, int statusline) 6023 { 6024 frame_T *fp; 6025 win_T *wp; 6026 6027 if (fr->fr_layout == FR_LEAF) 6028 { 6029 wp = fr->fr_win; 6030 if (wp->w_status_height != 0 && !statusline) 6031 { 6032 /* remove status line */ 6033 win_new_height(wp, wp->w_height + 1); 6034 wp->w_status_height = 0; 6035 comp_col(); 6036 } 6037 else if (wp->w_status_height == 0 && statusline) 6038 { 6039 /* Find a frame to take a line from. */ 6040 fp = fr; 6041 while (fp->fr_height <= frame_minheight(fp, NULL)) 6042 { 6043 if (fp == topframe) 6044 { 6045 EMSG(_(e_noroom)); 6046 return; 6047 } 6048 /* In a column of frames: go to frame above. If already at 6049 * the top or in a row of frames: go to parent. */ 6050 if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) 6051 fp = fp->fr_prev; 6052 else 6053 fp = fp->fr_parent; 6054 } 6055 wp->w_status_height = 1; 6056 if (fp != fr) 6057 { 6058 frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE); 6059 frame_fix_height(wp); 6060 (void)win_comp_pos(); 6061 } 6062 else 6063 win_new_height(wp, wp->w_height - 1); 6064 comp_col(); 6065 redraw_all_later(SOME_VALID); 6066 } 6067 } 6068 else if (fr->fr_layout == FR_ROW) 6069 { 6070 /* vertically split windows, set status line for each one */ 6071 FOR_ALL_FRAMES(fp, fr->fr_child) 6072 last_status_rec(fp, statusline); 6073 } 6074 else 6075 { 6076 /* horizontally split window, set status line for last one */ 6077 for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) 6078 ; 6079 last_status_rec(fp, statusline); 6080 } 6081 } 6082 6083 /* 6084 * Return the number of lines used by the tab page line. 6085 */ 6086 int 6087 tabline_height(void) 6088 { 6089 #ifdef FEAT_GUI_TABLINE 6090 /* When the GUI has the tabline then this always returns zero. */ 6091 if (gui_use_tabline()) 6092 return 0; 6093 #endif 6094 switch (p_stal) 6095 { 6096 case 0: return 0; 6097 case 1: return (first_tabpage->tp_next == NULL) ? 0 : 1; 6098 } 6099 return 1; 6100 } 6101 6102 #if defined(FEAT_SEARCHPATH) || defined(PROTO) 6103 /* 6104 * Get the file name at the cursor. 6105 * If Visual mode is active, use the selected text if it's in one line. 6106 * Returns the name in allocated memory, NULL for failure. 6107 */ 6108 char_u * 6109 grab_file_name(long count, linenr_T *file_lnum) 6110 { 6111 int options = FNAME_MESS|FNAME_EXP|FNAME_REL|FNAME_UNESC; 6112 6113 if (VIsual_active) 6114 { 6115 int len; 6116 char_u *ptr; 6117 6118 if (get_visual_text(NULL, &ptr, &len) == FAIL) 6119 return NULL; 6120 return find_file_name_in_path(ptr, len, options, 6121 count, curbuf->b_ffname); 6122 } 6123 return file_name_at_cursor(options | FNAME_HYP, count, file_lnum); 6124 } 6125 6126 /* 6127 * Return the file name under or after the cursor. 6128 * 6129 * The 'path' option is searched if the file name is not absolute. 6130 * The string returned has been alloc'ed and should be freed by the caller. 6131 * NULL is returned if the file name or file is not found. 6132 * 6133 * options: 6134 * FNAME_MESS give error messages 6135 * FNAME_EXP expand to path 6136 * FNAME_HYP check for hypertext link 6137 * FNAME_INCL apply "includeexpr" 6138 */ 6139 char_u * 6140 file_name_at_cursor(int options, long count, linenr_T *file_lnum) 6141 { 6142 return file_name_in_line(ml_get_curline(), 6143 curwin->w_cursor.col, options, count, curbuf->b_ffname, 6144 file_lnum); 6145 } 6146 6147 /* 6148 * Return the name of the file under or after ptr[col]. 6149 * Otherwise like file_name_at_cursor(). 6150 */ 6151 char_u * 6152 file_name_in_line( 6153 char_u *line, 6154 int col, 6155 int options, 6156 long count, 6157 char_u *rel_fname, /* file we are searching relative to */ 6158 linenr_T *file_lnum) /* line number after the file name */ 6159 { 6160 char_u *ptr; 6161 int len; 6162 int in_type = TRUE; 6163 int is_url = FALSE; 6164 6165 /* 6166 * search forward for what could be the start of a file name 6167 */ 6168 ptr = line + col; 6169 while (*ptr != NUL && !vim_isfilec(*ptr)) 6170 MB_PTR_ADV(ptr); 6171 if (*ptr == NUL) /* nothing found */ 6172 { 6173 if (options & FNAME_MESS) 6174 EMSG(_("E446: No file name under cursor")); 6175 return NULL; 6176 } 6177 6178 /* 6179 * Search backward for first char of the file name. 6180 * Go one char back to ":" before "//" even when ':' is not in 'isfname'. 6181 */ 6182 while (ptr > line) 6183 { 6184 #ifdef FEAT_MBYTE 6185 if (has_mbyte && (len = (*mb_head_off)(line, ptr - 1)) > 0) 6186 ptr -= len + 1; 6187 else 6188 #endif 6189 if (vim_isfilec(ptr[-1]) 6190 || ((options & FNAME_HYP) && path_is_url(ptr - 1))) 6191 --ptr; 6192 else 6193 break; 6194 } 6195 6196 /* 6197 * Search forward for the last char of the file name. 6198 * Also allow "://" when ':' is not in 'isfname'. 6199 */ 6200 len = 0; 6201 while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') 6202 || ((options & FNAME_HYP) && path_is_url(ptr + len)) 6203 || (is_url && vim_strchr((char_u *)"?&=", ptr[len]) != NULL)) 6204 { 6205 /* After type:// we also include ?, & and = as valid characters, so that 6206 * http://google.com?q=this&that=ok works. */ 6207 if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) 6208 { 6209 if (in_type && path_is_url(ptr + len + 1)) 6210 is_url = TRUE; 6211 } 6212 else 6213 in_type = FALSE; 6214 6215 if (ptr[len] == '\\') 6216 /* Skip over the "\" in "\ ". */ 6217 ++len; 6218 #ifdef FEAT_MBYTE 6219 if (has_mbyte) 6220 len += (*mb_ptr2len)(ptr + len); 6221 else 6222 #endif 6223 ++len; 6224 } 6225 6226 /* 6227 * If there is trailing punctuation, remove it. 6228 * But don't remove "..", could be a directory name. 6229 */ 6230 if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL 6231 && ptr[len - 2] != '.') 6232 --len; 6233 6234 if (file_lnum != NULL) 6235 { 6236 char_u *p; 6237 6238 /* Get the number after the file name and a separator character */ 6239 p = ptr + len; 6240 p = skipwhite(p); 6241 if (*p != NUL) 6242 { 6243 if (!isdigit(*p)) 6244 ++p; /* skip the separator */ 6245 p = skipwhite(p); 6246 if (isdigit(*p)) 6247 *file_lnum = (int)getdigits(&p); 6248 } 6249 } 6250 6251 return find_file_name_in_path(ptr, len, options, count, rel_fname); 6252 } 6253 6254 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6255 static char_u * 6256 eval_includeexpr(char_u *ptr, int len) 6257 { 6258 char_u *res; 6259 6260 set_vim_var_string(VV_FNAME, ptr, len); 6261 res = eval_to_string_safe(curbuf->b_p_inex, NULL, 6262 was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL)); 6263 set_vim_var_string(VV_FNAME, NULL, 0); 6264 return res; 6265 } 6266 #endif 6267 6268 /* 6269 * Return the name of the file ptr[len] in 'path'. 6270 * Otherwise like file_name_at_cursor(). 6271 */ 6272 char_u * 6273 find_file_name_in_path( 6274 char_u *ptr, 6275 int len, 6276 int options, 6277 long count, 6278 char_u *rel_fname) /* file we are searching relative to */ 6279 { 6280 char_u *file_name; 6281 int c; 6282 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6283 char_u *tofree = NULL; 6284 6285 if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) 6286 { 6287 tofree = eval_includeexpr(ptr, len); 6288 if (tofree != NULL) 6289 { 6290 ptr = tofree; 6291 len = (int)STRLEN(ptr); 6292 } 6293 } 6294 # endif 6295 6296 if (options & FNAME_EXP) 6297 { 6298 file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, 6299 TRUE, rel_fname); 6300 6301 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6302 /* 6303 * If the file could not be found in a normal way, try applying 6304 * 'includeexpr' (unless done already). 6305 */ 6306 if (file_name == NULL 6307 && !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) 6308 { 6309 tofree = eval_includeexpr(ptr, len); 6310 if (tofree != NULL) 6311 { 6312 ptr = tofree; 6313 len = (int)STRLEN(ptr); 6314 file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, 6315 TRUE, rel_fname); 6316 } 6317 } 6318 # endif 6319 if (file_name == NULL && (options & FNAME_MESS)) 6320 { 6321 c = ptr[len]; 6322 ptr[len] = NUL; 6323 EMSG2(_("E447: Can't find file \"%s\" in path"), ptr); 6324 ptr[len] = c; 6325 } 6326 6327 /* Repeat finding the file "count" times. This matters when it 6328 * appears several times in the path. */ 6329 while (file_name != NULL && --count > 0) 6330 { 6331 vim_free(file_name); 6332 file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname); 6333 } 6334 } 6335 else 6336 file_name = vim_strnsave(ptr, len); 6337 6338 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL) 6339 vim_free(tofree); 6340 # endif 6341 6342 return file_name; 6343 } 6344 #endif /* FEAT_SEARCHPATH */ 6345 6346 /* 6347 * Check if the "://" of a URL is at the pointer, return URL_SLASH. 6348 * Also check for ":\\", which MS Internet Explorer accepts, return 6349 * URL_BACKSLASH. 6350 */ 6351 static int 6352 path_is_url(char_u *p) 6353 { 6354 if (STRNCMP(p, "://", (size_t)3) == 0) 6355 return URL_SLASH; 6356 else if (STRNCMP(p, ":\\\\", (size_t)3) == 0) 6357 return URL_BACKSLASH; 6358 return 0; 6359 } 6360 6361 /* 6362 * Check if "fname" starts with "name://". Return URL_SLASH if it does. 6363 * Return URL_BACKSLASH for "name:\\". 6364 * Return zero otherwise. 6365 */ 6366 int 6367 path_with_url(char_u *fname) 6368 { 6369 char_u *p; 6370 6371 for (p = fname; isalpha(*p); ++p) 6372 ; 6373 return path_is_url(p); 6374 } 6375 6376 /* 6377 * Return TRUE if "name" is a full (absolute) path name or URL. 6378 */ 6379 int 6380 vim_isAbsName(char_u *name) 6381 { 6382 return (path_with_url(name) != 0 || mch_isFullName(name)); 6383 } 6384 6385 /* 6386 * Get absolute file name into buffer "buf[len]". 6387 * 6388 * return FAIL for failure, OK otherwise 6389 */ 6390 int 6391 vim_FullName( 6392 char_u *fname, 6393 char_u *buf, 6394 int len, 6395 int force) /* force expansion even when already absolute */ 6396 { 6397 int retval = OK; 6398 int url; 6399 6400 *buf = NUL; 6401 if (fname == NULL) 6402 return FAIL; 6403 6404 url = path_with_url(fname); 6405 if (!url) 6406 retval = mch_FullName(fname, buf, len, force); 6407 if (url || retval == FAIL) 6408 { 6409 /* something failed; use the file name (truncate when too long) */ 6410 vim_strncpy(buf, fname, len - 1); 6411 } 6412 #if defined(MSWIN) 6413 slash_adjust(buf); 6414 #endif 6415 return retval; 6416 } 6417 6418 /* 6419 * Return the minimal number of rows that is needed on the screen to display 6420 * the current number of windows. 6421 */ 6422 int 6423 min_rows(void) 6424 { 6425 int total; 6426 tabpage_T *tp; 6427 int n; 6428 6429 if (firstwin == NULL) /* not initialized yet */ 6430 return MIN_LINES; 6431 6432 total = 0; 6433 FOR_ALL_TABPAGES(tp) 6434 { 6435 n = frame_minheight(tp->tp_topframe, NULL); 6436 if (total < n) 6437 total = n; 6438 } 6439 total += tabline_height(); 6440 total += 1; /* count the room for the command line */ 6441 return total; 6442 } 6443 6444 /* 6445 * Return TRUE if there is only one window (in the current tab page), not 6446 * counting a help or preview window, unless it is the current window. 6447 * Does not count "aucmd_win". 6448 */ 6449 int 6450 only_one_window(void) 6451 { 6452 int count = 0; 6453 win_T *wp; 6454 6455 /* If there is another tab page there always is another window. */ 6456 if (first_tabpage->tp_next != NULL) 6457 return FALSE; 6458 6459 FOR_ALL_WINDOWS(wp) 6460 if (wp->w_buffer != NULL 6461 && (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) 6462 # ifdef FEAT_QUICKFIX 6463 || wp->w_p_pvw 6464 # endif 6465 ) || wp == curwin) && wp != aucmd_win) 6466 ++count; 6467 return (count <= 1); 6468 } 6469 6470 /* 6471 * Correct the cursor line number in other windows. Used after changing the 6472 * current buffer, and before applying autocommands. 6473 * When "do_curwin" is TRUE, also check current window. 6474 */ 6475 void 6476 check_lnums(int do_curwin) 6477 { 6478 win_T *wp; 6479 tabpage_T *tp; 6480 6481 FOR_ALL_TAB_WINDOWS(tp, wp) 6482 if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) 6483 { 6484 if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count) 6485 wp->w_cursor.lnum = curbuf->b_ml.ml_line_count; 6486 if (wp->w_topline > curbuf->b_ml.ml_line_count) 6487 wp->w_topline = curbuf->b_ml.ml_line_count; 6488 } 6489 } 6490 6491 /* 6492 * A snapshot of the window sizes, to restore them after closing the help 6493 * window. 6494 * Only these fields are used: 6495 * fr_layout 6496 * fr_width 6497 * fr_height 6498 * fr_next 6499 * fr_child 6500 * fr_win (only valid for the old curwin, NULL otherwise) 6501 */ 6502 6503 /* 6504 * Create a snapshot of the current frame sizes. 6505 */ 6506 void 6507 make_snapshot(int idx) 6508 { 6509 clear_snapshot(curtab, idx); 6510 make_snapshot_rec(topframe, &curtab->tp_snapshot[idx]); 6511 } 6512 6513 static void 6514 make_snapshot_rec(frame_T *fr, frame_T **frp) 6515 { 6516 *frp = (frame_T *)alloc_clear((unsigned)sizeof(frame_T)); 6517 if (*frp == NULL) 6518 return; 6519 (*frp)->fr_layout = fr->fr_layout; 6520 (*frp)->fr_width = fr->fr_width; 6521 (*frp)->fr_height = fr->fr_height; 6522 if (fr->fr_next != NULL) 6523 make_snapshot_rec(fr->fr_next, &((*frp)->fr_next)); 6524 if (fr->fr_child != NULL) 6525 make_snapshot_rec(fr->fr_child, &((*frp)->fr_child)); 6526 if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin) 6527 (*frp)->fr_win = curwin; 6528 } 6529 6530 /* 6531 * Remove any existing snapshot. 6532 */ 6533 static void 6534 clear_snapshot(tabpage_T *tp, int idx) 6535 { 6536 clear_snapshot_rec(tp->tp_snapshot[idx]); 6537 tp->tp_snapshot[idx] = NULL; 6538 } 6539 6540 static void 6541 clear_snapshot_rec(frame_T *fr) 6542 { 6543 if (fr != NULL) 6544 { 6545 clear_snapshot_rec(fr->fr_next); 6546 clear_snapshot_rec(fr->fr_child); 6547 vim_free(fr); 6548 } 6549 } 6550 6551 /* 6552 * Restore a previously created snapshot, if there is any. 6553 * This is only done if the screen size didn't change and the window layout is 6554 * still the same. 6555 */ 6556 void 6557 restore_snapshot( 6558 int idx, 6559 int close_curwin) /* closing current window */ 6560 { 6561 win_T *wp; 6562 6563 if (curtab->tp_snapshot[idx] != NULL 6564 && curtab->tp_snapshot[idx]->fr_width == topframe->fr_width 6565 && curtab->tp_snapshot[idx]->fr_height == topframe->fr_height 6566 && check_snapshot_rec(curtab->tp_snapshot[idx], topframe) == OK) 6567 { 6568 wp = restore_snapshot_rec(curtab->tp_snapshot[idx], topframe); 6569 win_comp_pos(); 6570 if (wp != NULL && close_curwin) 6571 win_goto(wp); 6572 redraw_all_later(CLEAR); 6573 } 6574 clear_snapshot(curtab, idx); 6575 } 6576 6577 /* 6578 * Check if frames "sn" and "fr" have the same layout, same following frames 6579 * and same children. And the window pointer is valid. 6580 */ 6581 static int 6582 check_snapshot_rec(frame_T *sn, frame_T *fr) 6583 { 6584 if (sn->fr_layout != fr->fr_layout 6585 || (sn->fr_next == NULL) != (fr->fr_next == NULL) 6586 || (sn->fr_child == NULL) != (fr->fr_child == NULL) 6587 || (sn->fr_next != NULL 6588 && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) 6589 || (sn->fr_child != NULL 6590 && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL) 6591 || (sn->fr_win != NULL && !win_valid(sn->fr_win))) 6592 return FAIL; 6593 return OK; 6594 } 6595 6596 /* 6597 * Copy the size of snapshot frame "sn" to frame "fr". Do the same for all 6598 * following frames and children. 6599 * Returns a pointer to the old current window, or NULL. 6600 */ 6601 static win_T * 6602 restore_snapshot_rec(frame_T *sn, frame_T *fr) 6603 { 6604 win_T *wp = NULL; 6605 win_T *wp2; 6606 6607 fr->fr_height = sn->fr_height; 6608 fr->fr_width = sn->fr_width; 6609 if (fr->fr_layout == FR_LEAF) 6610 { 6611 frame_new_height(fr, fr->fr_height, FALSE, FALSE); 6612 frame_new_width(fr, fr->fr_width, FALSE, FALSE); 6613 wp = sn->fr_win; 6614 } 6615 if (sn->fr_next != NULL) 6616 { 6617 wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next); 6618 if (wp2 != NULL) 6619 wp = wp2; 6620 } 6621 if (sn->fr_child != NULL) 6622 { 6623 wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child); 6624 if (wp2 != NULL) 6625 wp = wp2; 6626 } 6627 return wp; 6628 } 6629 6630 #if defined(FEAT_EVAL) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \ 6631 || defined(PROTO) 6632 /* 6633 * Set "win" to be the curwin and "tp" to be the current tab page. 6634 * restore_win() MUST be called to undo, also when FAIL is returned. 6635 * No autocommands will be executed until restore_win() is called. 6636 * When "no_display" is TRUE the display won't be affected, no redraw is 6637 * triggered, another tabpage access is limited. 6638 * Returns FAIL if switching to "win" failed. 6639 */ 6640 int 6641 switch_win( 6642 win_T **save_curwin, 6643 tabpage_T **save_curtab, 6644 win_T *win, 6645 tabpage_T *tp, 6646 int no_display) 6647 { 6648 block_autocmds(); 6649 *save_curwin = curwin; 6650 if (tp != NULL) 6651 { 6652 *save_curtab = curtab; 6653 if (no_display) 6654 { 6655 curtab->tp_firstwin = firstwin; 6656 curtab->tp_lastwin = lastwin; 6657 curtab = tp; 6658 firstwin = curtab->tp_firstwin; 6659 lastwin = curtab->tp_lastwin; 6660 } 6661 else 6662 goto_tabpage_tp(tp, FALSE, FALSE); 6663 } 6664 if (!win_valid(win)) 6665 return FAIL; 6666 curwin = win; 6667 curbuf = curwin->w_buffer; 6668 return OK; 6669 } 6670 6671 /* 6672 * Restore current tabpage and window saved by switch_win(), if still valid. 6673 * When "no_display" is TRUE the display won't be affected, no redraw is 6674 * triggered. 6675 */ 6676 void 6677 restore_win( 6678 win_T *save_curwin UNUSED, 6679 tabpage_T *save_curtab UNUSED, 6680 int no_display UNUSED) 6681 { 6682 if (save_curtab != NULL && valid_tabpage(save_curtab)) 6683 { 6684 if (no_display) 6685 { 6686 curtab->tp_firstwin = firstwin; 6687 curtab->tp_lastwin = lastwin; 6688 curtab = save_curtab; 6689 firstwin = curtab->tp_firstwin; 6690 lastwin = curtab->tp_lastwin; 6691 } 6692 else 6693 goto_tabpage_tp(save_curtab, FALSE, FALSE); 6694 } 6695 if (win_valid(save_curwin)) 6696 { 6697 curwin = save_curwin; 6698 curbuf = curwin->w_buffer; 6699 } 6700 unblock_autocmds(); 6701 } 6702 6703 /* 6704 * Make "buf" the current buffer. restore_buffer() MUST be called to undo. 6705 * No autocommands will be executed. Use aucmd_prepbuf() if there are any. 6706 */ 6707 void 6708 switch_buffer(bufref_T *save_curbuf, buf_T *buf) 6709 { 6710 block_autocmds(); 6711 set_bufref(save_curbuf, curbuf); 6712 --curbuf->b_nwindows; 6713 curbuf = buf; 6714 curwin->w_buffer = buf; 6715 ++curbuf->b_nwindows; 6716 } 6717 6718 /* 6719 * Restore the current buffer after using switch_buffer(). 6720 */ 6721 void 6722 restore_buffer(bufref_T *save_curbuf) 6723 { 6724 unblock_autocmds(); 6725 /* Check for valid buffer, just in case. */ 6726 if (bufref_valid(save_curbuf)) 6727 { 6728 --curbuf->b_nwindows; 6729 curwin->w_buffer = save_curbuf->br_buf; 6730 curbuf = save_curbuf->br_buf; 6731 ++curbuf->b_nwindows; 6732 } 6733 } 6734 #endif 6735 6736 #if defined(FEAT_GUI) || defined(PROTO) 6737 /* 6738 * Return TRUE if there is any vertically split window. 6739 */ 6740 int 6741 win_hasvertsplit(void) 6742 { 6743 frame_T *fr; 6744 6745 if (topframe->fr_layout == FR_ROW) 6746 return TRUE; 6747 6748 if (topframe->fr_layout == FR_COL) 6749 FOR_ALL_FRAMES(fr, topframe->fr_child) 6750 if (fr->fr_layout == FR_ROW) 6751 return TRUE; 6752 6753 return FALSE; 6754 } 6755 #endif 6756 6757 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) 6758 /* 6759 * Add match to the match list of window 'wp'. The pattern 'pat' will be 6760 * highlighted with the group 'grp' with priority 'prio'. 6761 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1). 6762 * If no particular ID is desired, -1 must be specified for 'id'. 6763 * Return ID of added match, -1 on failure. 6764 */ 6765 int 6766 match_add( 6767 win_T *wp, 6768 char_u *grp, 6769 char_u *pat, 6770 int prio, 6771 int id, 6772 list_T *pos_list, 6773 char_u *conceal_char UNUSED) /* pointer to conceal replacement char */ 6774 { 6775 matchitem_T *cur; 6776 matchitem_T *prev; 6777 matchitem_T *m; 6778 int hlg_id; 6779 regprog_T *regprog = NULL; 6780 int rtype = SOME_VALID; 6781 6782 if (*grp == NUL || (pat != NULL && *pat == NUL)) 6783 return -1; 6784 if (id < -1 || id == 0) 6785 { 6786 EMSGN(_("E799: Invalid ID: %ld (must be greater than or equal to 1)"), id); 6787 return -1; 6788 } 6789 if (id != -1) 6790 { 6791 cur = wp->w_match_head; 6792 while (cur != NULL) 6793 { 6794 if (cur->id == id) 6795 { 6796 EMSGN(_("E801: ID already taken: %ld"), id); 6797 return -1; 6798 } 6799 cur = cur->next; 6800 } 6801 } 6802 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) 6803 { 6804 EMSG2(_(e_nogroup), grp); 6805 return -1; 6806 } 6807 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) 6808 { 6809 EMSG2(_(e_invarg2), pat); 6810 return -1; 6811 } 6812 6813 /* Find available match ID. */ 6814 while (id == -1) 6815 { 6816 cur = wp->w_match_head; 6817 while (cur != NULL && cur->id != wp->w_next_match_id) 6818 cur = cur->next; 6819 if (cur == NULL) 6820 id = wp->w_next_match_id; 6821 wp->w_next_match_id++; 6822 } 6823 6824 /* Build new match. */ 6825 m = (matchitem_T *)alloc_clear(sizeof(matchitem_T)); 6826 m->id = id; 6827 m->priority = prio; 6828 m->pattern = pat == NULL ? NULL : vim_strsave(pat); 6829 m->hlg_id = hlg_id; 6830 m->match.regprog = regprog; 6831 m->match.rmm_ic = FALSE; 6832 m->match.rmm_maxcol = 0; 6833 # if defined(FEAT_CONCEAL) && defined(FEAT_MBYTE) 6834 m->conceal_char = 0; 6835 if (conceal_char != NULL) 6836 m->conceal_char = (*mb_ptr2char)(conceal_char); 6837 # endif 6838 6839 /* Set up position matches */ 6840 if (pos_list != NULL) 6841 { 6842 linenr_T toplnum = 0; 6843 linenr_T botlnum = 0; 6844 listitem_T *li; 6845 int i; 6846 6847 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; 6848 i++, li = li->li_next) 6849 { 6850 linenr_T lnum = 0; 6851 colnr_T col = 0; 6852 int len = 1; 6853 list_T *subl; 6854 listitem_T *subli; 6855 int error = FALSE; 6856 6857 if (li->li_tv.v_type == VAR_LIST) 6858 { 6859 subl = li->li_tv.vval.v_list; 6860 if (subl == NULL) 6861 goto fail; 6862 subli = subl->lv_first; 6863 if (subli == NULL) 6864 goto fail; 6865 lnum = tv_get_number_chk(&subli->li_tv, &error); 6866 if (error == TRUE) 6867 goto fail; 6868 if (lnum == 0) 6869 { 6870 --i; 6871 continue; 6872 } 6873 m->pos.pos[i].lnum = lnum; 6874 subli = subli->li_next; 6875 if (subli != NULL) 6876 { 6877 col = tv_get_number_chk(&subli->li_tv, &error); 6878 if (error == TRUE) 6879 goto fail; 6880 subli = subli->li_next; 6881 if (subli != NULL) 6882 { 6883 len = tv_get_number_chk(&subli->li_tv, &error); 6884 if (error == TRUE) 6885 goto fail; 6886 } 6887 } 6888 m->pos.pos[i].col = col; 6889 m->pos.pos[i].len = len; 6890 } 6891 else if (li->li_tv.v_type == VAR_NUMBER) 6892 { 6893 if (li->li_tv.vval.v_number == 0) 6894 { 6895 --i; 6896 continue; 6897 } 6898 m->pos.pos[i].lnum = li->li_tv.vval.v_number; 6899 m->pos.pos[i].col = 0; 6900 m->pos.pos[i].len = 0; 6901 } 6902 else 6903 { 6904 EMSG(_("List or number required")); 6905 goto fail; 6906 } 6907 if (toplnum == 0 || lnum < toplnum) 6908 toplnum = lnum; 6909 if (botlnum == 0 || lnum >= botlnum) 6910 botlnum = lnum + 1; 6911 } 6912 6913 /* Calculate top and bottom lines for redrawing area */ 6914 if (toplnum != 0) 6915 { 6916 if (wp->w_buffer->b_mod_set) 6917 { 6918 if (wp->w_buffer->b_mod_top > toplnum) 6919 wp->w_buffer->b_mod_top = toplnum; 6920 if (wp->w_buffer->b_mod_bot < botlnum) 6921 wp->w_buffer->b_mod_bot = botlnum; 6922 } 6923 else 6924 { 6925 wp->w_buffer->b_mod_set = TRUE; 6926 wp->w_buffer->b_mod_top = toplnum; 6927 wp->w_buffer->b_mod_bot = botlnum; 6928 wp->w_buffer->b_mod_xlines = 0; 6929 } 6930 m->pos.toplnum = toplnum; 6931 m->pos.botlnum = botlnum; 6932 rtype = VALID; 6933 } 6934 } 6935 6936 /* Insert new match. The match list is in ascending order with regard to 6937 * the match priorities. */ 6938 cur = wp->w_match_head; 6939 prev = cur; 6940 while (cur != NULL && prio >= cur->priority) 6941 { 6942 prev = cur; 6943 cur = cur->next; 6944 } 6945 if (cur == prev) 6946 wp->w_match_head = m; 6947 else 6948 prev->next = m; 6949 m->next = cur; 6950 6951 redraw_later(rtype); 6952 return id; 6953 6954 fail: 6955 vim_free(m); 6956 return -1; 6957 } 6958 6959 /* 6960 * Delete match with ID 'id' in the match list of window 'wp'. 6961 * Print error messages if 'perr' is TRUE. 6962 */ 6963 int 6964 match_delete(win_T *wp, int id, int perr) 6965 { 6966 matchitem_T *cur = wp->w_match_head; 6967 matchitem_T *prev = cur; 6968 int rtype = SOME_VALID; 6969 6970 if (id < 1) 6971 { 6972 if (perr == TRUE) 6973 EMSGN(_("E802: Invalid ID: %ld (must be greater than or equal to 1)"), 6974 id); 6975 return -1; 6976 } 6977 while (cur != NULL && cur->id != id) 6978 { 6979 prev = cur; 6980 cur = cur->next; 6981 } 6982 if (cur == NULL) 6983 { 6984 if (perr == TRUE) 6985 EMSGN(_("E803: ID not found: %ld"), id); 6986 return -1; 6987 } 6988 if (cur == prev) 6989 wp->w_match_head = cur->next; 6990 else 6991 prev->next = cur->next; 6992 vim_regfree(cur->match.regprog); 6993 vim_free(cur->pattern); 6994 if (cur->pos.toplnum != 0) 6995 { 6996 if (wp->w_buffer->b_mod_set) 6997 { 6998 if (wp->w_buffer->b_mod_top > cur->pos.toplnum) 6999 wp->w_buffer->b_mod_top = cur->pos.toplnum; 7000 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) 7001 wp->w_buffer->b_mod_bot = cur->pos.botlnum; 7002 } 7003 else 7004 { 7005 wp->w_buffer->b_mod_set = TRUE; 7006 wp->w_buffer->b_mod_top = cur->pos.toplnum; 7007 wp->w_buffer->b_mod_bot = cur->pos.botlnum; 7008 wp->w_buffer->b_mod_xlines = 0; 7009 } 7010 rtype = VALID; 7011 } 7012 vim_free(cur); 7013 redraw_later(rtype); 7014 return 0; 7015 } 7016 7017 /* 7018 * Delete all matches in the match list of window 'wp'. 7019 */ 7020 void 7021 clear_matches(win_T *wp) 7022 { 7023 matchitem_T *m; 7024 7025 while (wp->w_match_head != NULL) 7026 { 7027 m = wp->w_match_head->next; 7028 vim_regfree(wp->w_match_head->match.regprog); 7029 vim_free(wp->w_match_head->pattern); 7030 vim_free(wp->w_match_head); 7031 wp->w_match_head = m; 7032 } 7033 redraw_later(SOME_VALID); 7034 } 7035 7036 /* 7037 * Get match from ID 'id' in window 'wp'. 7038 * Return NULL if match not found. 7039 */ 7040 matchitem_T * 7041 get_match(win_T *wp, int id) 7042 { 7043 matchitem_T *cur = wp->w_match_head; 7044 7045 while (cur != NULL && cur->id != id) 7046 cur = cur->next; 7047 return cur; 7048 } 7049 #endif 7050 7051 #if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) || defined(PROTO) 7052 int 7053 get_win_number(win_T *wp, win_T *first_win) 7054 { 7055 int i = 1; 7056 win_T *w; 7057 7058 for (w = first_win; w != NULL && w != wp; w = W_NEXT(w)) 7059 ++i; 7060 7061 if (w == NULL) 7062 return 0; 7063 else 7064 return i; 7065 } 7066 7067 int 7068 get_tab_number(tabpage_T *tp UNUSED) 7069 { 7070 int i = 1; 7071 tabpage_T *t; 7072 7073 for (t = first_tabpage; t != NULL && t != tp; t = t->tp_next) 7074 ++i; 7075 7076 if (t == NULL) 7077 return 0; 7078 else 7079 return i; 7080 } 7081 #endif 7082 7083 /* 7084 * Return TRUE if "topfrp" and its children are at the right height. 7085 */ 7086 static int 7087 frame_check_height(frame_T *topfrp, int height) 7088 { 7089 frame_T *frp; 7090 7091 if (topfrp->fr_height != height) 7092 return FALSE; 7093 7094 if (topfrp->fr_layout == FR_ROW) 7095 FOR_ALL_FRAMES(frp, topfrp->fr_child) 7096 if (frp->fr_height != height) 7097 return FALSE; 7098 7099 return TRUE; 7100 } 7101 7102 /* 7103 * Return TRUE if "topfrp" and its children are at the right width. 7104 */ 7105 static int 7106 frame_check_width(frame_T *topfrp, int width) 7107 { 7108 frame_T *frp; 7109 7110 if (topfrp->fr_width != width) 7111 return FALSE; 7112 7113 if (topfrp->fr_layout == FR_COL) 7114 FOR_ALL_FRAMES(frp, topfrp->fr_child) 7115 if (frp->fr_width != width) 7116 return FALSE; 7117 7118 return TRUE; 7119 } 7120 7121 #if defined(FEAT_EVAL) || defined(PROTO) 7122 int 7123 win_getid(typval_T *argvars) 7124 { 7125 int winnr; 7126 win_T *wp; 7127 7128 if (argvars[0].v_type == VAR_UNKNOWN) 7129 return curwin->w_id; 7130 winnr = tv_get_number(&argvars[0]); 7131 if (winnr > 0) 7132 { 7133 if (argvars[1].v_type == VAR_UNKNOWN) 7134 wp = firstwin; 7135 else 7136 { 7137 tabpage_T *tp; 7138 int tabnr = tv_get_number(&argvars[1]); 7139 7140 FOR_ALL_TABPAGES(tp) 7141 if (--tabnr == 0) 7142 break; 7143 if (tp == NULL) 7144 return -1; 7145 if (tp == curtab) 7146 wp = firstwin; 7147 else 7148 wp = tp->tp_firstwin; 7149 } 7150 for ( ; wp != NULL; wp = wp->w_next) 7151 if (--winnr == 0) 7152 return wp->w_id; 7153 } 7154 return 0; 7155 } 7156 7157 int 7158 win_gotoid(typval_T *argvars) 7159 { 7160 win_T *wp; 7161 tabpage_T *tp; 7162 int id = tv_get_number(&argvars[0]); 7163 7164 FOR_ALL_TAB_WINDOWS(tp, wp) 7165 if (wp->w_id == id) 7166 { 7167 goto_tabpage_win(tp, wp); 7168 return 1; 7169 } 7170 return 0; 7171 } 7172 7173 void 7174 win_id2tabwin(typval_T *argvars, list_T *list) 7175 { 7176 win_T *wp; 7177 tabpage_T *tp; 7178 int winnr = 1; 7179 int tabnr = 1; 7180 int id = tv_get_number(&argvars[0]); 7181 7182 FOR_ALL_TABPAGES(tp) 7183 { 7184 FOR_ALL_WINDOWS_IN_TAB(tp, wp) 7185 { 7186 if (wp->w_id == id) 7187 { 7188 list_append_number(list, tabnr); 7189 list_append_number(list, winnr); 7190 return; 7191 } 7192 ++winnr; 7193 } 7194 ++tabnr; 7195 winnr = 1; 7196 } 7197 list_append_number(list, 0); 7198 list_append_number(list, 0); 7199 } 7200 7201 win_T * 7202 win_id2wp(typval_T *argvars) 7203 { 7204 win_T *wp; 7205 tabpage_T *tp; 7206 int id = tv_get_number(&argvars[0]); 7207 7208 FOR_ALL_TAB_WINDOWS(tp, wp) 7209 if (wp->w_id == id) 7210 return wp; 7211 7212 return NULL; 7213 } 7214 7215 int 7216 win_id2win(typval_T *argvars) 7217 { 7218 win_T *wp; 7219 int nr = 1; 7220 int id = tv_get_number(&argvars[0]); 7221 7222 FOR_ALL_WINDOWS(wp) 7223 { 7224 if (wp->w_id == id) 7225 return nr; 7226 ++nr; 7227 } 7228 return 0; 7229 } 7230 7231 void 7232 win_findbuf(typval_T *argvars, list_T *list) 7233 { 7234 win_T *wp; 7235 tabpage_T *tp; 7236 int bufnr = tv_get_number(&argvars[0]); 7237 7238 FOR_ALL_TAB_WINDOWS(tp, wp) 7239 if (wp->w_buffer->b_fnum == bufnr) 7240 list_append_number(list, wp->w_id); 7241 } 7242 7243 /* 7244 * Get the layout of the given tab page for winlayout(). 7245 */ 7246 void 7247 get_framelayout(frame_T *fr, list_T *l, int outer) 7248 { 7249 frame_T *child; 7250 list_T *fr_list; 7251 list_T *win_list; 7252 7253 if (fr == NULL) 7254 return; 7255 7256 if (outer) 7257 // outermost call from f_winlayout() 7258 fr_list = l; 7259 else 7260 { 7261 fr_list = list_alloc(); 7262 if (fr_list == NULL) 7263 return; 7264 list_append_list(l, fr_list); 7265 } 7266 7267 if (fr->fr_layout == FR_LEAF) 7268 { 7269 if (fr->fr_win != NULL) 7270 { 7271 list_append_string(fr_list, (char_u *)"leaf", -1); 7272 list_append_number(fr_list, fr->fr_win->w_id); 7273 } 7274 } 7275 else 7276 { 7277 list_append_string(fr_list, 7278 fr->fr_layout == FR_ROW ? (char_u *)"row" : (char_u *)"col", -1); 7279 7280 win_list = list_alloc(); 7281 if (win_list == NULL) 7282 return; 7283 list_append_list(fr_list, win_list); 7284 child = fr->fr_child; 7285 while (child != NULL) 7286 { 7287 get_framelayout(child, win_list, FALSE); 7288 child = child->fr_next; 7289 } 7290 } 7291 } 7292 #endif 7293