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