configure it to be the default in which case it's _dis_abled by
Alt+drag.
git-svn-id: svn://svn.tartarus.org/sgt/putty@1350
cda61777-01e9-0310-a592-
d414129be87e
unsigned char colours[22][3];
/* Selection options */
int mouse_is_xterm;
unsigned char colours[22][3];
/* Selection options */
int mouse_is_xterm;
int rawcnp;
int mouse_override;
short wordness[256];
int rawcnp;
int mouse_override;
short wordness[256];
void term_scroll(int, int);
void term_pwron(void);
void term_clrsb(void);
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);
void term_deselect(void);
void term_update(void);
void term_invalidate(void);
}
write_setting_i(sesskey, "RawCNP", cfg->rawcnp);
write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm);
}
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];
write_setting_i(sesskey, "MouseOverride", cfg->mouse_override);
for (i = 0; i < 256; i += 32) {
char buf[20], buf2[256];
}
gppi(sesskey, "RawCNP", 0, &cfg->rawcnp);
gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm);
}
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[] = {
gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override);
for (i = 0; i < 256; i += 32) {
static char *defaults[] = {
#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 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 */
static bufchain inbuf; /* terminal input buffer */
static pos curs; /* cursor */
static pos savecurs; /* saved cursor position */
NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
} selstate;
static enum {
NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
} selstate;
static enum {
+ LEXICOGRAPHIC, RECTANGULAR
+} seltype;
+static enum {
SM_CHAR, SM_WORD, SM_LINE
} selmode;
static pos selstart, selend, selanchor;
SM_CHAR, SM_WORD, SM_LINE
} selmode;
static pos selstart, selend, selanchor;
for (i = 0; i < rows; i++) {
unsigned long *ldata;
int lattr;
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;
unsigned long attr = 0;
int updated_line = 0;
int start = 0;
tattr |= ATTR_WIDE;
/* Video reversing things */
tattr |= ATTR_WIDE;
/* Video reversing things */
+ if (seltype == LEXICOGRAPHIC)
+ selected = posle(selstart, scrpos) && poslt(scrpos, selend);
+ else
+ selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
- ^ (posle(selstart, scrpos) &&
- poslt(scrpos, selend) ? ATTR_REVERSE : 0));
+ ^ (selected ? ATTR_REVERSE : 0));
/* 'Real' blinking ? */
if (blink_is_real && (tattr & ATTR_BLINK)) {
/* 'Real' blinking ? */
if (blink_is_real && (tattr & ATTR_BLINK)) {
-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 */
{
wchar_t *workbuf;
wchar_t *wbptr; /* where next char goes within workbuf */
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 */
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;
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;
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]) &&
if (!(ldata[cols] & LATTR_WRAPPED)) {
while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
(DIRECT_CHAR(ldata[nlpos.x - 1]) &&
if (poslt(nlpos, bottom))
nl = TRUE;
}
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;
while (poslt(top, bottom) && poslt(top, nlpos)) {
#if 0
char cbuf[16], *p;
+ top.x = rect ? old_top_x : 0;
pos top;
top.y = -count234(scrollback);
top.x = 0;
pos top;
top.y = -count234(scrollback);
top.x = 0;
static void sel_spread(void)
{
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)
}
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 alt)
{
pos selpoint;
unsigned long *ldata;
int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
{
pos selpoint;
unsigned long *ldata;
int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
+ /*
+ * 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;
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)) {
selanchor = selpoint;
selmode = SM_CHAR;
} else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
return;
if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
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;
+ }
+ /*
+ * 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;
}
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);
+ }
- 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) {
}
sel_spread();
} else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
* We've completed a selection. We now transfer the
* data to the clipboard.
*/
* 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;
selstate = SELECTED;
} else
selstate = NO_SELECTION;
PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
const static int percents[] = { 30, 40, 30 };
int i, xpos, percent;
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;
/* The static control. */
r.left = GAPBETWEEN;
IDC_MBWINDOWS,
IDC_MBXTERM,
IDC_MOUSEOVERRIDE,
IDC_MBWINDOWS,
IDC_MBXTERM,
IDC_MOUSEOVERRIDE,
+ IDC_SELTYPESTATIC,
+ IDC_SELTYPELEX,
+ IDC_SELTYPERECT,
IDC_CCSTATIC,
IDC_CCLIST,
IDC_CCSET,
IDC_CCSTATIC,
IDC_CCLIST,
IDC_CCSET,
CheckRadioButton(hwnd, IDC_MBWINDOWS, IDC_MBXTERM,
cfg.mouse_is_xterm ? IDC_MBXTERM : IDC_MBWINDOWS);
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);
{
CheckDlgButton(hwnd, IDC_MOUSEOVERRIDE, cfg.mouse_override);
CheckDlgButton(hwnd, IDC_RAWCNP, cfg.rawcnp);
{
}
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;
ctlposinit(&cp, hwnd, 80, 3, 13);
bartitle(&cp, "Options controlling copy and paste",
struct ctlpos cp;
ctlposinit(&cp, hwnd, 80, 3, 13);
bartitle(&cp, "Options controlling copy and paste",
checkbox(&cp,
"Shift overrides a&pplication's use of mouse",
IDC_MOUSEOVERRIDE);
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);
endbox(&cp);
beginbox(&cp, "Control the select-one-word-at-a-time mode",
IDC_BOX_SELECTION3);
case IDC_MBXTERM:
cfg.mouse_is_xterm = IsDlgButtonChecked(hwnd, IDC_MBXTERM);
break;
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;
case IDC_MOUSEOVERRIDE:
cfg.mouse_override = IsDlgButtonChecked(hwnd, IDC_MOUSEOVERRIDE);
break;
-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;
{
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);
lastact = MA_CLICK;
}
if (lastact != MA_NOTHING)
lastact = MA_CLICK;
}
if (lastact != MA_NOTHING)
- term_mouse(b, lastact, x, y, shift, ctrl);
+ term_mouse(b, lastact, x, y, shift, ctrl, alt);
+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)
{
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
if (send_raw_mouse) {
/* send a mouse-down followed by a mouse up */
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,
term_mouse(b,
MA_CLICK,
TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
+ 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,
term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
+ wParam & MK_CONTROL, is_alt_pressed());
} else {
/* trigger a scroll */
term_scroll(0,
} else {
/* trigger a scroll */
term_scroll(0,
case WM_RBUTTONUP:
{
int button, press;
case WM_RBUTTONUP:
{
int button, press;
switch (message) {
case WM_LBUTTONDOWN:
button = MBT_LEFT;
switch (message) {
case WM_LBUTTONDOWN:
button = MBT_LEFT;
if (press) {
click(button,
TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
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,
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, is_alt_pressed());
b = MBT_RIGHT;
term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
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, is_alt_pressed());
}
return 0;
case WM_NCMOUSEMOVE:
}
return 0;
case WM_NCMOUSEMOVE: