From af6c892e245dfae3b6e472e98afa68cf8d8e4939 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 5 Jun 2016 04:02:53 -0400 Subject: implement double click to select words and triple click to select lines --- src/config.c | 1 + src/config.h | 1 + src/window-xlib.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/window-xlib.h | 3 + 4 files changed, 173 insertions(+), 4 deletions(-) diff --git a/src/config.c b/src/config.c index b663a2e..acb9d15 100644 --- a/src/config.c +++ b/src/config.c @@ -54,6 +54,7 @@ static void runes_config_set_defaults(RunesConfig *config) config->bell_is_urgent = 1; config->redraw_rate = 20; + config->double_click_rate = 500; config->cursorcolor = cairo_pattern_create_rgb(0.0, 1.0, 0.0); config->mousecursorcolor = cairo_pattern_create_rgb(1.0, 1.0, 1.0); diff --git a/src/config.h b/src/config.h index da1561b..7324a10 100644 --- a/src/config.h +++ b/src/config.h @@ -18,6 +18,7 @@ struct runes_config { int scrollback_length; int redraw_rate; + int double_click_rate; char *cmd; char *font_name; diff --git a/src/window-xlib.c b/src/window-xlib.c index 8421ddd..0d1fe4f 100644 --- a/src/window-xlib.c +++ b/src/window-xlib.c @@ -96,8 +96,12 @@ static void runes_window_clear_urgent(RunesTerm *t); static void runes_window_paste(RunesTerm *t, Time time); static void runes_window_start_selection( RunesTerm *t, int xpixel, int ypixel, Time time); +static void runes_window_start_selection_loc( + RunesTerm *t, struct vt100_loc *loc, Time time); static void runes_window_update_selection( RunesTerm *t, int xpixel, int ypixel); +static void runes_window_update_selection_loc( + RunesTerm *t, struct vt100_loc *loc); static void runes_window_clear_selection(RunesTerm *t); static void runes_window_handle_key_event(RunesTerm *t, XKeyEvent *e); static void runes_window_handle_button_event(RunesTerm *t, XButtonEvent *e); @@ -117,6 +121,11 @@ static int runes_window_handle_builtin_keypress( RunesTerm *t, KeySym sym, XKeyEvent *e); static int runes_window_handle_builtin_button_press( RunesTerm *t, XButtonEvent *e); +static void runes_window_handle_multi_click(RunesTerm *t, XButtonEvent *e); +static void runes_window_beginning_of_word(RunesTerm *t, struct vt100_loc *loc); +static void runes_window_end_of_word(RunesTerm *t, struct vt100_loc *loc); +static int runes_window_is_word_char(char *buf, size_t len); +static void runes_window_multi_click_cb(void *t); static struct function_key *runes_window_find_key_sequence( RunesTerm *t, KeySym sym); static struct vt100_loc runes_window_get_mouse_position( @@ -697,13 +706,21 @@ static void runes_window_paste(RunesTerm *t, Time time) static void runes_window_start_selection( RunesTerm *t, int xpixel, int ypixel, Time time) +{ + struct vt100_loc loc; + + loc = runes_window_get_mouse_position(t, xpixel, ypixel); + runes_window_start_selection_loc(t, &loc, time); +} + +static void runes_window_start_selection_loc( + RunesTerm *t, struct vt100_loc *loc, Time time) { RunesWindow *w = t->w; struct vt100_loc *start = &t->display->selection_start; struct vt100_loc *end = &t->display->selection_end; - *start = runes_window_get_mouse_position(t, xpixel, ypixel); - *end = *start; + *start = *end = *loc; XSetSelectionOwner(w->wb->dpy, XA_PRIMARY, w->w, time); t->display->has_selection = (XGetSelectionOwner(w->wb->dpy, XA_PRIMARY) == w->w); @@ -713,6 +730,15 @@ static void runes_window_start_selection( } static void runes_window_update_selection(RunesTerm *t, int xpixel, int ypixel) +{ + struct vt100_loc loc; + + loc = runes_window_get_mouse_position(t, xpixel, ypixel); + runes_window_update_selection_loc(t, &loc); +} + +static void runes_window_update_selection_loc( + RunesTerm *t, struct vt100_loc *loc) { RunesWindow *w = t->w; struct vt100_loc *start = &t->display->selection_start; @@ -723,7 +749,7 @@ static void runes_window_update_selection(RunesTerm *t, int xpixel, int ypixel) return; } - *end = runes_window_get_mouse_position(t, xpixel, ypixel); + *end = *loc; if (orig_end.row != end->row || orig_end.col != end->col) { if (end->row < start->row || (end->row == start->row && end->col < start->col)) { @@ -1053,7 +1079,8 @@ static int runes_window_handle_builtin_keypress( static int runes_window_handle_builtin_button_press( RunesTerm *t, XButtonEvent *e) { - if (e->type != ButtonRelease) { + switch (e->type) { + case ButtonPress: switch (e->button) { case Button1: runes_window_start_selection(t, e->x, e->y, e->time); @@ -1074,11 +1101,148 @@ static int runes_window_handle_builtin_button_press( default: break; } + break; + case ButtonRelease: + runes_window_handle_multi_click(t, e); + break; + default: + break; } return 0; } +static void runes_window_handle_multi_click(RunesTerm *t, XButtonEvent *e) +{ + RunesWindow *w = t->w; + + if (e->button != Button1) { + return; + } + + w->multi_clicks++; + if (w->multi_clicks > 1) { + struct vt100_loc loc; + + loc = runes_window_get_mouse_position(t, e->x, e->y); + if (w->multi_clicks % 2) { + loc.col = 0; + runes_window_start_selection_loc(t, &loc, e->time); + loc.col = t->scr->grid->max.col; + runes_window_update_selection_loc(t, &loc); + } + else { + struct vt100_loc orig_loc = loc; + + runes_window_beginning_of_word(t, &loc); + runes_window_start_selection_loc(t, &loc, e->time); + loc = orig_loc; + runes_window_end_of_word(t, &loc); + runes_window_update_selection_loc(t, &loc); + } + } + + if (w->multi_click_timer_event) { + runes_loop_timer_clear(t->loop, w->multi_click_timer_event); + } + + w->multi_click_timer_event = runes_loop_timer_set( + t->loop, t->config->double_click_rate, t, + runes_window_multi_click_cb); +} + +static void runes_window_beginning_of_word(RunesTerm *t, struct vt100_loc *loc) +{ + struct vt100_cell *c; + struct vt100_row *r; + int row = loc->row - t->scr->grid->row_top, col = loc->col; + int prev_row = row, prev_col = col; + + /* XXX vt100 should expose a better way to get at row data */ + c = vt100_screen_cell_at(t->scr, row, col); + r = &t->scr->grid->rows[row + t->scr->grid->row_top]; + while (runes_window_is_word_char(c->contents, c->len)) { + prev_col = col; + prev_row = row; + col--; + if (col < 0) { + row--; + if (row + t->scr->grid->row_top < 0) { + break; + } + r = &t->scr->grid->rows[row + t->scr->grid->row_top]; + if (!r->wrapped) { + break; + } + col = t->scr->grid->max.col - 1; + } + c = vt100_screen_cell_at(t->scr, row, col); + } + + loc->row = prev_row + t->scr->grid->row_top; + loc->col = prev_col; +} + +static void runes_window_end_of_word(RunesTerm *t, struct vt100_loc *loc) +{ + struct vt100_cell *c; + struct vt100_row *r; + int row = loc->row - t->scr->grid->row_top, col = loc->col; + + /* XXX vt100 should expose a better way to get at row data */ + c = vt100_screen_cell_at(t->scr, row, col); + r = &t->scr->grid->rows[row + t->scr->grid->row_top]; + while (runes_window_is_word_char(c->contents, c->len)) { + col++; + if (col >= t->scr->grid->max.col) { + if (!r->wrapped) { + break; + } + row++; + if (row >= t->scr->grid->max.row) { + break; + } + r = &t->scr->grid->rows[row + t->scr->grid->row_top]; + col = 0; + } + c = vt100_screen_cell_at(t->scr, row, col); + } + + loc->row = row + t->scr->grid->row_top; + loc->col = col; +} + +static int runes_window_is_word_char(char *buf, size_t len) +{ + gunichar uc; + + if (len == 0) { + return 0; + } + + if (g_utf8_next_char(buf) < buf + len) { + return 1; + } + + uc = g_utf8_get_char(buf); + if (vt100_char_width(uc) == 0) { + return 0; + } + if (g_unichar_isspace(uc)) { + return 0; + } + + return 1; +} + +static void runes_window_multi_click_cb(void *t) +{ + RunesWindow *w = ((RunesTerm *)t)->w; + + w->multi_clicks = 0; + w->multi_click_timer_event = NULL; +} + static struct function_key *runes_window_find_key_sequence( RunesTerm *t, KeySym sym) { diff --git a/src/window-xlib.h b/src/window-xlib.h index 21ac4de..7819c5b 100644 --- a/src/window-xlib.h +++ b/src/window-xlib.h @@ -16,6 +16,9 @@ struct runes_window { cairo_t *backend_cr; + unsigned int multi_clicks; + void *multi_click_timer_event; + unsigned int visual_bell_is_ringing: 1; unsigned int delaying: 1; }; -- cgit v1.2.3