summaryrefslogtreecommitdiffstats
path: root/layout.c
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@openbsd.org>2009-07-19 13:21:40 +0000
committerNicholas Marriott <nicm@openbsd.org>2009-07-19 13:21:40 +0000
commit6036bdd06cba9fa500098f1f17983d62955b24e5 (patch)
treef19dc96b0b6f65ca03222018df09defd585bed90 /layout.c
parentfc6a65c6207dbefcd8b5f187148978f8d4111b2b (diff)
Improved layout code.
Each window now has a tree of layout cells associated with it. In this tree, each node is either a horizontal or vertical cell containing a list of other cells running from left-to-right or top-to-bottom, or a leaf cell which is associated with a pane. The major functional changes are: - panes may now be split arbitrarily both horizontally (splitw -h, C-b %) and vertically (splitw -v, C-b "); - panes may be resized both horizontally and vertically (resizep -L/-R/-U/-D, bound to C-b left/right/up/down and C-b M-left/right/up/down); - layouts are now applied and then may be modified by resizing or splitting panes, rather than being fixed and reapplied when the window is resized or panes are added; - manual-vertical layout is no longer necessary, and active-only layout is gone (but may return in future); - the main-pane layouts now reduce the size of the main pane to fit all panes if possible. Thanks to all who tested.
Diffstat (limited to 'layout.c')
-rw-r--r--layout.c787
1 files changed, 535 insertions, 252 deletions
diff --git a/layout.c b/layout.c
index 66cf7af2..0cf6e387 100644
--- a/layout.c
+++ b/layout.c
@@ -18,349 +18,632 @@
#include <sys/types.h>
-#include <string.h>
+#include <stdlib.h>
#include "tmux.h"
/*
- * Each layout has two functions, _refresh to relayout the panes and _resize to
- * resize a single pane.
+ * The window layout is a tree of cells each of which can be one of: a
+ * left-right container for a list of cells, a top-bottom container for a list
+ * of cells, or a container for a window pane.
*
- * Second argument (int) to _refresh is 1 if the only change has been that the
- * active pane has changed. If 0 then panes, active pane or both may have
- * changed.
+ * Each window has a pointer to the root of its layout tree (containing its
+ * panes), every pane has a pointer back to the cell containing it, and each
+ * cell a pointer to its parent cell.
*/
-void layout_active_only_refresh(struct window *, int);
-void layout_even_h_refresh(struct window *, int);
-void layout_even_v_refresh(struct window *, int);
-void layout_main_h_refresh(struct window *, int);
-void layout_main_v_refresh(struct window *, int);
-
-const struct {
- const char *name;
- void (*refresh)(struct window *, int);
- void (*resize)(struct window_pane *, int);
-} layouts[] = {
- { "manual-vertical", layout_manual_v_refresh, layout_manual_v_resize },
- { "active-only", layout_active_only_refresh, NULL },
- { "even-horizontal", layout_even_h_refresh, NULL },
- { "even-vertical", layout_even_v_refresh, NULL },
- { "main-horizontal", layout_main_h_refresh, NULL },
- { "main-vertical", layout_main_v_refresh, NULL },
-};
-
-const char *
-layout_name(struct window *w)
+int layout_resize_pane_grow(struct layout_cell *, enum layout_type, int);
+int layout_resize_pane_shrink(struct layout_cell *, enum layout_type, int);
+
+struct layout_cell *
+layout_create_cell(struct layout_cell *lcparent)
{
- return (layouts[w->layout].name);
+ struct layout_cell *lc;
+
+ lc = xmalloc(sizeof *lc);
+ lc->type = LAYOUT_WINDOWPANE;
+ lc->parent = lcparent;
+
+ TAILQ_INIT(&lc->cells);
+
+ lc->sx = UINT_MAX;
+ lc->sy = UINT_MAX;
+
+ lc->xoff = UINT_MAX;
+ lc->yoff = UINT_MAX;
+
+ lc->wp = NULL;
+
+ return (lc);
}
-int
-layout_lookup(const char *name)
+void
+layout_free_cell(struct layout_cell *lc)
{
- u_int i;
- int matched = -1;
-
- for (i = 0; i < nitems(layouts); i++) {
- if (strncmp(layouts[i].name, name, strlen(name)) == 0) {
- if (matched != -1) /* ambiguous */
- return (-1);
- matched = i;
+ struct layout_cell *lcchild;
+
+ switch (lc->type) {
+ case LAYOUT_LEFTRIGHT:
+ case LAYOUT_TOPBOTTOM:
+ while (!TAILQ_EMPTY(&lc->cells)) {
+ lcchild = TAILQ_FIRST(&lc->cells);
+ TAILQ_REMOVE(&lc->cells, lcchild, entry);
+ layout_free_cell(lcchild);
}
+ break;
+ case LAYOUT_WINDOWPANE:
+ if (lc->wp != NULL)
+ lc->wp->layout_cell = NULL;
+ break;
}
- return (matched);
+ xfree(lc);
}
-int
-layout_select(struct window *w, u_int layout)
+void
+layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
{
- if (layout > nitems(layouts) - 1 || layout == w->layout)
- return (-1);
- w->layout = layout;
-
- layout_refresh(w, 0);
- return (0);
+ struct layout_cell *lcchild;
+
+ log_debug(
+ "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc,
+ lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy);
+ switch (lc->type) {
+ case LAYOUT_LEFTRIGHT:
+ case LAYOUT_TOPBOTTOM:
+ TAILQ_FOREACH(lcchild, &lc->cells, entry)
+ layout_print_cell(lcchild, hdr, n + 1);
+ break;
+ case LAYOUT_WINDOWPANE:
+ break;
+ }
}
void
-layout_next(struct window *w)
+layout_set_size(
+ struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff)
{
- w->layout++;
- if (w->layout > nitems(layouts) - 1)
- w->layout = 0;
- layout_refresh(w, 0);
+ lc->sx = sx;
+ lc->sy = sy;
+
+ lc->xoff = xoff;
+ lc->yoff = yoff;
}
void
-layout_previous(struct window *w)
+layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
{
- if (w->layout == 0)
- w->layout = nitems(layouts) - 1;
- else
- w->layout--;
- layout_refresh(w, 0);
+ lc->type = LAYOUT_WINDOWPANE;
+
+ TAILQ_INIT(&lc->cells);
+
+ wp->layout_cell = lc;
+ lc->wp = wp;
}
void
-layout_refresh(struct window *w, int active_only)
+layout_make_node(struct layout_cell *lc, enum layout_type type)
{
- layouts[w->layout].refresh(w, active_only);
- server_redraw_window(w);
-}
+ if (type == LAYOUT_WINDOWPANE)
+ fatalx("bad layout type");
+ lc->type = type;
-int
-layout_resize(struct window_pane *wp, int adjust)
-{
- struct window *w = wp->window;
+ TAILQ_INIT(&lc->cells);
- if (layouts[w->layout].resize == NULL)
- return (-1);
- layouts[w->layout].resize(wp, adjust);
- return (0);
+ if (lc->wp != NULL)
+ lc->wp->layout_cell = NULL;
+ lc->wp = NULL;
}
+/* Fix cell offsets based on their sizes. */
void
-layout_active_only_refresh(struct window *w, unused int active_only)
+layout_fix_offsets(struct layout_cell *lc)
{
- struct window_pane *wp;
- u_int xoff;
-
- xoff = w->sx;
- TAILQ_FOREACH(wp, &w->panes, entry) {
- /* Put the active pane on screen and the rest to the right. */
- if (wp == w->active)
- wp->xoff = 0;
- else {
- wp->xoff = xoff;
- xoff += w->sx;
+ struct layout_cell *lcchild;
+ u_int xoff, yoff;
+
+ if (lc->type == LAYOUT_LEFTRIGHT) {
+ xoff = lc->xoff;
+ TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+ lcchild->xoff = xoff;
+ lcchild->yoff = lc->yoff;
+ if (lcchild->type != LAYOUT_WINDOWPANE)
+ layout_fix_offsets(lcchild);
+ xoff += lcchild->sx + 1;
+ }
+ } else {
+ yoff = lc->yoff;
+ TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+ lcchild->xoff = lc->xoff;
+ lcchild->yoff = yoff;
+ if (lcchild->type != LAYOUT_WINDOWPANE)
+ layout_fix_offsets(lcchild);
+ yoff += lcchild->sy + 1;
}
- wp->yoff = 0;
- window_pane_resize(wp, w->sx, w->sy);
}
}
+/* Update pane offsets and sizes based on their cells. */
void
-layout_even_h_refresh(struct window *w, int active_only)
+layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
{
struct window_pane *wp;
- u_int i, n, width, xoff;
+ struct layout_cell *lc;
+ u_int sx, sy;
- if (active_only)
- return;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if ((lc = wp->layout_cell) == NULL)
+ continue;
+ wp->xoff = lc->xoff;
+ wp->yoff = lc->yoff;
+
+ /*
+ * Layout cells are limited by the smallest size of other cells
+ * within the same row or column; if this isn't the case
+ * resizing becomes difficult.
+ *
+ * However, panes do not have to take up their entire cell, so
+ * they can be cropped to the window edge if the layout
+ * overflows and they are partly visible.
+ *
+ * This stops cells being hidden unnecessarily.
+ */
+
+ /*
+ * Work out the horizontal size. If the pane is actually
+ * outside the window or the entire pane is already visible,
+ * don't crop.
+ */
+ if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx)
+ sx = lc->sx;
+ else {
+ sx = wsx - lc->xoff;
+ if (sx < 1)
+ sx = lc->sx;
+ }
+
+ /*
+ * Similarly for the vertical size; the minimum vertical size
+ * is two because scroll regions cannot be one line.
+ */
+ if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
+ sy = lc->sy;
+ else {
+ sy = wsy - lc->yoff;
+ if (sy < 2)
+ sy = lc->sy;
+ }
- /* If the screen is too small, show active only. */
- if (w->sx < PANE_MINIMUM || w->sy < PANE_MINIMUM) {
- layout_active_only_refresh(w, active_only);
- return;
+ window_pane_resize(wp, sx, sy);
}
+}
- /* Get number of panes. */
- n = window_count_panes(w);
- if (n == 0)
- return;
-
- /* How many can we fit? */
- if (w->sx / n < PANE_MINIMUM) {
- width = PANE_MINIMUM;
- n = UINT_MAX;
- } else
- width = w->sx / n;
+/* Calculate how much size is available to be removed from a cell. */
+u_int
+layout_resize_check(struct layout_cell *lc, enum layout_type type)
+{
+ struct layout_cell *lcchild;
+ u_int available, minimum;
- /* Fit the panes. */
- i = xoff = 0;
- TAILQ_FOREACH(wp, &w->panes, entry) {
- wp->xoff = xoff;
- wp->yoff = 0;
- if (i != n - 1)
- window_pane_resize(wp, width - 1, w->sy);
+ if (lc->type == LAYOUT_WINDOWPANE) {
+ /* Space available in this cell only. */
+ if (type == LAYOUT_LEFTRIGHT)
+ available = lc->sx;
else
- window_pane_resize(wp, width, w->sy);
-
- i++;
- xoff += width;
+ available = lc->sy;
+
+ if (available > PANE_MINIMUM)
+ available -= PANE_MINIMUM;
+ else
+ available = 0;
+ } else if (lc->type == type) {
+ /* Same type: total of available space in all child cells. */
+ available = 0;
+ TAILQ_FOREACH(lcchild, &lc->cells, entry)
+ available += layout_resize_check(lcchild, type);
+ } else {
+ /* Different type: minimum of available space in child cells. */
+ minimum = UINT_MAX;
+ TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+ available = layout_resize_check(lcchild, type);
+ if (available < minimum)
+ minimum = available;
+ }
+ available = minimum;
}
- /* Any space left? */
- while (xoff++ < w->sx) {
- wp = TAILQ_LAST(&w->panes, window_panes);
- window_pane_resize(wp, wp->sx + 1, wp->sy);
- }
+ return (available);
}
+/*
+ * Adjust cell size evenly, including altering its children. This function
+ * expects the change to have already been bounded to the space available.
+ */
void
-layout_even_v_refresh(struct window *w, int active_only)
+layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change)
{
- struct window_pane *wp;
- u_int i, n, height, yoff;
+ struct layout_cell *lcchild;
- if (active_only)
+ /* Adjust the cell size. */
+ if (type == LAYOUT_LEFTRIGHT)
+ lc->sx += change;
+ else
+ lc->sy += change;
+
+ /* If this is a leaf cell, that is all that is necessary. */
+ if (type == LAYOUT_WINDOWPANE)
return;
- /* If the screen is too small, show active only. */
- if (w->sx < PANE_MINIMUM || w->sy < PANE_MINIMUM) {
- layout_active_only_refresh(w, active_only);
+ /* Child cell runs in a different direction. */
+ if (lc->type != type) {
+ TAILQ_FOREACH(lcchild, &lc->cells, entry)
+ layout_resize_adjust(lcchild, type, change);
return;
}
- /* Get number of panes. */
- n = window_count_panes(w);
- if (n == 0)
- return;
+ /*
+ * Child cell runs in the same direction. Adjust each child equally
+ * until no further change is possible.
+ */
+ while (change != 0) {
+ TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+ if (change == 0)
+ break;
+ if (change > 0) {
+ layout_resize_adjust(lcchild, type, 1);
+ change--;
+ continue;
+ }
+ if (layout_resize_check(lcchild, type) > 0) {
+ layout_resize_adjust(lcchild, type, -1);
+ change++;
+ }
+ }
+ }
+}
- /* How many can we fit? */
- if (w->sy / n < PANE_MINIMUM) {
- height = PANE_MINIMUM;
- n = UINT_MAX;
- } else
- height = w->sy / n;
+void
+layout_init(struct window *w)
+{
+ struct layout_cell *lc;
- /* Fit the panes. */
- i = yoff = 0;
- TAILQ_FOREACH(wp, &w->panes, entry) {
- wp->xoff = 0;
- wp->yoff = yoff;
- if (i != n - 1)
- window_pane_resize(wp, w->sx, height - 1);
- else
- window_pane_resize(wp, w->sx, height);
+ lc = w->layout_root = layout_create_cell(NULL);
+ layout_set_size(lc, w->sx, w->sy, 0, 0);
+ layout_make_leaf(lc, TAILQ_FIRST(&w->panes));
- i++;
- yoff += height;
- }
+ layout_fix_panes(w, w->sx, w->sy);
+}
- /* Any space left? */
- while (yoff++ < w->sy) {
- wp = TAILQ_LAST(&w->panes, window_panes);
- window_pane_resize(wp, wp->sx, wp->sy + 1);
+void
+layout_free(struct window *w)
+{
+ layout_free_cell(w->layout_root);
+}
+
+/* Resize the entire layout after window resize. */
+void
+layout_resize(struct window *w, u_int sx, u_int sy)
+{
+ struct layout_cell *lc = w->layout_root;
+ int xlimit, ylimit, xchange, ychange;
+
+ /*
+ * Adjust horizontally. Do not attempt to reduce the layout lower than
+ * the minimum (more than the amount returned by layout_resize_check).
+ *
+ * This can mean that the window size is smaller than the total layout
+ * size: redrawing this is handled at a higher level, but it does leave
+ * a problem with growing the window size here: if the current size is
+ * < the minimum, growing proportionately by adding to each pane is
+ * wrong as it would keep the layout size larger than the window size.
+ * Instead, spread the difference between the minimum and the new size
+ * out proportionately - this should leave the layout fitting the new
+ * window size.
+ */
+ xchange = sx - w->sx;
+ xlimit = layout_resize_check(lc, LAYOUT_LEFTRIGHT);
+ if (xchange < 0 && xchange < -xlimit)
+ xchange = -xlimit;
+ if (xlimit == 0) {
+ if (sx <= lc->sx) /* lc->sx is minimum possible */
+ xchange = 0;
+ else
+ xchange = sx - lc->sx;
}
+ if (xchange != 0)
+ layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, xchange);
+
+ /* Adjust vertically in a similar fashion. */
+ ychange = sy - w->sy;
+ ylimit = layout_resize_check(lc, LAYOUT_TOPBOTTOM);
+ if (ychange < 0 && ychange < -ylimit)
+ ychange = -ylimit;
+ if (ylimit == 0) {
+ if (sy <= lc->sy) /* lc->sy is minimum possible */
+ ychange = 0;
+ else
+ ychange = sy - lc->sy;
+ }
+ if (ychange != 0)
+ layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, ychange);
+
+ /* Fix cell offsets. */
+ layout_fix_offsets(lc);
+ layout_fix_panes(w, sx, sy);
}
+/* Resize a single pane within the layout. */
void
-layout_main_v_refresh(struct window *w, int active_only)
+layout_resize_pane(struct window_pane *wp, enum layout_type type, int change)
{
- struct window_pane *wp;
- u_int i, n, mainwidth, height, yoff;
+ struct layout_cell *lc, *lcparent;
+ int needed, size;
- if (active_only)
- return;
+ lc = wp->layout_cell;
- /* Get number of panes. */
- n = window_count_panes(w);
- if (n == 0)
+ /* Find next parent of the same type. */
+ lcparent = lc->parent;
+ while (lcparent != NULL && lcparent->type != type) {
+ lc = lcparent;
+ lcparent = lc->parent;
+ }
+ if (lcparent == NULL)
return;
- /* Get the main pane width and add one for separator line. */
- mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
+ /* If this is the last cell, move back one. */
+ if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
+ lc = TAILQ_PREV(lc, layout_cells, entry);
+
+ /* Grow or shrink the cell. */
+ needed = change;
+ while (needed != 0) {
+ if (change > 0) {
+ size = layout_resize_pane_grow(lc, type, needed);
+ needed -= size;
+ } else {
+ size = layout_resize_pane_shrink(lc, type, needed);
+ needed += size;
+ }
- /* Need >1 pane and minimum columns; if fewer, display active only. */
- if (n == 1 ||
- w->sx < mainwidth + PANE_MINIMUM || w->sy < PANE_MINIMUM) {
- layout_active_only_refresh(w, active_only);
- return;
+ if (size == 0) /* no more change possible */
+ break;
}
- n--;
+
+ /* Fix cell offsets. */
+ layout_fix_offsets(wp->window->layout_root);
+ layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
+}
- /* How many can we fit, not including first? */
- if (w->sy / n < PANE_MINIMUM) {
- height = PANE_MINIMUM;
- n = w->sy / PANE_MINIMUM;
- } else
- height = w->sy / n;
+int
+layout_resize_pane_grow(
+ struct layout_cell *lc, enum layout_type type, int needed)
+{
+ struct layout_cell *lcadd, *lcremove;
+ u_int size;
+
+ /* Growing. Always add to the current cell. */
+ lcadd = lc;
+
+ /* Look towards the tail for a suitable cell for reduction. */
+ lcremove = TAILQ_NEXT(lc, entry);
+ while (lcremove != NULL) {
+ size = layout_resize_check(lcremove, type);
+ if (size > 0)
+ break;
+ lcremove = TAILQ_NEXT(lcremove, entry);
+ }
- /* Fit the panes. */
- i = yoff = 0;
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp == TAILQ_FIRST(&w->panes)) {
- wp->xoff = 0;
- wp->yoff = 0;
- window_pane_resize(wp, mainwidth - 1, w->sy);
- continue;
+ /* If none found, look towards the head. */
+ if (lcremove == NULL) {
+ lcremove = TAILQ_PREV(lc, layout_cells, entry);
+ while (lcremove != NULL) {
+ size = layout_resize_check(lcremove, type);
+ if (size > 0)
+ break;
+ lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
}
-
- wp->xoff = mainwidth;
- wp->yoff = yoff;
- if (i != n - 1)
- window_pane_resize(wp, w->sx - mainwidth, height - 1);
- else
- window_pane_resize(wp, w->sx - mainwidth, height);
-
- i++;
- yoff += height;
+ if (lcremove == NULL)
+ return (0);
}
- /* Any space left? */
- while (yoff++ < w->sy) {
- wp = TAILQ_LAST(&w->panes, window_panes);
- while (wp != NULL && wp == TAILQ_FIRST(&w->panes))
- wp = TAILQ_PREV(wp, window_panes, entry);
- if (wp == NULL)
- break;
- window_pane_resize(wp, wp->sx, wp->sy + 1);
- }
+ /* Change the cells. */
+ if (size > (u_int) needed)
+ size = needed;
+ layout_resize_adjust(lcadd, type, size);
+ layout_resize_adjust(lcremove, type, -size);
+ return (size);
}
-void
-layout_main_h_refresh(struct window *w, int active_only)
+int
+layout_resize_pane_shrink(
+ struct layout_cell *lc, enum layout_type type, int needed)
{
- struct window_pane *wp;
- u_int i, n, mainheight, width, xoff;
-
- if (active_only)
- return;
-
- /* Get number of panes. */
- n = window_count_panes(w);
- if (n == 0)
- return;
+ struct layout_cell *lcadd, *lcremove;
+ u_int size;
+
+ /* Shrinking. Find cell to remove from by walking towards head. */
+ lcremove = lc;
+ do {
+ size = layout_resize_check(lcremove, type);
+ if (size != 0)
+ break;
+ lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
+ } while (lcremove != NULL);
+ if (lcremove == NULL)
+ return (0);
+
+ /* And add onto the next cell (from the original cell). */
+ lcadd = TAILQ_NEXT(lc, entry);
+ if (lcadd == NULL)
+ return (0);
+
+ /* Change the cells. */
+ if (size > (u_int) -needed)
+ size = -needed;
+ layout_resize_adjust(lcadd, type, size);
+ layout_resize_adjust(lcremove, type, -size);
+ return (size);
+}
- /* Get the main pane height and add one for separator line. */
- mainheight = options_get_number(&w->options, "main-pane-height") + 1;
+/* Split a pane into two. size is a hint, or -1 for default half/half split. */
+int
+layout_split_pane(struct window_pane *wp,
+ enum layout_type type, int size, struct window_pane *new_wp)
+{
+ struct layout_cell *lc, *lcparent, *lcnew;
+ u_int sx, sy, xoff, yoff, size1, size2;
+
+ lc = wp->layout_cell;
+
+ /* Copy the old cell size. */
+ sx = lc->sx;
+ sy = lc->sy;
+ xoff = lc->xoff;
+ yoff = lc->yoff;
+
+ /* Check there is enough space for the two new panes. */
+ switch (type) {
+ case LAYOUT_LEFTRIGHT:
+ if (sx < PANE_MINIMUM * 2 + 1)
+ return (-1);
+ break;
+ case LAYOUT_TOPBOTTOM:
+ if (sy < PANE_MINIMUM * 2 + 1)
+ return (-1);
+ break;
+ default:
+ fatalx("bad layout type");
+ }
+
+ if (lc->parent != NULL && lc->parent->type == type) {
+ /*
+ * If the parent exists and is of the same type as the split,
+ * create a new cell and insert it after this one.
+ */
+
+ /* Create the new child cell. */
+ lcnew = layout_create_cell(lc->parent);
+ TAILQ_INSERT_AFTER(&lc->parent->cells, lc, lcnew, entry);
+ } else {
+ /*
+ * Otherwise create a new parent and insert it.
+ */
+
+ /* Create and insert the replacement parent. */
+ lcparent = layout_create_cell(lc->parent);
+ layout_make_node(lcparent, type);
+ layout_set_size(lcparent, sx, sy, xoff, yoff);
+ if (lc->parent == NULL)
+ wp->window->layout_root = lcparent;
+ else
+ TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
+
+ /* Insert the old cell. */
+ lc->parent = lcparent;
+ TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
+
+ /* Create the new child cell. */
+ lcnew = layout_create_cell(lcparent);
+ TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
+ }
- /* Need >1 pane and minimum rows; if fewer, display active only. */
- if (n == 1 ||
- w->sy < mainheight + PANE_MINIMUM || w->sx < PANE_MINIMUM) {
- layout_active_only_refresh(w, active_only);
- return;
+ /* Set new cell sizes. size is the target size or -1 for middle split,
+ * size1 is the size of the top/left and size2 the bottom/right.
+ */
+ switch (type) {
+ case LAYOUT_LEFTRIGHT:
+ if (size < 0)
+ size2 = ((sx + 1) / 2) - 1;
+ else
+ size2 = size;
+ if (size2 < PANE_MINIMUM)
+ size2 = PANE_MINIMUM;
+ else if (size2 > sx - 2)
+ size2 = sx - 2;
+ size1 = sx - 1 - size2;
+ layout_set_size(lc, size1, sy, xoff, yoff);
+ layout_set_size(lcnew, size2, sy, xoff + lc->sx + 1, yoff);
+ break;
+ case LAYOUT_TOPBOTTOM:
+ if (size < 0)
+ size2 = ((sy + 1) / 2) - 1;
+ else
+ size2 = size;
+ if (size2 < PANE_MINIMUM)
+ size2 = PANE_MINIMUM;
+ else if (size2 > sy - 2)
+ size2 = sy - 2;
+ size1 = sy - 1 - size2;
+ layout_set_size(lc, sx, size1, xoff, yoff);
+ layout_set_size(lcnew, sx, size2, xoff, yoff + lc->sy + 1);
+ break;
+ default:
+ fatalx("bad layout type");
}
- n--;
- /* How many can we fit, not including first? */
- if (w->sx / n < PANE_MINIMUM) {
- width = PANE_MINIMUM;
- n = w->sx / PANE_MINIMUM;
- } else
- width = w->sx / n;
+ /* Assign the panes. */
+ layout_make_leaf(lc, wp);
+ layout_make_leaf(lcnew, new_wp);
- /* Fit the panes. */
- i = xoff = 0;
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp == TAILQ_FIRST(&w->panes)) {
- wp->xoff = 0;
- wp->yoff = 0;
- window_pane_resize(wp, w->sx, mainheight - 1);
- continue;
- }
+ /* Fix pane offsets and sizes. */
+ layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
- wp->xoff = xoff;
- wp->yoff = mainheight;
- if (i != n - 1)
- window_pane_resize(wp, width - 1, w->sy - mainheight);
- else
- window_pane_resize(wp, width - 1, w->sy - mainheight);
+ return (0);
+}
- i++;
- xoff += width;
+/* Destroy the layout associated with a pane and redistribute the space. */
+void
+layout_close_pane(struct window_pane *wp)
+{
+ struct layout_cell *lc, *lcother, *lcparent;
+
+ lc = wp->layout_cell;
+ lcparent = lc->parent;
+
+ /*
+ * If no parent, this is the last pane so window close is imminent and
+ * there is no need to resize anything.
+ */
+ if (lcparent == NULL) {
+ layout_free_cell(lc);
+ wp->window->layout_root = NULL;
+ return;
}
- /* Any space left? */
- while (xoff++ < w->sx + 1) {
- wp = TAILQ_LAST(&w->panes, window_panes);
- while (wp != NULL && wp == TAILQ_FIRST(&w->panes))
- wp = TAILQ_PREV(wp, window_panes, entry);
- if (wp == NULL)
- break;
- window_pane_resize(wp, wp->sx + 1, wp->sy);
+ /* Merge the space into the previous or next cell. */
+ if (lc == TAILQ_FIRST(&lcparent->cells))
+ lcother = TAILQ_NEXT(lc, entry);
+ else
+ lcother = TAILQ_PREV(lc, layout_cells, entry);
+ if (lcparent->type == LAYOUT_LEFTRIGHT)
+ layout_resize_adjust(lcother, lcparent->type, lc->sx + 1);
+ else
+ layout_resize_adjust(lcother, lcparent->type, lc->sy + 1);
+
+ /* Remove this from the parent's list. */
+ TAILQ_REMOVE(&lcparent->cells, lc, entry);
+ layout_free_cell(lc);
+
+ /*
+ * If the parent now has one cell, remove the parent from the tree and
+ * replace it by that cell.
+ */
+ lc = TAILQ_FIRST(&lcparent->cells);
+ if (TAILQ_NEXT(lc, entry) == NULL) {
+ TAILQ_REMOVE(&lcparent->cells, lc, entry);
+
+ lc->parent = lcparent->parent;
+ if (lc->parent == NULL) {
+ lc->xoff = 0; lc->yoff = 0;
+ wp->window->layout_root = lc;
+ } else
+ TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
+
+ layout_free_cell(lcparent);
}
+
+ /* Fix pane offsets and sizes. */
+ layout_fix_offsets(wp->window->layout_root);
+ layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
}
+