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