Make the selection able to exist independent of the cursor position, so

that it is not affected by scrolling. If MouseDragEnd1Pane is bound to
the new "stop-selection" command:

    bind -Tcopy-mode MouseDragEnd1Pane stop-selection

A selection made with the mouse will stay as it is after button 1 is
released. (It also works bound to a key.)

From Artem Fokin.
This commit is contained in:
nicm 2016-11-24 13:38:44 +00:00
parent 6de466cf8b
commit 7e6c2cb238
4 changed files with 162 additions and 50 deletions

View File

@ -258,6 +258,8 @@ screen_set_selection(struct screen *s, u_int sx, u_int sy,
memcpy(&sel->cell, gc, sizeof sel->cell);
sel->flag = 1;
sel->hidden = 0;
sel->rectflag = rectflag;
sel->sx = sx; sel->sy = sy;
@ -271,9 +273,19 @@ screen_clear_selection(struct screen *s)
struct screen_sel *sel = &s->sel;
sel->flag = 0;
sel->hidden = 0;
sel->lineflag = LINE_SEL_NONE;
}
/* Hide selection. */
void
screen_hide_selection(struct screen *s)
{
struct screen_sel *sel = &s->sel;
sel->hidden = 1;
}
/* Check if cell in selection. */
int
screen_check_selection(struct screen *s, u_int px, u_int py)
@ -281,7 +293,7 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
struct screen_sel *sel = &s->sel;
u_int xx;
if (!sel->flag)
if (!sel->flag || sel->hidden)
return (0);
if (sel->rectflag) {
@ -377,7 +389,7 @@ void
screen_select_cell(struct screen *s, struct grid_cell *dst,
const struct grid_cell *src)
{
if (!s->sel.flag)
if (!s->sel.flag || s->sel.hidden)
return;
memcpy(dst, &s->sel.cell, sizeof *dst);

1
tmux.1
View File

@ -1072,6 +1072,7 @@ The following commands are supported in copy mode:
.It Li "search-reverse" Ta "N" Ta "N"
.It Li "select-line" Ta "V" Ta ""
.It Li "start-of-line" Ta "0" Ta "C-a"
.It Li "stop-selection" Ta "" Ta ""
.It Li "top-line" Ta "H" Ta "M-R"
.El
.Pp

3
tmux.h
View File

@ -681,6 +681,8 @@ LIST_HEAD(joblist, job);
/* Screen selection. */
struct screen_sel {
int flag;
int hidden;
int rectflag;
enum {
LINE_SEL_NONE,
@ -2055,6 +2057,7 @@ void screen_resize(struct screen *, u_int, u_int, int);
void screen_set_selection(struct screen *,
u_int, u_int, u_int, u_int, u_int, struct grid_cell *);
void screen_clear_selection(struct screen *);
void screen_hide_selection(struct screen *);
int screen_check_selection(struct screen *, u_int, u_int);
void screen_select_cell(struct screen *, struct grid_cell *,
const struct grid_cell *);

View File

@ -62,7 +62,10 @@ static void window_copy_search_down(struct window_pane *, const char *,
static void window_copy_goto_line(struct window_pane *, const char *);
static void window_copy_update_cursor(struct window_pane *, u_int, u_int);
static void window_copy_start_selection(struct window_pane *);
static int window_copy_adjust_selection(struct window_pane *, u_int *,
u_int *);
static int window_copy_update_selection(struct window_pane *, int);
static void window_copy_synchronize_cursor(struct window_pane *wp);
static void *window_copy_get_selection(struct window_pane *, size_t *);
static void window_copy_copy_buffer(struct window_pane *, const char *,
void *, size_t);
@ -119,21 +122,25 @@ enum {
WINDOW_COPY_JUMPTOBACKWARD,
};
enum {
WINDOW_COPY_REL_POS_ABOVE,
WINDOW_COPY_REL_POS_ON_SCREEN,
WINDOW_COPY_REL_POS_BELOW,
};
/*
* Copy-mode's visible screen (the "screen" field) is filled from one of
* two sources: the original contents of the pane (used when we
* actually enter via the "copy-mode" command, to copy the contents of
* the current pane), or else a series of lines containing the output
* from an output-writing tmux command (such as any of the "show-*" or
* "list-*" commands).
* Copy mode's visible screen (the "screen" field) is filled from one of two
* sources: the original contents of the pane (used when we actually enter via
* the "copy-mode" command, to copy the contents of the current pane), or else
* a series of lines containing the output from an output-writing tmux command
* (such as any of the "show-*" or "list-*" commands).
*
* In either case, the full content of the copy-mode grid is pointed at
* by the "backing" field, and is copied into "screen" as needed (that
* is, when scrolling occurs). When copy-mode is backed by a pane,
* backing points directly at that pane's screen structure (&wp->base);
* when backed by a list of output-lines from a command, it points at
* a newly-allocated screen structure (which is deallocated when the
* mode ends).
* In either case, the full content of the copy-mode grid is pointed at by the
* "backing" field, and is copied into "screen" as needed (that is, when
* scrolling occurs). When copy-mode is backed by a pane, backing points
* directly at that pane's screen structure (&wp->base); when backed by a list
* of output-lines from a command, it points at a newly-allocated screen
* structure (which is deallocated when the mode ends).
*/
struct window_copy_mode_data {
struct screen screen;
@ -141,11 +148,20 @@ struct window_copy_mode_data {
struct screen *backing;
int backing_written; /* backing display started */
u_int oy;
u_int oy; /* number of lines scrolled up */
u_int selx;
u_int selx; /* beginning of selection */
u_int sely;
u_int endselx; /* end of selection */
u_int endsely;
enum {
CURSORDRAG_NONE, /* selection is independent of cursor */
CURSORDRAG_ENDSEL, /* end is synchronized with cursor */
CURSORDRAG_SEL, /* start is synchronized with cursor */
} cursordrag;
int rectflag; /* in rectangle copy mode? */
int scroll_exit; /* exit on scroll to end? */
@ -173,6 +189,8 @@ window_copy_init(struct window_pane *wp)
data->cx = 0;
data->cy = 0;
data->cursordrag = CURSORDRAG_NONE;
data->lastcx = 0;
data->lastsx = 0;
@ -358,7 +376,7 @@ window_copy_pagedown(struct window_pane *wp, int half_page)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
u_int n, ox, oy;
u_int n, ox, oy, px, py;
oy = screen_hsize(data->backing) + data->cy - data->oy;
ox = window_copy_find_length(wp, oy);
@ -386,9 +404,10 @@ window_copy_pagedown(struct window_pane *wp, int half_page)
data->oy -= n;
if (!data->screen.sel.flag || !data->rectflag) {
u_int py = screen_hsize(data->backing) + data->cy - data->oy;
u_int px = window_copy_find_length(wp, py);
if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px)
py = screen_hsize(data->backing) + data->cy - data->oy;
px = window_copy_find_length(wp, py);
if ((data->cx >= data->lastsx && data->cx != px) ||
data->cx > px)
window_copy_cursor_end_of_line(wp);
}
@ -514,6 +533,8 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
window_copy_redraw_screen(wp);
}
}
if (strcmp(command, "stop-selection") == 0)
data->cursordrag = CURSORDRAG_NONE;
if (strcmp(command, "bottom-line") == 0) {
data->cx = 0;
data->cy = screen_size_y(sn) - 1;
@ -1150,6 +1171,29 @@ window_copy_redraw_screen(struct window_pane *wp)
window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
}
static void
window_copy_synchronize_cursor(struct window_pane *wp)
{
struct window_copy_mode_data *data = wp->modedata;
u_int xx, yy;
xx = data->cx;
yy = screen_hsize(data->backing) + data->cy - data->oy;
switch (data->cursordrag) {
case CURSORDRAG_ENDSEL:
data->endselx = xx;
data->endsely = yy;
break;
case CURSORDRAG_SEL:
data->selx = xx;
data->sely = yy;
break;
case CURSORDRAG_NONE:
break;
}
}
static void
window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
{
@ -1180,10 +1224,47 @@ window_copy_start_selection(struct window_pane *wp)
data->selx = data->cx;
data->sely = screen_hsize(data->backing) + data->cy - data->oy;
data->endselx = data->selx;
data->endsely = data->sely;
data->cursordrag = CURSORDRAG_ENDSEL;
s->sel.flag = 1;
window_copy_update_selection(wp, 1);
}
static int
window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
u_int sx, sy, ty;
int relpos;
sx = *selx;
sy = *sely;
ty = screen_hsize(data->backing) - data->oy;
if (sy < ty) {
relpos = WINDOW_COPY_REL_POS_ABOVE;
if (!data->rectflag)
sx = 0;
sy = 0;
} else if (sy > ty + screen_size_y(s) - 1) {
relpos = WINDOW_COPY_REL_POS_BELOW;
if (!data->rectflag)
sx = screen_size_x(s) - 1;
sy = screen_size_y(s) - 1;
} else {
relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
sy -= ty;
}
*selx = sx;
*sely = screen_hsize(s) + sy;
return (relpos);
}
static int
window_copy_update_selection(struct window_pane *wp, int may_redraw)
{
@ -1191,34 +1272,34 @@ window_copy_update_selection(struct window_pane *wp, int may_redraw)
struct screen *s = &data->screen;
struct options *oo = wp->window->options;
struct grid_cell gc;
u_int sx, sy, ty, cy;
u_int sx, sy, cy, endsx, endsy;
int startrelpos, endrelpos;
if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
return (0);
/* Set colours. */
style_apply(&gc, oo, "mode-style");
/* Find top of screen. */
ty = screen_hsize(data->backing) - data->oy;
window_copy_synchronize_cursor(wp);
/* Adjust the selection. */
sx = data->selx;
sy = data->sely;
if (sy < ty) { /* above screen */
if (!data->rectflag)
sx = 0;
sy = 0;
} else if (sy > ty + screen_size_y(s) - 1) { /* below screen */
if (!data->rectflag)
sx = screen_size_x(s) - 1;
sy = screen_size_y(s) - 1;
} else
sy -= ty;
sy = screen_hsize(s) + sy;
startrelpos = window_copy_adjust_selection(wp, &sx, &sy);
screen_set_selection(s,
sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc);
/* Adjust the end of selection. */
endsx = data->endselx;
endsy = data->endsely;
endrelpos = window_copy_adjust_selection(wp, &endsx, &endsy);
/* Selection is outside of the current screen */
if (startrelpos == endrelpos &&
startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
screen_hide_selection(s);
return (0);
}
/* Set colours and selection. */
style_apply(&gc, oo, "mode-style");
screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, &gc);
if (data->rectflag && may_redraw) {
/*
@ -1261,8 +1342,8 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
*/
/* Find start and end. */
xx = data->cx;
yy = screen_hsize(data->backing) + data->cy - data->oy;
xx = data->endselx;
yy = data->endsely;
if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
sx = xx; sy = yy;
ex = data->selx; ey = data->sely;
@ -1500,6 +1581,8 @@ window_copy_clear_selection(struct window_pane *wp)
screen_clear_selection(&data->screen);
data->cursordrag = CURSORDRAG_NONE;
py = screen_hsize(data->backing) + data->cy - data->oy;
px = window_copy_find_length(wp, py);
if (data->cx > px)
@ -1630,7 +1713,7 @@ window_copy_other_end(struct window_pane *wp)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
u_int selx, sely, cx, cy, yy, hsize;
u_int selx, sely, cy, yy, hsize;
if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
return;
@ -1640,26 +1723,39 @@ window_copy_other_end(struct window_pane *wp)
else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
s->sel.lineflag = LINE_SEL_LEFT_RIGHT;
selx = data->selx;
sely = data->sely;
cx = data->cx;
switch (data->cursordrag) {
case CURSORDRAG_NONE:
case CURSORDRAG_SEL:
data->cursordrag = CURSORDRAG_ENDSEL;
break;
case CURSORDRAG_ENDSEL:
data->cursordrag = CURSORDRAG_SEL;
break;
}
selx = data->endselx;
sely = data->endsely;
if (data->cursordrag == CURSORDRAG_SEL) {
selx = data->selx;
sely = data->sely;
}
cy = data->cy;
yy = screen_hsize(data->backing) + data->cy - data->oy;
data->selx = cx;
data->sely = yy;
data->cx = selx;
hsize = screen_hsize(data->backing);
if (sely < hsize - data->oy) {
if (sely < hsize - data->oy) { /* above */
data->oy = hsize - sely;
data->cy = 0;
} else if (sely > hsize - data->oy + screen_size_y(s)) {
} else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
data->oy = hsize - sely + screen_size_y(s) - 1;
data->cy = screen_size_y(s) - 1;
} else
data->cy = cy + sely - yy;
window_copy_update_selection(wp, 1);
window_copy_redraw_screen(wp);
}