diff --git a/cmd-list-windows.c b/cmd-list-windows.c index be7a931e..1bd49bb9 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -1,4 +1,4 @@ -/* $Id: cmd-list-windows.c,v 1.42 2009-11-14 17:56:39 tcunha Exp $ */ +/* $Id: cmd-list-windows.c,v 1.43 2010-07-02 02:54:52 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -45,6 +45,7 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) struct cmd_target_data *data = self->data; struct session *s; struct winlink *wl; + char *layout; if ((s = cmd_find_session(ctx, data->target)) == NULL) return (-1); @@ -52,6 +53,9 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) RB_FOREACH(wl, winlinks, &s->windows) { ctx->print(ctx, "%d: %s [%ux%u]", wl->idx, wl->window->name, wl->window->sx, wl->window->sy); + layout = layout_dump(wl->window); + ctx->print(ctx, " layout: %s", layout); + xfree(layout); } return (0); diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 5e3e7c02..e66a180f 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -1,4 +1,4 @@ -/* $Id: cmd-select-layout.c,v 1.11 2010-05-14 14:16:37 tcunha Exp $ */ +/* $Id: cmd-select-layout.c,v 1.12 2010-07-02 02:54:52 tcunha Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -79,13 +79,16 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx) layout = wl->window->lastlayout; if (layout == -1) return (0); - } else if ((layout = layout_set_lookup(data->arg)) == -1) { - ctx->error(ctx, "unknown layout or ambiguous: %s", data->arg); - return (-1); + } else if ((layout = layout_set_lookup(data->arg)) != -1) { + layout = layout_set_select(wl->window, layout); + ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); + } else { + if (layout_parse(wl->window, data->arg) == -1) { + ctx->error(ctx, "can't set layout: %s", data->arg); + return (-1); + } + ctx->info(ctx, "arranging in: %s", data->arg); } - layout = layout_set_select(wl->window, layout); - ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); - return (0); } diff --git a/layout-custom.c b/layout-custom.c new file mode 100644 index 00000000..50c4627b --- /dev/null +++ b/layout-custom.c @@ -0,0 +1,264 @@ +/* $Id: layout-custom.c,v 1.1 2010-07-02 02:54:52 tcunha Exp $ */ + +/* + * Copyright (c) 2010 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +u_short layout_checksum(const char *); +int layout_append(struct layout_cell *, char *, size_t); +struct layout_cell *layout_construct(struct layout_cell *, const char **); +void layout_assign(struct window_pane **, struct layout_cell *); + +/* Calculate layout checksum. */ +u_short +layout_checksum(const char *layout) +{ + u_short csum; + + csum = 0; + for (; *layout != '\0'; layout++) { + csum = (csum >> 1) + ((csum & 1) << 15); + csum += *layout; + } + return (csum); +} + +/* Dump layout as a string. */ +char * +layout_dump(struct window *w) +{ + char layout[BUFSIZ], *out; + + *layout = '\0'; + if (layout_append(w->layout_root, layout, sizeof layout) != 0) + return (NULL); + + xasprintf(&out, "%4x,%s", layout_checksum(layout), layout); + return (out); +} + +/* Append information for a single cell. */ +int +layout_append(struct layout_cell *lc, char *buf, size_t len) +{ + struct layout_cell *lcchild; + char tmp[64]; + size_t tmplen; + const char *brackets = "]["; + + if (len == 0) + return (-1); + + tmplen = xsnprintf(tmp, sizeof tmp, + "%ux%u,%u,%u", lc->sx, lc->sy, lc->xoff, lc->yoff); + if (tmplen > (sizeof tmp) - 1) + return (-1); + if (strlcat(buf, tmp, len) >= len) + return (-1); + + switch (lc->type) { + case LAYOUT_LEFTRIGHT: + brackets = "}{"; + /* FALLTHROUGH */ + case LAYOUT_TOPBOTTOM: + if (strlcat(buf, &brackets[1], len) >= len) + return (-1); + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (layout_append(lcchild, buf, len) != 0) + return (-1); + if (strlcat(buf, ",", len) >= len) + return (-1); + } + buf[strlen(buf) - 1] = brackets[0]; + break; + case LAYOUT_WINDOWPANE: + break; + } + + return (0); +} + +/* Parse a layout string and arrange window as layout. */ +int +layout_parse(struct window *w, const char *layout) +{ + struct layout_cell *lc, *lcchild; + struct window_pane *wp; + u_int npanes, ncells, sx, sy; + u_short csum; + + /* Check validity. */ + if (sscanf(layout, "%hx,", &csum) != 1) + return (-1); + layout += 5; + if (csum != layout_checksum(layout)) + return (-1); + + /* Build the layout. */ + lc = layout_construct(NULL, &layout); + if (lc == NULL) + return (-1); + if (*layout != '\0') + goto fail; + + /* Check this window will fit into the layout. */ + for (;;) { + npanes = window_count_panes(w); + ncells = layout_count_cells(lc); + if (npanes > ncells) + goto fail; + if (npanes == ncells) + break; + + /* Fewer panes than cells - close the bottom right. */ + lcchild = layout_find_bottomright(lc); + layout_destroy_cell(lcchild, &lc); + } + + /* Save the old window size and resize to the layout size. */ + sx = w->sx; sy = w->sy; + window_resize(w, lc->sx, lc->sy); + + /* Destroy the old layout and swap to the new. */ + layout_free_cell(w->layout_root); + w->layout_root = lc; + + /* Assign the panes into the cells. */ + wp = TAILQ_FIRST(&w->panes); + layout_assign(&wp, lc); + + /* Update pane offsets and sizes. */ + layout_fix_offsets(lc); + layout_fix_panes(w, lc->sx, lc->sy); + + /* Then resize the layout back to the original window size. */ + layout_resize(w, sx, sy); + window_resize(w, sx, sy); + + layout_print_cell(lc, __func__, 0); + + return (0); + +fail: + layout_free_cell(lc); + return (-1); +} + +/* Assign panes into cells. */ +void +layout_assign(struct window_pane **wp, struct layout_cell *lc) +{ + struct layout_cell *lcchild; + + switch (lc->type) { + case LAYOUT_WINDOWPANE: + layout_make_leaf(lc, *wp); + *wp = TAILQ_NEXT(*wp, entry); + return; + case LAYOUT_LEFTRIGHT: + case LAYOUT_TOPBOTTOM: + TAILQ_FOREACH(lcchild, &lc->cells, entry) + layout_assign(wp, lcchild); + return; + } +} + +/* Construct a cell from all or part of a layout tree. */ +struct layout_cell * +layout_construct(struct layout_cell *lcparent, const char **layout) +{ + struct layout_cell *lc, *lcchild; + u_int sx, sy, xoff, yoff; + + if (!isdigit((u_char) **layout)) + return (NULL); + if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) + return (NULL); + + while (isdigit((u_char) **layout)) + (*layout)++; + if (**layout != 'x') + return (NULL); + (*layout)++; + while (isdigit((u_char) **layout)) + (*layout)++; + if (**layout != ',') + return (NULL); + (*layout)++; + while (isdigit((u_char) **layout)) + (*layout)++; + if (**layout != ',') + return (NULL); + (*layout)++; + while (isdigit((u_char) **layout)) + (*layout)++; + + lc = layout_create_cell(lcparent); + lc->sx = sx; + lc->sy = sy; + lc->xoff = xoff; + lc->yoff = yoff; + + switch (**layout) { + case ',': + case '}': + case ']': + case '\0': + return (lc); + case '{': + lc->type = LAYOUT_LEFTRIGHT; + break; + case '[': + lc->type = LAYOUT_TOPBOTTOM; + break; + default: + goto fail; + } + + do { + (*layout)++; + lcchild = layout_construct(lc, layout); + if (lcchild == NULL) + goto fail; + TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry); + } while (**layout == ','); + + switch (lc->type) { + case LAYOUT_LEFTRIGHT: + if (**layout != '}') + goto fail; + break; + case LAYOUT_TOPBOTTOM: + if (**layout != ']') + goto fail; + break; + default: + goto fail; + } + (*layout)++; + + return (lc); + +fail: + layout_free_cell(lc); + return (NULL); +} diff --git a/layout-string.c b/layout-string.c index 37a68bb2..6f8834b2 100644 --- a/layout-string.c +++ b/layout-string.c @@ -1,4 +1,4 @@ -/* $Id: layout-string.c,v 1.3 2010-01-08 16:28:04 tcunha Exp $ */ +/* $Id: layout-string.c,v 1.4 2010-07-02 02:54:52 tcunha Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -36,7 +36,6 @@ struct layout_cell *layout_find_right(struct layout_cell *); struct layout_cell *layout_find_topleft(struct layout_cell *); struct layout_cell *layout_find_topright(struct layout_cell *); struct layout_cell *layout_find_bottomleft(struct layout_cell *); -struct layout_cell *layout_find_bottomright(struct layout_cell *); /* Find the cell; returns NULL if string not understood. */ struct layout_cell * diff --git a/layout.c b/layout.c index a11fdbab..3b087d47 100644 --- a/layout.c +++ b/layout.c @@ -1,4 +1,4 @@ -/* $Id: layout.c,v 1.18 2010-01-08 16:31:35 tcunha Exp $ */ +/* $Id: layout.c,v 1.19 2010-07-02 02:54:52 tcunha Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -218,6 +218,27 @@ layout_fix_panes(struct window *w, u_int wsx, u_int wsy) } } +/* Count the number of available cells in a layout. */ +u_int +layout_count_cells(struct layout_cell *lc) +{ + struct layout_cell *lcchild; + u_int n; + + switch (lc->type) { + case LAYOUT_WINDOWPANE: + return (1); + case LAYOUT_LEFTRIGHT: + case LAYOUT_TOPBOTTOM: + n = 0; + TAILQ_FOREACH(lcchild, &lc->cells, entry) + n += layout_count_cells(lcchild); + return (n); + default: + fatalx("bad layout type"); + } +} + /* 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) @@ -302,6 +323,56 @@ layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change) } } +/* Destroy a cell and redistribute the space. */ +void +layout_destroy_cell(struct layout_cell *lc, struct layout_cell **lcroot) +{ + struct layout_cell *lcother, *lcparent; + + /* + * If no parent, this is the last pane so window close is imminent and + * there is no need to resize anything. + */ + lcparent = lc->parent; + if (lcparent == NULL) { + layout_free_cell(lc); + *lcroot = NULL; + return; + } + + /* 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; + *lcroot = lc; + } else + TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); + + layout_free_cell(lcparent); + } +} + void layout_init(struct window *w) { @@ -597,59 +668,16 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size) return (lcnew); } -/* Destroy the layout associated with a pane and redistribute the space. */ +/* Destroy the cell associated with a pane. */ 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; - } - - /* 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); - } + /* Remove the cell. */ + layout_destroy_cell(wp->layout_cell, &wp->window->layout_root); /* Fix pane offsets and sizes. */ - layout_fix_offsets(wp->window->layout_root); - layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); + if (wp->window->layout_root != NULL) { + layout_fix_offsets(wp->window->layout_root); + layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); + } } - diff --git a/tmux.1 b/tmux.1 index 9f2de163..61fee080 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $Id: tmux.1,v 1.262 2010-07-02 02:49:19 tcunha Exp $ +.\" $Id: tmux.1,v 1.263 2010-07-02 02:54:52 tcunha Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: June 27 2010 $ +.Dd $Mdocdate: June 29 2010 $ .Dt TMUX 1 .Os .Sh NAME @@ -877,6 +877,24 @@ Panes are spread out as evenly as possible over the window in both rows and columns. .El .Pp +In addition, +.Ic select-layout +may be used to apply a previously used layout - the +.Ic list-windows +command displays the layout of each window in a form suitable for use with +.Ic select-layout . +For example: +.Bd -literal -offset indent +$ tmux list-windows +0: ksh [159x48] + layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} +$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} +.Ed +.Nm +automatically adjusts the size of the layout for the current window size. +Note that a layout cannot be applied to a window with more panes than that +from which the layout was originally defined. +.Pp Commands related to windows and panes are as follows: .Bl -tag -width Ds .It Xo Ic break-pane @@ -1224,7 +1242,7 @@ or downward (numerically higher). Choose a specific layout for a window. If .Ar layout-name -is not given, the last layout used (if any) is reapplied. +is not given, the last preset layout used (if any) is reapplied. .It Xo Ic select-pane .Op Fl DLRU .Op Fl t Ar target-pane diff --git a/tmux.h b/tmux.h index e2c1c0aa..48440312 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.567 2010-07-02 02:52:13 tcunha Exp $ */ +/* $Id: tmux.h,v 1.568 2010-07-02 02:54:52 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1849,9 +1849,11 @@ struct window_pane *window_pane_find_left(struct window_pane *); struct window_pane *window_pane_find_right(struct window_pane *); /* layout.c */ +u_int layout_count_cells(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *); void layout_free_cell(struct layout_cell *); void layout_print_cell(struct layout_cell *, const char *, u_int); +void layout_destroy_cell(struct layout_cell *, struct layout_cell **); void layout_set_size( struct layout_cell *, u_int, u_int, u_int, u_int); void layout_make_leaf( @@ -1872,6 +1874,10 @@ struct layout_cell *layout_split_pane( struct window_pane *, enum layout_type, int); void layout_close_pane(struct window_pane *); +/* layout-custom.c */ +char *layout_dump(struct window *); +int layout_parse(struct window *, const char *); + /* layout-set.c */ const char *layout_set_name(u_int); int layout_set_lookup(const char *); @@ -1882,6 +1888,7 @@ void layout_set_active_changed(struct window *); /* layout-string.c */ struct layout_cell *layout_find_string(struct window *, const char *); +struct layout_cell *layout_find_bottomright(struct layout_cell *); /* window-clock.c */ extern const struct window_mode window_clock_mode;