1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-22 06:38:37 -05:00

Rectangular-block selection. Enabled by Alt+drag, unless you

configure it to be the default in which case it's _dis_abled by
Alt+drag.

[originally from svn r1350]
This commit is contained in:
Simon Tatham 2001-10-31 18:50:09 +00:00
parent ec1d8c1408
commit d2c9937691
6 changed files with 160 additions and 36 deletions

View File

@ -326,6 +326,7 @@ typedef struct {
unsigned char colours[22][3]; unsigned char colours[22][3];
/* Selection options */ /* Selection options */
int mouse_is_xterm; int mouse_is_xterm;
int rect_select;
int rawcnp; int rawcnp;
int mouse_override; int mouse_override;
short wordness[256]; short wordness[256];
@ -452,7 +453,7 @@ void term_paint(Context, int, int, int, int);
void term_scroll(int, int); void term_scroll(int, int);
void term_pwron(void); void term_pwron(void);
void term_clrsb(void); void term_clrsb(void);
void term_mouse(Mouse_Button, Mouse_Action, int, int, int, int); void term_mouse(Mouse_Button, Mouse_Action, int, int, int, int, int);
void term_deselect(void); void term_deselect(void);
void term_update(void); void term_update(void);
void term_invalidate(void); void term_invalidate(void);

View File

@ -235,6 +235,7 @@ void save_settings(char *section, int do_host, Config * cfg)
} }
write_setting_i(sesskey, "RawCNP", cfg->rawcnp); write_setting_i(sesskey, "RawCNP", cfg->rawcnp);
write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm); write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm);
write_setting_i(sesskey, "RectSelect", cfg->rect_select);
write_setting_i(sesskey, "MouseOverride", cfg->mouse_override); write_setting_i(sesskey, "MouseOverride", cfg->mouse_override);
for (i = 0; i < 256; i += 32) { for (i = 0; i < 256; i += 32) {
char buf[20], buf2[256]; char buf[20], buf2[256];
@ -440,6 +441,7 @@ void load_settings(char *section, int do_host, Config * cfg)
} }
gppi(sesskey, "RawCNP", 0, &cfg->rawcnp); gppi(sesskey, "RawCNP", 0, &cfg->rawcnp);
gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm); gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm);
gppi(sesskey, "RectSelect", 0, &cfg->rect_select);
gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override); gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override);
for (i = 0; i < 256; i += 32) { for (i = 0; i < 256; i += 32) {
static char *defaults[] = { static char *defaults[] = {

View File

@ -86,6 +86,10 @@ typedef struct {
#define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) ) #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
#define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) ) #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
/* Product-order comparisons for rectangular block selection. */
#define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
#define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
static bufchain inbuf; /* terminal input buffer */ static bufchain inbuf; /* terminal input buffer */
static pos curs; /* cursor */ static pos curs; /* cursor */
static pos savecurs; /* saved cursor position */ static pos savecurs; /* saved cursor position */
@ -162,6 +166,9 @@ static enum {
static enum { static enum {
NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
} selstate; } selstate;
static enum {
LEXICOGRAPHIC, RECTANGULAR
} seltype;
static enum { static enum {
SM_CHAR, SM_WORD, SM_LINE SM_CHAR, SM_WORD, SM_LINE
} selmode; } selmode;
@ -2586,7 +2593,7 @@ static void do_paint(Context ctx, int may_optimise)
for (i = 0; i < rows; i++) { for (i = 0; i < rows; i++) {
unsigned long *ldata; unsigned long *ldata;
int lattr; int lattr;
int idx, dirty_line, dirty_run; int idx, dirty_line, dirty_run, selected;
unsigned long attr = 0; unsigned long attr = 0;
int updated_line = 0; int updated_line = 0;
int start = 0; int start = 0;
@ -2626,9 +2633,12 @@ static void do_paint(Context ctx, int may_optimise)
tattr |= ATTR_WIDE; tattr |= ATTR_WIDE;
/* Video reversing things */ /* Video reversing things */
if (seltype == LEXICOGRAPHIC)
selected = posle(selstart, scrpos) && poslt(scrpos, selend);
else
selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
tattr = (tattr ^ rv tattr = (tattr ^ rv
^ (posle(selstart, scrpos) && ^ (selected ? ATTR_REVERSE : 0));
poslt(scrpos, selend) ? ATTR_REVERSE : 0));
/* 'Real' blinking ? */ /* 'Real' blinking ? */
if (blink_is_real && (tattr & ATTR_BLINK)) { if (blink_is_real && (tattr & ATTR_BLINK)) {
@ -2816,25 +2826,38 @@ void term_scroll(int rel, int where)
term_update(); term_update();
} }
static void clipme(pos top, pos bottom) static void clipme(pos top, pos bottom, int rect)
{ {
wchar_t *workbuf; wchar_t *workbuf;
wchar_t *wbptr; /* where next char goes within workbuf */ wchar_t *wbptr; /* where next char goes within workbuf */
int old_top_x;
int wblen = 0; /* workbuf len */ int wblen = 0; /* workbuf len */
int buflen; /* amount of memory allocated to workbuf */ int buflen; /* amount of memory allocated to workbuf */
buflen = 5120; /* Default size */ buflen = 5120; /* Default size */
workbuf = smalloc(buflen * sizeof(wchar_t)); workbuf = smalloc(buflen * sizeof(wchar_t));
wbptr = workbuf; /* start filling here */ wbptr = workbuf; /* start filling here */
old_top_x = top.x; /* needed for rect==1 */
while (poslt(top, bottom)) { while (poslt(top, bottom)) {
int nl = FALSE; int nl = FALSE;
unsigned long *ldata = lineptr(top.y); unsigned long *ldata = lineptr(top.y);
pos nlpos; pos nlpos;
/*
* nlpos will point at the maximum position on this line we
* should copy up to. So we start it at the end of the
* line...
*/
nlpos.y = top.y; nlpos.y = top.y;
nlpos.x = cols; nlpos.x = cols;
/*
* ... move it backwards if there's unused space at the end
* of the line (and also set `nl' if this is the case,
* because in normal selection mode this means we need a
* newline at the end)...
*/
if (!(ldata[cols] & LATTR_WRAPPED)) { if (!(ldata[cols] & LATTR_WRAPPED)) {
while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 || while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
(DIRECT_CHAR(ldata[nlpos.x - 1]) && (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
@ -2844,6 +2867,20 @@ static void clipme(pos top, pos bottom)
if (poslt(nlpos, bottom)) if (poslt(nlpos, bottom))
nl = TRUE; nl = TRUE;
} }
/*
* ... and then clip it to the terminal x coordinate if
* we're doing rectangular selection. (In this case we
* still did the above, so that copying e.g. the right-hand
* column from a table doesn't fill with spaces on the
* right.)
*/
if (rect) {
if (nlpos.x > bottom.x)
nlpos.x = bottom.x;
nl = (top.y < bottom.y);
}
while (poslt(top, bottom) && poslt(top, nlpos)) { while (poslt(top, bottom) && poslt(top, nlpos)) {
#if 0 #if 0
char cbuf[16], *p; char cbuf[16], *p;
@ -2931,7 +2968,7 @@ static void clipme(pos top, pos bottom)
} }
} }
top.y++; top.y++;
top.x = 0; top.x = rect ? old_top_x : 0;
} }
wblen++; wblen++;
*wbptr++ = 0; *wbptr++ = 0;
@ -2945,7 +2982,7 @@ void term_copyall(void)
pos top; pos top;
top.y = -count234(scrollback); top.y = -count234(scrollback);
top.x = 0; top.x = 0;
clipme(top, curs); clipme(top, curs, 0);
} }
/* /*
@ -3149,10 +3186,12 @@ static pos sel_spread_half(pos p, int dir)
static void sel_spread(void) static void sel_spread(void)
{ {
if (seltype == LEXICOGRAPHIC) {
selstart = sel_spread_half(selstart, -1); selstart = sel_spread_half(selstart, -1);
decpos(selend); decpos(selend);
selend = sel_spread_half(selend, +1); selend = sel_spread_half(selend, +1);
incpos(selend); incpos(selend);
}
} }
void term_do_paste(void) void term_do_paste(void)
@ -3204,11 +3243,12 @@ void term_do_paste(void)
} }
void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
int shift, int ctrl) int shift, int ctrl, int alt)
{ {
pos selpoint; pos selpoint;
unsigned long *ldata; unsigned long *ldata;
int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift); int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
int default_seltype;
if (y < 0) { if (y < 0) {
y = 0; y = 0;
@ -3290,9 +3330,23 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
b = translate_button(b); b = translate_button(b);
/*
* Set the selection type (rectangular or normal) at the start
* of a selection attempt, from the state of Alt.
*/
if (!alt ^ !cfg.rect_select)
default_seltype = RECTANGULAR;
else
default_seltype = LEXICOGRAPHIC;
if (selstate == NO_SELECTION) {
seltype = default_seltype;
}
if (b == MBT_SELECT && a == MA_CLICK) { if (b == MBT_SELECT && a == MA_CLICK) {
deselect(); deselect();
selstate = ABOUT_TO; selstate = ABOUT_TO;
seltype = default_seltype;
selanchor = selpoint; selanchor = selpoint;
selmode = SM_CHAR; selmode = SM_CHAR;
} else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) { } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
@ -3308,6 +3362,12 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
if (selstate == ABOUT_TO && poseq(selanchor, selpoint)) if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
return; return;
if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) { if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
if (seltype == LEXICOGRAPHIC) {
/*
* For normal selection, we extend by moving
* whichever end of the current selection is closer
* to the mouse.
*/
if (posdiff(selpoint, selstart) < if (posdiff(selpoint, selstart) <
posdiff(selend, selstart) / 2) { posdiff(selend, selstart) / 2) {
selanchor = selend; selanchor = selend;
@ -3315,11 +3375,32 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
} else { } else {
selanchor = selstart; selanchor = selstart;
} }
} else {
/*
* For rectangular selection, we have a choice of
* _four_ places to put selanchor and selpoint: the
* four corners of the selection.
*/
if (2*selpoint.x < selstart.x + selend.x)
selanchor.x = selend.x-1;
else
selanchor.x = selstart.x;
if (2*selpoint.y < selstart.y + selend.y)
selanchor.y = selend.y;
else
selanchor.y = selstart.y;
}
selstate = DRAGGING; selstate = DRAGGING;
} }
if (selstate != ABOUT_TO && selstate != DRAGGING) if (selstate != ABOUT_TO && selstate != DRAGGING)
selanchor = selpoint; selanchor = selpoint;
selstate = DRAGGING; selstate = DRAGGING;
if (seltype == LEXICOGRAPHIC) {
/*
* For normal selection, we set (selstart,selend) to
* (selpoint,selanchor) in some order.
*/
if (poslt(selpoint, selanchor)) { if (poslt(selpoint, selanchor)) {
selstart = selpoint; selstart = selpoint;
selend = selanchor; selend = selanchor;
@ -3329,6 +3410,17 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
selend = selpoint; selend = selpoint;
incpos(selend); incpos(selend);
} }
} else {
/*
* For rectangular selection, we may need to
* interchange x and y coordinates (if the user has
* dragged in the -x and +y directions, or vice versa).
*/
selstart.x = min(selanchor.x, selpoint.x);
selend.x = 1+max(selanchor.x, selpoint.x);
selstart.y = min(selanchor.y, selpoint.y);
selend.y = max(selanchor.y, selpoint.y);
}
sel_spread(); sel_spread();
} else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) { } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
if (selstate == DRAGGING) { if (selstate == DRAGGING) {
@ -3336,7 +3428,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
* We've completed a selection. We now transfer the * We've completed a selection. We now transfer the
* data to the clipboard. * data to the clipboard.
*/ */
clipme(selstart, selend); clipme(selstart, selend, (seltype == RECTANGULAR));
selstate = SELECTED; selstate = SELECTED;
} else } else
selstate = NO_SELECTION; selstate = NO_SELECTION;

View File

@ -712,7 +712,7 @@ void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT); PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
const static int percents[] = { 30, 40, 30 }; const static int percents[] = { 30, 40, 30 };
int i, xpos, percent; int i, xpos, percent;
const int LISTHEIGHT = 66; const int LISTHEIGHT = 52;
/* The static control. */ /* The static control. */
r.left = GAPBETWEEN; r.left = GAPBETWEEN;

View File

@ -479,6 +479,9 @@ enum { IDCX_ABOUT =
IDC_MBWINDOWS, IDC_MBWINDOWS,
IDC_MBXTERM, IDC_MBXTERM,
IDC_MOUSEOVERRIDE, IDC_MOUSEOVERRIDE,
IDC_SELTYPESTATIC,
IDC_SELTYPELEX,
IDC_SELTYPERECT,
IDC_CCSTATIC, IDC_CCSTATIC,
IDC_CCLIST, IDC_CCLIST,
IDC_CCSET, IDC_CCSET,
@ -763,9 +766,10 @@ static void init_dlg_ctrls(HWND hwnd, int keepsess)
} }
CheckRadioButton(hwnd, IDC_MBWINDOWS, IDC_MBXTERM, CheckRadioButton(hwnd, IDC_MBWINDOWS, IDC_MBXTERM,
cfg.mouse_is_xterm ? IDC_MBXTERM : IDC_MBWINDOWS); cfg.mouse_is_xterm ? IDC_MBXTERM : IDC_MBWINDOWS);
CheckRadioButton(hwnd, IDC_SELTYPELEX, IDC_SELTYPERECT,
cfg.rect_select == 0 ? IDC_SELTYPELEX : IDC_SELTYPERECT);
CheckDlgButton(hwnd, IDC_MOUSEOVERRIDE, cfg.mouse_override); CheckDlgButton(hwnd, IDC_MOUSEOVERRIDE, cfg.mouse_override);
CheckDlgButton(hwnd, IDC_RAWCNP, cfg.rawcnp); CheckDlgButton(hwnd, IDC_RAWCNP, cfg.rawcnp);
{ {
@ -1141,7 +1145,7 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
} }
if (panel == selectionpanelstart) { if (panel == selectionpanelstart) {
/* The Selection panel. Accelerators used: [acgo] d wxp hst */ /* The Selection panel. Accelerators used: [acgo] d wxp hst nr */
struct ctlpos cp; struct ctlpos cp;
ctlposinit(&cp, hwnd, 80, 3, 13); ctlposinit(&cp, hwnd, 80, 3, 13);
bartitle(&cp, "Options controlling copy and paste", bartitle(&cp, "Options controlling copy and paste",
@ -1161,6 +1165,11 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
checkbox(&cp, checkbox(&cp,
"Shift overrides a&pplication's use of mouse", "Shift overrides a&pplication's use of mouse",
IDC_MOUSEOVERRIDE); IDC_MOUSEOVERRIDE);
radioline(&cp,
"Default selection mode (Alt+drag does the other one):",
IDC_SELTYPESTATIC, 2,
"&Normal", IDC_SELTYPELEX,
"&Rectangular block", IDC_SELTYPERECT, NULL);
endbox(&cp); endbox(&cp);
beginbox(&cp, "Control the select-one-word-at-a-time mode", beginbox(&cp, "Control the select-one-word-at-a-time mode",
IDC_BOX_SELECTION3); IDC_BOX_SELECTION3);
@ -2407,6 +2416,10 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
case IDC_MBXTERM: case IDC_MBXTERM:
cfg.mouse_is_xterm = IsDlgButtonChecked(hwnd, IDC_MBXTERM); cfg.mouse_is_xterm = IsDlgButtonChecked(hwnd, IDC_MBXTERM);
break; break;
case IDC_SELTYPELEX:
case IDC_SELTYPERECT:
cfg.rect_select = IsDlgButtonChecked(hwnd, IDC_SELTYPERECT);
break;
case IDC_MOUSEOVERRIDE: case IDC_MOUSEOVERRIDE:
cfg.mouse_override = IsDlgButtonChecked(hwnd, IDC_MOUSEOVERRIDE); cfg.mouse_override = IsDlgButtonChecked(hwnd, IDC_MOUSEOVERRIDE);
break; break;

View File

@ -1335,13 +1335,13 @@ static void reset_window(int reinit) {
} }
} }
static void click(Mouse_Button b, int x, int y, int shift, int ctrl) static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
{ {
int thistime = GetMessageTime(); int thistime = GetMessageTime();
if (send_raw_mouse && !(cfg.mouse_override && shift)) { if (send_raw_mouse && !(cfg.mouse_override && shift)) {
lastbtn = MBT_NOTHING; lastbtn = MBT_NOTHING;
term_mouse(b, MA_CLICK, x, y, shift, ctrl); term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
return; return;
} }
@ -1354,7 +1354,7 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
lastact = MA_CLICK; lastact = MA_CLICK;
} }
if (lastact != MA_NOTHING) if (lastact != MA_NOTHING)
term_mouse(b, lastact, x, y, shift, ctrl); term_mouse(b, lastact, x, y, shift, ctrl, alt);
lasttime = thistime; lasttime = thistime;
} }
@ -1385,6 +1385,19 @@ static void show_mouseptr(int show)
cursor_visible = show; cursor_visible = show;
} }
static int is_alt_pressed(void)
{
BYTE keystate[256];
int r = GetKeyboardState(keystate);
if (!r)
return FALSE;
if (keystate[VK_MENU] & 0x80)
return TRUE;
if (keystate[VK_RMENU] & 0x80)
return TRUE;
return FALSE;
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) WPARAM wParam, LPARAM lParam)
{ {
@ -1730,14 +1743,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
if (send_raw_mouse) { if (send_raw_mouse) {
/* send a mouse-down followed by a mouse up */ /* send a mouse-down followed by a mouse up */
term_mouse(b, term_mouse(b,
MA_CLICK, MA_CLICK,
TO_CHR_X(X_POS(lParam)), TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
wParam & MK_CONTROL); wParam & MK_CONTROL, is_alt_pressed());
term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)), term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
wParam & MK_CONTROL); wParam & MK_CONTROL, is_alt_pressed());
} else { } else {
/* trigger a scroll */ /* trigger a scroll */
term_scroll(0, term_scroll(0,
@ -1754,6 +1768,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
case WM_RBUTTONUP: case WM_RBUTTONUP:
{ {
int button, press; int button, press;
switch (message) { switch (message) {
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
button = MBT_LEFT; button = MBT_LEFT;
@ -1786,13 +1801,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
if (press) { if (press) {
click(button, click(button,
TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
wParam & MK_SHIFT, wParam & MK_CONTROL); wParam & MK_SHIFT, wParam & MK_CONTROL,
is_alt_pressed());
SetCapture(hwnd); SetCapture(hwnd);
} else { } else {
term_mouse(button, MA_RELEASE, term_mouse(button, MA_RELEASE,
TO_CHR_X(X_POS(lParam)), TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
wParam & MK_CONTROL); wParam & MK_CONTROL, is_alt_pressed());
ReleaseCapture(); ReleaseCapture();
} }
} }
@ -1815,7 +1831,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
b = MBT_RIGHT; b = MBT_RIGHT;
term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)), term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
wParam & MK_CONTROL); wParam & MK_CONTROL, is_alt_pressed());
} }
return 0; return 0;
case WM_NCMOUSEMOVE: case WM_NCMOUSEMOVE: