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