| 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_MAX(2147483647 *2U +1U); | ||||||
| 62 | lc->sy = UINT_MAX(2147483647 *2U +1U); | ||||||
| 63 | |||||||
| 64 | lc->xoff = UINT_MAX(2147483647 *2U +1U); | ||||||
| 65 | lc->yoff = UINT_MAX(2147483647 *2U +1U); | ||||||
| 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_MAX(2147483647 *2U +1U); | ||||||
| 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 | } |