Index: trunk/apps/gui/list.c =================================================================== --- trunk.orig/apps/gui/list.c +++ trunk/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_lis { 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; @@ -96,6 +98,7 @@ static void gui_list_init(struct gui_lis 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 @@ -803,6 +806,12 @@ void gui_synclist_set_icon_callback(stru } } +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; @@ -887,6 +896,43 @@ static void gui_synclist_scroll_left(str } #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); + 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 && talk_menus_enabled()) + 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, @@ -965,9 +1011,10 @@ bool gui_synclist_do_button(struct gui_s #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; @@ -979,9 +1026,10 @@ bool gui_synclist_do_button(struct gui_s #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; @@ -1030,6 +1078,7 @@ bool gui_synclist_do_button(struct gui_s SCREEN_MAIN; gui_synclist_select_previous_page(lists, screen); gui_synclist_draw(lists); + _gui_synclist_speak_item(lists, false); yield(); *actionptr = ACTION_STD_NEXT; } @@ -1045,10 +1094,44 @@ bool gui_synclist_do_button(struct gui_s 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); +} Index: trunk/apps/gui/list.h =================================================================== --- trunk.orig/apps/gui/list.h +++ trunk/apps/gui/list.h @@ -62,6 +62,14 @@ typedef enum themable_icons list_get_ico * 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 @@ -98,9 +106,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 */ @@ -141,6 +151,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 @@ -201,6 +219,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); @@ -225,4 +245,15 @@ extern bool gui_synclist_do_button(struc unsigned *action, enum list_wrap); +/* 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_ */ Index: trunk/apps/talk.c =================================================================== --- trunk.orig/apps/talk.c +++ trunk/apps/talk.c @@ -140,7 +140,7 @@ static void mp3_callback(unsigned char** static int queue_clip(unsigned char* buf, long size, bool enqueue); static int open_voicefile(void); static unsigned char* get_clip(long id, long* p_size); -static int shutup(void); /* Interrupt voice, as when enqueue is false */ +int shutup(void); /* Interrupt voice, as when enqueue is false */ /***************** Private implementation *****************/ @@ -323,7 +323,7 @@ int do_shutup(void) #if CONFIG_CODEC == SWCODEC mp3_play_stop(); #endif - return 0; + return 0; } #if CONFIG_CODEC != SWCODEC #if CONFIG_CPU == SH7034 @@ -377,7 +377,7 @@ int do_shutup(void) } /* Shutup the voice, except if force_enqueue_next is set. */ -static int shutup(void) +int shutup(void) { if (!force_enqueue_next) return do_shutup(); Index: trunk/apps/talk.h =================================================================== --- trunk.orig/apps/talk.h +++ trunk/apps/talk.h @@ -79,6 +79,7 @@ bool talk_menus_enabled(void); /* return void talk_disable_menus(void); /* disable voice menus (temporarily, not persisted) */ void talk_enable_menus(void); /* re-enable voice menus */ int do_shutup(void); /* kill voice unconditionally */ +int shutup(void); /* Interrupt voice, as when enqueue is false */ /* This (otherwise invalid) ID signals the end of the array. */ #define TALK_FINAL_ID LANG_LAST_INDEX_IN_ARRAY @@ -87,6 +88,7 @@ int do_shutup(void); /* kill voice uncon #if defined(SIMULATOR) && !(CONFIG_CODEC == SWCODEC) #define talk_force_enqueue_next() do {} while(0) #define talk_idarray(idarray, enqueue) ({ (void)idarray; (void)enqueue; 0; }) +#define shutup() ({ 0; }) #else /* Enqueue next utterance even if enqueue parameter is false: don't Index: trunk/apps/playback.c =================================================================== --- trunk.orig/apps/playback.c +++ trunk/apps/playback.c @@ -380,6 +380,7 @@ void mp3_play_stop(void) queue_remove_from_head(&voice_queue, Q_VOICE_STOP); LOGFQUEUE("mp3 > voice Q_VOICE_STOP"); queue_post(&voice_queue, Q_VOICE_STOP, 1); + yield(); #endif } Index: trunk/apps/lang/english.lang =================================================================== --- trunk.orig/apps/lang/english.lang +++ trunk/apps/lang/english.lang @@ -11261,12 +11261,26 @@ desc: spoken only, On exiting a context, specifically the quick screen user: *: "" *: "" *: "OK" + + id: VOICE_EMPTY_LIST + desc: spoken only, when a list dialog contains no elements + user: + + *: "" + + + *: "" + + + *: "Empty list" + +