Index: apps/gui/list.c =================================================================== --- apps/gui/list.c (revision 21257) +++ apps/gui/list.c (working copy) @@ -166,9 +166,9 @@ } /* this toggles the selection bar or cursor */ -void gui_synclist_hide_selection_marker(struct gui_synclist * lists, bool hide) +void gui_synclist_hide_selection_marker(struct gui_synclist * gui_list, bool hide) { - lists->show_selection_marker = !hide; + gui_list->show_selection_marker = !hide; } @@ -210,7 +210,7 @@ * Force a full screen update. */ -void gui_synclist_draw(struct gui_synclist *gui_list) +void gui_synclist_draw(struct gui_synclist * gui_list) { int i; static struct gui_synclist *last_list = NULL; @@ -305,6 +305,34 @@ } } } + + +static void _gui_synclist_speak_item(struct gui_synclist *gui_list) +{ + list_speak_item *cb = gui_list->callback_speak_item; + if (cb && gui_list->nb_items != 0) + { + talk_shutup(); + /* If 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 ((gui_list->scheduled_talk_tick && + TIME_BEFORE(current_tick, gui_list->scheduled_talk_tick)) || + (gui_list->last_talked_tick && + TIME_BEFORE(current_tick, gui_list->last_talked_tick + HZ/5))) + { + gui_list->scheduled_talk_tick = current_tick + HZ/5; + } + else + { + gui_list->scheduled_talk_tick = 0; /* work done */ + cb(gui_list->selected_item, gui_list->data); + gui_list->last_talked_tick = current_tick; + } + } +} + /* * Selects an item in the list * - gui_list : the list structure @@ -313,32 +341,35 @@ void gui_synclist_select_item(struct gui_synclist * gui_list, int item_number) { int i; - if( item_number > gui_list->nb_items-1 || item_number < 0 ) + if( item_number < 0 || item_number >= gui_list->nb_items) return; - gui_list->selected_item = item_number; - FOR_NB_SCREENS(i) - gui_list_put_selection_on_screen(gui_list, i); + if (item_number != gui_list->selected_item) + { + gui_list->selected_item = item_number; + _gui_synclist_speak_item(gui_list); + FOR_NB_SCREENS(i) + gui_list_put_selection_on_screen(gui_list, i); + } } -static void gui_list_select_at_offset(struct gui_synclist * gui_list, - int offset) +static void gui_list_select_at_offset(struct gui_synclist * gui_list, int offset) { int new_selection; if (gui_list->selected_size > 1) { offset *= gui_list->selected_size; /* always select the first item of multi-line lists */ - offset -= offset%gui_list->selected_size; + offset -= offset % gui_list->selected_size; } new_selection = gui_list->selected_item + offset; if (new_selection >= gui_list->nb_items) { - gui_list->selected_item = gui_list->limit_scroll ? + new_selection = gui_list->limit_scroll ? gui_list->nb_items - gui_list->selected_size : 0; } else if (new_selection < 0) { - gui_list->selected_item = gui_list->limit_scroll ? + new_selection = gui_list->limit_scroll ? 0 : gui_list->nb_items - gui_list->selected_size; } else if (gui_list->show_selection_marker == false) @@ -359,19 +390,17 @@ screen_top = 0; gui_list->start_item[i] = MIN(screen_top, gui_list->start_item[i] + gui_list->selected_size); - gui_list->selected_item = gui_list->start_item[i]; + new_selection = gui_list->start_item[i]; } else { gui_list->start_item[i] = MAX(0, gui_list->start_item[i] - gui_list->selected_size); - gui_list->selected_item = gui_list->start_item[i] + nb_lines; + new_selection = gui_list->start_item[i] + nb_lines; } } - return; } - else gui_list->selected_item += offset; - gui_synclist_select_item(gui_list, gui_list->selected_item); + gui_synclist_select_item(gui_list, new_selection); } /* @@ -382,8 +411,8 @@ { gui_list->nb_items++; /* if only one item in the list, select it */ - if(gui_list->nb_items == 1) - gui_list->selected_item = 0; + if (gui_list->nb_items == 1) + gui_synclist_select_item(gui_list, 0); } /* @@ -440,64 +469,64 @@ } -void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items) +void gui_synclist_set_nb_items(struct gui_synclist * gui_list, int nb_items) { #ifdef HAVE_LCD_BITMAP int i; #endif - lists->nb_items = nb_items; + gui_list->nb_items = nb_items; #ifdef HAVE_LCD_BITMAP FOR_NB_SCREENS(i) { - lists->offset_position[i] = 0; + gui_list->offset_position[i] = 0; } #endif } -int gui_synclist_get_nb_items(struct gui_synclist * lists) +int gui_synclist_get_nb_items(struct gui_synclist * gui_list) { - return lists->nb_items; + return gui_list->nb_items; } -int gui_synclist_get_sel_pos(struct gui_synclist * lists) +int gui_synclist_get_sel_pos(struct gui_synclist * gui_list) { - return lists->selected_item; + return gui_list->selected_item; } -void gui_synclist_set_icon_callback(struct gui_synclist * lists, +void gui_synclist_set_icon_callback(struct gui_synclist * gui_list, list_get_icon icon_callback) { - lists->callback_get_item_icon = icon_callback; + gui_list->callback_get_item_icon = icon_callback; } -void gui_synclist_set_voice_callback(struct gui_synclist * lists, +void gui_synclist_set_voice_callback(struct gui_synclist * gui_list, list_speak_item voice_callback) { - lists->callback_speak_item = voice_callback; + gui_list->callback_speak_item = voice_callback; } #ifdef HAVE_LCD_COLOR -void gui_synclist_set_color_callback(struct gui_synclist * lists, +void gui_synclist_set_color_callback(struct gui_synclist * gui_list, list_get_color color_callback) { - lists->callback_get_item_color = color_callback; + gui_list->callback_get_item_color = color_callback; } #endif -static void gui_synclist_select_next_page(struct gui_synclist * lists, +static void gui_synclist_select_next_page(struct gui_synclist * gui_list, enum screen_type screen) { - int nb_lines = viewport_get_nb_lines(lists->parent[screen]); - gui_list_select_at_offset(lists, nb_lines); + int nb_lines = viewport_get_nb_lines(gui_list->parent[screen]); + gui_list_select_at_offset(gui_list, nb_lines); } -static void gui_synclist_select_previous_page(struct gui_synclist * lists, +static void gui_synclist_select_previous_page(struct gui_synclist * gui_list, enum screen_type screen) { - int nb_lines = viewport_get_nb_lines(lists->parent[screen]); - gui_list_select_at_offset(lists, -nb_lines); + int nb_lines = viewport_get_nb_lines(gui_list->parent[screen]); + gui_list_select_at_offset(gui_list, -nb_lines); } -void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll) +void gui_synclist_limit_scroll(struct gui_synclist * gui_list, bool scroll) { - lists->limit_scroll = scroll; + gui_list->limit_scroll = scroll; } #ifdef HAVE_LCD_BITMAP @@ -506,7 +535,7 @@ * Should stop increasing the value when reaching the widest item value * in the list. */ -static void gui_synclist_scroll_right(struct gui_synclist * lists) +static void gui_synclist_scroll_right(struct gui_synclist * gui_list) { int i; FOR_NB_SCREENS(i) @@ -514,9 +543,9 @@ /* FIXME: This is a fake right boundry limiter. there should be some * callback function to find the longest item on the list in pixels, * to stop the list from scrolling past that point */ - lists->offset_position[i]+=offset_step; - if (lists->offset_position[i] > 1000) - lists->offset_position[i] = 1000; + gui_list->offset_position[i]+=offset_step; + if (gui_list->offset_position[i] > 1000) + gui_list->offset_position[i] = 1000; } } @@ -524,60 +553,38 @@ * Makes all the item in the list scroll by one step to the left. * stops at starting position. */ -static void gui_synclist_scroll_left(struct gui_synclist * lists) +static void gui_synclist_scroll_left(struct gui_synclist * gui_list) { int i; FOR_NB_SCREENS(i) { - lists->offset_position[i]-=offset_step; - if (lists->offset_position[i] < 0) - lists->offset_position[i] = 0; + gui_list->offset_position[i]-=offset_step; + if (gui_list->offset_position[i] < 0) + gui_list->offset_position[i] = 0; } } #endif /* HAVE_LCD_BITMAP */ -static void _gui_synclist_speak_item(struct gui_synclist *lists, bool repeating) + +void gui_synclist_speak_item(struct gui_synclist * gui_list) +/* The list user should call this to speak the first item on entering + the list, and whenever the list is updated. */ { - list_speak_item *cb = lists->callback_speak_item; - if(cb && gui_synclist_get_nb_items(lists) != 0) + if (global_settings.talk_menu) { - 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 - || (lists->scheduled_talk_tick - && TIME_BEFORE(current_tick, lists->scheduled_talk_tick)) - || (lists->last_talked_tick - && TIME_BEFORE(current_tick, lists->last_talked_tick +HZ/4))) - { - lists->scheduled_talk_tick = current_tick +HZ/4; - return; - } else { - lists->scheduled_talk_tick = 0; /* work done */ - cb(sel, lists->data); - lists->last_talked_tick = current_tick; - } + if (gui_list->nb_items == 0) + talk_id(VOICE_EMPTY_LIST, true); + else + _gui_synclist_speak_item(gui_list); } } -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); -} #if defined(HAVE_TOUCHSCREEN) /* this needs to be fixed if we ever get more than 1 touchscreen on a target */ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list); #endif -bool gui_synclist_do_button(struct gui_synclist * lists, +bool gui_synclist_do_button(struct gui_synclist * gui_list, int *actionptr, enum list_wrap wrap) { int action = *actionptr; @@ -617,31 +624,31 @@ #if defined(HAVE_TOUCHSCREEN) if (action == ACTION_TOUCHSCREEN) - action = *actionptr = gui_synclist_do_touchscreen(lists); + action = *actionptr = gui_synclist_do_touchscreen(gui_list); #endif switch (wrap) { case LIST_WRAP_ON: - gui_synclist_limit_scroll(lists, false); + gui_synclist_limit_scroll(gui_list, false); break; case LIST_WRAP_OFF: - gui_synclist_limit_scroll(lists, true); + gui_synclist_limit_scroll(gui_list, true); break; case LIST_WRAP_UNLESS_HELD: if (action == ACTION_STD_PREVREPEAT || action == ACTION_STD_NEXTREPEAT || action == ACTION_LISTTREE_PGUP || action == ACTION_LISTTREE_PGDOWN) - gui_synclist_limit_scroll(lists, true); - else gui_synclist_limit_scroll(lists, false); + gui_synclist_limit_scroll(gui_list, true); + else gui_synclist_limit_scroll(gui_list, false); break; }; switch (action) { case ACTION_REDRAW: - gui_synclist_draw(lists); + gui_synclist_draw(gui_list); return true; #ifdef HAVE_VOLUME_IN_LIST @@ -655,36 +662,30 @@ #endif case ACTION_STD_PREV: case ACTION_STD_PREVREPEAT: - gui_list_select_at_offset(lists, -next_item_modifier); + gui_list_select_at_offset(gui_list, -next_item_modifier); #ifndef HAVE_WHEEL_ACCELERATION if (button_queue_count() < FRAMEDROP_TRIGGER) #endif - gui_synclist_draw(lists); - _gui_synclist_speak_item(lists, - action == ACTION_STD_PREVREPEAT - || next_item_modifier >1); + gui_synclist_draw(gui_list); yield(); *actionptr = ACTION_STD_PREV; return true; case ACTION_STD_NEXT: case ACTION_STD_NEXTREPEAT: - gui_list_select_at_offset(lists, next_item_modifier); + gui_list_select_at_offset(gui_list, next_item_modifier); #ifndef HAVE_WHEEL_ACCELERATION if (button_queue_count() < FRAMEDROP_TRIGGER) #endif - gui_synclist_draw(lists); - _gui_synclist_speak_item(lists, - action == ACTION_STD_NEXTREPEAT - || next_item_modifier >1); + gui_synclist_draw(gui_list); yield(); *actionptr = ACTION_STD_NEXT; return true; #ifdef HAVE_LCD_BITMAP case ACTION_TREE_PGRIGHT: - gui_synclist_scroll_right(lists); - gui_synclist_draw(lists); + gui_synclist_scroll_right(gui_list); + gui_synclist_draw(gui_list); return true; case ACTION_TREE_ROOT_INIT: /* After this button press ACTION_TREE_PGLEFT is allowed @@ -692,7 +693,7 @@ keymaps as a repeated button press (the same as the repeated ACTION_TREE_PGLEFT) with the pre condition being the non-repeated button press */ - if (lists->offset_position[0] == 0) + if (gui_list->offset_position[0] == 0) { scrolling_left = false; *actionptr = ACTION_STD_CANCEL; @@ -700,13 +701,13 @@ } *actionptr = ACTION_TREE_PGLEFT; case ACTION_TREE_PGLEFT: - if(!scrolling_left && (lists->offset_position[0] == 0)) + if(!scrolling_left && (gui_list->offset_position[0] == 0)) { *actionptr = ACTION_STD_CANCEL; return false; } - gui_synclist_scroll_left(lists); - gui_synclist_draw(lists); + gui_synclist_scroll_left(gui_list); + gui_synclist_draw(gui_list); scrolling_left = true; /* stop ACTION_TREE_PAGE_LEFT skipping to root */ return true; @@ -722,9 +723,8 @@ SCREEN_REMOTE : #endif SCREEN_MAIN; - gui_synclist_select_previous_page(lists, screen); - gui_synclist_draw(lists); - _gui_synclist_speak_item(lists, false); + gui_synclist_select_previous_page(gui_list, screen); + gui_synclist_draw(gui_list); yield(); *actionptr = ACTION_STD_NEXT; } @@ -738,28 +738,29 @@ SCREEN_REMOTE : #endif SCREEN_MAIN; - gui_synclist_select_next_page(lists, screen); - gui_synclist_draw(lists); - _gui_synclist_speak_item(lists, false); + gui_synclist_select_next_page(gui_list, screen); + gui_synclist_draw(gui_list); yield(); *actionptr = ACTION_STD_PREV; } return true; } - if(lists->scheduled_talk_tick - && TIME_AFTER(current_tick, lists->scheduled_talk_tick)) + if (gui_list->scheduled_talk_tick + && TIME_AFTER(current_tick, gui_list->scheduled_talk_tick)) + { /* scheduled postponed item announcement is due */ - _gui_synclist_speak_item(lists, false); + _gui_synclist_speak_item(gui_list); + } return false; } -int list_do_action_timeout(struct gui_synclist *lists, int timeout) +int list_do_action_timeout(struct gui_synclist *gui_list, int timeout) /* Returns the lowest of timeout or the delay until a postponed scheduled announcement is due (if any). */ { - if(lists->scheduled_talk_tick) + if(gui_list->scheduled_talk_tick) { - long delay = lists->scheduled_talk_tick -current_tick +1; + long delay = gui_list->scheduled_talk_tick -current_tick +1; /* +1 because the trigger condition uses TIME_AFTER(), which is implemented as strictly greater than. */ if(delay < 0) @@ -771,26 +772,26 @@ } bool list_do_action(int context, int timeout, - struct gui_synclist *lists, int *action, + struct gui_synclist *gui_list, 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); + timeout = list_do_action_timeout(gui_list, timeout); *action = get_action(context, timeout); - return gui_synclist_do_button(lists, action, wrap); + return gui_synclist_do_button(gui_list, action, wrap); } -bool gui_synclist_item_is_onscreen(struct gui_synclist *lists, +bool gui_synclist_item_is_onscreen(struct gui_synclist *gui_list, enum screen_type screen, int item) { - struct viewport vp = *lists->parent[screen]; + struct viewport vp = *gui_list->parent[screen]; #ifdef HAVE_LCD_BITMAP - if (list_display_title(lists, screen)) - vp.height -= font_get(lists->parent[screen]->font)->height; + if (list_display_title(gui_list, screen)) + vp.height -= font_get(gui_list->parent[screen]->font)->height; #endif - return item <= (lists->start_item[screen] + viewport_get_nb_lines(&vp)); + return item <= (gui_list->start_item[screen] + viewport_get_nb_lines(&vp)); } /* Simple use list implementation */