X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/f2003e3230fa6f3a1cc5d60256d1a19c8b0022d5..6908fed739ac49c43d4400ef572e0811a0ac676e:/terminal.c diff --git a/terminal.c b/terminal.c index 1e2d50c6..7f6f8bba 100644 --- a/terminal.c +++ b/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 */ @@ -163,6 +167,9 @@ 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; @@ -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;