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