commit 872a7f18eaffd96eefb31e3dcb45fd86bc67029b
parent 3c546ae73924804ddc6d29dc3ab2f12a93287009
Author: Alexander Sedov <alex0player@gmail.com>
Date: Mon, 15 Apr 2013 10:28:31 +0400
Added support for double/triple click+dragging.
Now double-click+dragging automatically snaps both ends to word boundaries
(unless on series of spaces), and triple-click selects whole lines.
As a side effect, snapping now occurs on button press, not button release
like it previously was, but I hope that won't be inconvenient for anyone.
Signed-off-by: Christoph Lohmann <20h@r-36.net>
Diffstat:
M | st.c | | | 90 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
1 file changed, 63 insertions(+), 27 deletions(-)
diff --git a/st.c b/st.c
@@ -135,6 +135,11 @@ enum selection_type {
SEL_RECTANGULAR = 2
};
+enum selection_snap {
+ SNAP_WORD = 1,
+ SNAP_LINE = 2
+};
+
/* bit macro */
#undef B0
enum { B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128 };
@@ -232,6 +237,7 @@ typedef struct {
typedef struct {
int mode;
int type;
+ int snap;
int bx, by;
int ex, ey;
struct {
@@ -372,6 +378,7 @@ static void selinit(void);
static inline bool selected(int, int);
static void selcopy(void);
static void selscroll(int, int);
+static void selsnap(int, int *, int *, int);
static int utf8decode(char *, long *);
static int utf8encode(long *, char *);
@@ -658,6 +665,25 @@ selected(int x, int y) {
}
void
+selsnap(int mode, int *x, int *y, int direction) {
+ switch(mode) {
+ case SNAP_WORD:
+ while(*x > 0 && *x < term.col-1 && term.line[*y][*x + direction].c[0] != ' ') {
+ *x += direction;
+ }
+ break;
+
+ case SNAP_LINE:
+ *x = (direction < 0) ? 0 : term.col - 1;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+}
+
+void
getbuttoninfo(XEvent *e) {
int type;
uint state = e->xbutton.state &~Button1Mask;
@@ -667,6 +693,15 @@ getbuttoninfo(XEvent *e) {
sel.ex = x2col(e->xbutton.x);
sel.ey = y2row(e->xbutton.y);
+ if (sel.by < sel.ey
+ || (sel.by == sel.ey && sel.bx < sel.ex)) {
+ selsnap(sel.snap, &sel.bx, &sel.by, -1);
+ selsnap(sel.snap, &sel.ex, &sel.ey, +1);
+ } else {
+ selsnap(sel.snap, &sel.ex, &sel.ey, -1);
+ selsnap(sel.snap, &sel.bx, &sel.by, +1);
+ }
+
sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex;
sel.b.y = MIN(sel.by, sel.ey);
sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx;
@@ -730,9 +765,13 @@ mousereport(XEvent *e) {
void
bpress(XEvent *e) {
+ struct timeval now;
+
if(IS_SET(MODE_MOUSE)) {
mousereport(e);
} else if(e->xbutton.button == Button1) {
+ gettimeofday(&now, NULL);
+ /* Clear previous selection, logically and visually. */
if(sel.bx != -1) {
sel.bx = -1;
tsetdirt(sel.b.y, sel.e.y);
@@ -742,6 +781,30 @@ bpress(XEvent *e) {
sel.type = SEL_REGULAR;
sel.ex = sel.bx = x2col(e->xbutton.x);
sel.ey = sel.by = y2row(e->xbutton.y);
+ /*
+ * Snap handling.
+ * If user clicks are fasst enough (e.g. below timeouts),
+ * we ignore if his hand slipped left or down and accidentally selected more;
+ * we are just snapping to whatever we're snapping.
+ */
+ if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
+ /* Snap to line */
+ sel.snap = SNAP_LINE;
+ } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
+ sel.snap = SNAP_WORD;
+ } else {
+ sel.snap = 0;
+ }
+ selsnap(sel.snap, &sel.bx, &sel.by, -1);
+ selsnap(sel.snap, &sel.ex, &sel.ey, 1);
+ sel.b.x = sel.bx, sel.b.y = sel.by, sel.e.x = sel.ex, sel.e.y = sel.ey;
+ /* Draw selection, unless it's regular and we don't want to make clicks visible */
+ if (sel.snap != 0) {
+ tsetdirt(sel.b.y, sel.e.y);
+ draw();
+ }
+ sel.tclick2 = sel.tclick1;
+ sel.tclick1 = now;
} else if(e->xbutton.button == Button4) {
ttywrite("\031", 1);
} else if(e->xbutton.button == Button5) {
@@ -907,8 +970,6 @@ xsetsel(char *str) {
void
brelease(XEvent *e) {
- struct timeval now;
-
if(IS_SET(MODE_MOUSE)) {
mousereport(e);
return;
@@ -922,35 +983,10 @@ brelease(XEvent *e) {
term.dirty[sel.ey] = 1;
if(sel.bx == sel.ex && sel.by == sel.ey) {
sel.bx = -1;
- gettimeofday(&now, NULL);
-
- if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
- /* triple click on the line */
- sel.b.x = sel.bx = 0;
- sel.e.x = sel.ex = term.col;
- sel.b.y = sel.e.y = sel.ey;
- selcopy();
- } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
- /* double click to select word */
- sel.bx = sel.ex;
- while(sel.bx > 0 && term.line[sel.ey][sel.bx-1].c[0] != ' ') {
- sel.bx--;
- }
- sel.b.x = sel.bx;
- while(sel.ex < term.col-1 && term.line[sel.ey][sel.ex+1].c[0] != ' ') {
- sel.ex++;
- }
- sel.e.x = sel.ex;
- sel.b.y = sel.e.y = sel.ey;
- selcopy();
- }
} else {
selcopy();
}
}
-
- memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval));
- gettimeofday(&sel.tclick1, NULL);
}
void