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