From c5ea653b1d1883fc5f929876bdd4bdc66448a665 Mon Sep 17 00:00:00 2001
From: Alexander Sedov <elec.lomy.ru@gmail.com>
Date: Tue, 19 Feb 2013 17:10:53 +0400
Subject: [PATCH] Rectangular selection support added.

---
 config.def.h |   11 ++++++++++
 st.c         |   67 +++++++++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 63 insertions(+), 15 deletions(-)

diff --git a/config.def.h b/config.def.h
index 07a22ed..7ea623a 100644
--- a/config.def.h
+++ b/config.def.h
@@ -305,3 +305,14 @@ static Key key[] = {
 	{ XK_F35,           XK_NO_MOD,      "\033[23;5~",    0,    0,    0},
 };
 
+/*
+ * Selection types' masks.
+ * Use the same masks as usual.
+ * Button1Mask is always unset, to make masks match between ButtonPress,
+ * ButtonRelease and MotionNotify.
+ * If no match is found, regular selection is used.
+ */
+static uint selmasks[] = {
+	[SEL_RECTANGULAR] = Mod1Mask,
+};
+
diff --git a/st.c b/st.c
index 5d1cfde..edd8fea 100644
--- a/st.c
+++ b/st.c
@@ -137,6 +137,11 @@ enum window_state {
 	WIN_FOCUSED = 4
 };
 
+enum selection_type {
+    SEL_REGULAR = 1,
+    SEL_RECTANGULAR = 2
+};
+
 /* bit macro */
 #undef B0
 enum { B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128 };
@@ -234,6 +239,7 @@ typedef struct {
 /* TODO: use better name for vars... */
 typedef struct {
 	int mode;
+	int type;
 	int bx, by;
 	int ex, ey;
 	struct {
@@ -646,15 +652,23 @@ selected(int x, int y) {
 		ex = MAX(sel.bx, sel.ex);
 		return BETWEEN(x, bx, ex);
 	}
-
-	return ((sel.b.y < y && y < sel.e.y)
-			|| (y == sel.e.y && x <= sel.e.x))
-			|| (y == sel.b.y && x >= sel.b.x
-				&& (x <= sel.e.x || sel.b.y != sel.e.y));
+	switch (sel.type) {
+	case SEL_REGULAR:
+		return ((sel.b.y < y && y < sel.e.y)
+				|| (y == sel.e.y && x <= sel.e.x))
+				|| (y == sel.b.y && x >= sel.b.x
+					&& (x <= sel.e.x || sel.b.y != sel.e.y));
+		
+	case SEL_RECTANGULAR:
+		return ((sel.b.y <= y && y <= sel.e.y)
+				&& (sel.b.x <= x && x <= sel.e.x));
+	};
 }
 
 void
 getbuttoninfo(XEvent *e) {
+	int type;
+	uint state = e->xbutton.state &~Button1Mask;
 	sel.alt = IS_SET(MODE_ALTSCREEN);
 
 	sel.ex = x2col(e->xbutton.x);
@@ -664,6 +678,13 @@ getbuttoninfo(XEvent *e) {
 	sel.b.y = MIN(sel.by, sel.ey);
 	sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx;
 	sel.e.y = MAX(sel.by, sel.ey);
+	sel.type = SEL_REGULAR;
+	for (type = 1; type < LEN(selmasks); ++type) {
+		if (match(selmasks[type], state)) {
+			sel.type = type;
+			break;
+		}
+	}
 }
 
 void
@@ -724,6 +745,7 @@ bpress(XEvent *e) {
 			draw();
 		}
 		sel.mode = 1;
+		sel.type = SEL_REGULAR;
 		sel.ex = sel.bx = x2col(e->xbutton.x);
 		sel.ey = sel.by = y2row(e->xbutton.y);
 	} else if(e->xbutton.button == Button4) {
@@ -747,6 +769,7 @@ selcopy(void) {
 
 		/* append every set & selected glyph to the selection */
 		for(y = 0; y < term.row; y++) {
+			is_selected = 0;
 			gp = &term.line[y][0];
 			last = gp + term.col;
 
@@ -754,8 +777,10 @@ selcopy(void) {
 				/* nothing */;
 
 			for(x = 0; gp <= last; x++, ++gp) {
-				if(!(is_selected = selected(x, y)))
+				if(!selected(x, y))
 					continue;
+				else
+					is_selected = 1;
 
 				p = (gp->state & GLYPH_SET) ? gp->c : " ";
 				size = utf8size(p);
@@ -907,7 +932,7 @@ brelease(XEvent *e) {
 
 void
 bmotion(XEvent *e) {
-	int starty, endy, oldey, oldex;
+	int /*starty, endy,*/ oldey, oldex;
 
 	if(IS_SET(MODE_MOUSE)) {
 		mousereport(e);
@@ -922,9 +947,11 @@ bmotion(XEvent *e) {
 	getbuttoninfo(e);
 
 	if(oldey != sel.ey || oldex != sel.ex) {
+		/*
 		starty = MIN(oldey, sel.ey);
 		endy = MAX(oldey, sel.ey);
-		tsetdirt(starty, endy);
+		*/
+		tsetdirt(sel.b.y, sel.e.y);
 	}
 }
 
@@ -1216,13 +1243,23 @@ selscroll(int orig, int n) {
 			sel.bx = -1;
 			return;
 		}
-		if(sel.by < term.top) {
-			sel.by = term.top;
-			sel.bx = 0;
-		}
-		if(sel.ey > term.bot) {
-			sel.ey = term.bot;
-			sel.ex = term.col;
+		switch (sel.type) {
+		case SEL_REGULAR:
+			if(sel.by < term.top) {
+				sel.by = term.top;
+				sel.bx = 0;
+			}
+			if(sel.ey > term.bot) {
+				sel.ey = term.bot;
+				sel.ex = term.col;
+			}
+			break;
+		case SEL_RECTANGULAR:
+			if(sel.by < term.top)
+				sel.by = term.top;
+			if(sel.ey > term.bot)
+				sel.ey = term.bot;
+			break;
 		}
 		sel.b.y = sel.by, sel.b.x = sel.bx;
 		sel.e.y = sel.ey, sel.e.x = sel.ex;
-- 
1.7.10.4

