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