From 9c91aba7c7f7e47ec1a90b58d7f238c6b043dac1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Thorsten=20Wi=C3=9Fmann?= Date: Sun, 8 Apr 2012 13:08:13 +0200 Subject: [PATCH] Add basic mouse support This adds the following optional mouse features: * Select items in the main view by clicking and scrolling * Go to the edit-form by double-clicking * Edit fields by clicking on them * Select tabs by clicking on them * Return to main view/quit abook by clicking on the first row --- abookrc.5 | 4 ++++ edit.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++--- list.c | 6 ++++++ list.h | 1 + options.c | 1 + options.h | 1 + ui.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui.h | 4 ++++ 8 files changed, 131 insertions(+), 3 deletions(-) diff --git a/abookrc.5 b/abookrc.5 index fef99d6..8c13366 100644 --- a/abookrc.5 +++ b/abookrc.5 @@ -146,6 +146,10 @@ Defines the field to be used by the "sort by field" command. Default is "nick" ( \fBshow_cursor\fP=[true|false] Defines if the cursor is visible in main display. Default is false. +.TP +\fBuse_mouse\fP=[true|false] +Defines if navigation via the mouse is activated. Default is false. + .TP \fBuse_colors\fP=[true|false] Defines if the output of abook is colorized. Default is false. diff --git a/edit.c b/edit.c index 81da889..9a76c8a 100644 --- a/edit.c +++ b/edit.c @@ -588,13 +588,14 @@ key_to_field_number(char c) static void edit_field(int tab, char c, int item_number) { + ui_enable_mouse(FALSE); int i = 0, number, idx; char *msg; abook_field_list *f; list_item item; if((number = key_to_field_number(c)) < 0) - return; + goto detachfield; edit_undo(item_number, BACKUP_ITEM); @@ -602,7 +603,7 @@ edit_field(int tab, char c, int item_number) while(1) { if(!f) - return; + goto detachfield; if(i == number) break; @@ -631,10 +632,14 @@ edit_field(int tab, char c, int item_number) break; case FIELD_DATE: edit_date(item_number, idx); - return; + goto detachfield; default: assert(0); } + + detachfield: + if(opt_get_bool(BOOL_USE_MOUSE)) + ui_enable_mouse(TRUE); } static int @@ -668,6 +673,53 @@ edit_loop(int item) return item; } + if(c == KEY_MOUSE) { + MEVENT event; + if(getmouse(&event) == OK) { + if(event.bstate & BUTTON1_CLICKED + || event.bstate & BUTTON1_DOUBLE_CLICKED) { + int window_y, window_x; + getbegyx(editw, window_y, window_x); + if(event.y == 0) { + /* if first row is selected, then go back to list */ + return -1; + } else if(event.y == window_y + TABLINE + || event.y == window_y + TABLINE + 1) { + char* tab_name; + int mouse_x = event.x; + int xpos = 2 + 1; /* look at editor_tab() and try out */ + int clicked_tab = 0; + while(clicked_tab < views_count) { + view_info(clicked_tab, &tab_name, NULL); + xpos += strwidth(tab_name) + 5; + /* fprintf(stderr, "trying tab %d\n", clicked_tab); */ + if(xpos >= mouse_x) { + break; /* clicked tab was found */ + } else { + /* try next tab */ + clicked_tab++; + } + } + if(clicked_tab < views_count) { + tab = clicked_tab; + } + } else if(event.y >= window_y + FIELDS_START_Y) { + /* is mouse in field area? */ + int j = 1 + event.y - window_y - FIELDS_START_Y; + /* field numbers start with 1, but if j='0', then char='0' */ + /* so fix this, by adding 1 to j */ + int field_char = (j < 10) ? '0' + j : 'A' + j - 10; + edit_field(tab, field_char, item); + } + } else if(event.bstate & BUTTON4_PRESSED) { + tab = tab == 0 ? views_count - 1 : tab - 1; + } + else if(event.bstate & BUTTON5_PRESSED) { + tab = tab == views_count - 1 ? 0 : tab + 1; + } + return item; + } + } /* No uppercase nor numeric key should be used in this menu, * as they are reserved for field selection */ diff --git a/list.c b/list.c index 9eb4ede..ca4892b 100644 --- a/list.c +++ b/list.c @@ -498,6 +498,12 @@ list_get_curitem() return curitem; } +int +list_get_firstitem() +{ + return first_list_item; +} + void list_set_curitem(int i) { diff --git a/list.h b/list.h index 45b00f8..0cc3113 100644 --- a/list.h +++ b/list.h @@ -47,6 +47,7 @@ int selected_items(); void invert_selection(); int list_is_empty(); int list_get_curitem(); +int list_get_firstitem(); void list_set_curitem(int i); int duplicate_item(); diff --git a/options.c b/options.c index 57cb334..2c1303c 100644 --- a/options.c +++ b/options.c @@ -67,6 +67,7 @@ static struct option abook_vars[] = { { "preserve_fields", OT_STR, STR_PRESERVE_FIELDS, UL "standard" }, { "sort_field", OT_STR, STR_SORT_FIELD, UL "nick" }, { "show_cursor", OT_BOOL, BOOL_SHOW_CURSOR, FALSE }, + { "use_mouse", OT_BOOL, BOOL_USE_MOUSE, FALSE }, { "use_colors", OT_BOOL, BOOL_USE_COLORS, FALSE }, { "color_header_fg", OT_STR, STR_COLOR_HEADER_FG, UL "blue" }, { "color_header_fg", OT_STR, STR_COLOR_HEADER_FG, UL "blue" }, diff --git a/options.h b/options.h index 2dbadc3..d2cc8c0 100644 --- a/options.h +++ b/options.h @@ -27,6 +27,7 @@ enum bool_opts { BOOL_ADD_EMAIL_PREVENT_DUPLICATES, BOOL_SHOW_CURSOR, BOOL_USE_COLORS, + BOOL_USE_MOUSE, BOOL_MAX }; diff --git a/ui.c b/ui.c index 69fb71d..9ef3ee6 100644 --- a/ui.c +++ b/ui.c @@ -26,6 +26,7 @@ #include "filter.h" #include "xmalloc.h" #include "color.h" +#include #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -51,6 +52,8 @@ static bool ui_initialized = FALSE; static bool should_resize = FALSE; static bool can_resize = FALSE; +static struct timeval last_click_time; +static int double_click_interval = 200; /* maximum time in milliseconds */ static WINDOW *top = NULL, *bottom = NULL; @@ -130,6 +133,11 @@ ui_init_curses() noecho(); nonl(); intrflush(stdscr, FALSE); + if(opt_get_bool(BOOL_USE_MOUSE)) { + mouseinterval(0); + timerclear(&last_click_time); + ui_enable_mouse(TRUE); + } keypad(stdscr, TRUE); if(opt_get_bool(BOOL_USE_COLORS)) { start_color(); @@ -138,6 +146,35 @@ ui_init_curses() } } +void +ui_enable_mouse(bool enabled) +{ + mmask_t mask; + if(enabled) { + mask = BUTTON1_CLICKED | BUTTON4_PRESSED; +#if NCURSES_MOUSE_VERSION == 2 + mask |= BUTTON5_PRESSED; +#endif + } else { + mask = 0; + } + mousemask(mask, NULL); +} + +/** Check the time elapsed since last click and tell if it should be + * interpreted as a double click + */ +static bool +was_double_click() { + struct timeval click_time, click_diff, maxdiff; + maxdiff.tv_sec = double_click_interval / 1000; + maxdiff.tv_usec = (double_click_interval % 1000)*1000; + gettimeofday(&click_time, NULL); + + timersub(&click_time, &last_click_time, &click_diff); + last_click_time = click_time; + return !timercmp(&click_diff, &maxdiff, >); +} #define CHECK_COLOR_NAME(value, name, DEFNAME) \ if(!strcmp((name), (value))){ \ @@ -503,6 +540,28 @@ get_commands() if(!opt_get_bool(BOOL_SHOW_CURSOR)) show_cursor(); can_resize = FALSE; /* it's not safe to resize anymore */ + if(ch == KEY_MOUSE) { + MEVENT event; + bool double_clicked = was_double_click(); + if(getmouse(&event) == OK) { + if(event.bstate & BUTTON1_CLICKED + || event.bstate & BUTTON1_DOUBLE_CLICKED) { + if(event.y == 0) { + return; + } + list_set_curitem(event.y + list_get_firstitem() - LIST_TOP); + if(double_clicked) { + edit_item(-1); + } else { + refresh_list(); + } + } else if(event.bstate & BUTTON4_PRESSED) { + scroll_up(); + } else if(event.bstate & BUTTON5_PRESSED) { + scroll_down(); + } + } + } switch(ch) { case 'q': return; case 'Q': quit_abook(QUIT_DONTSAVE); break; diff --git a/ui.h b/ui.h index 3e7483b..fd7670e 100644 --- a/ui.h +++ b/ui.h @@ -11,6 +11,7 @@ enum { int is_ui_initialized(); void ui_init_curses(); void ui_init_color_pairs_user(); +void ui_enable_mouse(bool enabled); int init_ui(); void close_ui(); void headerline(const char *str); @@ -36,6 +37,9 @@ char *get_surname(char *s); void ui_print_database(); void ui_open_datafile(); +#if NCURSES_MOUSE_VERSION != 2 +#define BUTTON5_PRESSED (0x80 | 0x8000000) +#endif #include "options.h" /* needed for options_get_bool */ -- 2.39.2