blob: 7d6e3994527ca0e43e7448191c06340696e81a98 [file] [log] [blame] [raw]
/* $Id: layout.c,v 1.14 2009-05-18 22:17:24 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
*
* 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 <sys/types.h>
#include <string.h>
#include "tmux.h"
/*
* Each layout has two functions, _refresh to relayout the panes and _resize to
* resize a single 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.
*/
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)
{
return (layouts[w->layout].name);
}
int
layout_lookup(const char *name)
{
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;
}
}
return (matched);
}
int
layout_select(struct window *w, u_int layout)
{
if (layout > nitems(layouts) - 1 || layout == w->layout)
return (-1);
w->layout = layout;
layout_refresh(w, 0);
return (0);
}
void
layout_next(struct window *w)
{
w->layout++;
if (w->layout > nitems(layouts) - 1)
w->layout = 0;
layout_refresh(w, 0);
}
void
layout_previous(struct window *w)
{
if (w->layout == 0)
w->layout = nitems(layouts) - 1;
else
w->layout--;
layout_refresh(w, 0);
}
void
layout_refresh(struct window *w, int active_only)
{
layouts[w->layout].refresh(w, active_only);
server_redraw_window(w);
}
int
layout_resize(struct window_pane *wp, int adjust)
{
struct window *w = wp->window;
if (layouts[w->layout].resize == NULL)
return (-1);
layouts[w->layout].resize(wp, adjust);
return (0);
}
void
layout_active_only_refresh(struct window *w, unused int active_only)
{
struct window_pane *wp;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == w->active) {
wp->flags &= ~PANE_HIDDEN;
wp->xoff = wp->yoff = 0;
window_pane_resize(wp, w->sx, w->sy);
} else
wp->flags |= PANE_HIDDEN;
}
}
void
layout_even_h_refresh(struct window *w, int active_only)
{
struct window_pane *wp;
u_int i, n, width, xoff;
if (active_only)
return;
/* 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 = w->sx / PANE_MINIMUM;
} else
width = w->sx / n;
/* Fit the panes. */
i = xoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (i > n) {
wp->flags |= PANE_HIDDEN;
continue;
}
wp->flags &= ~PANE_HIDDEN;
wp->xoff = xoff;
wp->yoff = 0;
if (i != n - 1)
window_pane_resize(wp, width - 1, w->sy);
else
window_pane_resize(wp, width, w->sy);
i++;
xoff += width;
}
/* Any space left? */
while (xoff++ < w->sx) {
wp = TAILQ_LAST(&w->panes, window_panes);
window_pane_resize(wp, wp->sx + 1, wp->sy);
}
}
void
layout_even_v_refresh(struct window *w, int active_only)
{
struct window_pane *wp;
u_int i, n, height, yoff;
if (active_only)
return;
/* Get number of panes. */
n = window_count_panes(w);
if (n == 0)
return;
/* How many can we fit? */
if (w->sy / n < PANE_MINIMUM) {
height = PANE_MINIMUM;
n = w->sy / PANE_MINIMUM;
} else
height = w->sy / n;
/* Fit the panes. */
i = yoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (i > n) {
wp->flags |= PANE_HIDDEN;
continue;
}
wp->flags &= ~PANE_HIDDEN;
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);
i++;
yoff += height;
}
/* 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_main_v_refresh(struct window *w, int active_only)
{
struct window_pane *wp;
u_int i, n, mainwidth, height, yoff;
if (active_only)
return;
/* Get number of panes. */
n = window_count_panes(w);
if (n == 0)
return;
/* Get the main pane width and add one for separator line. */
mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
/* Need >1 pane and minimum columns; if fewer, display active only. */
if (n == 1 || w->sx < mainwidth + PANE_MINIMUM) {
layout_active_only_refresh(w, active_only);
return;
}
n--;
/* 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;
/* 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);
wp->flags &= ~PANE_HIDDEN;
continue;
}
if (i > n) {
wp->flags |= PANE_HIDDEN;
continue;
}
wp->flags &= ~PANE_HIDDEN;
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;
}
/* 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);
}
}
void
layout_main_h_refresh(struct window *w, int active_only)
{
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;
/* Get the main pane height and add one for separator line. */
mainheight = options_get_number(&w->options, "main-pane-height") + 1;
/* Need >1 pane and minimum rows; if fewer, display active only. */
if (n == 1 || w->sy < mainheight + PANE_MINIMUM) {
layout_active_only_refresh(w, active_only);
return;
}
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;
/* 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);
wp->flags &= ~PANE_HIDDEN;
continue;
}
if (i > n) {
wp->flags |= PANE_HIDDEN;
continue;
}
wp->flags &= ~PANE_HIDDEN;
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);
i++;
xoff += width;
}
/* 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);
}
}