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