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