mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00: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:
parent
ec1d8c1408
commit
d2c9937691
3
putty.h
3
putty.h
@ -326,6 +326,7 @@ typedef struct {
|
||||
unsigned char colours[22][3];
|
||||
/* Selection options */
|
||||
int mouse_is_xterm;
|
||||
int rect_select;
|
||||
int rawcnp;
|
||||
int mouse_override;
|
||||
short wordness[256];
|
||||
@ -452,7 +453,7 @@ void term_paint(Context, int, int, int, int);
|
||||
void term_scroll(int, int);
|
||||
void term_pwron(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_update(void);
|
||||
void term_invalidate(void);
|
||||
|
@ -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, "MouseIsXterm", cfg->mouse_is_xterm);
|
||||
write_setting_i(sesskey, "RectSelect", cfg->rect_select);
|
||||
write_setting_i(sesskey, "MouseOverride", cfg->mouse_override);
|
||||
for (i = 0; i < 256; i += 32) {
|
||||
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, "MouseIsXterm", 0, &cfg->mouse_is_xterm);
|
||||
gppi(sesskey, "RectSelect", 0, &cfg->rect_select);
|
||||
gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override);
|
||||
for (i = 0; i < 256; i += 32) {
|
||||
static char *defaults[] = {
|
||||
|
140
terminal.c
140
terminal.c
@ -86,6 +86,10 @@ typedef struct {
|
||||
#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) )
|
||||
|
||||
/* 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 pos curs; /* cursor */
|
||||
static pos savecurs; /* saved cursor position */
|
||||
@ -162,6 +166,9 @@ static enum {
|
||||
static enum {
|
||||
NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
|
||||
} selstate;
|
||||
static enum {
|
||||
LEXICOGRAPHIC, RECTANGULAR
|
||||
} seltype;
|
||||
static enum {
|
||||
SM_CHAR, SM_WORD, SM_LINE
|
||||
} selmode;
|
||||
@ -2586,7 +2593,7 @@ static void do_paint(Context ctx, int may_optimise)
|
||||
for (i = 0; i < rows; i++) {
|
||||
unsigned long *ldata;
|
||||
int lattr;
|
||||
int idx, dirty_line, dirty_run;
|
||||
int idx, dirty_line, dirty_run, selected;
|
||||
unsigned long attr = 0;
|
||||
int updated_line = 0;
|
||||
int start = 0;
|
||||
@ -2626,9 +2633,12 @@ static void do_paint(Context ctx, int may_optimise)
|
||||
tattr |= ATTR_WIDE;
|
||||
|
||||
/* Video reversing things */
|
||||
if (seltype == LEXICOGRAPHIC)
|
||||
selected = posle(selstart, scrpos) && poslt(scrpos, selend);
|
||||
else
|
||||
selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
|
||||
tattr = (tattr ^ rv
|
||||
^ (posle(selstart, scrpos) &&
|
||||
poslt(scrpos, selend) ? ATTR_REVERSE : 0));
|
||||
^ (selected ? ATTR_REVERSE : 0));
|
||||
|
||||
/* 'Real' blinking ? */
|
||||
if (blink_is_real && (tattr & ATTR_BLINK)) {
|
||||
@ -2816,25 +2826,38 @@ void term_scroll(int rel, int where)
|
||||
term_update();
|
||||
}
|
||||
|
||||
static void clipme(pos top, pos bottom)
|
||||
static void clipme(pos top, pos bottom, int rect)
|
||||
{
|
||||
wchar_t *workbuf;
|
||||
wchar_t *wbptr; /* where next char goes within workbuf */
|
||||
int old_top_x;
|
||||
int wblen = 0; /* workbuf len */
|
||||
int buflen; /* amount of memory allocated to workbuf */
|
||||
|
||||
buflen = 5120; /* Default size */
|
||||
workbuf = smalloc(buflen * sizeof(wchar_t));
|
||||
wbptr = workbuf; /* start filling here */
|
||||
old_top_x = top.x; /* needed for rect==1 */
|
||||
|
||||
while (poslt(top, bottom)) {
|
||||
int nl = FALSE;
|
||||
unsigned long *ldata = lineptr(top.y);
|
||||
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.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)) {
|
||||
while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
|
||||
(DIRECT_CHAR(ldata[nlpos.x - 1]) &&
|
||||
@ -2844,6 +2867,20 @@ static void clipme(pos top, pos bottom)
|
||||
if (poslt(nlpos, bottom))
|
||||
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)) {
|
||||
#if 0
|
||||
char cbuf[16], *p;
|
||||
@ -2931,7 +2968,7 @@ static void clipme(pos top, pos bottom)
|
||||
}
|
||||
}
|
||||
top.y++;
|
||||
top.x = 0;
|
||||
top.x = rect ? old_top_x : 0;
|
||||
}
|
||||
wblen++;
|
||||
*wbptr++ = 0;
|
||||
@ -2945,7 +2982,7 @@ void term_copyall(void)
|
||||
pos top;
|
||||
top.y = -count234(scrollback);
|
||||
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)
|
||||
{
|
||||
selstart = sel_spread_half(selstart, -1);
|
||||
decpos(selend);
|
||||
selend = sel_spread_half(selend, +1);
|
||||
incpos(selend);
|
||||
if (seltype == LEXICOGRAPHIC) {
|
||||
selstart = sel_spread_half(selstart, -1);
|
||||
decpos(selend);
|
||||
selend = sel_spread_half(selend, +1);
|
||||
incpos(selend);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
int shift, int ctrl)
|
||||
int shift, int ctrl, int alt)
|
||||
{
|
||||
pos selpoint;
|
||||
unsigned long *ldata;
|
||||
int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
|
||||
int default_seltype;
|
||||
|
||||
if (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);
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
deselect();
|
||||
selstate = ABOUT_TO;
|
||||
seltype = default_seltype;
|
||||
selanchor = selpoint;
|
||||
selmode = SM_CHAR;
|
||||
} else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
|
||||
@ -3308,26 +3362,64 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
|
||||
if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
|
||||
return;
|
||||
if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
|
||||
if (posdiff(selpoint, selstart) <
|
||||
posdiff(selend, selstart) / 2) {
|
||||
selanchor = selend;
|
||||
decpos(selanchor);
|
||||
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) <
|
||||
posdiff(selend, selstart) / 2) {
|
||||
selanchor = selend;
|
||||
decpos(selanchor);
|
||||
} else {
|
||||
selanchor = selstart;
|
||||
}
|
||||
} else {
|
||||
selanchor = selstart;
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
if (selstate != ABOUT_TO && selstate != DRAGGING)
|
||||
selanchor = selpoint;
|
||||
selstate = DRAGGING;
|
||||
if (poslt(selpoint, selanchor)) {
|
||||
selstart = selpoint;
|
||||
selend = selanchor;
|
||||
incpos(selend);
|
||||
if (seltype == LEXICOGRAPHIC) {
|
||||
/*
|
||||
* For normal selection, we set (selstart,selend) to
|
||||
* (selpoint,selanchor) in some order.
|
||||
*/
|
||||
if (poslt(selpoint, selanchor)) {
|
||||
selstart = selpoint;
|
||||
selend = selanchor;
|
||||
incpos(selend);
|
||||
} else {
|
||||
selstart = selanchor;
|
||||
selend = selpoint;
|
||||
incpos(selend);
|
||||
}
|
||||
} else {
|
||||
selstart = selanchor;
|
||||
selend = selpoint;
|
||||
incpos(selend);
|
||||
/*
|
||||
* 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();
|
||||
} else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
|
||||
@ -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
|
||||
* data to the clipboard.
|
||||
*/
|
||||
clipme(selstart, selend);
|
||||
clipme(selstart, selend, (seltype == RECTANGULAR));
|
||||
selstate = SELECTED;
|
||||
} else
|
||||
selstate = NO_SELECTION;
|
||||
|
@ -712,7 +712,7 @@ void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
|
||||
PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
|
||||
const static int percents[] = { 30, 40, 30 };
|
||||
int i, xpos, percent;
|
||||
const int LISTHEIGHT = 66;
|
||||
const int LISTHEIGHT = 52;
|
||||
|
||||
/* The static control. */
|
||||
r.left = GAPBETWEEN;
|
||||
|
17
windlg.c
17
windlg.c
@ -479,6 +479,9 @@ enum { IDCX_ABOUT =
|
||||
IDC_MBWINDOWS,
|
||||
IDC_MBXTERM,
|
||||
IDC_MOUSEOVERRIDE,
|
||||
IDC_SELTYPESTATIC,
|
||||
IDC_SELTYPELEX,
|
||||
IDC_SELTYPERECT,
|
||||
IDC_CCSTATIC,
|
||||
IDC_CCLIST,
|
||||
IDC_CCSET,
|
||||
@ -763,9 +766,10 @@ static void init_dlg_ctrls(HWND hwnd, int keepsess)
|
||||
|
||||
}
|
||||
|
||||
|
||||
CheckRadioButton(hwnd, IDC_MBWINDOWS, IDC_MBXTERM,
|
||||
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_RAWCNP, cfg.rawcnp);
|
||||
{
|
||||
@ -1141,7 +1145,7 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
|
||||
}
|
||||
|
||||
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;
|
||||
ctlposinit(&cp, hwnd, 80, 3, 13);
|
||||
bartitle(&cp, "Options controlling copy and paste",
|
||||
@ -1161,6 +1165,11 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
|
||||
checkbox(&cp,
|
||||
"Shift overrides a&pplication's use of mouse",
|
||||
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);
|
||||
beginbox(&cp, "Control the select-one-word-at-a-time mode",
|
||||
IDC_BOX_SELECTION3);
|
||||
@ -2407,6 +2416,10 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
|
||||
case IDC_MBXTERM:
|
||||
cfg.mouse_is_xterm = IsDlgButtonChecked(hwnd, IDC_MBXTERM);
|
||||
break;
|
||||
case IDC_SELTYPELEX:
|
||||
case IDC_SELTYPERECT:
|
||||
cfg.rect_select = IsDlgButtonChecked(hwnd, IDC_SELTYPERECT);
|
||||
break;
|
||||
case IDC_MOUSEOVERRIDE:
|
||||
cfg.mouse_override = IsDlgButtonChecked(hwnd, IDC_MOUSEOVERRIDE);
|
||||
break;
|
||||
|
32
window.c
32
window.c
@ -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();
|
||||
|
||||
if (send_raw_mouse && !(cfg.mouse_override && shift)) {
|
||||
lastbtn = MBT_NOTHING;
|
||||
term_mouse(b, MA_CLICK, x, y, shift, ctrl);
|
||||
term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1354,7 +1354,7 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
|
||||
lastact = MA_CLICK;
|
||||
}
|
||||
if (lastact != MA_NOTHING)
|
||||
term_mouse(b, lastact, x, y, shift, ctrl);
|
||||
term_mouse(b, lastact, x, y, shift, ctrl, alt);
|
||||
lasttime = thistime;
|
||||
}
|
||||
|
||||
@ -1385,6 +1385,19 @@ static void show_mouseptr(int 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,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
@ -1730,14 +1743,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
|
||||
|
||||
if (send_raw_mouse) {
|
||||
/* send a mouse-down followed by a mouse up */
|
||||
|
||||
term_mouse(b,
|
||||
MA_CLICK,
|
||||
TO_CHR_X(X_POS(lParam)),
|
||||
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)),
|
||||
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
|
||||
wParam & MK_CONTROL);
|
||||
wParam & MK_CONTROL, is_alt_pressed());
|
||||
} else {
|
||||
/* trigger a scroll */
|
||||
term_scroll(0,
|
||||
@ -1754,6 +1768,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
int button, press;
|
||||
|
||||
switch (message) {
|
||||
case WM_LBUTTONDOWN:
|
||||
button = MBT_LEFT;
|
||||
@ -1786,13 +1801,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
|
||||
if (press) {
|
||||
click(button,
|
||||
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);
|
||||
} else {
|
||||
term_mouse(button, MA_RELEASE,
|
||||
TO_CHR_X(X_POS(lParam)),
|
||||
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
|
||||
wParam & MK_CONTROL);
|
||||
wParam & MK_CONTROL, is_alt_pressed());
|
||||
ReleaseCapture();
|
||||
}
|
||||
}
|
||||
@ -1815,7 +1831,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
|
||||
b = MBT_RIGHT;
|
||||
term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
|
||||
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
|
||||
wParam & MK_CONTROL);
|
||||
wParam & MK_CONTROL, is_alt_pressed());
|
||||
}
|
||||
return 0;
|
||||
case WM_NCMOUSEMOVE:
|
||||
|
Loading…
Reference in New Issue
Block a user