So I used tmux with my mouse changes for a week. There are two
annoyances with the previous patch. When you single click in pane which
is not listening for mouse events it automatically enters the copy mode.
This is very annoying because I often use mouse to select a pane. So
I've made modifications so that a single click doesn't enter the copy
mode but if you start dragging or double click on a word it will do what
you'd expect.

The other problem is that when you enter the copy via other means (mouse
wheel or ^B-[) and then you click via mouse then it will quit the copy
mode first. This is very annoying when you scroll up a bit and then you
want to double click on a word to select it. You would not expect it to
quit the copy mode and put you at the bottom of the history. So I've
fixed this as well.

Also, previously I kept counting the number of clicks myself in order to
determine that a click was double or triple click. I've noticed tmux is
already counting this but albeit a bit buggily so I've fixed that and
started to use that mechanism which simplified some parts of my change.

I've attached the new patch which replaces my previous attempt.

-- 
Balazs
>From cc474a2d97e9062761b2a5a74b95044b4f845ef8 Mon Sep 17 00:00:00 2001
From: Balazs Kezes <rlblas...@gmail.com>
Date: Sat, 17 May 2014 10:47:33 +0100
Subject: [PATCH] Implement new mouse selection mechanism

Left button starts the selection. You can double/triple click in order
to select full words/lines. Right button will extend the selection from
the original starting point to the mouse position. You can quit the copy
mode via the middle button which will copy the current selection. Note
that the word separating characters are coming from the
"word-separators" option.
---
 CHANGES         |   6 +++
 cmd-copy-mode.c |   2 +-
 input-keys.c    |  24 ++++++----
 tmux.1          |   6 +++
 tmux.h          |   2 +-
 tty-keys.c      |  16 +++----
 window-copy.c   | 143 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 7 files changed, 161 insertions(+), 38 deletions(-)

diff --git a/CHANGES b/CHANGES
index abd1ac0..3bfc8bd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,12 @@ Normal Changes
 
 * Fix crash due to uninitialized lastwp member of layout_cell
 * Fix -fg/-bg/-style with 256 colour terminals.  
+* New mouse selection mechanism: Left button starts the selection. You can
+  double/triple click in order to select full words/lines. Right button will
+  extend the selection from the original starting point to the mouse position.
+  You can quit the copy mode via the middle button which will copy the current
+  selection. Note that the word separating characters are coming from the
+  "word-separators" option.
 
 CHANGES FROM 1.8 to 1.9, 20 February 2014
 
diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c
index f11c7af..7e1a995 100644
--- a/cmd-copy-mode.c
+++ b/cmd-copy-mode.c
@@ -56,7 +56,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq)
 	if (wp->mode != &window_copy_mode) {
 		if (window_pane_set_mode(wp, &window_copy_mode) != 0)
 			return (CMD_RETURN_NORMAL);
-		window_copy_init_from_pane(wp);
+		window_copy_init_from_pane(wp, 0);
 	}
 	if (wp->mode == &window_copy_mode && args_has(self->args, 'u'))
 		window_copy_pageup(wp);
diff --git a/input-keys.c b/input-keys.c
index 7531f22..52d81a1 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -205,6 +205,7 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m)
 	size_t			 len;
 	struct paste_buffer	*pb;
 	u_int			 i;
+	int			 wheel;
 
 	/*
 	 * If the alternate screen is active and hasn't enabled the mouse, send
@@ -252,17 +253,22 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m)
 		return;
 	}
 
-	if (m->button == 1 && (m->event & MOUSE_EVENT_CLICK) &&
-	    options_get_number(&wp->window->options, "mode-mouse") == 1) {
-		pb = paste_get_top();
-		if (pb != NULL) {
-			paste_send_pane(pb, wp, "\r",
-			    wp->screen->mode & MODE_BRACKETPASTE);
+	if (options_get_number(&wp->window->options, "mode-mouse") != 1)
+		return;
+
+	/* We don't care about m->button in case of wheel events. */
+	wheel = (m->event & MOUSE_EVENT_WHEEL);
+	if (wp->mode == NULL && !wheel && m->button == 1) {
+		if (m->event & MOUSE_EVENT_DOWN) {
+			pb = paste_get_top();
+			if (pb != NULL) {
+				paste_send_pane(pb, wp, "\r",
+					wp->screen->mode & MODE_BRACKETPASTE);
+			}
 		}
-	} else if (m->button != 1 &&
-	    options_get_number(&wp->window->options, "mode-mouse") == 1) {
+	} else {
 		if (window_pane_set_mode(wp, &window_copy_mode) == 0) {
-			window_copy_init_from_pane(wp);
+			window_copy_init_from_pane(wp, !wheel);
 			if (wp->mode->mouse != NULL)
 				wp->mode->mouse(wp, s, m);
 		}
diff --git a/tmux.1 b/tmux.1
index 30447ed..5b537ff 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2821,6 +2821,12 @@ If set to
 .Em copy-mode ,
 the mouse behaves as set to on, but cannot be used to enter copy
 mode.
+
+When in copy mode left button starts the selection. You can double/triple click
+in order to select full words/lines. Right button will extend the selection from
+the original starting point to the mouse position. You can quit the copy mode
+via the middle button which will copy the current selection. Note that the word
+separating characters are coming from the "word-separators" option.
 .Pp
 .It Ic mode-style Ar style
 Set window modes style.
diff --git a/tmux.h b/tmux.h
index 3de1da6..128b67c 100644
--- a/tmux.h
+++ b/tmux.h
@@ -2246,7 +2246,7 @@ extern const char window_clock_table[14][5][5];
 
 /* window-copy.c */
 extern const struct window_mode window_copy_mode;
-void		 window_copy_init_from_pane(struct window_pane *);
+void		 window_copy_init_from_pane(struct window_pane *, int);
 void		 window_copy_init_for_output(struct window_pane *);
 void printflike2 window_copy_add(struct window_pane *, const char *, ...);
 void		 window_copy_vadd(struct window_pane *, const char *, va_list);
diff --git a/tty-keys.c b/tty-keys.c
index 37b8960..95bd2cd 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -765,25 +765,25 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
 			m->wheel = MOUSE_WHEEL_DOWN;
 		m->event = MOUSE_EVENT_WHEEL;
 	} else if ((b & MOUSE_MASK_BUTTONS) == 3) {
-		if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y)
+		if (~m->event & MOUSE_EVENT_DRAG && x == m->sx && y == m->sy) {
 			m->event = MOUSE_EVENT_CLICK;
-		else
+			m->clicks = (m->clicks + 1) % 3;
+		} else {
 			m->event = MOUSE_EVENT_DRAG;
+		}
 		m->event |= MOUSE_EVENT_UP;
 	} else {
 		if (b & MOUSE_MASK_DRAG)
 			m->event = MOUSE_EVENT_DRAG;
 		else {
-			if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y)
-				m->clicks = (m->clicks + 1) % 3;
-			else
-				m->clicks = 0;
-			m->sx = x;
-			m->sy = y;
 			m->event = MOUSE_EVENT_DOWN;
+			if (x != m->sx || y != m->sy)
+				m->clicks = 0;
 		}
 		m->button = (b & MOUSE_MASK_BUTTONS);
 	}
+	m->sx = x;
+	m->sy = y;
 
 	return (0);
 }
diff --git a/window-copy.c b/window-copy.c
index 3ac6b82..3f08b85 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -155,6 +155,10 @@ struct window_copy_mode_data {
 
 	enum window_copy_input_type jumptype;
 	char		jumpchar;
+
+	int		quit_on_mouse_release;
+	u_int		mouse_sx, mouse_sy; /* the starting point of dragging */
+	u_int		mouse_x0, mouse_x1; /* possible starting points */
 };
 
 struct screen *
@@ -190,6 +194,8 @@ window_copy_init(struct window_pane *wp)
 	data->jumptype = WINDOW_COPY_OFF;
 	data->jumpchar = '\0';
 
+	data->quit_on_mouse_release = 0;
+
 	s = &data->screen;
 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
 	if (options_get_number(&wp->window->options, "mode-mouse"))
@@ -207,7 +213,7 @@ window_copy_init(struct window_pane *wp)
 }
 
 void
-window_copy_init_from_pane(struct window_pane *wp)
+window_copy_init_from_pane(struct window_pane *wp, int quit_on_mouse_release)
 {
 	struct window_copy_mode_data	*data = wp->modedata;
 	struct screen			*s = &data->screen;
@@ -220,6 +226,7 @@ window_copy_init_from_pane(struct window_pane *wp)
 	data->backing = &wp->base;
 	data->cx = data->backing->cx;
 	data->cy = data->backing->cy;
+	data->quit_on_mouse_release = quit_on_mouse_release;
 
 	s->cx = data->cx;
 	s->cy = data->cy;
@@ -870,13 +877,65 @@ window_copy_key_numeric_prefix(struct window_pane *wp, int key)
 	return (0);
 }
 
+static void
+window_copy_select_via_mouse(
+	struct window_pane *wp, struct mouse_event *m, const char *sep)
+{
+	struct window_copy_mode_data	*data = wp->modedata;
+	int				 dir;
+	u_int				 my, by;
+
+	/*
+	 * Based on where the user pressed when they started the selection and
+	 * where is the current mouse position, extend the selection. Care needs
+	 * to be taken to ensure character/word/line modes are respected.
+	 */
+
+	/* Make sure the starting place is correct. */
+	window_copy_update_cursor(wp, m->x, m->y);
+	my = data->mouse_sy;
+	by = screen_hsize(data->backing) + data->cy - data->oy;
+	if (by < my || (by == my && data->cx < data->mouse_sx)) {
+		dir = -1;
+		data->selx = data->mouse_x1;
+	} else {
+		dir = +1;
+		data->selx = data->mouse_x0;
+	}
+
+	if (m->clicks == 0) {
+		window_copy_update_cursor(wp, m->x, m->y);
+		if (window_copy_update_selection(wp, 1))
+			window_copy_redraw_screen(wp);
+	} else if (m->clicks == 1) {
+		if (dir < 0) {
+			data->cx += 1; /* Handle first letter clicks. */
+			window_copy_cursor_previous_word(wp, sep);
+		} else {
+			if (data->cx > 0)
+				data->cx -= 1; /* Handle last letter clicks. */
+			window_copy_cursor_next_word_end(wp, sep);
+		}
+	} else if (m->clicks == 2) {
+		if (dir < 0)
+			window_copy_cursor_start_of_line(wp);
+		else
+			window_copy_cursor_end_of_line(wp);
+	}
+
+	if (window_copy_update_selection(wp, 1))
+		window_copy_redraw_screen(wp);
+}
+
 void
 window_copy_mouse(
     struct window_pane *wp, struct session *sess, struct mouse_event *m)
 {
 	struct window_copy_mode_data	*data = wp->modedata;
 	struct screen			*s = &data->screen;
-	u_int				 i;
+	u_int				 i, mouse_backing_y;
+	int				 pressed, released, dragged;
+	const char			*sep;
 
 	if (m->x >= screen_size_x(s))
 		return;
@@ -902,35 +961,81 @@ window_copy_mouse(
 		return;
 	}
 
-	/*
-	 * If already reading motion, move the cursor while buttons are still
-	 * pressed, or stop the selection on their release.
-	 */
-	if (s->mode & MODE_MOUSE_BUTTON) {
-		if (~m->event & MOUSE_EVENT_UP) {
-			window_copy_update_cursor(wp, m->x, m->y);
-			if (window_copy_update_selection(wp, 1))
-				window_copy_redraw_screen(wp);
-			return;
-		}
-		goto reset_mode;
-	}
+	pressed = (m->event & MOUSE_EVENT_DOWN);
+	released = (m->event & MOUSE_EVENT_UP);
+	dragged = (m->event & MOUSE_EVENT_DRAG);
+	mouse_backing_y = screen_hsize(data->backing) + m->y - data->oy;
+	sep = options_get_string(&sess->options, "word-separators");
 
-	/* Otherwise if other buttons pressed, start selection and motion. */
-	if (~m->event & MOUSE_EVENT_UP) {
+	if (!(s->mode & MODE_MOUSE_BUTTON) && pressed) {
 		s->mode &= ~MODE_MOUSE_STANDARD;
 		s->mode |= MODE_MOUSE_BUTTON;
+	} else if ((s->mode & MODE_MOUSE_BUTTON) && released) {
+		s->mode &= ~MODE_MOUSE_BUTTON;
+		s->mode |= MODE_MOUSE_STANDARD;
+	}
 
+	if (data->quit_on_mouse_release && m->clicks == 1 && released) {
+		if (sess != NULL)
+			window_pane_reset_mode(wp);
+		return;
+        } else if (!pressed) {
+		/* Let's reset this on release, dragged or wheel events. */
+		data->quit_on_mouse_release = 0;
+        }
+
+	if (pressed && m->button == 1) {
+		goto reset_mode;
+	}
+	
+	if (pressed && m->button == 0 && !s->sel.flag) {
 		window_copy_update_cursor(wp, m->x, m->y);
+		data->mouse_sx = m->x;
+		data->mouse_sy = mouse_backing_y;
+		data->mouse_x0 = m->x;
+		data->mouse_x1 = m->x;
 		window_copy_start_selection(wp);
 		window_copy_redraw_screen(wp);
 	}
 
+	if (dragged || (pressed && m->button == 2)) {
+		window_copy_select_via_mouse(wp, m, sep);
+	} else if (pressed && m->button == 0) {
+		/*
+		 * Single click: normal selection.
+		 * Double click: word selection.
+		 * Triple click: line selection.
+		 */
+
+		window_copy_update_cursor(wp, m->x, m->y);
+		data->mouse_sx = m->x;
+		data->mouse_sy = mouse_backing_y;
+		if (m->clicks == 0) {
+			window_copy_start_selection(wp);
+			data->mouse_x0 = m->x;
+			data->mouse_x1 = m->x;
+		} else if (m->clicks == 1) {
+			if (data->cx > 0)
+				data->cx -= 1; /* Handle last letter clicks. */
+			window_copy_cursor_next_word_end(wp, sep);
+			data->mouse_x1 = data->cx;
+			data->cx += 1; /* Handle single letter words. */
+			window_copy_cursor_previous_word(wp, sep);
+			data->mouse_x0 = data->cx;
+			window_copy_start_selection(wp);
+		} else if (m->clicks == 2) {
+			window_copy_cursor_end_of_line(wp);
+			data->mouse_x1 = data->cx;
+			window_copy_cursor_start_of_line(wp);
+			data->mouse_x0 = data->cx;
+			window_copy_start_selection(wp);
+		}
+		window_copy_select_via_mouse(wp, m, sep);
+	}
+
 	return;
 
 reset_mode:
-	s->mode &= ~MODE_MOUSE_BUTTON;
-	s->mode |= MODE_MOUSE_STANDARD;
 	if (sess != NULL) {
 		window_copy_copy_selection(wp, NULL);
 		window_pane_reset_mode(wp);
-- 
1.9.3

------------------------------------------------------------------------------
"Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE
Instantly run your Selenium tests across 300+ browser/OS combos.
Get unparalleled scalability from the best Selenium testing platform available
Simple to use. Nothing to install. Get started now for free."
http://p.sf.net/sfu/SauceLabs
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users

Reply via email to