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