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