diff options
author | Kent Overstreet <koverstreet@google.com> | 2013-03-18 07:19:06 -0700 |
---|---|---|
committer | Kent Overstreet <koverstreet@google.com> | 2013-04-23 16:37:36 -0700 |
commit | 4056c980881d2c5aa456eed6cb7a775b1ba8a237 (patch) | |
tree | 1436edf136893912918508b2060469e569f256cb | |
parent | ff24d7e418ac11629c86b7d8f17145aab75c08ff (diff) |
fix selections
-rw-r--r-- | st.c | 198 | ||||
-rw-r--r-- | term.c | 201 | ||||
-rw-r--r-- | term.h | 29 |
3 files changed, 208 insertions, 220 deletions
@@ -123,8 +123,13 @@ struct st_window { struct coord winsize; struct coord fixedsize; /* kill? */ struct coord charsize; + struct coord mousepos; unsigned mousebutton; + struct timeval mousedown; + struct timeval mouseup[3]; + + unsigned mousemotion:1; unsigned visible:1; unsigned focused:1; }; @@ -269,12 +274,7 @@ static void clippaste(struct st_window *xw, const union st_arg *dummy) static void selclear(struct st_window *xw, XEvent *e) { - struct st_selection *sel = &xw->term.sel; - - if (sel->bx == -1) - return; - sel->bx = -1; - tsetdirt(&xw->term, sel->b.y, sel->e.y); + term_sel_start(&xw->term, SEL_NONE, ORIGIN); } static void selrequest(struct st_window *xw, XEvent *e) @@ -576,9 +576,7 @@ static struct st_glyph sel_glyph(struct st_window *xw, unsigned x, unsigned y) { struct st_glyph ret = xw->term.line[y][x]; - if (xw->term.sel.bx != -1 && - xw->term.sel.alt == xw->term.altscreen && - term_selected(&xw->term.sel, x, y)) + if (term_selected(&xw->term.sel, x, y)) ret.reverse ^= 1; return ret; @@ -717,55 +715,21 @@ static void kpress(struct st_window *xw, XEvent *ev) /* Mouse code */ -static int x2col(struct st_window *xw, unsigned x) -{ - x -= borderpx; - x /= xw->charsize.x; - - return min(x, xw->term.size.x - 1); -} - -static int y2row(struct st_window *xw, unsigned y) -{ - y -= borderpx; - y /= xw->charsize.y; - - return min(y, xw->term.size.y - 1); -} - -static void getbuttoninfo(struct st_window *xw, XEvent *ev) +static struct coord mouse_pos(struct st_window *xw, XEvent *ev) { - int type; - unsigned state = ev->xbutton.state & ~Button1Mask; - struct st_selection *sel = &xw->term.sel; - - sel->alt = xw->term.altscreen; - - sel->ex = x2col(xw, ev->xbutton.x); - sel->ey = y2row(xw, ev->xbutton.y); - - sel->b.x = sel->by < sel->ey ? sel->bx : sel->ex; - 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 < ARRAY_SIZE(selmasks); ++type) { - if (match(selmasks[type], state)) { - sel->type = type; - break; - } - } + return (struct coord) { + .x = min((ev->xbutton.x - borderpx) / xw->charsize.x, + xw->term.size.x - 1), + .y = min((ev->xbutton.y - borderpx) / xw->charsize.y, + xw->term.size.y - 1), + }; } static void mousereport(struct st_window *xw, XEvent *ev) { - int button = ev->xbutton.button, state = ev->xbutton.state, len; char buf[40]; - struct coord pos = { - x2col(xw, ev->xbutton.x), - y2row(xw, ev->xbutton.y) - }; + int button = ev->xbutton.button, state = ev->xbutton.state, len; + struct coord pos = mouse_pos(xw, ev); /* from urxvt */ if (ev->xbutton.type == MotionNotify) { @@ -790,22 +754,20 @@ static void mousereport(struct st_window *xw, XEvent *ev) } } - button += (state & ShiftMask ? 4 : 0) - + (state & Mod4Mask ? 8 : 0) - + (state & ControlMask ? 16 : 0); + button += (state & ShiftMask ? 4 : 0) + + (state & Mod4Mask ? 8 : 0) + + (state & ControlMask ? 16 : 0); - len = 0; - if (xw->term.mousesgr) { + if (xw->term.mousesgr) len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", button, pos.x + 1, pos.y + 1, ev->xbutton.type == ButtonRelease ? 'm' : 'M'); - } else if (pos.x < 223 && pos.y < 223) { + else if (pos.x < 223 && pos.y < 223) len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 32 + button, 32 + pos.x + 1, 32 + pos.y + 1); - } else { + else return; - } ttywrite(&xw->term, buf, len); } @@ -813,20 +775,22 @@ static void mousereport(struct st_window *xw, XEvent *ev) static void bpress(struct st_window *xw, XEvent *ev) { struct st_term *term = &xw->term; - struct st_selection *sel = &xw->term.sel; if (term->mousebtn || term->mousemotion) { mousereport(xw, ev); } else if (ev->xbutton.button == Button1) { - if (sel->bx != -1) { - sel->bx = -1; - xw->term.dirty[sel->b.y] = 1; - tfulldirt(&xw->term); // XXX - } - sel->mode = 1; - sel->type = SEL_REGULAR; - sel->ex = sel->bx = x2col(xw, ev->xbutton.x); - sel->ey = sel->by = y2row(xw, ev->xbutton.y); + unsigned type = SEL_REGULAR; + unsigned state = ev->xbutton.state & ~Button1Mask; + + for (unsigned i = 1; i < ARRAY_SIZE(selmasks); i++) + if (match(selmasks[i], state)) { + type = i; + break; + } + + xw->mousemotion = 1; + term_sel_start(term, type, mouse_pos(xw, ev)); + gettimeofday(&xw->mousedown, NULL); } else if (ev->xbutton.button == Button4) { ttywrite(term, "\031", 1); } else if (ev->xbutton.button == Button5) { @@ -834,84 +798,72 @@ static void bpress(struct st_window *xw, XEvent *ev) } } -static void brelease(struct st_window *xw, XEvent *e) +static bool isword(unsigned c) +{ + return c && !isspace(c); +} + +static void brelease(struct st_window *xw, XEvent *ev) { struct st_term *term = &xw->term; struct st_selection *sel = &term->sel; - struct timeval now; if (term->mousebtn || term->mousemotion) { - mousereport(xw, e); + mousereport(xw, ev); return; } - if (e->xbutton.button == Button2) { + if (ev->xbutton.button == Button2) { selpaste(xw, NULL); - } else if (e->xbutton.button == Button1) { - sel->mode = 0; - getbuttoninfo(xw, e); - term->dirty[sel->ey] = 1; - if (sel->bx == sel->ex && sel->by == sel->ey) { - sel->bx = -1; - gettimeofday(&now, NULL); - - if (TIMEDIFF(now, sel->tclick2) <= + } else if (ev->xbutton.button == Button1) { + struct coord end = mouse_pos(xw, ev); + + memmove(xw->mouseup + 1, + xw->mouseup, sizeof(struct timeval) * 2); + gettimeofday(xw->mouseup, NULL); + + if (sel->start.x == end.x && + sel->start.y == end.y) { + if (TIMEDIFF(xw->mouseup[0], xw->mouseup[2]) < tripleclicktimeout) { /* triple click on the line */ - sel->b.x = sel->bx = 0; - sel->e.x = sel->ex = term->size.x; - sel->b.y = sel->e.y = sel->ey; - term_selcopy(term); - xsetsel(xw); - } else if (TIMEDIFF(now, sel->tclick1) <= + sel->start.x = 0; + end.x = term->size.x - 1; + } else if (TIMEDIFF(xw->mouseup[0], xw->mouseup[1]) < doubleclicktimeout) { /* double click to select word */ - sel->bx = sel->ex; - while (sel->bx > 0 && - isalpha(term->line[sel->ey][sel->bx - 1].c)) - sel->bx--; - sel->b.x = sel->bx; - while (sel->ex < term->size.x - 1 && - isalpha(term->line[sel->ey][sel->ex + 1].c)) - sel->ex++; - sel->e.x = sel->ex; - sel->b.y = sel->e.y = sel->ey; - term_selcopy(term); - xsetsel(xw); + while (sel->start.x && + isword(term_pos(term, sel->start)[-1].c)) + sel->start.x--; + + while (end.x < term->size.x - 1 && + isword(term_pos(term, end)[1].c)) + end.x++; + } else if (TIMEDIFF(xw->mouseup[0], xw->mousedown) < + doubleclicktimeout) { + sel->type = SEL_NONE; } - } else { - term_selcopy(term); - xsetsel(xw); } - } - memcpy(&sel->tclick2, &sel->tclick1, sizeof(struct timeval)); - gettimeofday(&sel->tclick1, NULL); + xw->mousemotion = 0; + term_sel_end(term, end); + term_sel_copy(term); + if (sel->clip) + xsetsel(xw); + } } -static void bmotion(struct st_window *xw, XEvent *e) +static void bmotion(struct st_window *xw, XEvent *ev) { struct st_term *term = &xw->term; - int oldey, oldex, oldsby, oldsey; if (term->mousebtn || term->mousemotion) { - mousereport(xw, e); + mousereport(xw, ev); return; } - if (!term->sel.mode) - return; - - oldey = term->sel.ey; - oldex = term->sel.ex; - oldsby = term->sel.b.y; - oldsey = term->sel.e.y; - getbuttoninfo(xw, e); - - if (oldey != term->sel.ey || oldex != term->sel.ex) - tsetdirt(term, - min(term->sel.b.y, oldsby), - max(term->sel.e.y, oldsey)); + if (xw->mousemotion) + term_sel_end(term, mouse_pos(xw, ev)); } /* Resizing code */ @@ -24,110 +24,151 @@ static void selscroll(struct st_term *term, int orig, int n) { - if (term->sel.bx == -1) + struct st_selection *sel = &term->sel; + + if (sel->type == SEL_NONE) return; - if (BETWEEN(term->sel.by, orig, term->bot) - || BETWEEN(term->sel.ey, orig, term->bot)) { - if ((term->sel.by += n) > term->bot || - (term->sel.ey += n) < term->top) { - term->sel.bx = -1; + if (BETWEEN(sel->start.y, orig, term->bot) || + BETWEEN(sel->end.y, orig, term->bot)) { + if ((sel->start.y += n) > term->bot || + (sel->end.y += n) < term->top) { + sel->type = SEL_NONE; return; } - switch (term->sel.type) { + switch (sel->type) { + case SEL_NONE: + break; case SEL_REGULAR: - if (term->sel.by < term->top) { - term->sel.by = term->top; - term->sel.bx = 0; + if (sel->start.y < term->top) { + sel->start.y = term->top; + sel->start.x = 0; } - if (term->sel.ey > term->bot) { - term->sel.ey = term->bot; - term->sel.ex = term->size.y; + if (sel->end.y > term->bot) { + sel->end.y = term->bot; + sel->end.x = term->size.y; } break; case SEL_RECTANGULAR: - if (term->sel.by < term->top) - term->sel.by = term->top; - if (term->sel.ey > term->bot) - term->sel.ey = term->bot; + if (sel->start.y < term->top) + sel->start.y = term->top; + if (sel->end.y > term->bot) + sel->end.y = term->bot; break; }; - term->sel.b.y = term->sel.by, term->sel.b.x = term->sel.bx; - term->sel.e.y = term->sel.ey, term->sel.e.x = term->sel.ex; + + sel->p1 = sel->start; + sel->p2 = sel->end; } } bool term_selected(struct st_selection *sel, int x, int y) { - int bx, ex; + switch (sel->type) { + case SEL_NONE: + return false; + case SEL_REGULAR: + if (y < sel->p1.y || y > sel->p2.y) + return false; - if (sel->ey == y && sel->by == y) { - bx = min(sel->bx, sel->ex); - ex = max(sel->bx, sel->ex); - return BETWEEN(x, bx, ex); - } + if (y == sel->p1.y && x < sel->p1.x) + return false; - 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)); + if (y == sel->p2.y && x > sel->p2.x) + return false; - 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)); + return true; case SEL_RECTANGULAR: - return ((sel->b.y <= y && y <= sel->e.y) - && (sel->b.x <= x && x <= sel->e.x)); - }; + return sel->p1.y <= y && y <= sel->p2.y && + sel->p1.x <= x && x <= sel->p2.x; + } + + return false; } -void term_selcopy(struct st_term *term) +void term_sel_copy(struct st_term *term) { - unsigned char *str, *ptr; - int x, y, bufsize, is_selected = 0; - struct st_glyph *gp, *last; + struct st_selection *sel = &term->sel; + unsigned char *str = NULL, *ptr; - if (term->sel.bx == -1) { - str = NULL; - } else { - bufsize = (term->size.y + 1) * - (term->sel.e.y - term->sel.b.y + 1) * UTF_SIZ; - ptr = str = xmalloc(bufsize); - - /* append every set & selected glyph to the selection */ - for (y = term->sel.b.y; y < term->sel.e.y + 1; y++) { - is_selected = 0; - gp = &term->line[y][0]; - last = gp + term->size.y; - - while (--last >= gp && !last->c) - /* nothing */ ; - - for (x = 0; gp <= last; x++, ++gp) { - if (!term_selected(&term->sel, x, y)) { - continue; - } else { - is_selected = 1; - } + if (sel->type == SEL_NONE) + goto out; - if (gp->c) - ptr += FcUcs4ToUtf8(gp->c, ptr); - else - *(ptr++) = ' '; - } - /* \n at the end of every selected line except for the last one */ - if (is_selected && y < term->sel.e.y) - *ptr++ = '\r'; + ptr = str = xmalloc((sel->p2.y - sel->p1.y + 1) * + term->size.y * UTF_SIZ); + + /* append every set & selected glyph to the selection */ + for (unsigned y = sel->p1.y; y <= sel->p2.y; y++) { + struct st_glyph *gp = &term->line[y][0]; + struct st_glyph *last = &term->line[y][term->size.x - 1]; + + if (sel->type == SEL_RECTANGULAR || + y == sel->p1.y) + gp = &term->line[y][sel->p1.x]; + + if (sel->type == SEL_RECTANGULAR || + y == sel->p2.y) + last = &term->line[y][sel->p2.x]; + + while (last > gp && !last->c) + last--; + + for (; gp <= last; gp++) { + int ret = FcUcs4ToUtf8(gp->c, ptr); + if (ret > 0) + ptr += ret; + else + *ptr++ = ' '; } - *ptr = 0; + + /* \n at the end of every selected line except for the last one */ + if (y < sel->p2.y) + *ptr++ = '\r'; + } + *ptr = 0; +out: + free(sel->clip); + sel->clip = (char *) str; +} + +void term_sel_start(struct st_term *term, unsigned type, struct coord start) +{ + struct st_selection *sel = &term->sel; + + sel->type = type; + sel->start = sel->end = sel->p1 = sel->p2 = start; + + tfulldirt(term); +} + +void term_sel_end(struct st_term *term, struct coord end) +{ + struct st_selection *sel = &term->sel; + + sel->p1 = sel->start; + sel->p2 = sel->end = end; + + switch (sel->type) { + case SEL_NONE: + break; + case SEL_REGULAR: + if (sel->p1.y > sel->p2.y || + (sel->p1.y == sel->p2.y && + sel->p1.x > sel->p2.x)) + swap(sel->p1, sel->p2); + + break; + case SEL_RECTANGULAR: + if (sel->p1.x > sel->p2.x) + swap(sel->p1.x, sel->p2.x); + if (sel->p1.y > sel->p2.y) + swap(sel->p1.y, sel->p2.y); + + break; } - free(term->sel.clip); - term->sel.clip = (char *) str; + tfulldirt(term); } /* Escape handling */ @@ -580,6 +621,7 @@ static void tsetscroll(struct st_term *term, unsigned t, unsigned b) static void tswapscreen(struct st_term *term) { swap(term->line, term->alt); + term->sel.type = SEL_NONE; term->altscreen ^= 1; tfulldirt(term); } @@ -763,7 +805,7 @@ static void csihandle(struct st_term *term) tputtab(term, 1); break; case 'J': /* ED -- Clear screen */ - term->sel.bx = -1; + term->sel.type = SEL_NONE; switch (csi->arg[0]) { case 0: /* below */ tclearregion(term, term->c.pos, term->size); @@ -1194,9 +1236,9 @@ static void tputc(struct st_term *term, unsigned c) if (control && !term->c.attr.gfx) return; - if (term->sel.bx != -1 && - BETWEEN(term->c.pos.y, term->sel.by, term->sel.ey)) - term->sel.bx = -1; + if (term->sel.type != SEL_NONE && + BETWEEN(term->c.pos.y, term->sel.p1.y, term->sel.p2.y)) + term->sel.type = SEL_NONE; if (term->wrap && term->c.wrapnext) tnewline(term, 1); /* always go to first col */ @@ -1499,7 +1541,6 @@ void term_init(struct st_term *term, int col, int row, char *shell, term->dirty[row] = 0; } - term->sel.bx = -1; term->numlock = 1; memset(term->tabs, 0, term->size.x * sizeof(*term->tabs)); /* setup screen */ @@ -78,11 +78,6 @@ enum escape_state { ESC_TEST = 32, /* Enter in test mode */ }; -enum selection_type { - SEL_REGULAR = 1, - SEL_RECTANGULAR = 2 -}; - struct st_glyph { unsigned c; /* character code */ union { @@ -134,19 +129,17 @@ struct str_escape { int narg; /* nb of args */ }; -/* TODO: use better name for vars... */ struct st_selection { - int mode; - int type; - int bx, by; - int ex, ey; - struct { - int x, y; - } b, e; + enum { + SEL_NONE, + SEL_REGULAR, + SEL_RECTANGULAR, + } type; + + struct coord start, end; + struct coord p1, p2; + char *clip; - bool alt; - struct timeval tclick1; - struct timeval tclick2; }; /* Internal representation of the screen */ @@ -202,7 +195,9 @@ struct st_term { }; bool term_selected(struct st_selection *sel, int x, int y); -void term_selcopy(struct st_term *term); +void term_sel_copy(struct st_term *term); +void term_sel_start(struct st_term *term, unsigned type, struct coord start); +void term_sel_end(struct st_term *term, struct coord end); void term_echo(struct st_term *term, char *buf, int len); void term_read(struct st_term *term); |