diff --git a/cmd-split-window.c b/cmd-split-window.c index e130cf12c..604618b80 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = { .args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL }, .usage = "[-bdefhIklPvWZ] [-B border-lines] " - "[-c start-directory] [-e environment] " - "[-F format] [-l size] [-m message] [-p percentage] " + "[-c start-directory] [-e environment] " + "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " "[-R inactive-border-style] [-T title] [-x width] [-y height] " "[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " " @@ -95,7 +95,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (cmd_get_entry(self) == &cmd_new_pane_entry) is_floating = !args_has(args, 'L'); else - is_floating = 0; + is_floating = window_pane_is_floating(wp); flags = is_floating ? SPAWN_FLOATING : 0; if (args_has(args, 'b')) diff --git a/layout.c b/layout.c index 569616f1f..279f504af 100644 --- a/layout.c +++ b/layout.c @@ -31,8 +31,9 @@ * a cell which contains a list of cells, and 'leaf' to refer to a cell that * contains a window pane. A leaf is considered to be 'tiled' if it is to be * drawn as a part of the tiled layout. A 'neighbour' is a sibling that is also - * tiled. A cell's 'split' size refers to the side that is shortened when - * splitting it, determined by the parent's type. + * tiled or a node that contains a tiled leaf in a subtree. A cell's 'split' + * size refers to the side that is shortened when splitting it, determined by + * the parent's type. * * 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 @@ -296,6 +297,27 @@ layout_cell_is_first_tiled(struct layout_cell *lc) return (lcchild == lc); } +static struct layout_cell * +layout_cell_get_first_tiled(struct layout_cell *lc) +{ + struct layout_cell *lcchild, *lcchild2; + + if (layout_cell_is_tiled(lc)) + return (lc); + if (lc->type == LAYOUT_WINDOWPANE) + return (NULL); + + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (layout_cell_is_tiled(lcchild)) + return (lcchild); + if (lcchild->type != LAYOUT_WINDOWPANE) { + lcchild2 = layout_cell_get_first_tiled(lcchild); + if (lcchild2 != NULL) + return (lcchild2); + } + } + return (NULL); +} /* Fix cell offsets for a child cell. */ static void @@ -609,6 +631,20 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc, } } +/* Resizes a cell to a specified size */ +void +layout_resize_set_size(struct window *w, struct layout_cell *lc, + enum layout_type type, u_int size) +{ + int change; + + if (type == LAYOUT_LEFTRIGHT) + change = size - lc->sx; + else + change = size - lc->sy; + layout_resize_adjust(w, lc, type, change); +} + /* Find and return the nearest neighbour to a cell in a specific direction. */ static struct layout_cell * layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction) @@ -1184,6 +1220,101 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) } } +/* + * Replaces the provided layout cell with a new node of the specified type and + * inserts the cell into it. Used when creating new cells requires a different + * layout type, or when the root layout is a window pane. + */ +struct layout_cell * +layout_replace_with_node(struct window *w, struct layout_cell *lc, + enum layout_type type) +{ + struct layout_cell *lcparent; + + lcparent = layout_create_cell(lc->parent); + layout_make_node(lcparent, type); + layout_set_size(lcparent, lc->sx, lc->sy, lc->xoff, lc->yoff); + if (lc->parent == NULL) + w->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); + + return (lcparent); +} + +/* Checks if there is enough space for two new panes. */ +int +layout_split_check_space(struct window_pane *wp, struct layout_cell *lc, + enum layout_type type) +{ + struct style *sb_style = &wp->scrollbar_style; + u_int minimum, sx = lc->sx, sy = lc->sy; + int scrollbars, status; + + if (lc->flags & LAYOUT_CELL_FLOATING) + fatalx("floating cells cannot be split"); + + status = window_get_pane_status(wp->window); + scrollbars = options_get_number(wp->window->options, "pane-scrollbars"); + + switch (type) { + case LAYOUT_LEFTRIGHT: + if (scrollbars) { + minimum = PANE_MINIMUM * 2 + sb_style->width + + sb_style->pad; + } else + minimum = PANE_MINIMUM * 2 + 1; + if (sx < minimum) + return (0); + break; + case LAYOUT_TOPBOTTOM: + if (layout_add_horizontal_border(wp->window, lc, status)) + minimum = PANE_MINIMUM * 2 + 2; + else + minimum = PANE_MINIMUM * 2 + 1; + if (sy < minimum) + return (0); + break; + default: + fatalx("bad layout type"); + } + + return (1); +} + +/* Calculates the new cell sizes when splitting a pane. */ +void +layout_split_sizes(struct layout_cell *lc, int size, int before, + enum layout_type type, u_int *size1, u_int *size2, u_int *saved_size) +{ + u_int s1, s2, ss; + u_int sx = lc->sx, sy = lc->sy; + + if (type == LAYOUT_LEFTRIGHT) + ss = sx; + else + ss = sy; + if (size < 0) + s2 = ((ss + 1) / 2) - 1; + else if (before) + s2 = ss - size - 1; + else + s2 = size; + if (s2 < PANE_MINIMUM) + s2 = PANE_MINIMUM; + else if (s2 > sx - 2) + s2 = ss - 2; + s1 = ss - 1 - s2; + + *size1 = s1; + *size2 = s2; + *saved_size = ss; +} + /* * 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! @@ -1193,11 +1324,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, int flags) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; - struct style *sb_style = &wp->scrollbar_style; - u_int sx, sy, xoff, yoff, size1, size2, minimum; + u_int sx, sy, xoff, yoff, size1, size2; u_int new_size, saved_size, resize_first = 0; - int full_size = (flags & SPAWN_FULLSIZE), status; - int scrollbars; + int full_size = (flags & SPAWN_FULLSIZE); + int before = (flags & SPAWN_BEFORE); /* * If full_size is specified, add a new cell at the top of the window @@ -1207,8 +1337,6 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, lc = wp->window->layout_root; else lc = wp->layout_cell; - status = window_get_pane_status(wp->window); - scrollbars = options_get_number(wp->window->options, "pane-scrollbars"); /* Copy the old cell size. */ sx = lc->sx; @@ -1217,47 +1345,14 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, yoff = lc->yoff; /* Check there is enough space for the two new panes. */ - switch (type) { - case LAYOUT_LEFTRIGHT: - if (scrollbars) { - minimum = PANE_MINIMUM * 2 + sb_style->width + - sb_style->pad; - } else - minimum = PANE_MINIMUM * 2 + 1; - if (sx < minimum) - return (NULL); - break; - case LAYOUT_TOPBOTTOM: - if (layout_add_horizontal_border(wp->window, lc, status)) - minimum = PANE_MINIMUM * 2 + 2; - else - minimum = PANE_MINIMUM * 2 + 1; - if (sy < minimum) - return (NULL); - break; - default: - fatalx("bad layout type"); - } + if (!layout_split_check_space(wp, lc, type)) + return (NULL); /* * Calculate 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. */ - if (type == LAYOUT_LEFTRIGHT) - saved_size = sx; - else - saved_size = sy; - if (size < 0) - size2 = ((saved_size + 1) / 2) - 1; - else if (flags & SPAWN_BEFORE) - size2 = saved_size - size - 1; - else - size2 = size; - if (size2 < PANE_MINIMUM) - size2 = PANE_MINIMUM; - else if (size2 > saved_size - 2) - size2 = saved_size - 2; - size1 = saved_size - 1 - size2; + layout_split_sizes(lc, size, before, type, &size1, &size2, &saved_size); /* Which size are we using? */ if (flags & SPAWN_BEFORE) @@ -1315,17 +1410,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, */ /* 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); + lcparent = layout_replace_with_node(wp->window, lc, type); /* Create the new child cell. */ lcnew = layout_create_cell(lcparent); @@ -1384,14 +1469,7 @@ layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx, * Adding a pane to a root that isn't node. Must create and * insert a new root. */ - lcparent = layout_create_cell(NULL); - layout_make_node(lcparent, LAYOUT_TOPBOTTOM); - layout_set_size(lcparent, w->sx, w->sy, 0, 0); - w->layout_root = lcparent; - - /* Insert the old cell. */ - lc->parent = lcparent; - TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); + lcparent = layout_replace_with_node(w, lc, LAYOUT_TOPBOTTOM); } lcnew = layout_create_cell(lcparent); @@ -1712,7 +1790,75 @@ layout_remove_tile(struct window *w, struct layout_cell *lc) layout_resize_adjust(w, lcneighbour, type, change); } - /* Zeroing out the cell geometry until the cell is retiled. */ - layout_set_size(lc, 0, 0, 0, 0); + /* + * Zeroing out the cell geometry until the cell is retiled unless this + * is the top level node. + */ + if (lc->parent != NULL) + layout_set_size(lc, 0, 0, 0, 0); + return (1); +} + +/* + * Inserts a cell back into the tiled layout by taking half the space from its + * nearest neighbour. + */ +int +layout_insert_tile(struct window *w, struct layout_cell *lc) +{ + struct layout_cell *lcneighbour, *lctiled, *lcparent; + enum layout_type type; + u_int size1, size2, saved_size; + + if (lc == NULL) + fatalx("layout cell cannot be null when tiling"); + + lcparent = lc->parent; + if (lc->flags & LAYOUT_CELL_FLOATING) + return (1); + + if (lcparent == NULL) { + /* Only pane in the layout. */ + layout_set_size(lc, w->sx, w->sy, 0, 0); + return (1); + } + + type = lcparent->type; + lcneighbour = layout_cell_get_neighbour(lc); + if (lcneighbour == NULL) { + /* + * This will become the only visible cell in the parent. + * Tile the parent, then set the child's 'split' size. + */ + layout_insert_tile(w, lcparent); + if (type == LAYOUT_LEFTRIGHT) + size1 = lcparent->sx; + else + size1 = lcparent->sy; + layout_resize_set_size(w, lc, type, size1); + } else { + /* + * If the neighbour is a node, a tiled child in the subtree of + * the neighbour is needed to check for space. + */ + lctiled = layout_cell_get_first_tiled(lcneighbour); + if (!layout_split_check_space(lctiled->wp, lcneighbour, type)) + return (0); + layout_split_sizes(lcneighbour, -1, 0, type, &size1, &size2, + &saved_size); + layout_resize_set_size(w, lc, type, size1); + layout_resize_set_size(w, lcneighbour, type, size2); + } + + /* Setting opposite of the 'split' size to that of the parent. */ + if (lcparent->type == LAYOUT_LEFTRIGHT) { + size1 = lcparent->sy; + type = LAYOUT_TOPBOTTOM; + } else { + size1 = lcparent->sx; + type = LAYOUT_LEFTRIGHT; + } + layout_resize_set_size(w, lc, type, size1); + return (1); } diff --git a/screen-write.c b/screen-write.c index e98551395..a0db2cb86 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1524,6 +1524,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) struct grid_line *gl; u_int sx = screen_size_x(s); struct screen_write_citem *ci = ctx->item; + u_int flags; gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) @@ -1534,7 +1535,10 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) ctx->wp->flags |= PANE_REDRAW; #endif + flags = gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_START_OUTPUT); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); + gl = grid_get_line(s->grid, s->grid->hsize + s->cy); + gl->flags |= flags; screen_write_collect_clear(ctx, s->cy, 1); ci->x = 0; diff --git a/tmux.1 b/tmux.1 index 9f504ae83..f0e18595e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2744,6 +2744,15 @@ and .Fl Y options set the position of the upper-left corner of the pane. If omitted, new floating panes are cascaded from the top-left of the window. +The +.Fl x , +.Fl y , +.Fl X , +and +.Fl Y +options may be followed by +.Ql % +to specify a percentage of the window size. .Pp If the pane had previously been floating, the position and sizes are restored from the saved values not specified by the @@ -3588,7 +3597,7 @@ but a different format may be specified with .Fl F . .Tg newp .It Xo Ic new\-pane -.Op Fl bdefhIkPvWZ +.Op Fl bdefhIkLPvWZ .Op Fl B Ar border\-lines .Op Fl c Ar start\-directory .Op Fl e Ar environment @@ -3601,101 +3610,49 @@ but a different format may be specified with .Op Fl S Ar active\-border\-style .Op Fl t Ar target\-pane .Op Fl T Ar title +.Op Fl x Ar width +.Op Fl y Ar height +.Op Fl X Ar x-position +.Op Fl Y Ar y-position .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic newp -Create a new pane. -The new pane is created by splitting -.Ar target\-pane . -If -.Fl d -is given, the session does not make the new pane the current pane. -.Fl Z -zooms if the window is not zoomed, or keeps it zoomed if already zoomed. -.Fl s -sets the style for the pane content. -.Fl S -sets the border style when the pane is active and -.Fl R -sets the border style when the pane is inactive (see -.Sx STYLES ) . -.Fl T -sets the pane title. +Creates a new floating pane. +The +.Fl x +and +.Fl y +options set the width and height of the floating pane in columns and lines +respectively. +The default is half the window width and a quarter the window height. +The +.Fl X +and +.Fl Y +options set the position of the upper-left corner of the pane. +If omitted, new floating panes are cascaded from the top-left of the window. +The +.Fl x , +.Fl y , +.Fl X , +and +.Fl Y +options may be followed by +.Ql % +to specify a percentage of the window size. .Fl B sets the pane border lines for floating panes; see .Ic pane\-border\-lines . .Pp -.Fl h -does a horizontal split and -.Fl v -a vertical split; if neither is specified, -.Fl v -is assumed. The -.Fl l -option specifies the size of the new pane in lines (for vertical split) or in -columns (for horizontal split); -.Ar size -may be followed by -.Ql % -to specify a percentage of the available space. -.Fl p -is a shorthand option for this. -The -.Fl b -option causes the new pane to be created to the left of or above -.Ar target\-pane . -The -.Fl f -option creates a new pane spanning the full window height (with -.Fl h ) -or full window width (with -.Fl v ) , -instead of splitting the active pane. -.Pp -.Fl k -keeps the pane open after the optional -.Ar shell\-command -exits and waits for a key to be pressed before closing it. -The message shown is controlled by the -.Ic remain\-on\-exit\-format -option. -.Fl m Ar message -is equivalent to -.Fl k -but also sets the -.Ic remain\-on\-exit\-format -option for this pane to -.Ar message . -.Pp -.Fl W -Waits until -.Ar shell\-command -exits, then returns its exit status. -For example: -.Bd -literal -offset indent -$ tmux new-pane -W 'vi afile' -$ echo $? -0 -.Ed -.Pp -.Fl E , -or an empty -.Ar shell\-command , -(\[aq]\[aq]) will create an empty pane with no command running in it; -.Ic display-message -.Fl I -can write to an empty pane. -The -.Fl I -flag will create an empty pane and forward any output from stdin to it. -For example: -.Bd -literal -offset indent -$ make 2>&1|tmux new\-pane \-dI & -.Ed +.Fl L +option makes +.Ic new\-pane +behave like +.Ic split\-window . .Pp All other options have the same meaning as for the -.Ic new\-window +.Ic split\-window command. .Tg nextl .It Ic next\-layout Op Fl t Ar target\-window @@ -4050,14 +4007,92 @@ the command behaves like .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic splitw -Creates a new pane by splitting +Create a new pane by splitting .Ar target\-pane . -Shares behavior with -.Ic new\-pane . +If +.Fl d +is given, the session does not make the new pane the current pane. +.Fl Z +zooms if the window is not zoomed, or keeps it zoomed if already zoomed. +.Fl s +sets the style for the pane content. +.Fl S +sets the border style when the pane is active and +.Fl R +sets the border style when the pane is inactive (see +.Sx STYLES ) . +.Fl T +sets the pane title. +.Pp +.Fl h +does a horizontal split and +.Fl v +a vertical split; if neither is specified, +.Fl v +is assumed. +The +.Fl l +option specifies the size of the new pane in lines (for vertical split) or in +columns (for horizontal split); +.Ar size +may be followed by +.Ql % +to specify a percentage of the available space. +.Fl p +is a shorthand option for this. +The +.Fl b +option causes the new pane to be created to the left of or above +.Ar target\-pane . +The +.Fl f +option creates a new pane spanning the full window height (with +.Fl h ) +or full window width (with +.Fl v ) , +instead of splitting the active pane. +.Pp +.Fl k +keeps the pane open after the optional +.Ar shell\-command +exits and waits for a key to be pressed before closing it. +The message shown is controlled by the +.Ic remain\-on\-exit\-format +option. +.Fl m Ar message +is equivalent to +.Fl k +but also sets the +.Ic remain\-on\-exit\-format +option for this pane to +.Ar message . +.Pp +.Fl W +Waits until +.Ar shell\-command +exits, then returns its exit status. +For example: +.Bd -literal -offset indent +$ tmux new-pane -W 'vi afile' +$ echo $? +0 +.Ed +.Pp +.Fl E , +or an empty +.Ar shell\-command , +(\[aq]\[aq]) will create an empty pane with no command running in it; +.Ic display-message +.Fl I +can write to an empty pane. +The +.Fl I +flag will create an empty pane and forward any output from stdin to it. +For example: +.Bd -literal -offset indent +$ make 2>&1|tmux new\-pane \-dI & +.Ed .Pp -See -.Ic new\-pane -for more details. .Tg swapp .It Xo Ic swap\-pane .Op Fl dDUZ diff --git a/tmux.c b/tmux.c index c06a24350..c2557226d 100644 --- a/tmux.c +++ b/tmux.c @@ -290,7 +290,10 @@ clean_name(const char *name, const char* forbid) return (NULL); copy = xstrdup(name); for (cp = copy; *cp != '\0'; cp++) { - if (strchr(forbid, *cp) != NULL) + if (*cp == '#' && strchr(forbid, '#') != NULL) { + if (cp[1] == '(') + *cp = '_'; + } else if (strchr(forbid, *cp) != NULL) *cp = '_'; } utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); diff --git a/tmux.h b/tmux.h index 5e74b9cac..9e0d338e0 100644 --- a/tmux.h +++ b/tmux.h @@ -3678,6 +3678,8 @@ void layout_fix_offsets(struct window *); void layout_fix_panes(struct window *, struct window_pane *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); +void layout_resize_set_size(struct window *, struct layout_cell *, + enum layout_type, u_int); struct layout_cell *layout_cell_get_neighbour(struct layout_cell *); void layout_init(struct window *, struct window_pane *); void layout_free(struct window *); @@ -3692,6 +3694,12 @@ int layout_resize_floating_pane_to(struct window_pane *, enum layout_type, u_int, char **); void layout_assign_pane(struct layout_cell *, struct window_pane *, int); +int layout_split_check_space(struct window_pane *, + struct layout_cell *, enum layout_type); +void layout_split_sizes(struct layout_cell *, int, int, + enum layout_type, u_int *, u_int *, u_int *); +struct layout_cell *layout_replace_with_node(struct window *, + struct layout_cell *, enum layout_type); struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, int, int); struct layout_cell *layout_floating_pane(struct window *, struct window_pane *, @@ -3708,6 +3716,7 @@ int layout_floating_args_parse(struct cmdq_item *, struct args *, enum pane_lines, struct window *, u_int *, u_int *, int *, int *, char **); int layout_remove_tile(struct window *, struct layout_cell *); +int layout_insert_tile(struct window *, struct layout_cell *); /* layout-custom.c */ char *layout_dump(struct window *, struct layout_cell *); diff --git a/window-copy.c b/window-copy.c index 4ba791e75..0a37c5e9d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -5154,6 +5154,9 @@ window_copy_write_line(struct window_mode_entry *wme, else content_sx = sx; + screen_write_cursormove(ctx, 0, py, 0); + screen_write_clearline(ctx, 8); + ft = format_create_defaults(NULL, NULL, NULL, NULL, wp); style_apply(&gc, oo, "copy-mode-position-style", ft); diff --git a/window-tree.c b/window-tree.c index 638c7232d..878d050b8 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1138,6 +1138,7 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, window_tree_get_key, window_tree_swap, window_tree_sort, window_tree_help, data, window_tree_menu_items, &s); mode_tree_zoom(data->data, args); + mode_tree_view_name(data->data, "preview"); mode_tree_build(data->data); mode_tree_draw(data->data); diff --git a/window.c b/window.c index db968c026..f13d6dedc 100644 --- a/window.c +++ b/window.c @@ -631,6 +631,7 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); wp->flags |= PANE_REDRAW; + redraw_invalidate_scene(w); } wp = w->active;