diff --git a/Makefile b/Makefile index a1dda3d2..a3c3af6a 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ SRCS= attributes.c cfg.c client.c clock.c \ cmd-last-window.c cmd-link-window.c cmd-list-buffers.c \ cmd-list-clients.c cmd-list-commands.c cmd-list-keys.c \ cmd-list-sessions.c cmd-list-windows.c cmd-list-panes.c \ - cmd-list.c cmd-load-buffer.c \ + cmd-list.c cmd-load-buffer.c cmd-join-pane.c \ cmd-lock-server.c cmd-lock-client.c cmd-lock-session.c \ cmd-move-window.c cmd-new-session.c cmd-new-window.c \ cmd-next-layout.c cmd-next-window.c cmd-paste-buffer.c \ diff --git a/cmd-join-pane.c b/cmd-join-pane.c new file mode 100644 index 00000000..425fe92a --- /dev/null +++ b/cmd-join-pane.c @@ -0,0 +1,257 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 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 + +#include "tmux.h" + +/* + * Join a pane into another (like split/swap/kill). + */ + +int cmd_join_pane_parse(struct cmd *, int, char **, char **); +int cmd_join_pane_exec(struct cmd *, struct cmd_ctx *); +void cmd_join_pane_free(struct cmd *); +void cmd_join_pane_init(struct cmd *, int); +size_t cmd_join_pane_print(struct cmd *, char *, size_t); + +struct cmd_join_pane_data { + char *src; + char *dst; + int flag_detached; + int flag_horizontal; + int percentage; + int size; +}; + +const struct cmd_entry cmd_join_pane_entry = { + "join-pane", "joinp", + "[-dhv] [-p percentage|-l size] [-t src-pane] [-t dst-pane] [command]", + 0, "", + cmd_join_pane_init, + cmd_join_pane_parse, + cmd_join_pane_exec, + cmd_join_pane_free, + cmd_join_pane_print +}; + +void +cmd_join_pane_init(struct cmd *self, int key) +{ + struct cmd_join_pane_data *data; + + self->data = data = xmalloc(sizeof *data); + data->src = NULL; + data->dst = NULL; + data->flag_detached = 0; + data->flag_horizontal = 0; + data->percentage = -1; + data->size = -1; + + switch (key) { + case '%': + data->flag_horizontal = 1; + break; + case '"': + data->flag_horizontal = 0; + break; + } +} + +int +cmd_join_pane_parse(struct cmd *self, int argc, char **argv, char **cause) +{ + struct cmd_join_pane_data *data; + int opt; + const char *errstr; + + self->entry->init(self, KEYC_NONE); + data = self->data; + + while ((opt = getopt(argc, argv, "dhl:p:s:t:v")) != -1) { + switch (opt) { + case 'd': + data->flag_detached = 1; + break; + case 'h': + data->flag_horizontal = 1; + break; + case 's': + if (data->src == NULL) + data->src = xstrdup(optarg); + break; + case 't': + if (data->dst == NULL) + data->dst = xstrdup(optarg); + break; + case 'l': + if (data->percentage != -1 || data->size != -1) + break; + data->size = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr != NULL) { + xasprintf(cause, "size %s", errstr); + goto error; + } + break; + case 'p': + if (data->size != -1 || data->percentage != -1) + break; + data->percentage = strtonum(optarg, 1, 100, &errstr); + if (errstr != NULL) { + xasprintf(cause, "percentage %s", errstr); + goto error; + } + break; + case 'v': + data->flag_horizontal = 0; + break; + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0) + goto usage; + + return (0); + +usage: + xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); + +error: + self->entry->free(self); + return (-1); +} + +int +cmd_join_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_join_pane_data *data = self->data; + struct session *dst_s; + struct winlink *src_wl, *dst_wl; + struct window *src_w, *dst_w; + struct window_pane *src_wp, *dst_wp; + int size; + enum layout_type type; + struct layout_cell *lc; + + if ((dst_wl = cmd_find_pane(ctx, data->dst, &dst_s, &dst_wp)) == NULL) + return (-1); + dst_w = dst_wl->window; + + if ((src_wl = cmd_find_pane(ctx, data->src, NULL, &src_wp)) == NULL) + return (-1); + src_w = src_wl->window; + + if (src_w == dst_w) { + ctx->error(ctx, "can't join a pane to its own window"); + return (-1); + } + + type = LAYOUT_TOPBOTTOM; + if (data->flag_horizontal) + type = LAYOUT_LEFTRIGHT; + + size = -1; + if (data->size != -1) + size = data->size; + else if (data->percentage != -1) { + if (type == LAYOUT_TOPBOTTOM) + size = (dst_wp->sy * data->percentage) / 100; + else + size = (dst_wp->sx * data->percentage) / 100; + } + + if ((lc = layout_split_pane(dst_wp, type, size)) == NULL) { + ctx->error(ctx, "create pane failed: pane too small"); + return (-1); + } + + layout_close_pane(src_wp); + + if (src_w->active == src_wp) { + src_w->active = TAILQ_PREV(src_wp, window_panes, entry); + if (src_w->active == NULL) + src_w->active = TAILQ_NEXT(src_wp, entry); + } + TAILQ_REMOVE(&src_w->panes, src_wp, entry); + + if (window_count_panes(src_w) == 0) + server_kill_window(src_w); + + src_wp->window = dst_w; + TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); + layout_assign_pane(lc, src_wp); + + recalculate_sizes(); + + server_redraw_window(src_w); + server_redraw_window(dst_w); + + if (!data->flag_detached) { + window_set_active_pane(dst_w, src_wp); + session_select(dst_s, dst_wl->idx); + server_redraw_session(dst_s); + } else + server_status_session(dst_s); + + return (0); +} + +void +cmd_join_pane_free(struct cmd *self) +{ + struct cmd_join_pane_data *data = self->data; + + if (data->src != NULL) + xfree(data->src); + if (data->dst != NULL) + xfree(data->dst); + xfree(data); +} + +size_t +cmd_join_pane_print(struct cmd *self, char *buf, size_t len) +{ + struct cmd_join_pane_data *data = self->data; + size_t off = 0; + + off += xsnprintf(buf, len, "%s", self->entry->name); + if (data == NULL) + return (off); + if (off < len && data->flag_detached) + off += xsnprintf(buf + off, len - off, " -d"); + if (off < len && data->flag_horizontal) + off += xsnprintf(buf + off, len - off, " -h"); + if (off < len && data->size > 0) + off += xsnprintf(buf + off, len - off, " -l %d", data->size); + if (off < len && data->percentage > 0) { + off += xsnprintf( + buf + off, len - off, " -p %d", data->percentage); + } + if (off < len && data->src != NULL) + off += cmd_prarg(buf + off, len - off, " -s ", data->src); + if (off < len && data->dst != NULL) + off += cmd_prarg(buf + off, len - off, " -t ", data->dst); + return (off); +} diff --git a/cmd-split-window.c b/cmd-split-window.c index c159a714..f4bfe4e2 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -149,13 +149,14 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) struct session *s; struct winlink *wl; struct window *w; - struct window_pane *wp, *new_wp; + struct window_pane *wp, *new_wp = NULL; struct environ env; char *cmd, *cwd, *cause; const char *shell; u_int hlimit; int size; enum layout_type type; + struct layout_cell *lc; if ((wl = cmd_find_pane(ctx, data->target, &s, &wp)) == NULL) return (-1); @@ -193,13 +194,15 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - new_wp = window_add_pane(w, hlimit); - if (window_pane_spawn(new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0) - goto error; - if (layout_split_pane(wp, type, size, new_wp) != 0) { + if ((lc = layout_split_pane(wp, type, size)) == NULL) { cause = xstrdup("pane too small"); goto error; } + new_wp = window_add_pane(w, hlimit); + if (window_pane_spawn( + new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0) + goto error; + layout_assign_pane(lc, new_wp); server_redraw_window(w); diff --git a/cmd.c b/cmd.c index 5ad87c23..72fec848 100644 --- a/cmd.c +++ b/cmd.c @@ -49,6 +49,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_find_window_entry, &cmd_has_session_entry, &cmd_if_shell_entry, + &cmd_join_pane_entry, &cmd_kill_pane_entry, &cmd_kill_server_entry, &cmd_kill_session_entry, diff --git a/layout.c b/layout.c index 7835e3f9..2d7fd596 100644 --- a/layout.c +++ b/layout.c @@ -485,10 +485,20 @@ layout_resize_pane_shrink( return (size); } -/* 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) +/* Assign window pane to newly split cell. */ +void +layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) +{ + layout_make_leaf(lc, wp); + layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); +} + +/* + * Split a pane into two. size is a hint, or -1 for default half/half + * split. This must be followed by layout_assign_pane before much else happens! + **/ +struct layout_cell * +layout_split_pane(struct window_pane *wp, enum layout_type type, int size) { struct layout_cell *lc, *lcparent, *lcnew; u_int sx, sy, xoff, yoff, size1, size2; @@ -505,11 +515,11 @@ layout_split_pane(struct window_pane *wp, switch (type) { case LAYOUT_LEFTRIGHT: if (sx < PANE_MINIMUM * 2 + 1) - return (-1); + return (NULL); break; case LAYOUT_TOPBOTTOM: if (sy < PANE_MINIMUM * 2 + 1) - return (-1); + return (NULL); break; default: fatalx("bad layout type"); @@ -583,12 +593,8 @@ layout_split_pane(struct window_pane *wp, /* Assign the panes. */ layout_make_leaf(lc, wp); - layout_make_leaf(lcnew, new_wp); - /* Fix pane offsets and sizes. */ - layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); - - return (0); + return (lcnew); } /* Destroy the layout associated with a pane and redistribute the space. */ diff --git a/tmux.1 b/tmux.1 index 7f091c60..71391e94 100644 --- a/tmux.1 +++ b/tmux.1 @@ -767,6 +767,24 @@ If only one window is matched, it'll be automatically selected, otherwise a choice list is shown. This command only works from inside .Nm . +.It Xo Ic join-pane +.Op Fl dhv +.Oo Fl l +.Ar size | +.Fl p Ar percentage Oc +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 (alias: Ic joinp ) +Like +.Ic split-window , +but instead of splitting +.Ar dst-pane +and creating a new pane, split it and move +.Ar src-pane +into the space. +This can be used to reverse +.Ic break-pane . .It Xo Ic kill-pane .Op Fl a .Op Fl t Ar target-pane diff --git a/tmux.h b/tmux.h index 56ef4aac..e9b08e90 100644 --- a/tmux.h +++ b/tmux.h @@ -1437,6 +1437,7 @@ extern const struct cmd_entry cmd_down_pane_entry; extern const struct cmd_entry cmd_find_window_entry; extern const struct cmd_entry cmd_has_session_entry; extern const struct cmd_entry cmd_if_shell_entry; +extern const struct cmd_entry cmd_join_pane_entry; extern const struct cmd_entry cmd_kill_pane_entry; extern const struct cmd_entry cmd_kill_server_entry; extern const struct cmd_entry cmd_kill_session_entry; @@ -1833,8 +1834,9 @@ void layout_free(struct window *); void layout_resize(struct window *, u_int, u_int); void layout_resize_pane( struct window_pane *, enum layout_type, int); -int layout_split_pane(struct window_pane *, - enum layout_type, int, struct window_pane *); +void layout_assign_pane(struct layout_cell *, struct window_pane *); +struct layout_cell *layout_split_pane( + struct window_pane *, enum layout_type, int); void layout_close_pane(struct window_pane *); /* layout-set.c */