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