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);
unsigned gui_synclist_do_button(struct gui_synclist * lists,
@@ -964,9 +1010,10 @@ unsigned gui_synclist_do_button(struct g
#ifndef HAVE_SCROLLWHEEL
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
- {
gui_synclist_draw(lists);
- }
+ _gui_synclist_speak_item(lists,
+ button == ACTION_STD_PREVREPEAT
+ || next_item_modifier >1);
yield();
return ACTION_STD_PREV;
@@ -977,9 +1024,10 @@ unsigned gui_synclist_do_button(struct g
#ifndef HAVE_SCROLLWHEEL
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
- {
gui_synclist_draw(lists);
- }
+ _gui_synclist_speak_item(lists,
+ button == ACTION_STD_NEXTREPEAT
+ || next_item_modifier >1);
yield();
return ACTION_STD_NEXT;
@@ -1022,6 +1070,7 @@ unsigned gui_synclist_do_button(struct g
SCREEN_MAIN;
gui_synclist_select_previous_page(lists, screen);
gui_synclist_draw(lists);
+ _gui_synclist_speak_item(lists, false);
yield();
}
return ACTION_STD_NEXT;
@@ -1036,9 +1085,46 @@ unsigned gui_synclist_do_button(struct g
SCREEN_MAIN;
gui_synclist_select_next_page(lists, screen);
gui_synclist_draw(lists);
+ _gui_synclist_speak_item(lists, false);
yield();
}
return ACTION_STD_PREV;
}
+ 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 0;
}
+
+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;
+}
+
+unsigned list_do_action(int context, int timeout,
+ struct gui_synclist *lists, enum list_wrap wrap,
+ int *action)
+/* 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);
+ int button = get_action(context, timeout);
+ unsigned list_act = gui_synclist_do_button(lists, button, wrap);
+ if(action)
+ *action = button;
+ return list_act;
+}
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);
@@ -230,4 +250,15 @@ extern unsigned gui_synclist_do_button(s
unsigned button,
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 unsigned list_do_action(int context, int timeout,
+ struct gui_synclist *lists, enum list_wrap wrap,
+ int *action);
+
#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 *****************/
@@ -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
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"
+
+