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