mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 09:26:05 +00:00 
			
		
		
		
	Change mouse selection so that after selecting a word, dragging selects
only words and similar for lines. From Anindya Mukherjee.
This commit is contained in:
		
							
								
								
									
										260
									
								
								window-copy.c
									
									
									
									
									
								
							
							
						
						
									
										260
									
								
								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);
 | 
			
		||||
 | 
			
		||||
			/* 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:
 | 
			
		||||
		data->endselx = xx;
 | 
			
		||||
		data->endsely = yy;
 | 
			
		||||
		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;
 | 
			
		||||
@@ -3709,6 +3817,55 @@ window_copy_cursor_next_word(struct window_mode_entry *wme,
 | 
			
		||||
		window_copy_redraw_lines(wme, data->cy, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user