summaryrefslogtreecommitdiffstats
path: root/window-copy.c
diff options
context:
space:
mode:
authornicm <nicm>2020-02-24 09:53:59 +0000
committernicm <nicm>2020-02-24 09:53:59 +0000
commitf65b9c0d36e40ded66d4366b473cc2529e1aa8f8 (patch)
treed5a219f000b68daa7d03a1aa4ed75d778a3b5bb0 /window-copy.c
parent229be034fb561ae32405f33797fc5a2b7f0c1b0c (diff)
Change mouse selection so that after selecting a word, dragging selects
only words and similar for lines. From Anindya Mukherjee.
Diffstat (limited to 'window-copy.c')
-rw-r--r--window-copy.c260
1 files changed, 243 insertions, 17 deletions
diff --git a/window-copy.c b/window-copy.c
index 301fc16e..8e3f63d1 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -119,8 +119,12 @@ static void window_copy_cursor_jump_to(struct window_mode_entry *);
static void window_copy_cursor_jump_to_back(struct window_mode_entry *);
static void window_copy_cursor_next_word(struct window_mode_entry *,
const char *);
+static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *,
+ const char *, u_int *, u_int *);
static void window_copy_cursor_next_word_end(struct window_mode_entry *,
const char *);
+static void window_copy_cursor_previous_word_pos(struct window_mode_entry *,
+ const char *, int, u_int *, u_int *);
static void window_copy_cursor_previous_word(struct window_mode_entry *,
const char *, int);
static void window_copy_scroll_up(struct window_mode_entry *, u_int);
@@ -227,6 +231,22 @@ struct window_copy_mode_data {
int rectflag; /* in rectangle copy mode? */
int scroll_exit; /* exit on scroll to end? */
+ enum {
+ SEL_CHAR, /* select one char at a time */
+ SEL_WORD, /* select one word at a time */
+ SEL_LINE, /* select one line at a time */
+ } selflag;
+
+ const char *ws; /* word separators */
+
+ u_int dx; /* drag start position */
+ u_int dy;
+
+ u_int selrx; /* selection reset positions */
+ u_int selry;
+ u_int endselrx;
+ u_int endselry;
+
u_int cx;
u_int cy;
@@ -695,6 +715,9 @@ window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
struct client *c = cs->c;
struct mouse_event *m = cs->m;
struct window_copy_mode_data *data = wme->data;
+ struct options *oo = cs->s->options;
+
+ data->ws = options_get_string(oo, "word-separators");
if (m != NULL) {
window_copy_start_drag(c, m);
@@ -1507,12 +1530,19 @@ window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
data->lineflag = LINE_SEL_LEFT_RIGHT;
data->rectflag = 0;
+ data->selflag = SEL_LINE;
+ data->dx = data->cx;
+ data->dy = screen_hsize(data->backing) + data->cy - data->oy;
window_copy_cursor_start_of_line(wme);
+ data->selrx = data->cx;
+ data->selry = screen_hsize(data->backing) + data->cy - data->oy;
window_copy_start_selection(wme);
for (; np > 1; np--)
window_copy_cursor_down(wme, 0);
window_copy_cursor_end_of_line(wme);
+ data->endselrx = data->cx;
+ data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -1524,26 +1554,33 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
struct session *s = cs->s;
struct window_copy_mode_data *data = wme->data;
const char *ws;
- u_int px, py, xx;
+ u_int px, py;
data->lineflag = LINE_SEL_LEFT_RIGHT;
data->rectflag = 0;
+ data->selflag = SEL_WORD;
+ data->dx = data->cx;
+ data->dy = screen_hsize(data->backing) + data->cy - data->oy;
px = data->cx;
py = screen_hsize(data->backing) + data->cy - data->oy;
- xx = window_copy_find_length(wme, py);
ws = options_get_string(s->options, "word-separators");
window_copy_cursor_previous_word(wme, ws, 0);
+ data->selrx = data->cx;
+ data->selry = screen_hsize(data->backing) + data->cy - data->oy;
window_copy_start_selection(wme);
- if (px >= xx || !window_copy_in_set(wme, px + 1, py, ws))
+ if (px >= window_copy_find_length(wme, py) ||
+ !window_copy_in_set(wme, px + 1, py, ws))
window_copy_cursor_next_word_end(wme, ws);
else {
window_copy_update_cursor(wme, px, data->cy);
if (window_copy_update_selection(wme, 1))
window_copy_redraw_lines(wme, data->cy, 1);
}
+ data->endselrx = data->cx;
+ data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -2788,6 +2825,7 @@ static void
window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
{
struct window_copy_mode_data *data = wme->data;
+ struct grid *gd = data->backing->grid;
u_int new_y, start, end;
new_y = data->cy;
@@ -2798,6 +2836,16 @@ window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
start = new_y;
end = old_y;
}
+
+ /*
+ * In word selection mode the first word on the line below the cursor
+ * might be selected, so add this line to the redraw area.
+ */
+ if (data->selflag == SEL_WORD) {
+ /* Last grid line in data coordinates. */
+ if (end < gd->sy + data->oy - 1)
+ end++;
+ }
window_copy_redraw_lines(wme, start, end - start + 1);
}
@@ -2825,22 +2873,82 @@ window_copy_redraw_screen(struct window_mode_entry *wme)
}
static void
-window_copy_synchronize_cursor(struct window_mode_entry *wme)
+window_copy_synchronize_cursor_end(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
u_int xx, yy;
+ int begin = 0;
- xx = data->cx;
yy = screen_hsize(data->backing) + data->cy - data->oy;
+ switch (data->selflag) {
+ case SEL_WORD:
+ xx = data->cx;
+ if (data->ws == NULL)
+ break;
+ if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
+ /* Right to left selection. */
+ window_copy_cursor_previous_word_pos(wme, data->ws, 0,
+ &xx, &yy);
+ begin = 1;
+
+ /* Reset the end. */
+ data->endselx = data->endselrx;
+ data->endsely = data->endselry;
+ } else {
+ /* Left to right selection. */
+ if (xx >= window_copy_find_length(wme, yy) ||
+ !window_copy_in_set(wme, xx + 1, yy, data->ws))
+ window_copy_cursor_next_word_end_pos(wme,
+ data->ws, &xx, &yy);
+
+ /* Reset the start. */
+ data->selx = data->selrx;
+ data->sely = data->selry;
+ }
+ break;
+ case SEL_LINE:
+ if (data->dy > yy) {
+ /* Right to left selection. */
+ xx = 0;
+ begin = 1;
+
+ /* Reset the end. */
+ data->endselx = data->endselrx;
+ data->endsely = data->endselry;
+ } else {
+ /* Left to right selection. */
+ xx = window_copy_find_length(wme, yy);
- switch (data->cursordrag) {
- case CURSORDRAG_ENDSEL:
+ /* Reset the start. */
+ data->selx = data->selrx;
+ data->sely = data->selry;
+ }
+ break;
+ case SEL_CHAR:
+ xx = data->cx;
+ break;
+ }
+ if (begin) {
+ data->selx = xx;
+ data->sely = yy;
+ } else {
data->endselx = xx;
data->endsely = yy;
+ }
+}
+
+static void
+window_copy_synchronize_cursor(struct window_mode_entry *wme)
+{
+ struct window_copy_mode_data *data = wme->data;
+
+ switch (data->cursordrag) {
+ case CURSORDRAG_ENDSEL:
+ window_copy_synchronize_cursor_end(wme);
break;
case CURSORDRAG_SEL:
- data->selx = xx;
- data->sely = yy;
+ data->selx = data->cx;
+ data->sely = screen_hsize(data->backing) + data->cy - data->oy;
break;
case CURSORDRAG_NONE:
break;
@@ -3710,6 +3818,55 @@ window_copy_cursor_next_word(struct window_mode_entry *wme,
}
static void
+window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
+ const char *separators, u_int *ppx, u_int *ppy)
+{
+ struct window_pane *wp = wme->wp;
+ struct window_copy_mode_data *data = wme->data;
+ struct options *oo = wp->window->options;
+ struct screen *back_s = data->backing;
+ u_int px, py, xx, yy;
+ int keys, expected = 1;
+
+ px = data->cx;
+ py = screen_hsize(back_s) + data->cy - data->oy;
+ xx = window_copy_find_length(wme, py);
+ yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
+
+ keys = options_get_number(oo, "mode-keys");
+ if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators))
+ px++;
+
+ /*
+ * First skip past any word characters, then any non-word characters.
+ *
+ * expected is initially set to 1 for the former and then 0 for the
+ * latter.
+ */
+ do {
+ while (px > xx ||
+ window_copy_in_set(wme, px, py, separators) == expected) {
+ /* Move down if we're past the end of the line. */
+ if (px > xx) {
+ if (py == yy)
+ return;
+ py++;
+ px = 0;
+ xx = window_copy_find_length(wme, py);
+ } else
+ px++;
+ }
+ expected = !expected;
+ } while (expected == 0);
+
+ if (keys == MODEKEY_VI && px != 0)
+ px--;
+
+ *ppx = px;
+ *ppy = py;
+}
+
+static void
window_copy_cursor_next_word_end(struct window_mode_entry *wme,
const char *separators)
{
@@ -3761,6 +3918,54 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme,
window_copy_redraw_lines(wme, data->cy, 1);
}
+/* Compute the previous place where a word begins. */
+static void
+window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
+ const char *separators, int already, u_int *ppx, u_int *ppy)
+{
+ struct window_copy_mode_data *data = wme->data;
+ u_int px, py;
+
+ px = data->cx;
+ py = screen_hsize(data->backing) + data->cy - data->oy;
+
+ /* Move back to the previous word character. */
+ if (already || window_copy_in_set(wme, px, py, separators)) {
+ for (;;) {
+ if (px > 0) {
+ px--;
+ if (!window_copy_in_set(wme, px, py,
+ separators))
+ break;
+ } else {
+ if (data->cy == 0 &&
+ (screen_hsize(data->backing) == 0 ||
+ data->oy >=
+ screen_hsize(data->backing) - 1))
+ goto out;
+ py--;
+
+ py = screen_hsize(data->backing) + data->cy -
+ data->oy;
+ px = window_copy_find_length(wme, py);
+
+ /* Stop if separator at EOL. */
+ if (px > 0 && window_copy_in_set(wme, px - 1,
+ py, separators))
+ break;
+ }
+ }
+ }
+
+ /* Move back to the beginning of this word. */
+ while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators))
+ px--;
+
+out:
+ *ppx = px;
+ *ppy = py;
+}
+
/* Move to the previous place where a word begins. */
static void
window_copy_cursor_previous_word(struct window_mode_entry *wme,
@@ -3777,21 +3982,24 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme,
for (;;) {
if (px > 0) {
px--;
- if (!window_copy_in_set(wme, px, py, separators))
+ if (!window_copy_in_set(wme, px, py,
+ separators))
break;
} else {
if (data->cy == 0 &&
(screen_hsize(data->backing) == 0 ||
- data->oy >= screen_hsize(data->backing) - 1))
+ data->oy >=
+ screen_hsize(data->backing) - 1))
goto out;
window_copy_cursor_up(wme, 0);
- py = screen_hsize(data->backing) + data->cy - data->oy;
+ py = screen_hsize(data->backing) + data->cy -
+ data->oy;
px = window_copy_find_length(wme, py);
/* Stop if separator at EOL. */
- if (px > 0 &&
- window_copy_in_set(wme, px - 1, py, separators))
+ if (px > 0 && window_copy_in_set(wme, px - 1,
+ py, separators))
break;
}
}
@@ -3913,6 +4121,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
{
struct window_pane *wp;
struct window_mode_entry *wme;
+ struct window_copy_mode_data *data;
u_int x, y;
if (c == NULL)
@@ -3933,10 +4142,27 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
c->tty.mouse_drag_update = window_copy_drag_update;
c->tty.mouse_drag_release = window_copy_drag_release;
- window_copy_update_cursor(wme, x, y);
- window_copy_start_selection(wme);
- window_copy_redraw_screen(wme);
+ data = wme->data;
+ switch (data->selflag) {
+ case SEL_WORD:
+ if (data->ws) {
+ window_copy_update_cursor(wme, x, y);
+ window_copy_cursor_previous_word_pos(wme,
+ data->ws, 0, &x, &y);
+ y -= screen_hsize(data->backing) - data->oy;
+ }
+ window_copy_update_cursor(wme, x, y);
+ break;
+ case SEL_LINE:
+ window_copy_update_cursor(wme, 0, y);
+ break;
+ case SEL_CHAR:
+ window_copy_update_cursor(wme, x, y);
+ window_copy_start_selection(wme);
+ break;
+ }
+ window_copy_redraw_screen(wme);
window_copy_drag_update(c, m);
}