Line | Count | Source |
1 | | /* $OpenBSD$ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> |
5 | | * Copyright (c) 2016 Stephen Kent <smkent@smkent.net> |
6 | | * |
7 | | * Permission to use, copy, modify, and distribute this software for any |
8 | | * purpose with or without fee is hereby granted, provided that the above |
9 | | * copyright notice and this permission notice appear in all copies. |
10 | | * |
11 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
16 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
17 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | | */ |
19 | | |
20 | | #include <sys/types.h> |
21 | | |
22 | | #include <stdlib.h> |
23 | | |
24 | | #include "tmux.h" |
25 | | |
26 | | /* |
27 | | * The window layout is a tree of cells each of which can be one of: a |
28 | | * left-right container for a list of cells, a top-bottom container for a list |
29 | | * of cells, or a container for a window pane. |
30 | | * |
31 | | * Each window has a pointer to the root of its layout tree (containing its |
32 | | * panes), every pane has a pointer back to the cell containing it, and each |
33 | | * cell a pointer to its parent cell. |
34 | | */ |
35 | | |
36 | | static u_int layout_resize_check(struct window *, struct layout_cell *, |
37 | | enum layout_type); |
38 | | static int layout_resize_pane_grow(struct window *, struct layout_cell *, |
39 | | enum layout_type, int, int); |
40 | | static int layout_resize_pane_shrink(struct window *, struct layout_cell *, |
41 | | enum layout_type, int); |
42 | | static u_int layout_new_pane_size(struct window *, u_int, |
43 | | struct layout_cell *, enum layout_type, u_int, u_int, |
44 | | u_int); |
45 | | static int layout_set_size_check(struct window *, struct layout_cell *, |
46 | | enum layout_type, int); |
47 | | static void layout_resize_child_cells(struct window *, |
48 | | struct layout_cell *); |
49 | | void layout_redistribute_cells(struct window *, struct layout_cell *, |
50 | | enum layout_type); |
51 | | |
52 | | /* Create a new layout cell. */ |
53 | | struct layout_cell * |
54 | | layout_create_cell(struct layout_cell *lcparent) |
55 | 0 | { |
56 | 0 | struct layout_cell *lc; |
57 | |
|
58 | 0 | lc = xmalloc(sizeof *lc); |
59 | 0 | lc->type = LAYOUT_WINDOWPANE; |
60 | 0 | lc->parent = lcparent; |
61 | |
|
62 | 0 | TAILQ_INIT(&lc->cells); |
63 | |
|
64 | 0 | lc->sx = UINT_MAX; |
65 | 0 | lc->sy = UINT_MAX; |
66 | |
|
67 | 0 | lc->xoff = INT_MAX; |
68 | 0 | lc->yoff = INT_MAX; |
69 | |
|
70 | 0 | lc->wp = NULL; |
71 | |
|
72 | 0 | return (lc); |
73 | 0 | } |
74 | | |
75 | | /* Free a layout cell. */ |
76 | | void |
77 | | layout_free_cell(struct layout_cell *lc) |
78 | 0 | { |
79 | 0 | struct layout_cell *lcchild; |
80 | |
|
81 | 0 | if (lc == NULL) |
82 | 0 | return; |
83 | | |
84 | 0 | switch (lc->type) { |
85 | 0 | case LAYOUT_LEFTRIGHT: |
86 | 0 | case LAYOUT_TOPBOTTOM: |
87 | 0 | while (!TAILQ_EMPTY(&lc->cells)) { |
88 | 0 | lcchild = TAILQ_FIRST(&lc->cells); |
89 | 0 | TAILQ_REMOVE(&lc->cells, lcchild, entry); |
90 | 0 | layout_free_cell(lcchild); |
91 | 0 | } |
92 | 0 | break; |
93 | 0 | case LAYOUT_FLOATING: |
94 | | /* |
95 | | * A floating layout cell is only used temporarily while |
96 | | * select-layout constructs a layout. Remove the children from |
97 | | * the temporary layout, then free temporary floating layout |
98 | | * cell. Each floating pane has stub layout. |
99 | | */ |
100 | 0 | while (!TAILQ_EMPTY(&lc->cells)) { |
101 | 0 | lcchild = TAILQ_FIRST(&lc->cells); |
102 | 0 | TAILQ_REMOVE(&lc->cells, lcchild, entry); |
103 | 0 | lcchild->parent = NULL; |
104 | 0 | } |
105 | 0 | break; |
106 | 0 | case LAYOUT_WINDOWPANE: |
107 | 0 | if (lc->wp != NULL) { |
108 | 0 | lc->wp->layout_cell->parent = NULL; |
109 | 0 | lc->wp->layout_cell = NULL; |
110 | 0 | } |
111 | 0 | break; |
112 | 0 | } |
113 | | |
114 | 0 | free(lc); |
115 | 0 | } |
116 | | |
117 | | /* Log a cell. */ |
118 | | void |
119 | | layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) |
120 | 0 | { |
121 | 0 | struct layout_cell *lcchild; |
122 | 0 | const char *type; |
123 | |
|
124 | 0 | if (lc == NULL) |
125 | 0 | return; |
126 | | |
127 | 0 | switch (lc->type) { |
128 | 0 | case LAYOUT_LEFTRIGHT: |
129 | 0 | type = "LEFTRIGHT"; |
130 | 0 | break; |
131 | 0 | case LAYOUT_TOPBOTTOM: |
132 | 0 | type = "TOPBOTTOM"; |
133 | 0 | break; |
134 | 0 | case LAYOUT_FLOATING: |
135 | 0 | type = "FLOATING"; |
136 | 0 | break; |
137 | 0 | case LAYOUT_WINDOWPANE: |
138 | 0 | type = "WINDOWPANE"; |
139 | 0 | break; |
140 | 0 | default: |
141 | 0 | type = "UNKNOWN"; |
142 | 0 | break; |
143 | 0 | } |
144 | 0 | log_debug("%s:%*s%p type %s [parent %p] wp=%p [%d,%d %ux%u]", hdr, n, |
145 | 0 | " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, |
146 | 0 | lc->sy); |
147 | 0 | switch (lc->type) { |
148 | 0 | case LAYOUT_LEFTRIGHT: |
149 | 0 | case LAYOUT_TOPBOTTOM: |
150 | 0 | case LAYOUT_FLOATING: |
151 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) |
152 | 0 | layout_print_cell(lcchild, hdr, n + 1); |
153 | 0 | break; |
154 | 0 | case LAYOUT_WINDOWPANE: |
155 | 0 | break; |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | | /* Search for a cell by the border position. */ |
160 | | struct layout_cell * |
161 | | layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) |
162 | 0 | { |
163 | 0 | struct layout_cell *lcchild, *last = NULL; |
164 | |
|
165 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
166 | 0 | if ((int)x >= lcchild->xoff && |
167 | 0 | (int)x < lcchild->xoff + (int)lcchild->sx && |
168 | 0 | (int)y >= lcchild->yoff && |
169 | 0 | (int)y < lcchild->yoff + (int)lcchild->sy) { |
170 | | /* Inside the cell - recurse. */ |
171 | 0 | return (layout_search_by_border(lcchild, x, y)); |
172 | 0 | } |
173 | | |
174 | 0 | if (last == NULL) { |
175 | 0 | last = lcchild; |
176 | 0 | continue; |
177 | 0 | } |
178 | | |
179 | 0 | switch (lc->type) { |
180 | 0 | case LAYOUT_LEFTRIGHT: |
181 | 0 | if ((int)x < lcchild->xoff && |
182 | 0 | (int)x >= last->xoff + (int)last->sx) |
183 | 0 | return (last); |
184 | 0 | break; |
185 | 0 | case LAYOUT_TOPBOTTOM: |
186 | 0 | if ((int)y < lcchild->yoff && |
187 | 0 | (int)y >= last->yoff + (int)last->sy) |
188 | 0 | return (last); |
189 | 0 | break; |
190 | 0 | case LAYOUT_WINDOWPANE: |
191 | 0 | case LAYOUT_FLOATING: |
192 | 0 | break; |
193 | 0 | } |
194 | | |
195 | 0 | last = lcchild; |
196 | 0 | } |
197 | | |
198 | 0 | return (NULL); |
199 | 0 | } |
200 | | |
201 | | /* Set cell size. */ |
202 | | void |
203 | | layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, int xoff, int yoff) |
204 | 0 | { |
205 | 0 | lc->sx = sx; |
206 | 0 | lc->sy = sy; |
207 | |
|
208 | 0 | lc->xoff = xoff; |
209 | 0 | lc->yoff = yoff; |
210 | 0 | } |
211 | | |
212 | | /* Make a cell a leaf cell. */ |
213 | | void |
214 | | layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) |
215 | 0 | { |
216 | 0 | lc->type = LAYOUT_WINDOWPANE; |
217 | |
|
218 | 0 | TAILQ_INIT(&lc->cells); |
219 | |
|
220 | 0 | wp->layout_cell = lc; |
221 | 0 | lc->wp = wp; |
222 | 0 | } |
223 | | |
224 | | /* Make a cell a node cell. */ |
225 | | void |
226 | | layout_make_node(struct layout_cell *lc, enum layout_type type) |
227 | 0 | { |
228 | 0 | if (type == LAYOUT_WINDOWPANE) |
229 | 0 | fatalx("bad layout type"); |
230 | 0 | lc->type = type; |
231 | |
|
232 | 0 | TAILQ_INIT(&lc->cells); |
233 | |
|
234 | 0 | if (lc->wp != NULL) |
235 | 0 | lc->wp->layout_cell = NULL; |
236 | 0 | lc->wp = NULL; |
237 | 0 | } |
238 | | |
239 | | /* Fix z-indexes. */ |
240 | | void |
241 | | layout_fix_zindexes(struct window *w, struct layout_cell *lc) |
242 | 0 | { |
243 | 0 | struct layout_cell *lcchild; |
244 | |
|
245 | 0 | if (lc == NULL) |
246 | 0 | return; |
247 | | |
248 | 0 | switch (lc->type) { |
249 | 0 | case LAYOUT_WINDOWPANE: |
250 | 0 | TAILQ_INSERT_TAIL(&w->z_index, lc->wp, zentry); |
251 | 0 | break; |
252 | 0 | case LAYOUT_LEFTRIGHT: |
253 | 0 | case LAYOUT_TOPBOTTOM: |
254 | 0 | case LAYOUT_FLOATING: |
255 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) |
256 | 0 | layout_fix_zindexes(w, lcchild); |
257 | 0 | return; |
258 | 0 | default: |
259 | 0 | fatalx("bad layout type"); |
260 | 0 | } |
261 | 0 | } |
262 | | |
263 | | /* Fix cell offsets for a child cell. */ |
264 | | static void |
265 | | layout_fix_offsets1(struct layout_cell *lc) |
266 | 0 | { |
267 | 0 | struct layout_cell *lcchild; |
268 | 0 | int xoff, yoff; |
269 | |
|
270 | 0 | if (lc->type == LAYOUT_LEFTRIGHT) { |
271 | 0 | xoff = lc->xoff; |
272 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
273 | 0 | lcchild->xoff = xoff; |
274 | 0 | lcchild->yoff = lc->yoff; |
275 | 0 | if (lcchild->type != LAYOUT_WINDOWPANE) |
276 | 0 | layout_fix_offsets1(lcchild); |
277 | 0 | xoff += lcchild->sx + 1; |
278 | 0 | } |
279 | 0 | } else { |
280 | 0 | yoff = lc->yoff; |
281 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
282 | 0 | lcchild->xoff = lc->xoff; |
283 | 0 | lcchild->yoff = yoff; |
284 | 0 | if (lcchild->type != LAYOUT_WINDOWPANE) |
285 | 0 | layout_fix_offsets1(lcchild); |
286 | 0 | yoff += lcchild->sy + 1; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | | /* Update cell offsets based on their sizes. */ |
292 | | void |
293 | | layout_fix_offsets(struct window *w) |
294 | 0 | { |
295 | 0 | struct layout_cell *lc = w->layout_root; |
296 | |
|
297 | 0 | lc->xoff = 0; |
298 | 0 | lc->yoff = 0; |
299 | |
|
300 | 0 | layout_fix_offsets1(lc); |
301 | 0 | } |
302 | | |
303 | | /* Is this a top cell? */ |
304 | | static int |
305 | | layout_cell_is_top(struct window *w, struct layout_cell *lc) |
306 | 0 | { |
307 | 0 | struct layout_cell *next; |
308 | |
|
309 | 0 | while (lc != w->layout_root) { |
310 | 0 | next = lc->parent; |
311 | 0 | if (next == NULL) |
312 | 0 | return (0); |
313 | 0 | if (next->type == LAYOUT_TOPBOTTOM && |
314 | 0 | lc != TAILQ_FIRST(&next->cells)) |
315 | 0 | return (0); |
316 | 0 | lc = next; |
317 | 0 | } |
318 | 0 | return (1); |
319 | 0 | } |
320 | | |
321 | | /* Is this a bottom cell? */ |
322 | | static int |
323 | | layout_cell_is_bottom(struct window *w, struct layout_cell *lc) |
324 | 0 | { |
325 | 0 | struct layout_cell *next; |
326 | |
|
327 | 0 | while (lc != w->layout_root) { |
328 | 0 | next = lc->parent; |
329 | 0 | if (next == NULL) |
330 | 0 | return (0); |
331 | 0 | if (next->type == LAYOUT_TOPBOTTOM && |
332 | 0 | lc != TAILQ_LAST(&next->cells, layout_cells)) |
333 | 0 | return (0); |
334 | 0 | lc = next; |
335 | 0 | } |
336 | 0 | return (1); |
337 | 0 | } |
338 | | |
339 | | /* |
340 | | * Returns 1 if we need to add an extra line for the pane status line. This is |
341 | | * the case for the most upper or lower panes only. |
342 | | */ |
343 | | static int |
344 | | layout_add_horizontal_border(struct window *w, struct layout_cell *lc, |
345 | | int status) |
346 | 0 | { |
347 | 0 | if (status == PANE_STATUS_TOP) |
348 | 0 | return (layout_cell_is_top(w, lc)); |
349 | 0 | if (status == PANE_STATUS_BOTTOM) |
350 | 0 | return (layout_cell_is_bottom(w, lc)); |
351 | 0 | return (0); |
352 | 0 | } |
353 | | |
354 | | /* Update pane offsets and sizes based on their cells. */ |
355 | | void |
356 | | layout_fix_panes(struct window *w, struct window_pane *skip) |
357 | 0 | { |
358 | 0 | struct window_pane *wp; |
359 | 0 | struct layout_cell *lc; |
360 | 0 | int status, scrollbars, sb_pos, sb_w, sb_pad; |
361 | 0 | u_int sx, sy; |
362 | |
|
363 | 0 | status = options_get_number(w->options, "pane-border-status"); |
364 | 0 | scrollbars = options_get_number(w->options, "pane-scrollbars"); |
365 | 0 | sb_pos = options_get_number(w->options, "pane-scrollbars-position"); |
366 | |
|
367 | 0 | TAILQ_FOREACH(wp, &w->panes, entry) { |
368 | 0 | if ((lc = wp->layout_cell) == NULL || wp == skip) |
369 | 0 | continue; |
370 | | |
371 | 0 | wp->xoff = lc->xoff; |
372 | 0 | wp->yoff = lc->yoff; |
373 | 0 | sx = lc->sx; |
374 | 0 | sy = lc->sy; |
375 | |
|
376 | 0 | if ((~wp->flags & PANE_FLOATING) && |
377 | 0 | layout_add_horizontal_border(w, lc, status)) { |
378 | 0 | if (status == PANE_STATUS_TOP) |
379 | 0 | wp->yoff++; |
380 | 0 | sy--; |
381 | 0 | } |
382 | |
|
383 | 0 | if (window_pane_show_scrollbar(wp, scrollbars)) { |
384 | 0 | sb_w = wp->scrollbar_style.width; |
385 | 0 | sb_pad = wp->scrollbar_style.pad; |
386 | 0 | if (sb_w < 1) |
387 | 0 | sb_w = 1; |
388 | 0 | if (sb_pad < 0) |
389 | 0 | sb_pad = 0; |
390 | 0 | if (sb_pos == PANE_SCROLLBARS_LEFT) { |
391 | 0 | if ((int)sx - sb_w < PANE_MINIMUM) { |
392 | 0 | wp->xoff = wp->xoff + |
393 | 0 | (int)sx - PANE_MINIMUM; |
394 | 0 | sx = PANE_MINIMUM; |
395 | 0 | } else { |
396 | 0 | sx = sx - sb_w - sb_pad; |
397 | 0 | wp->xoff = wp->xoff + sb_w + sb_pad; |
398 | 0 | } |
399 | 0 | } else /* sb_pos == PANE_SCROLLBARS_RIGHT */ |
400 | 0 | if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) |
401 | 0 | sx = PANE_MINIMUM; |
402 | 0 | else |
403 | 0 | sx = sx - sb_w - sb_pad; |
404 | 0 | wp->flags |= PANE_REDRAWSCROLLBAR; |
405 | 0 | } |
406 | |
|
407 | 0 | window_pane_resize(wp, sx, sy); |
408 | 0 | } |
409 | 0 | } |
410 | | |
411 | | /* Count the number of available cells in a layout. */ |
412 | | u_int |
413 | | layout_count_cells(struct layout_cell *lc) |
414 | 0 | { |
415 | 0 | struct layout_cell *lcchild; |
416 | 0 | u_int count = 0; |
417 | |
|
418 | 0 | switch (lc->type) { |
419 | 0 | case LAYOUT_WINDOWPANE: |
420 | 0 | return (1); |
421 | 0 | case LAYOUT_LEFTRIGHT: |
422 | 0 | case LAYOUT_TOPBOTTOM: |
423 | 0 | case LAYOUT_FLOATING: |
424 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) |
425 | 0 | count += layout_count_cells(lcchild); |
426 | 0 | return (count); |
427 | 0 | default: |
428 | 0 | fatalx("bad layout type"); |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | /* Calculate how much size is available to be removed from a cell. */ |
433 | | static u_int |
434 | | layout_resize_check(struct window *w, struct layout_cell *lc, |
435 | | enum layout_type type) |
436 | 0 | { |
437 | 0 | struct layout_cell *lcchild; |
438 | 0 | struct style *sb_style = &w->active->scrollbar_style; |
439 | 0 | u_int available, minimum; |
440 | 0 | int status, scrollbars; |
441 | |
|
442 | 0 | status = options_get_number(w->options, "pane-border-status"); |
443 | 0 | scrollbars = options_get_number(w->options, "pane-scrollbars"); |
444 | |
|
445 | 0 | if (lc->type == LAYOUT_WINDOWPANE) { |
446 | | /* Space available in this cell only. */ |
447 | 0 | if (type == LAYOUT_LEFTRIGHT) { |
448 | 0 | available = lc->sx; |
449 | 0 | if (scrollbars) |
450 | 0 | minimum = PANE_MINIMUM + sb_style->width + |
451 | 0 | sb_style->pad; |
452 | 0 | else |
453 | 0 | minimum = PANE_MINIMUM; |
454 | 0 | } else { |
455 | 0 | available = lc->sy; |
456 | 0 | if (layout_add_horizontal_border(w, lc, status)) |
457 | 0 | minimum = PANE_MINIMUM + 1; |
458 | 0 | else |
459 | 0 | minimum = PANE_MINIMUM; |
460 | 0 | } |
461 | 0 | if (available > minimum) |
462 | 0 | available -= minimum; |
463 | 0 | else |
464 | 0 | available = 0; |
465 | 0 | } else if (lc->type == type) { |
466 | | /* Same type: total of available space in all child cells. */ |
467 | 0 | available = 0; |
468 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) |
469 | 0 | available += layout_resize_check(w, lcchild, type); |
470 | 0 | } else { |
471 | | /* Different type: minimum of available space in child cells. */ |
472 | 0 | minimum = UINT_MAX; |
473 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
474 | 0 | available = layout_resize_check(w, lcchild, type); |
475 | 0 | if (available < minimum) |
476 | 0 | minimum = available; |
477 | 0 | } |
478 | 0 | available = minimum; |
479 | 0 | } |
480 | |
|
481 | 0 | return (available); |
482 | 0 | } |
483 | | |
484 | | /* |
485 | | * Adjust cell size evenly, including altering its children. This function |
486 | | * expects the change to have already been bounded to the space available. |
487 | | */ |
488 | | void |
489 | | layout_resize_adjust(struct window *w, struct layout_cell *lc, |
490 | | enum layout_type type, int change) |
491 | 0 | { |
492 | 0 | struct layout_cell *lcchild; |
493 | | |
494 | | /* Adjust the cell size. */ |
495 | 0 | if (type == LAYOUT_LEFTRIGHT) |
496 | 0 | lc->sx += change; |
497 | 0 | else |
498 | 0 | lc->sy += change; |
499 | | |
500 | | /* If this is a leaf cell, that is all that is necessary. */ |
501 | 0 | if (type == LAYOUT_WINDOWPANE) |
502 | 0 | return; |
503 | | |
504 | | /* Child cell runs in a different direction. */ |
505 | 0 | if (lc->type != type) { |
506 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) |
507 | 0 | layout_resize_adjust(w, lcchild, type, change); |
508 | 0 | return; |
509 | 0 | } |
510 | | |
511 | | /* |
512 | | * Child cell runs in the same direction. Adjust each child equally |
513 | | * until no further change is possible. |
514 | | */ |
515 | 0 | while (change != 0) { |
516 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
517 | 0 | if (change == 0) |
518 | 0 | break; |
519 | 0 | if (change > 0) { |
520 | 0 | layout_resize_adjust(w, lcchild, type, 1); |
521 | 0 | change--; |
522 | 0 | continue; |
523 | 0 | } |
524 | 0 | if (layout_resize_check(w, lcchild, type) > 0) { |
525 | 0 | layout_resize_adjust(w, lcchild, type, -1); |
526 | 0 | change++; |
527 | 0 | } |
528 | 0 | } |
529 | 0 | } |
530 | 0 | } |
531 | | |
532 | | /* Destroy a cell and redistribute the space. */ |
533 | | void |
534 | | layout_destroy_cell(struct window *w, struct layout_cell *lc, |
535 | | struct layout_cell **lcroot) |
536 | 0 | { |
537 | 0 | struct layout_cell *lcother, *lcparent; |
538 | | |
539 | | /* |
540 | | * If no parent, this is either a floating pane or the last |
541 | | * pane so window close is imminent and there is no need to |
542 | | * resize anything. |
543 | | */ |
544 | 0 | lcparent = lc->parent; |
545 | 0 | if (lcparent == NULL) { |
546 | 0 | if (lc->wp != NULL && ~lc->wp->flags & PANE_FLOATING) |
547 | 0 | *lcroot = NULL; |
548 | 0 | layout_free_cell(lc); |
549 | 0 | return; |
550 | 0 | } |
551 | | |
552 | | /* A floating cell need only be removed from the parent. */ |
553 | 0 | if (lcparent->type == LAYOUT_FLOATING) { |
554 | 0 | TAILQ_REMOVE(&lcparent->cells, lc, entry); |
555 | 0 | layout_free_cell(lc); |
556 | 0 | return; |
557 | 0 | } |
558 | | |
559 | | /* Merge the space into the previous or next cell. */ |
560 | 0 | if (lc == TAILQ_FIRST(&lcparent->cells)) |
561 | 0 | lcother = TAILQ_NEXT(lc, entry); |
562 | 0 | else |
563 | 0 | lcother = TAILQ_PREV(lc, layout_cells, entry); |
564 | 0 | if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) |
565 | 0 | layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); |
566 | 0 | else if (lcother != NULL) |
567 | 0 | layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); |
568 | | |
569 | | /* Remove this from the parent's list. */ |
570 | 0 | TAILQ_REMOVE(&lcparent->cells, lc, entry); |
571 | 0 | layout_free_cell(lc); |
572 | | |
573 | | /* |
574 | | * If the parent now has one cell, remove the parent from the tree and |
575 | | * replace it by that cell. |
576 | | */ |
577 | 0 | lc = TAILQ_FIRST(&lcparent->cells); |
578 | 0 | if (lc != NULL && TAILQ_NEXT(lc, entry) == NULL) { |
579 | 0 | TAILQ_REMOVE(&lcparent->cells, lc, entry); |
580 | |
|
581 | 0 | lc->parent = lcparent->parent; |
582 | 0 | if (lc->parent == NULL) { |
583 | 0 | lc->xoff = 0; |
584 | 0 | lc->yoff = 0; |
585 | 0 | *lcroot = lc; |
586 | 0 | } else |
587 | 0 | TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); |
588 | |
|
589 | 0 | layout_free_cell(lcparent); |
590 | 0 | } |
591 | 0 | } |
592 | | |
593 | | /* Initialize layout for pane. */ |
594 | | void |
595 | | layout_init(struct window *w, struct window_pane *wp) |
596 | 0 | { |
597 | 0 | struct layout_cell *lc; |
598 | |
|
599 | 0 | lc = w->layout_root = layout_create_cell(NULL); |
600 | 0 | layout_set_size(lc, w->sx, w->sy, 0, 0); |
601 | 0 | layout_make_leaf(lc, wp); |
602 | 0 | layout_fix_panes(w, NULL); |
603 | 0 | } |
604 | | |
605 | | /* Free layout for pane. */ |
606 | | void |
607 | | layout_free(struct window *w) |
608 | 0 | { |
609 | 0 | layout_free_cell(w->layout_root); |
610 | 0 | } |
611 | | |
612 | | /* Resize the entire layout after window resize. */ |
613 | | void |
614 | | layout_resize(struct window *w, u_int sx, u_int sy) |
615 | 0 | { |
616 | 0 | struct layout_cell *lc = w->layout_root; |
617 | 0 | int xlimit, ylimit, xchange, ychange; |
618 | | |
619 | | /* |
620 | | * Adjust horizontally. Do not attempt to reduce the layout lower than |
621 | | * the minimum (more than the amount returned by layout_resize_check). |
622 | | * |
623 | | * This can mean that the window size is smaller than the total layout |
624 | | * size: redrawing this is handled at a higher level, but it does leave |
625 | | * a problem with growing the window size here: if the current size is |
626 | | * < the minimum, growing proportionately by adding to each pane is |
627 | | * wrong as it would keep the layout size larger than the window size. |
628 | | * Instead, spread the difference between the minimum and the new size |
629 | | * out proportionately - this should leave the layout fitting the new |
630 | | * window size. |
631 | | */ |
632 | 0 | xchange = sx - lc->sx; |
633 | 0 | xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT); |
634 | 0 | if (xchange < 0 && xchange < -xlimit) |
635 | 0 | xchange = -xlimit; |
636 | 0 | if (xlimit == 0) { |
637 | 0 | if (sx <= lc->sx) /* lc->sx is minimum possible */ |
638 | 0 | xchange = 0; |
639 | 0 | else |
640 | 0 | xchange = sx - lc->sx; |
641 | 0 | } |
642 | 0 | if (xchange != 0) |
643 | 0 | layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange); |
644 | | |
645 | | /* Adjust vertically in a similar fashion. */ |
646 | 0 | ychange = sy - lc->sy; |
647 | 0 | ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM); |
648 | 0 | if (ychange < 0 && ychange < -ylimit) |
649 | 0 | ychange = -ylimit; |
650 | 0 | if (ylimit == 0) { |
651 | 0 | if (sy <= lc->sy) /* lc->sy is minimum possible */ |
652 | 0 | ychange = 0; |
653 | 0 | else |
654 | 0 | ychange = sy - lc->sy; |
655 | 0 | } |
656 | 0 | if (ychange != 0) |
657 | 0 | layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange); |
658 | | |
659 | | /* Fix cell offsets. */ |
660 | 0 | layout_fix_offsets(w); |
661 | 0 | layout_fix_panes(w, NULL); |
662 | 0 | } |
663 | | |
664 | | /* Resize a pane to an absolute size. */ |
665 | | void |
666 | | layout_resize_pane_to(struct window_pane *wp, enum layout_type type, |
667 | | u_int new_size) |
668 | 0 | { |
669 | 0 | struct layout_cell *lc, *lcparent; |
670 | 0 | int change, size; |
671 | |
|
672 | 0 | lc = wp->layout_cell; |
673 | | |
674 | | /* Find next parent of the same type. */ |
675 | 0 | lcparent = lc->parent; |
676 | 0 | while (lcparent != NULL && lcparent->type != type) { |
677 | 0 | lc = lcparent; |
678 | 0 | lcparent = lc->parent; |
679 | 0 | } |
680 | 0 | if (lcparent == NULL) |
681 | 0 | return; |
682 | | |
683 | | /* Work out the size adjustment. */ |
684 | 0 | if (type == LAYOUT_LEFTRIGHT) |
685 | 0 | size = lc->sx; |
686 | 0 | else |
687 | 0 | size = lc->sy; |
688 | 0 | if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) |
689 | 0 | change = size - new_size; |
690 | 0 | else |
691 | 0 | change = new_size - size; |
692 | | |
693 | | /* Resize the pane. */ |
694 | 0 | layout_resize_pane(wp, type, change, 1); |
695 | 0 | } |
696 | | |
697 | | void |
698 | | layout_resize_layout(struct window *w, struct layout_cell *lc, |
699 | | enum layout_type type, int change, int opposite) |
700 | 0 | { |
701 | 0 | int needed, size; |
702 | | |
703 | | /* Grow or shrink the cell. */ |
704 | 0 | needed = change; |
705 | 0 | while (needed != 0) { |
706 | 0 | if (change > 0) { |
707 | 0 | size = layout_resize_pane_grow(w, lc, type, needed, |
708 | 0 | opposite); |
709 | 0 | needed -= size; |
710 | 0 | } else { |
711 | 0 | size = layout_resize_pane_shrink(w, lc, type, needed); |
712 | 0 | needed += size; |
713 | 0 | } |
714 | |
|
715 | 0 | if (size == 0) /* no more change possible */ |
716 | 0 | break; |
717 | 0 | } |
718 | | |
719 | | /* Fix cell offsets. */ |
720 | 0 | layout_fix_offsets(w); |
721 | 0 | layout_fix_panes(w, NULL); |
722 | 0 | notify_window("window-layout-changed", w); |
723 | 0 | } |
724 | | |
725 | | /* Resize a single pane within the layout. */ |
726 | | void |
727 | | layout_resize_pane(struct window_pane *wp, enum layout_type type, int change, |
728 | | int opposite) |
729 | 0 | { |
730 | 0 | struct layout_cell *lc, *lcparent; |
731 | |
|
732 | 0 | lc = wp->layout_cell; |
733 | | |
734 | | /* Find next parent of the same type. */ |
735 | 0 | lcparent = lc->parent; |
736 | 0 | while (lcparent != NULL && lcparent->type != type) { |
737 | 0 | lc = lcparent; |
738 | 0 | lcparent = lc->parent; |
739 | 0 | } |
740 | 0 | if (lcparent == NULL) |
741 | 0 | return; |
742 | | |
743 | | /* If this is the last cell, move back one. */ |
744 | 0 | if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) |
745 | 0 | lc = TAILQ_PREV(lc, layout_cells, entry); |
746 | |
|
747 | 0 | layout_resize_layout(wp->window, lc, type, change, opposite); |
748 | 0 | } |
749 | | |
750 | | /* Helper function to grow pane. */ |
751 | | static int |
752 | | layout_resize_pane_grow(struct window *w, struct layout_cell *lc, |
753 | | enum layout_type type, int needed, int opposite) |
754 | 0 | { |
755 | 0 | struct layout_cell *lcadd, *lcremove; |
756 | 0 | u_int size = 0; |
757 | | |
758 | | /* Growing. Always add to the current cell. */ |
759 | 0 | lcadd = lc; |
760 | | |
761 | | /* Look towards the tail for a suitable cell for reduction. */ |
762 | 0 | lcremove = TAILQ_NEXT(lc, entry); |
763 | 0 | while (lcremove != NULL) { |
764 | 0 | size = layout_resize_check(w, lcremove, type); |
765 | 0 | if (size > 0) |
766 | 0 | break; |
767 | 0 | lcremove = TAILQ_NEXT(lcremove, entry); |
768 | 0 | } |
769 | | |
770 | | /* If none found, look towards the head. */ |
771 | 0 | if (opposite && lcremove == NULL) { |
772 | 0 | lcremove = TAILQ_PREV(lc, layout_cells, entry); |
773 | 0 | while (lcremove != NULL) { |
774 | 0 | size = layout_resize_check(w, lcremove, type); |
775 | 0 | if (size > 0) |
776 | 0 | break; |
777 | 0 | lcremove = TAILQ_PREV(lcremove, layout_cells, entry); |
778 | 0 | } |
779 | 0 | } |
780 | 0 | if (lcremove == NULL) |
781 | 0 | return (0); |
782 | | |
783 | | /* Change the cells. */ |
784 | 0 | if (size > (u_int) needed) |
785 | 0 | size = needed; |
786 | 0 | layout_resize_adjust(w, lcadd, type, size); |
787 | 0 | layout_resize_adjust(w, lcremove, type, -size); |
788 | 0 | return (size); |
789 | 0 | } |
790 | | |
791 | | /* Helper function to shrink pane. */ |
792 | | static int |
793 | | layout_resize_pane_shrink(struct window *w, struct layout_cell *lc, |
794 | | enum layout_type type, int needed) |
795 | 0 | { |
796 | 0 | struct layout_cell *lcadd, *lcremove; |
797 | 0 | u_int size; |
798 | | |
799 | | /* Shrinking. Find cell to remove from by walking towards head. */ |
800 | 0 | lcremove = lc; |
801 | 0 | do { |
802 | 0 | size = layout_resize_check(w, lcremove, type); |
803 | 0 | if (size != 0) |
804 | 0 | break; |
805 | 0 | lcremove = TAILQ_PREV(lcremove, layout_cells, entry); |
806 | 0 | } while (lcremove != NULL); |
807 | 0 | if (lcremove == NULL) |
808 | 0 | return (0); |
809 | | |
810 | | /* And add onto the next cell (from the original cell). */ |
811 | 0 | lcadd = TAILQ_NEXT(lc, entry); |
812 | 0 | if (lcadd == NULL) |
813 | 0 | return (0); |
814 | | |
815 | | /* Change the cells. */ |
816 | 0 | if (size > (u_int) -needed) |
817 | 0 | size = -needed; |
818 | 0 | layout_resize_adjust(w, lcadd, type, size); |
819 | 0 | layout_resize_adjust(w, lcremove, type, -size); |
820 | 0 | return (size); |
821 | 0 | } |
822 | | |
823 | | /* Assign window pane to new cell. */ |
824 | | void |
825 | | layout_assign_pane(struct layout_cell *lc, struct window_pane *wp, |
826 | | int do_not_resize) |
827 | 0 | { |
828 | 0 | layout_make_leaf(lc, wp); |
829 | 0 | if (do_not_resize) |
830 | 0 | layout_fix_panes(wp->window, wp); |
831 | 0 | else |
832 | 0 | layout_fix_panes(wp->window, NULL); |
833 | 0 | } |
834 | | |
835 | | /* Calculate the new pane size for resized parent. */ |
836 | | static u_int |
837 | | layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc, |
838 | | enum layout_type type, u_int size, u_int count_left, u_int size_left) |
839 | 0 | { |
840 | 0 | u_int new_size, min, max, available; |
841 | | |
842 | | /* If this is the last cell, it can take all of the remaining size. */ |
843 | 0 | if (count_left == 1) |
844 | 0 | return (size_left); |
845 | | |
846 | | /* How much is available in this parent? */ |
847 | 0 | available = layout_resize_check(w, lc, type); |
848 | | |
849 | | /* |
850 | | * Work out the minimum size of this cell and the new size |
851 | | * proportionate to the previous size. |
852 | | */ |
853 | 0 | min = (PANE_MINIMUM + 1) * (count_left - 1); |
854 | 0 | if (type == LAYOUT_LEFTRIGHT) { |
855 | 0 | if (lc->sx - available > min) |
856 | 0 | min = lc->sx - available; |
857 | 0 | new_size = (lc->sx * size) / previous; |
858 | 0 | } else { |
859 | 0 | if (lc->sy - available > min) |
860 | 0 | min = lc->sy - available; |
861 | 0 | new_size = (lc->sy * size) / previous; |
862 | 0 | } |
863 | | |
864 | | /* Check against the maximum and minimum size. */ |
865 | 0 | max = size_left - min; |
866 | 0 | if (new_size > max) |
867 | 0 | new_size = max; |
868 | 0 | if (new_size < PANE_MINIMUM) |
869 | 0 | new_size = PANE_MINIMUM; |
870 | 0 | return (new_size); |
871 | 0 | } |
872 | | |
873 | | /* Check if the cell and all its children can be resized to a specific size. */ |
874 | | static int |
875 | | layout_set_size_check(struct window *w, struct layout_cell *lc, |
876 | | enum layout_type type, int size) |
877 | 0 | { |
878 | 0 | struct layout_cell *lcchild; |
879 | 0 | u_int new_size, available, previous, count, idx; |
880 | | |
881 | | /* Cells with no children must just be bigger than minimum. */ |
882 | 0 | if (lc->type == LAYOUT_WINDOWPANE) |
883 | 0 | return (size >= PANE_MINIMUM); |
884 | 0 | available = size; |
885 | | |
886 | | /* Count number of children. */ |
887 | 0 | count = 0; |
888 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) |
889 | 0 | count++; |
890 | | |
891 | | /* Check new size will work for each child. */ |
892 | 0 | if (lc->type == type) { |
893 | 0 | if (available < (count * 2) - 1) |
894 | 0 | return (0); |
895 | | |
896 | 0 | if (type == LAYOUT_LEFTRIGHT) |
897 | 0 | previous = lc->sx; |
898 | 0 | else |
899 | 0 | previous = lc->sy; |
900 | |
|
901 | 0 | idx = 0; |
902 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
903 | 0 | new_size = layout_new_pane_size(w, previous, lcchild, |
904 | 0 | type, size, count - idx, available); |
905 | 0 | if (idx == count - 1) { |
906 | 0 | if (new_size > available) |
907 | 0 | return (0); |
908 | 0 | available -= new_size; |
909 | 0 | } else { |
910 | 0 | if (new_size + 1 > available) |
911 | 0 | return (0); |
912 | 0 | available -= new_size + 1; |
913 | 0 | } |
914 | 0 | if (!layout_set_size_check(w, lcchild, type, new_size)) |
915 | 0 | return (0); |
916 | 0 | idx++; |
917 | 0 | } |
918 | 0 | } else { |
919 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
920 | 0 | if (lcchild->type == LAYOUT_WINDOWPANE) |
921 | 0 | continue; |
922 | 0 | if (!layout_set_size_check(w, lcchild, type, size)) |
923 | 0 | return (0); |
924 | 0 | } |
925 | 0 | } |
926 | | |
927 | 0 | return (1); |
928 | 0 | } |
929 | | |
930 | | /* Resize all child cells to fit within the current cell. */ |
931 | | static void |
932 | | layout_resize_child_cells(struct window *w, struct layout_cell *lc) |
933 | 0 | { |
934 | 0 | struct layout_cell *lcchild; |
935 | 0 | u_int previous, available, count, idx; |
936 | |
|
937 | 0 | if (lc->type == LAYOUT_WINDOWPANE) |
938 | 0 | return; |
939 | | |
940 | | /* What is the current size used? */ |
941 | 0 | count = 0; |
942 | 0 | previous = 0; |
943 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
944 | 0 | count++; |
945 | 0 | if (lc->type == LAYOUT_LEFTRIGHT) |
946 | 0 | previous += lcchild->sx; |
947 | 0 | else if (lc->type == LAYOUT_TOPBOTTOM) |
948 | 0 | previous += lcchild->sy; |
949 | 0 | } |
950 | 0 | previous += (count - 1); |
951 | | |
952 | | /* And how much is available? */ |
953 | 0 | available = 0; |
954 | 0 | if (lc->type == LAYOUT_LEFTRIGHT) |
955 | 0 | available = lc->sx; |
956 | 0 | else if (lc->type == LAYOUT_TOPBOTTOM) |
957 | 0 | available = lc->sy; |
958 | | |
959 | | /* Resize children into the new size. */ |
960 | 0 | idx = 0; |
961 | 0 | TAILQ_FOREACH(lcchild, &lc->cells, entry) { |
962 | 0 | if (lc->type == LAYOUT_TOPBOTTOM) { |
963 | 0 | lcchild->sx = lc->sx; |
964 | 0 | lcchild->xoff = lc->xoff; |
965 | 0 | } else { |
966 | 0 | lcchild->sx = layout_new_pane_size(w, previous, lcchild, |
967 | 0 | lc->type, lc->sx, count - idx, available); |
968 | 0 | available -= (lcchild->sx + 1); |
969 | 0 | } |
970 | 0 | if (lc->type == LAYOUT_LEFTRIGHT) |
971 | 0 | lcchild->sy = lc->sy; |
972 | 0 | else { |
973 | 0 | lcchild->sy = layout_new_pane_size(w, previous, lcchild, |
974 | 0 | lc->type, lc->sy, count - idx, available); |
975 | 0 | available -= (lcchild->sy + 1); |
976 | 0 | } |
977 | 0 | layout_resize_child_cells(w, lcchild); |
978 | 0 | idx++; |
979 | 0 | } |
980 | 0 | } |
981 | | |
982 | | /* |
983 | | * Split a pane into two. size is a hint, or -1 for default half/half |
984 | | * split. This must be followed by layout_assign_pane before much else happens! |
985 | | */ |
986 | | struct layout_cell * |
987 | | layout_split_pane(struct window_pane *wp, enum layout_type type, int size, |
988 | | int flags) |
989 | 0 | { |
990 | 0 | struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; |
991 | 0 | struct style *sb_style = &wp->scrollbar_style; |
992 | 0 | u_int sx, sy, xoff, yoff, size1, size2, minimum; |
993 | 0 | u_int new_size, saved_size, resize_first = 0; |
994 | 0 | int full_size = (flags & SPAWN_FULLSIZE), status; |
995 | 0 | int scrollbars; |
996 | | |
997 | | /* |
998 | | * If full_size is specified, add a new cell at the top of the window |
999 | | * layout. Otherwise, split the cell for the current pane. |
1000 | | */ |
1001 | 0 | if (full_size) |
1002 | 0 | lc = wp->window->layout_root; |
1003 | 0 | else |
1004 | 0 | lc = wp->layout_cell; |
1005 | 0 | status = options_get_number(wp->window->options, "pane-border-status"); |
1006 | 0 | scrollbars = options_get_number(wp->window->options, "pane-scrollbars"); |
1007 | | |
1008 | | /* Copy the old cell size. */ |
1009 | 0 | sx = lc->sx; |
1010 | 0 | sy = lc->sy; |
1011 | 0 | xoff = lc->xoff; |
1012 | 0 | yoff = lc->yoff; |
1013 | | |
1014 | | /* Check there is enough space for the two new panes. */ |
1015 | 0 | switch (type) { |
1016 | 0 | case LAYOUT_LEFTRIGHT: |
1017 | 0 | if (scrollbars) { |
1018 | 0 | minimum = PANE_MINIMUM * 2 + sb_style->width + |
1019 | 0 | sb_style->pad; |
1020 | 0 | } else |
1021 | 0 | minimum = PANE_MINIMUM * 2 + 1; |
1022 | 0 | if (sx < minimum) |
1023 | 0 | return (NULL); |
1024 | 0 | break; |
1025 | 0 | case LAYOUT_TOPBOTTOM: |
1026 | 0 | if (layout_add_horizontal_border(wp->window, lc, status)) |
1027 | 0 | minimum = PANE_MINIMUM * 2 + 2; |
1028 | 0 | else |
1029 | 0 | minimum = PANE_MINIMUM * 2 + 1; |
1030 | 0 | if (sy < minimum) |
1031 | 0 | return (NULL); |
1032 | 0 | break; |
1033 | 0 | default: |
1034 | 0 | fatalx("bad layout type"); |
1035 | 0 | } |
1036 | | |
1037 | | /* |
1038 | | * Calculate new cell sizes. size is the target size or -1 for middle |
1039 | | * split, size1 is the size of the top/left and size2 the bottom/right. |
1040 | | */ |
1041 | 0 | if (type == LAYOUT_LEFTRIGHT) |
1042 | 0 | saved_size = sx; |
1043 | 0 | else |
1044 | 0 | saved_size = sy; |
1045 | 0 | if (size < 0) |
1046 | 0 | size2 = ((saved_size + 1) / 2) - 1; |
1047 | 0 | else if (flags & SPAWN_BEFORE) |
1048 | 0 | size2 = saved_size - size - 1; |
1049 | 0 | else |
1050 | 0 | size2 = size; |
1051 | 0 | if (size2 < PANE_MINIMUM) |
1052 | 0 | size2 = PANE_MINIMUM; |
1053 | 0 | else if (size2 > saved_size - 2) |
1054 | 0 | size2 = saved_size - 2; |
1055 | 0 | size1 = saved_size - 1 - size2; |
1056 | | |
1057 | | /* Which size are we using? */ |
1058 | 0 | if (flags & SPAWN_BEFORE) |
1059 | 0 | new_size = size2; |
1060 | 0 | else |
1061 | 0 | new_size = size1; |
1062 | | |
1063 | | /* Confirm there is enough space for full size pane. */ |
1064 | 0 | if (full_size && !layout_set_size_check(wp->window, lc, type, new_size)) |
1065 | 0 | return (NULL); |
1066 | | |
1067 | 0 | if (lc->parent != NULL && lc->parent->type == type) { |
1068 | | /* |
1069 | | * If the parent exists and is of the same type as the split, |
1070 | | * create a new cell and insert it after this one. |
1071 | | */ |
1072 | 0 | lcparent = lc->parent; |
1073 | 0 | lcnew = layout_create_cell(lcparent); |
1074 | 0 | if (flags & SPAWN_BEFORE) |
1075 | 0 | TAILQ_INSERT_BEFORE(lc, lcnew, entry); |
1076 | 0 | else |
1077 | 0 | TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); |
1078 | 0 | } else if (full_size && lc->parent == NULL && lc->type == type) { |
1079 | | /* |
1080 | | * If the new full size pane is the same type as the root |
1081 | | * split, insert the new pane under the existing root cell |
1082 | | * instead of creating a new root cell. The existing layout |
1083 | | * must be resized before inserting the new cell. |
1084 | | */ |
1085 | 0 | if (lc->type == LAYOUT_LEFTRIGHT) { |
1086 | 0 | lc->sx = new_size; |
1087 | 0 | layout_resize_child_cells(wp->window, lc); |
1088 | 0 | lc->sx = saved_size; |
1089 | 0 | } else if (lc->type == LAYOUT_TOPBOTTOM) { |
1090 | 0 | lc->sy = new_size; |
1091 | 0 | layout_resize_child_cells(wp->window, lc); |
1092 | 0 | lc->sy = saved_size; |
1093 | 0 | } |
1094 | 0 | resize_first = 1; |
1095 | | |
1096 | | /* Create the new cell. */ |
1097 | 0 | lcnew = layout_create_cell(lc); |
1098 | 0 | size = saved_size - 1 - new_size; |
1099 | 0 | if (lc->type == LAYOUT_LEFTRIGHT) |
1100 | 0 | layout_set_size(lcnew, size, sy, 0, 0); |
1101 | 0 | else if (lc->type == LAYOUT_TOPBOTTOM) |
1102 | 0 | layout_set_size(lcnew, sx, size, 0, 0); |
1103 | 0 | if (flags & SPAWN_BEFORE) |
1104 | 0 | TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry); |
1105 | 0 | else |
1106 | 0 | TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); |
1107 | 0 | } else { |
1108 | | /* |
1109 | | * Otherwise create a new parent and insert it. |
1110 | | */ |
1111 | | |
1112 | | /* Create and insert the replacement parent. */ |
1113 | 0 | lcparent = layout_create_cell(lc->parent); |
1114 | 0 | layout_make_node(lcparent, type); |
1115 | 0 | layout_set_size(lcparent, sx, sy, xoff, yoff); |
1116 | 0 | if (lc->parent == NULL) |
1117 | 0 | wp->window->layout_root = lcparent; |
1118 | 0 | else |
1119 | 0 | TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry); |
1120 | | |
1121 | | /* Insert the old cell. */ |
1122 | 0 | lc->parent = lcparent; |
1123 | 0 | TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); |
1124 | | |
1125 | | /* Create the new child cell. */ |
1126 | 0 | lcnew = layout_create_cell(lcparent); |
1127 | 0 | if (flags & SPAWN_BEFORE) |
1128 | 0 | TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); |
1129 | 0 | else |
1130 | 0 | TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); |
1131 | 0 | } |
1132 | 0 | if (flags & SPAWN_BEFORE) { |
1133 | 0 | lc1 = lcnew; |
1134 | 0 | lc2 = lc; |
1135 | 0 | } else { |
1136 | 0 | lc1 = lc; |
1137 | 0 | lc2 = lcnew; |
1138 | 0 | } |
1139 | | |
1140 | | /* |
1141 | | * Set new cell sizes. size1 is the size of the top/left and size2 the |
1142 | | * bottom/right. |
1143 | | */ |
1144 | 0 | if (!resize_first && type == LAYOUT_LEFTRIGHT) { |
1145 | 0 | layout_set_size(lc1, size1, sy, xoff, yoff); |
1146 | 0 | layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff); |
1147 | 0 | } else if (!resize_first && type == LAYOUT_TOPBOTTOM) { |
1148 | 0 | layout_set_size(lc1, sx, size1, xoff, yoff); |
1149 | 0 | layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1); |
1150 | 0 | } |
1151 | 0 | if (full_size) { |
1152 | 0 | if (!resize_first) |
1153 | 0 | layout_resize_child_cells(wp->window, lc); |
1154 | 0 | layout_fix_offsets(wp->window); |
1155 | 0 | } else |
1156 | 0 | layout_make_leaf(lc, wp); |
1157 | |
|
1158 | 0 | return (lcnew); |
1159 | 0 | } |
1160 | | |
1161 | | /* Destroy the cell associated with a pane. */ |
1162 | | void |
1163 | | layout_close_pane(struct window_pane *wp) |
1164 | 0 | { |
1165 | 0 | struct window *w = wp->window; |
1166 | |
|
1167 | 0 | if (wp->layout_cell == NULL) |
1168 | 0 | return; |
1169 | | |
1170 | | /* Remove the cell. */ |
1171 | 0 | layout_destroy_cell(w, wp->layout_cell, &w->layout_root); |
1172 | | |
1173 | | /* Fix pane offsets and sizes. */ |
1174 | 0 | if (w->layout_root != NULL) { |
1175 | 0 | layout_fix_offsets(w); |
1176 | 0 | layout_fix_panes(w, NULL); |
1177 | 0 | } |
1178 | 0 | notify_window("window-layout-changed", w); |
1179 | 0 | } |
1180 | | |
1181 | | int |
1182 | | layout_spread_cell(struct window *w, struct layout_cell *parent) |
1183 | 0 | { |
1184 | 0 | struct layout_cell *lc; |
1185 | 0 | u_int number, each, size, this, remainder; |
1186 | 0 | int change, changed, status; |
1187 | |
|
1188 | 0 | number = 0; |
1189 | 0 | TAILQ_FOREACH (lc, &parent->cells, entry) |
1190 | 0 | number++; |
1191 | 0 | if (number <= 1) |
1192 | 0 | return (0); |
1193 | 0 | status = options_get_number(w->options, "pane-border-status"); |
1194 | |
|
1195 | 0 | if (parent->type == LAYOUT_LEFTRIGHT) |
1196 | 0 | size = parent->sx; |
1197 | 0 | else if (parent->type == LAYOUT_TOPBOTTOM) { |
1198 | 0 | if (layout_add_horizontal_border(w, parent, status)) |
1199 | 0 | size = parent->sy - 1; |
1200 | 0 | else |
1201 | 0 | size = parent->sy; |
1202 | 0 | } else |
1203 | 0 | return (0); |
1204 | 0 | if (size < number - 1) |
1205 | 0 | return (0); |
1206 | 0 | each = (size - (number - 1)) / number; |
1207 | 0 | if (each == 0) |
1208 | 0 | return (0); |
1209 | | /* |
1210 | | * Remaining space after assigning that which can be evenly |
1211 | | * distributed. |
1212 | | */ |
1213 | 0 | remainder = size - (number * (each + 1)) + 1; |
1214 | |
|
1215 | 0 | changed = 0; |
1216 | 0 | TAILQ_FOREACH (lc, &parent->cells, entry) { |
1217 | 0 | change = 0; |
1218 | 0 | if (parent->type == LAYOUT_LEFTRIGHT) { |
1219 | 0 | change = each - (int)lc->sx; |
1220 | 0 | if (remainder > 0) { |
1221 | 0 | change++; |
1222 | 0 | remainder--; |
1223 | 0 | } |
1224 | 0 | layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); |
1225 | 0 | } else if (parent->type == LAYOUT_TOPBOTTOM) { |
1226 | 0 | if (layout_add_horizontal_border(w, lc, status)) |
1227 | 0 | this = each + 1; |
1228 | 0 | else |
1229 | 0 | this = each; |
1230 | 0 | if (remainder > 0) { |
1231 | 0 | this++; |
1232 | 0 | remainder--; |
1233 | 0 | } |
1234 | 0 | change = this - (int)lc->sy; |
1235 | 0 | layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); |
1236 | 0 | } |
1237 | 0 | if (change != 0) |
1238 | 0 | changed = 1; |
1239 | 0 | } |
1240 | 0 | return (changed); |
1241 | 0 | } |
1242 | | |
1243 | | void |
1244 | | layout_spread_out(struct window_pane *wp) |
1245 | 0 | { |
1246 | 0 | struct layout_cell *parent; |
1247 | 0 | struct window *w = wp->window; |
1248 | |
|
1249 | 0 | parent = wp->layout_cell->parent; |
1250 | 0 | if (parent == NULL) |
1251 | 0 | return; |
1252 | | |
1253 | 0 | do { |
1254 | 0 | if (layout_spread_cell(w, parent)) { |
1255 | 0 | layout_fix_offsets(w); |
1256 | 0 | layout_fix_panes(w, NULL); |
1257 | 0 | break; |
1258 | 0 | } |
1259 | 0 | } while ((parent = parent->parent) != NULL); |
1260 | 0 | } |