diff --git a/apps/gui/list.c b/apps/gui/list.c index 52c1e4a3b9..d9725e451a 100644 --- a/apps/gui/list.c +++ b/apps/gui/list.c @@ -36,6 +36,7 @@ #include "lang.h" #include "sound.h" #include "misc.h" +#include "talk.h" #ifdef HAVE_LCD_CHARCELLS #define SCROLL_LIMIT 1 @@ -78,6 +79,7 @@ static void gui_list_init(struct gui_list * gui_list, { gui_list->callback_get_item_icon = NULL; gui_list->callback_get_item_name = callback_get_item_name; + gui_list->callback_speak_item = NULL; gui_list->display = NULL; gui_list_set_nb_items(gui_list, 0); gui_list->selected_item = 0; @@ -95,6 +97,7 @@ static void gui_list_init(struct gui_list * gui_list, gui_list->last_displayed_selected_item = -1 ; gui_list->last_displayed_start_item = -1 ; + gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0; gui_list->show_selection_marker = true; #ifdef HAVE_LCD_COLOR @@ -786,6 +789,12 @@ void gui_synclist_set_icon_callback(struct gui_synclist * lists, } } +void gui_synclist_set_voice_callback(struct gui_synclist * lists, + list_speak_item voice_callback) +{ + gui_list_set_voice_callback(&(lists->gui_list[0]), voice_callback); +} + void gui_synclist_draw(struct gui_synclist * lists) { int i; @@ -863,6 +872,43 @@ static void gui_synclist_scroll_left(struct gui_synclist * lists) } #endif /* HAVE_LCD_BITMAP */ +static void _gui_synclist_speak_item(struct gui_synclist *lists, bool repeating) +{ + struct gui_list *l = &lists->gui_list[0]; + list_speak_item *cb = l->callback_speak_item; + if(cb && gui_synclist_get_nb_items(lists) != 0) + { + int sel = gui_synclist_get_sel_pos(lists); + talk_shutup(); + /* If we got a repeating key action, or we have just very + recently started talking, then we want to stay silent for a + while until things settle. Likewise if we already had a + pending scheduled announcement not yet due: we need to + reschedule it. */ + if(repeating + || (l->scheduled_talk_tick + && TIME_BEFORE(current_tick, l->scheduled_talk_tick)) + || (l->last_talked_tick + && TIME_BEFORE(current_tick, l->last_talked_tick +HZ/4))) + { + l->scheduled_talk_tick = current_tick +HZ/4; + return; + } else { + l->scheduled_talk_tick = 0; /* work done */ + cb(sel, l->data); + l->last_talked_tick = current_tick; + } + } +} +void gui_synclist_speak_item(struct gui_synclist * lists) +/* The list user should call this to speak the first item on entering + the list, and whenever the list is updated. */ +{ + if(gui_synclist_get_nb_items(lists) == 0 && global_settings.talk_menu) + talk_id(VOICE_EMPTY_LIST, true); + else _gui_synclist_speak_item(lists, false); +} + extern intptr_t get_action_data(void); bool gui_synclist_do_button(struct gui_synclist * lists, @@ -941,9 +987,10 @@ bool gui_synclist_do_button(struct gui_synclist * lists, #ifndef HAVE_SCROLLWHEEL if (queue_count(&button_queue) < FRAMEDROP_TRIGGER) #endif - { gui_synclist_draw(lists); - } + _gui_synclist_speak_item(lists, + action == ACTION_STD_PREVREPEAT + || next_item_modifier >1); yield(); *actionptr = ACTION_STD_PREV; return true; @@ -955,9 +1002,10 @@ bool gui_synclist_do_button(struct gui_synclist * lists, #ifndef HAVE_SCROLLWHEEL if (queue_count(&button_queue) < FRAMEDROP_TRIGGER) #endif - { gui_synclist_draw(lists); - } + _gui_synclist_speak_item(lists, + action == ACTION_STD_NEXTREPEAT + || next_item_modifier >1); yield(); *actionptr = ACTION_STD_NEXT; return true; @@ -1006,6 +1054,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, SCREEN_MAIN; gui_synclist_select_previous_page(lists, screen); gui_synclist_draw(lists); + _gui_synclist_speak_item(lists, false); yield(); *actionptr = ACTION_STD_NEXT; } @@ -1021,14 +1070,48 @@ bool gui_synclist_do_button(struct gui_synclist * lists, SCREEN_MAIN; gui_synclist_select_next_page(lists, screen); gui_synclist_draw(lists); + _gui_synclist_speak_item(lists, false); yield(); *actionptr = ACTION_STD_PREV; } return true; } + if(lists->gui_list[0].scheduled_talk_tick + && TIME_AFTER(current_tick, lists->gui_list[0].scheduled_talk_tick)) + /* scheduled postponed item announcement is due */ + _gui_synclist_speak_item(lists, false); return false; } +int list_do_action_timeout(struct gui_synclist *lists, int timeout) +/* Returns the lowest of timeout or the delay until a postponed + scheduled announcement is due (if any). */ +{ + if(lists->gui_list[0].scheduled_talk_tick) + { + long delay = lists->gui_list[0].scheduled_talk_tick -current_tick +1; + /* +1 because the trigger condition uses TIME_AFTER(), which + is implemented as strictly greater than. */ + if(delay < 0) + delay = 0; + if(timeout > delay || timeout == TIMEOUT_BLOCK) + timeout = delay; + } + return timeout; +} + +bool list_do_action(int context, int timeout, + struct gui_synclist *lists, int *action, + enum list_wrap wrap) +/* Combines the get_action() (with possibly overridden timeout) and + gui_synclist_do_button() calls. Returns the list action from + do_button, and places the action from get_action in *action. */ +{ + timeout = list_do_action_timeout(lists, timeout); + *action = get_action(context, timeout); + return gui_synclist_do_button(lists, action, wrap); +} + /* Simple use list implementation */ static int simplelist_line_count = 0; static char simplelist_text[SIMPLELIST_MAX_LINES][SIMPLELIST_MAX_LINELENGTH]; diff --git a/apps/gui/list.h b/apps/gui/list.h index 2de67f5219..253dd9bdbf 100644 --- a/apps/gui/list.h +++ b/apps/gui/list.h @@ -62,6 +62,14 @@ typedef enum themable_icons list_get_icon(int selected_item, void * data); * Returns a pointer to a string that contains the text to display */ typedef char * list_get_name(int selected_item, void * data, char * buffer); +/* + * Voice callback + * - selected_item : an integer that tells the number of the item to speak + * - data : a void pointer to the data you gave to the list when you + * initialized it + * Returns an integer, 0 means success, ignored really... + */ +typedef int list_speak_item(int selected_item, void * data); #ifdef HAVE_LCD_COLOR /* * Color callback @@ -97,9 +105,11 @@ struct gui_list #endif /* Cache the width of the title string in pixels/characters */ int title_width; + long scheduled_talk_tick, last_talked_tick; list_get_icon *callback_get_item_icon; list_get_name *callback_get_item_name; + list_speak_item *callback_speak_item; struct screen * display; /* The data that will be passed to the callback function YOU implement */ @@ -140,6 +150,14 @@ struct gui_list #define gui_list_set_icon_callback(gui_list, _callback) \ (gui_list)->callback_get_item_icon=_callback +/* + * Sets the voice callback function + * - gui_list : the list structure + * - _callback : the callback function + */ +#define gui_list_set_voice_callback(gui_list, _callback) \ + (gui_list)->callback_speak_item=_callback + #ifdef HAVE_LCD_COLOR /* * Sets the color callback function @@ -200,6 +218,8 @@ extern void gui_synclist_init( ); extern void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items); extern void gui_synclist_set_icon_callback(struct gui_synclist * lists, list_get_icon icon_callback); +extern void gui_synclist_set_voice_callback(struct gui_synclist * lists, list_speak_item voice_callback); +extern void gui_synclist_speak_item(struct gui_synclist * lists); extern int gui_synclist_get_nb_items(struct gui_synclist * lists); extern int gui_synclist_get_sel_pos(struct gui_synclist * lists); @@ -266,4 +286,15 @@ void simplelist_addline(int line_number, const char *fmt, ...); before the list is dislplayed for the first time */ bool simplelist_show_list(struct simplelist_info *info); +/* If the list has a pending postponed scheduled announcement, that + may become due before the next get_action tmieout. This function + adjusts the get_action timeout appropriately. */ +extern int list_do_action_timeout(struct gui_synclist *lists, int timeout); +/* This one combines a get_action call (with timeout overridden by + list_do_action_timeout) with the gui_synclist_do_button call, for + convenience. */ +extern bool list_do_action(int context, int timeout, + struct gui_synclist *lists, int *action, + enum list_wrap wrap); + #endif /* _GUI_LIST_H_ */ diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 45e30a6c44..29957eceb6 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -11417,3 +11417,17 @@ *: "Blank" + + id: VOICE_EMPTY_LIST + desc: spoken only, when a list dialog contains no elements + user: + + *: "" + + + *: "" + + + *: "Empty list" + +