GUACAMOLE-192: Select word in terminal on double click
This commit is contained in:
parent
ad0b4401a5
commit
d00ce0a3f0
@ -194,7 +194,7 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
|
||||
|
||||
/* Only update if selection has changed */
|
||||
if (row != terminal->selection_end_row
|
||||
|| column < terminal->selection_end_column
|
||||
|| column <= terminal->selection_end_column
|
||||
|| column >= terminal->selection_end_column + terminal->selection_end_width) {
|
||||
|
||||
int width = guac_terminal_find_char(terminal, row, &column);
|
||||
|
||||
@ -590,6 +590,10 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
||||
/* Configure backspace */
|
||||
term->backspace = options->backspace;
|
||||
|
||||
/* Initialize mouse latest click time and counter */
|
||||
term->click_timer = 0;
|
||||
term->click_counter = 0;
|
||||
|
||||
return term;
|
||||
|
||||
}
|
||||
@ -1748,6 +1752,132 @@ int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given character is part of a word.
|
||||
* Match these chars :[0-9A-Za-z\$\%\&\-\.\/\:\=\?\\_~]
|
||||
* This allows a path, URL, variable name or IP address to be treated as a word.
|
||||
*
|
||||
* @param ascii_char
|
||||
* The character to check.
|
||||
*
|
||||
* @return
|
||||
* true if match a "word" char,
|
||||
* false otherwise.
|
||||
*/
|
||||
static bool guac_terminal_is_part_of_word(int ascii_char) {
|
||||
return ((ascii_char >= '0' && ascii_char <= '9') ||
|
||||
(ascii_char >= 'A' && ascii_char <= 'Z') ||
|
||||
(ascii_char >= 'a' && ascii_char <= 'z') ||
|
||||
(ascii_char == '$') ||
|
||||
(ascii_char == '%') ||
|
||||
(ascii_char == '&') ||
|
||||
(ascii_char == '-') ||
|
||||
(ascii_char == '.') ||
|
||||
(ascii_char == '/') ||
|
||||
(ascii_char == ':') ||
|
||||
(ascii_char == '=') ||
|
||||
(ascii_char == '?') ||
|
||||
(ascii_char == '\\') ||
|
||||
(ascii_char == '_') ||
|
||||
(ascii_char == '~'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given character is part of blank block.
|
||||
*
|
||||
* @param ascii_char
|
||||
* The character to check.
|
||||
*
|
||||
* @return
|
||||
* true if match space (char 0x20) or NULL (char 0x00),
|
||||
* false otherwise.
|
||||
*/
|
||||
static bool guac_terminal_is_blank(int ascii_char) {
|
||||
return (ascii_char == '\0' || ascii_char == ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the char (int ASCII code) at a specific row/col of the display.
|
||||
*
|
||||
* @param terminal
|
||||
* The terminal on which we want to read a character.
|
||||
*
|
||||
* @param row
|
||||
* The row where to read the character.
|
||||
*
|
||||
* @param col
|
||||
* The column where to read the character.
|
||||
*
|
||||
* @return
|
||||
* The ASCII code of the character at the given row/col.
|
||||
*/
|
||||
static int guac_terminal_get_char(guac_terminal* terminal, int row, int col) {
|
||||
guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
|
||||
guac_terminal_char* ascii_char = &(buffer_row->characters[col]);
|
||||
|
||||
return ascii_char->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selection of a word during a double click event.
|
||||
* - Fetching the character under the mouse cursor.
|
||||
* - Determining the type of character :
|
||||
* Letter, digit, acceptable symbol within a word,
|
||||
* or space/NULL,
|
||||
* all other chars are treated as single.
|
||||
* - Calculating the word boundaries.
|
||||
* - Visual selection of the found word.
|
||||
* - Adding it to clipboard.
|
||||
*
|
||||
* @param terminal
|
||||
* The terminal that received a double click event.
|
||||
*
|
||||
* @param row
|
||||
* The row where is the mouse at the double click event.
|
||||
*
|
||||
* @param col
|
||||
* The column where is the mouse at the double click event.
|
||||
*/
|
||||
static void guac_terminal_double_click(guac_terminal* terminal, int row, int col) {
|
||||
|
||||
/* (char)10 behind cursor */
|
||||
int cursor_char = guac_terminal_get_char(terminal, row, col);
|
||||
|
||||
/* Position of the word behind cursor.
|
||||
* Default = col required to select a char if not a word and not blank. */
|
||||
int word_head = col;
|
||||
int word_tail = col;
|
||||
int flag;
|
||||
|
||||
/* The function used to calculate the word borders */
|
||||
bool (*is_part_of_word)(int) = NULL;
|
||||
|
||||
/* If selection is on a word, get its borders */
|
||||
if (guac_terminal_is_part_of_word(cursor_char))
|
||||
is_part_of_word = guac_terminal_is_part_of_word;
|
||||
|
||||
/* If selection is on a blank, get its borders */
|
||||
else if (guac_terminal_is_blank(cursor_char))
|
||||
is_part_of_word = guac_terminal_is_blank;
|
||||
|
||||
if (is_part_of_word != NULL) {
|
||||
/* Get word head*/
|
||||
do {
|
||||
flag = guac_terminal_get_char(terminal, row, word_head-1);
|
||||
} while (is_part_of_word(flag) && (word_head >= 0 && word_head <= terminal->display->width) && word_head--);
|
||||
|
||||
/* Get word tail */
|
||||
do {
|
||||
flag = guac_terminal_get_char(terminal, row, word_tail+1);
|
||||
} while (is_part_of_word(flag) && (word_tail >= 0 && word_tail <= terminal->display->width) && word_tail++);
|
||||
}
|
||||
|
||||
/* Select and add to clipboard the "word" */
|
||||
guac_terminal_select_start(terminal, row, word_head);
|
||||
guac_terminal_select_update(terminal, row, word_tail);
|
||||
|
||||
}
|
||||
|
||||
static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
||||
int x, int y, int mask) {
|
||||
|
||||
@ -1813,8 +1943,34 @@ static int __guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
|
||||
if (pressed_mask & GUAC_CLIENT_MOUSE_LEFT) {
|
||||
if (term->mod_shift)
|
||||
guac_terminal_select_resume(term, row, col);
|
||||
else
|
||||
guac_terminal_select_start(term, row, col);
|
||||
else {
|
||||
|
||||
/* Reset click counter if last click was 300ms before */
|
||||
if (guac_timestamp_current() - term->click_timer > 300)
|
||||
term->click_counter = 0;
|
||||
|
||||
/* New click time */
|
||||
term->click_timer = guac_timestamp_current();
|
||||
|
||||
switch (term->click_counter++) {
|
||||
|
||||
/* First click = start selection */
|
||||
case 0:
|
||||
guac_terminal_select_start(term, row, col);
|
||||
break;
|
||||
|
||||
/* Second click = word selection */
|
||||
case 1:
|
||||
guac_terminal_double_click(term, row, col);
|
||||
break;
|
||||
|
||||
/* third click or more = line selection */
|
||||
default:
|
||||
guac_terminal_select_start(term, row, 0);
|
||||
guac_terminal_select_update(term, row, term->display->width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In all other cases, simply update the existing selection as long as
|
||||
|
||||
@ -465,6 +465,16 @@ struct guac_terminal {
|
||||
*/
|
||||
bool disable_copy;
|
||||
|
||||
/**
|
||||
* The time betwen two left clicks.
|
||||
*/
|
||||
guac_timestamp click_timer;
|
||||
|
||||
/**
|
||||
* Counter for left clicks.
|
||||
*/
|
||||
int click_counter;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user