Manual: The c200v2 also needs to be set to MSC mode for installation.
[maemo-rb.git] / apps / gui / list.c
blobcc43843e463eab5c8e8658e466a26ead15af9f1b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Kevin Ferrare
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include "config.h"
25 #include "lcd.h"
26 #include "font.h"
27 #include "button.h"
28 #include "string.h"
29 #include "settings.h"
30 #include "kernel.h"
31 #include "system.h"
33 #include "action.h"
34 #include "screen_access.h"
35 #include "list.h"
36 #include "scrollbar.h"
37 #include "lang.h"
38 #include "sound.h"
39 #include "misc.h"
40 #include "talk.h"
41 #include "viewport.h"
42 #include "appevents.h"
43 #include "statusbar-skinned.h"
45 /* The minimum number of pending button events in queue before starting
46 * to limit list drawing interval.
48 #define FRAMEDROP_TRIGGER 6
50 #ifdef HAVE_LCD_BITMAP
51 static int offset_step = 16; /* pixels per screen scroll step */
52 /* should lines scroll out of the screen */
53 static bool offset_out_of_view = false;
54 #endif
56 static void gui_list_select_at_offset(struct gui_synclist * gui_list,
57 int offset);
58 void list_draw(struct screen *display, struct gui_synclist *list);
60 #ifdef HAVE_LCD_BITMAP
61 static long last_dirty_tick;
62 static struct viewport parent[NB_SCREENS];
64 static bool list_is_dirty(struct gui_synclist *list)
66 return TIME_BEFORE(list->dirty_tick, last_dirty_tick);
69 static void list_force_reinit(void *param)
71 (void)param;
72 last_dirty_tick = current_tick;
75 void list_init(void)
77 last_dirty_tick = current_tick;
78 add_event(GUI_EVENT_THEME_CHANGED, false, list_force_reinit);
81 #ifdef HAVE_TOUCHSCREEN
82 static int line_height_from_lcd_dpi(const struct viewport *vp)
84 /* the 4/12 factor is designed for reasonable item size on a 160dpi screen */
85 return MAX(lcd_get_dpi()*4/12, (int)font_get(vp->font)->height);
87 #endif
89 static int list_line_height(const struct viewport *vp)
91 #ifdef HAVE_TOUCHSCREEN
92 if (global_settings.list_line_padding == -1)
93 return line_height_from_lcd_dpi(vp);
94 return font_get(vp->font)->height + global_settings.list_line_padding;
95 #else
96 return font_get(vp->font)->height;
97 #endif
100 static void list_init_viewports(struct gui_synclist *list)
102 bool parent_used = (*list->parent == &parent[SCREEN_MAIN]);
104 if (parent_used)
106 FOR_NB_SCREENS(i)
108 gui_synclist_set_viewport_defaults(list->parent[i], i);
111 list->dirty_tick = current_tick;
113 #else
114 static struct viewport parent[NB_SCREENS] =
116 [SCREEN_MAIN] =
118 .x = 0,
119 .y = 0,
120 .width = LCD_WIDTH,
121 .height = LCD_HEIGHT
125 #define list_init_viewports(a)
126 #define list_is_dirty(a) false
127 #endif
129 #ifdef HAVE_LCD_BITMAP
130 bool list_display_title(struct gui_synclist *list, enum screen_type screen)
132 return list->title != NULL &&
133 !sb_set_title_text(list->title, list->title_icon, screen) &&
134 viewport_get_nb_lines(list->parent[screen]) > 2;
137 static int list_get_nb_lines(struct gui_synclist *list, enum screen_type screen)
139 struct viewport *vp = list->parent[screen];
140 int lines = skinlist_get_line_count(screen, list);
141 if (lines < 0)
143 lines = viewport_get_nb_lines(vp);
144 if (list_display_title(list, screen))
145 lines -= 1;
147 return lines;
149 #else
150 #define list_display_title(l, i) false
151 #define list_get_nb_lines(list, screen) \
152 viewport_get_nb_lines((list)->parent[(screen)]);
153 #endif
156 * Initializes a scrolling list
157 * - gui_list : the list structure to initialize
158 * - callback_get_item_name : pointer to a function that associates a label
159 * to a given item number
160 * - data : extra data passed to the list callback
161 * - scroll_all :
162 * - selected_size :
163 * - parent : the parent viewports to use. NULL means the full screen minus
164 * statusbar if enabled. NOTE: new screens should NOT set this to NULL.
166 void gui_synclist_init(struct gui_synclist * gui_list,
167 list_get_name callback_get_item_name,
168 void * data,
169 bool scroll_all,
170 int selected_size, struct viewport list_parent[NB_SCREENS]
173 gui_list->callback_get_item_icon = NULL;
174 gui_list->callback_get_item_name = callback_get_item_name;
175 gui_list->callback_speak_item = NULL;
176 gui_list->nb_items = 0;
177 gui_list->selected_item = 0;
178 FOR_NB_SCREENS(i)
180 gui_list->start_item[i] = 0;
181 #ifdef HAVE_LCD_BITMAP
182 gui_list->offset_position[i] = 0;
183 #endif
184 if (list_parent)
185 gui_list->parent[i] = &list_parent[i];
186 else
187 gui_list->parent[i] = &parent[i];
189 list_init_viewports(gui_list);
190 gui_list->limit_scroll = false;
191 gui_list->data = data;
192 gui_list->scroll_all = scroll_all;
193 gui_list->selected_size = selected_size;
194 gui_list->title = NULL;
195 gui_list->title_icon = Icon_NOICON;
197 gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0;
198 gui_list->dirty_tick = current_tick;
199 gui_list->show_selection_marker = true;
201 #ifdef HAVE_LCD_COLOR
202 gui_list->title_color = -1;
203 gui_list->callback_get_item_color = NULL;
204 #endif
207 /* this toggles the selection bar or cursor */
208 void gui_synclist_hide_selection_marker(struct gui_synclist * lists, bool hide)
210 lists->show_selection_marker = !hide;
214 #ifdef HAVE_LCD_BITMAP
215 int gui_list_get_item_offset(struct gui_synclist * gui_list,
216 int item_width,
217 int text_pos,
218 struct screen * display,
219 struct viewport *vp)
221 int item_offset;
223 if (offset_out_of_view)
225 item_offset = gui_list->offset_position[display->screen_type];
227 else
229 /* if text is smaller than view */
230 if (item_width <= vp->width - text_pos)
232 item_offset = 0;
234 /* if text got out of view */
235 else if (gui_list->offset_position[display->screen_type] >
236 item_width - (vp->width - text_pos))
238 item_offset = item_width - (vp->width - text_pos);
240 else
241 item_offset = gui_list->offset_position[display->screen_type];
244 return item_offset;
246 #endif
249 * Force a full screen update.
251 void gui_synclist_draw(struct gui_synclist *gui_list)
253 if (list_is_dirty(gui_list))
255 list_init_viewports(gui_list);
256 gui_synclist_select_item(gui_list, gui_list->selected_item);
258 FOR_NB_SCREENS(i)
260 #ifdef HAVE_LCD_BITMAP
261 if (!skinlist_draw(&screens[i], gui_list))
262 #endif
263 list_draw(&screens[i], gui_list);
267 /* sets up the list so the selection is shown correctly on the screen */
268 static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list,
269 enum screen_type screen)
271 int nb_lines = list_get_nb_lines(gui_list, screen);
272 int bottom = MAX(0, gui_list->nb_items - nb_lines);
273 int new_start_item = gui_list->start_item[screen];
274 int difference = gui_list->selected_item - gui_list->start_item[screen];
275 #ifdef HAVE_LCD_CHARCELLS
276 const int scroll_limit_up = 0;
277 const int scroll_limit_down = 1;
278 #else
279 const int scroll_limit_up = (nb_lines < gui_list->selected_size+2 ? 0:1);
280 const int scroll_limit_down = (scroll_limit_up+gui_list->selected_size);
281 #endif
283 if (gui_list->show_selection_marker == false)
285 new_start_item = gui_list->selected_item;
287 else if (gui_list->selected_size >= nb_lines)
289 new_start_item = gui_list->selected_item;
291 else if (global_settings.scroll_paginated)
293 nb_lines -= nb_lines%gui_list->selected_size;
294 if (difference < 0 || difference >= nb_lines)
296 new_start_item = gui_list->selected_item -
297 (gui_list->selected_item%nb_lines);
300 else if (difference <= scroll_limit_up) /* list moved up */
302 new_start_item = gui_list->selected_item - scroll_limit_up;
304 else if (difference > nb_lines - scroll_limit_down) /* list moved down */
306 new_start_item = gui_list->selected_item + scroll_limit_down - nb_lines;
308 if (new_start_item < 0)
309 gui_list->start_item[screen] = 0;
310 else if (new_start_item > bottom)
311 gui_list->start_item[screen] = bottom;
312 else
313 gui_list->start_item[screen] = new_start_item;
316 static void _gui_synclist_speak_item(struct gui_synclist *lists)
318 list_speak_item *cb = lists->callback_speak_item;
319 if (cb && lists->nb_items != 0)
321 talk_shutup();
322 /* If we have just very recently started talking, then we want
323 to stay silent for a while until things settle. Likewise if
324 we already had a pending scheduled announcement not yet due
325 we need to reschedule it. */
326 if ((lists->scheduled_talk_tick &&
327 TIME_BEFORE(current_tick, lists->scheduled_talk_tick)) ||
328 (lists->last_talked_tick &&
329 TIME_BEFORE(current_tick, lists->last_talked_tick + HZ/5)))
331 lists->scheduled_talk_tick = current_tick + HZ/5;
333 else
335 lists->scheduled_talk_tick = 0; /* work done */
336 cb(lists->selected_item, lists->data);
337 lists->last_talked_tick = current_tick;
342 void gui_synclist_speak_item(struct gui_synclist *lists)
344 if (global_settings.talk_menu)
346 if (lists->nb_items == 0)
347 talk_id(VOICE_EMPTY_LIST, true);
348 else
349 _gui_synclist_speak_item(lists);
354 * Selects an item in the list
355 * - gui_list : the list structure
356 * - item_number : the number of the item which will be selected
358 void gui_synclist_select_item(struct gui_synclist * gui_list, int item_number)
360 if (item_number >= gui_list->nb_items || item_number < 0)
361 return;
362 if (item_number != gui_list->selected_item)
364 gui_list->selected_item = item_number;
365 _gui_synclist_speak_item(gui_list);
367 FOR_NB_SCREENS(i)
368 gui_list_put_selection_on_screen(gui_list, i);
371 static void gui_list_select_at_offset(struct gui_synclist * gui_list,
372 int offset)
374 int new_selection;
375 if (gui_list->selected_size > 1)
377 offset *= gui_list->selected_size;
380 new_selection = gui_list->selected_item + offset;
382 if (new_selection >= gui_list->nb_items)
384 new_selection = gui_list->limit_scroll ?
385 gui_list->nb_items - gui_list->selected_size : 0;
387 else if (new_selection < 0)
389 new_selection = gui_list->limit_scroll ?
390 0 : gui_list->nb_items - gui_list->selected_size;
392 else if (gui_list->show_selection_marker == false)
394 FOR_NB_SCREENS(i)
396 int nb_lines = list_get_nb_lines(gui_list, i);
397 if (offset > 0)
399 int screen_top = MAX(0, gui_list->nb_items - nb_lines);
400 gui_list->start_item[i] = MIN(screen_top, gui_list->start_item[i] +
401 gui_list->selected_size);
402 gui_list->selected_item = gui_list->start_item[i];
404 else
406 gui_list->start_item[i] = MAX(0, gui_list->start_item[i] -
407 gui_list->selected_size);
408 gui_list->selected_item = gui_list->start_item[i] + nb_lines;
411 return;
413 gui_synclist_select_item(gui_list, new_selection);
417 * Adds an item to the list (the callback will be asked for one more item)
418 * - gui_list : the list structure
420 void gui_synclist_add_item(struct gui_synclist * gui_list)
422 gui_list->nb_items++;
423 /* if only one item in the list, select it */
424 if (gui_list->nb_items == 1)
425 gui_list->selected_item = 0;
429 * Removes an item to the list (the callback will be asked for one less item)
430 * - gui_list : the list structure
432 void gui_synclist_del_item(struct gui_synclist * gui_list)
434 if (gui_list->nb_items > 0)
436 if (gui_list->selected_item == gui_list->nb_items-1)
437 gui_list->selected_item--;
438 gui_list->nb_items--;
439 gui_synclist_select_item(gui_list, gui_list->selected_item);
443 #ifdef HAVE_LCD_BITMAP
444 void gui_list_screen_scroll_step(int ofs)
446 offset_step = ofs;
449 void gui_list_screen_scroll_out_of_view(bool enable)
451 offset_out_of_view = enable;
453 #endif /* HAVE_LCD_BITMAP */
456 * Set the title and title icon of the list. Setting title to NULL disables
457 * both the title and icon. Use NOICON if there is no icon.
459 void gui_synclist_set_title(struct gui_synclist * gui_list,
460 char * title, enum themable_icons icon)
462 gui_list->title = title;
463 gui_list->title_icon = icon;
464 #ifdef HAVE_LCD_BITMAP
465 FOR_NB_SCREENS(i)
466 sb_set_title_text(title, icon, i);
467 #endif
468 send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
471 void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items)
473 lists->nb_items = nb_items;
474 #ifdef HAVE_LCD_BITMAP
475 FOR_NB_SCREENS(i)
477 lists->offset_position[i] = 0;
479 #endif
481 int gui_synclist_get_nb_items(struct gui_synclist * lists)
483 return lists->nb_items;
485 int gui_synclist_get_sel_pos(struct gui_synclist * lists)
487 return lists->selected_item;
489 void gui_synclist_set_icon_callback(struct gui_synclist * lists,
490 list_get_icon icon_callback)
492 lists->callback_get_item_icon = icon_callback;
495 void gui_synclist_set_voice_callback(struct gui_synclist * lists,
496 list_speak_item voice_callback)
498 lists->callback_speak_item = voice_callback;
501 void gui_synclist_set_viewport_defaults(struct viewport *vp,
502 enum screen_type screen)
504 viewport_set_defaults(vp, screen);
505 #ifdef HAVE_LCD_BITMAP
506 vp->line_height = list_line_height(vp);
507 #endif
508 #ifdef HAVE_BUTTONBAR
509 if (screens[screen].has_buttonbar)
510 vp->height -= BUTTONBAR_HEIGHT;
511 #endif
514 #ifdef HAVE_LCD_COLOR
515 void gui_synclist_set_color_callback(struct gui_synclist * lists,
516 list_get_color color_callback)
518 lists->callback_get_item_color = color_callback;
520 #endif
522 static void gui_synclist_select_next_page(struct gui_synclist * lists,
523 enum screen_type screen)
525 int nb_lines = list_get_nb_lines(lists, screen);
526 if (lists->selected_size > 1)
527 nb_lines = MAX(1, nb_lines/lists->selected_size);
528 gui_list_select_at_offset(lists, nb_lines);
531 static void gui_synclist_select_previous_page(struct gui_synclist * lists,
532 enum screen_type screen)
534 int nb_lines = list_get_nb_lines(lists, screen);
535 if (lists->selected_size > 1)
536 nb_lines = MAX(1, nb_lines/lists->selected_size);
537 gui_list_select_at_offset(lists, -nb_lines);
540 void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll)
542 lists->limit_scroll = scroll;
545 #ifdef HAVE_LCD_BITMAP
547 * Makes all the item in the list scroll by one step to the right.
548 * Should stop increasing the value when reaching the widest item value
549 * in the list.
551 static void gui_synclist_scroll_right(struct gui_synclist * lists)
553 FOR_NB_SCREENS(i)
555 /* FIXME: This is a fake right boundry limiter. there should be some
556 * callback function to find the longest item on the list in pixels,
557 * to stop the list from scrolling past that point */
558 lists->offset_position[i] += offset_step;
559 if (lists->offset_position[i] > 1000)
560 lists->offset_position[i] = 1000;
565 * Makes all the item in the list scroll by one step to the left.
566 * stops at starting position.
568 static void gui_synclist_scroll_left(struct gui_synclist * lists)
570 FOR_NB_SCREENS(i)
572 lists->offset_position[i] -= offset_step;
573 if (lists->offset_position[i] < 0)
574 lists->offset_position[i] = 0;
577 #endif /* HAVE_LCD_BITMAP */
579 #if CONFIG_CODEC == SWCODEC
580 bool gui_synclist_keyclick_callback(int action, void* data)
582 struct gui_synclist *lists = (struct gui_synclist *)data;
584 /* block the beep if we are at the end of the list and we are not wrapping.
585 * CAVEAT: mosts lists don't set limit_scroll untill it sees a repeat
586 * press at the end of the list so this can cause an extra beep.
588 if (lists->limit_scroll == false)
589 return true;
590 if (lists->selected_item == 0)
591 return (action != ACTION_STD_PREV && action != ACTION_STD_PREVREPEAT);
592 if (lists->selected_item == lists->nb_items - lists->selected_size)
593 return (action != ACTION_STD_NEXT && action != ACTION_STD_NEXTREPEAT);
595 return action != ACTION_NONE;
597 #endif
600 * Magic to make sure the list gets updated correctly if the skin does
601 * something naughty like a full screen update when we are in a button
602 * loop.
604 * The GUI_EVENT_NEED_UI_UPDATE event is registered for in list_do_action_timeout()
605 * and unregistered in gui_synclict_do_button(). This is done because
606 * if something is using the list UI they *must* be calling those
607 * two functions in the correct order or the list wont work.
609 static struct gui_synclist *current_lists;
610 static bool ui_update_event_registered = false;
611 static void _lists_uiviewport_update_callback(void *data)
613 (void)data;
614 if (current_lists)
615 gui_synclist_draw(current_lists);
618 bool gui_synclist_do_button(struct gui_synclist * lists,
619 int *actionptr, enum list_wrap wrap)
621 int action = *actionptr;
622 #ifdef HAVE_LCD_BITMAP
623 static bool scrolling_left = false;
624 #endif
626 #ifdef HAVE_WHEEL_ACCELERATION
627 int next_item_modifier = button_apply_acceleration(get_action_data());
628 #else
629 static int next_item_modifier = 1;
630 static int last_accel_tick = 0;
632 if (action != ACTION_TOUCHSCREEN)
634 if (global_settings.list_accel_start_delay)
636 int start_delay = global_settings.list_accel_start_delay * HZ;
637 int accel_wait = global_settings.list_accel_wait * HZ;
639 if (get_action_statuscode(NULL)&ACTION_REPEAT)
641 if (!last_accel_tick)
642 last_accel_tick = current_tick + start_delay;
643 else if (TIME_AFTER(current_tick, last_accel_tick + accel_wait))
645 last_accel_tick = current_tick;
646 next_item_modifier++;
649 else if (last_accel_tick)
651 next_item_modifier = 1;
652 last_accel_tick = 0;
656 #endif
657 #if defined(HAVE_TOUCHSCREEN)
658 if (action == ACTION_TOUCHSCREEN)
659 action = *actionptr = gui_synclist_do_touchscreen(lists);
660 else if (action > ACTION_TOUCHSCREEN_MODE)
661 /* cancel kinetic if we got a normal button event */
662 _gui_synclist_stop_kinetic_scrolling();
663 #endif
665 /* Disable the skin redraw callback */
666 current_lists = NULL;
668 switch (wrap)
670 case LIST_WRAP_ON:
671 gui_synclist_limit_scroll(lists, false);
672 break;
673 case LIST_WRAP_OFF:
674 gui_synclist_limit_scroll(lists, true);
675 break;
676 case LIST_WRAP_UNLESS_HELD:
677 if (action == ACTION_STD_PREVREPEAT ||
678 action == ACTION_STD_NEXTREPEAT ||
679 action == ACTION_LISTTREE_PGUP ||
680 action == ACTION_LISTTREE_PGDOWN)
681 gui_synclist_limit_scroll(lists, true);
682 else gui_synclist_limit_scroll(lists, false);
683 break;
686 switch (action)
688 case ACTION_REDRAW:
689 gui_synclist_draw(lists);
690 return true;
692 #ifdef HAVE_VOLUME_IN_LIST
693 case ACTION_LIST_VOLUP:
694 global_settings.volume += 2;
695 /* up two because the falthrough brings it down one */
696 case ACTION_LIST_VOLDOWN:
697 global_settings.volume--;
698 setvol();
699 return true;
700 #endif
701 case ACTION_STD_PREV:
702 case ACTION_STD_PREVREPEAT:
703 gui_list_select_at_offset(lists, -next_item_modifier);
704 #ifndef HAVE_WHEEL_ACCELERATION
705 if (button_queue_count() < FRAMEDROP_TRIGGER)
706 #endif
707 gui_synclist_draw(lists);
708 yield();
709 *actionptr = ACTION_STD_PREV;
710 return true;
712 case ACTION_STD_NEXT:
713 case ACTION_STD_NEXTREPEAT:
714 gui_list_select_at_offset(lists, next_item_modifier);
715 #ifndef HAVE_WHEEL_ACCELERATION
716 if (button_queue_count() < FRAMEDROP_TRIGGER)
717 #endif
718 gui_synclist_draw(lists);
719 yield();
720 *actionptr = ACTION_STD_NEXT;
721 return true;
723 #ifdef HAVE_LCD_BITMAP
724 case ACTION_TREE_PGRIGHT:
725 gui_synclist_scroll_right(lists);
726 gui_synclist_draw(lists);
727 return true;
728 case ACTION_TREE_ROOT_INIT:
729 /* After this button press ACTION_TREE_PGLEFT is allowed
730 to skip to root. ACTION_TREE_ROOT_INIT must be defined in the
731 keymaps as a repeated button press (the same as the repeated
732 ACTION_TREE_PGLEFT) with the pre condition being the non-repeated
733 button press */
734 if (lists->offset_position[0] == 0)
736 scrolling_left = false;
737 *actionptr = ACTION_STD_CANCEL;
738 return true;
740 *actionptr = ACTION_TREE_PGLEFT;
741 case ACTION_TREE_PGLEFT:
742 if(!scrolling_left && (lists->offset_position[0] == 0))
744 *actionptr = ACTION_STD_CANCEL;
745 return false;
747 gui_synclist_scroll_left(lists);
748 gui_synclist_draw(lists);
749 scrolling_left = true; /* stop ACTION_TREE_PAGE_LEFT
750 skipping to root */
751 return true;
752 #endif
753 /* for pgup / pgdown, we are obliged to have a different behaviour depending
754 * on the screen for which the user pressed the key since for example, remote
755 * and main screen doesn't have the same number of lines */
756 case ACTION_LISTTREE_PGUP:
758 int screen =
759 #ifdef HAVE_REMOTE_LCD
760 get_action_statuscode(NULL)&ACTION_REMOTE ?
761 SCREEN_REMOTE :
762 #endif
763 SCREEN_MAIN;
764 gui_synclist_select_previous_page(lists, screen);
765 gui_synclist_draw(lists);
766 yield();
767 *actionptr = ACTION_STD_NEXT;
769 return true;
771 case ACTION_LISTTREE_PGDOWN:
773 int screen =
774 #ifdef HAVE_REMOTE_LCD
775 get_action_statuscode(NULL)&ACTION_REMOTE ?
776 SCREEN_REMOTE :
777 #endif
778 SCREEN_MAIN;
779 gui_synclist_select_next_page(lists, screen);
780 gui_synclist_draw(lists);
781 yield();
782 *actionptr = ACTION_STD_PREV;
784 return true;
786 if(lists->scheduled_talk_tick
787 && TIME_AFTER(current_tick, lists->scheduled_talk_tick))
788 /* scheduled postponed item announcement is due */
789 _gui_synclist_speak_item(lists);
790 return false;
793 int list_do_action_timeout(struct gui_synclist *lists, int timeout)
794 /* Returns the lowest of timeout or the delay until a postponed
795 scheduled announcement is due (if any). */
797 if (lists != current_lists)
799 if (!ui_update_event_registered)
800 ui_update_event_registered =
801 add_event(GUI_EVENT_NEED_UI_UPDATE, false,
802 _lists_uiviewport_update_callback);
803 current_lists = lists;
805 if(lists->scheduled_talk_tick)
807 long delay = lists->scheduled_talk_tick -current_tick +1;
808 /* +1 because the trigger condition uses TIME_AFTER(), which
809 is implemented as strictly greater than. */
810 if(delay < 0)
811 delay = 0;
812 if(timeout > delay || timeout == TIMEOUT_BLOCK)
813 timeout = delay;
815 return timeout;
818 bool list_do_action(int context, int timeout,
819 struct gui_synclist *lists, int *action,
820 enum list_wrap wrap)
821 /* Combines the get_action() (with possibly overridden timeout) and
822 gui_synclist_do_button() calls. Returns the list action from
823 do_button, and places the action from get_action in *action. */
825 timeout = list_do_action_timeout(lists, timeout);
826 #if CONFIG_CODEC == SWCODEC
827 keyclick_set_callback(gui_synclist_keyclick_callback, lists);
828 #endif
829 *action = get_action(context, timeout);
830 return gui_synclist_do_button(lists, action, wrap);
833 /* Simple use list implementation */
834 static int simplelist_line_count = 0, simplelist_line_remaining;
835 static int simplelist_line_pos;
836 static char simplelist_buffer[SIMPLELIST_MAX_LINES * SIMPLELIST_MAX_LINELENGTH];
837 static char *simplelist_text[SIMPLELIST_MAX_LINES];
838 /* set the amount of lines shown in the list */
839 void simplelist_set_line_count(int lines)
841 if (lines <= 0) {
842 simplelist_line_pos = 0;
843 simplelist_line_remaining = sizeof(simplelist_buffer);
844 simplelist_line_count = 0;
846 else if (lines >= SIMPLELIST_MAX_LINES)
847 simplelist_line_count = SIMPLELIST_MAX_LINES;
849 /* get the current amount of lines shown */
850 int simplelist_get_line_count(void)
852 return simplelist_line_count;
854 /* add/edit a line in the list.
855 if line_number > number of lines shown it adds the line,
856 else it edits the line */
857 void simplelist_addline(const char *fmt, ...)
859 va_list ap;
860 size_t len = simplelist_line_remaining;
861 int line_number = simplelist_line_count++;
863 simplelist_text[line_number] = &simplelist_buffer[simplelist_line_pos];
864 va_start(ap, fmt);
865 len = vsnprintf(simplelist_text[line_number], simplelist_line_remaining, fmt, ap);
866 va_end(ap);
867 len++;
868 simplelist_line_remaining -= len;
869 simplelist_line_pos += len;
872 static const char* simplelist_static_getname(int item,
873 void * data,
874 char *buffer,
875 size_t buffer_len)
877 (void)data; (void)buffer; (void)buffer_len;
878 return simplelist_text[item];
881 bool simplelist_show_list(struct simplelist_info *info)
883 struct gui_synclist lists;
884 int action, old_line_count = simplelist_line_count;
885 list_get_name *getname;
886 int wrap = LIST_WRAP_UNLESS_HELD;
887 if (info->get_name)
888 getname = info->get_name;
889 else
890 getname = simplelist_static_getname;
892 FOR_NB_SCREENS(i)
893 viewportmanager_theme_enable(i, true, NULL);
895 gui_synclist_init(&lists, getname, info->callback_data,
896 info->scroll_all, info->selection_size, NULL);
898 if (info->title)
899 gui_synclist_set_title(&lists, info->title, info->title_icon);
900 if (info->get_icon)
901 gui_synclist_set_icon_callback(&lists, info->get_icon);
902 if (info->get_talk)
903 gui_synclist_set_voice_callback(&lists, info->get_talk);
904 #ifdef HAVE_LCD_COLOR
905 if (info->get_color)
906 gui_synclist_set_color_callback(&lists, info->get_color);
907 #endif
909 if (info->hide_selection)
911 gui_synclist_hide_selection_marker(&lists, true);
912 wrap = LIST_WRAP_OFF;
915 if (info->action_callback)
916 info->action_callback(ACTION_REDRAW, &lists);
918 if (info->get_name == NULL)
919 gui_synclist_set_nb_items(&lists,
920 simplelist_line_count*info->selection_size);
921 else
922 gui_synclist_set_nb_items(&lists, info->count*info->selection_size);
924 gui_synclist_select_item(&lists, info->selection);
926 gui_synclist_draw(&lists);
927 gui_synclist_speak_item(&lists);
929 while(1)
931 list_do_action(CONTEXT_LIST, info->timeout,
932 &lists, &action, wrap);
934 /* We must yield in this case or no other thread can run */
935 if (info->timeout == TIMEOUT_NOBLOCK)
936 yield();
938 if (info->action_callback)
940 bool stdok = action==ACTION_STD_OK;
941 action = info->action_callback(action, &lists);
942 if (stdok && action == ACTION_STD_CANCEL)
944 /* callback asked us to exit */
945 info->selection = gui_synclist_get_sel_pos(&lists);
946 break;
949 if (info->get_name == NULL)
950 gui_synclist_set_nb_items(&lists,
951 simplelist_line_count*info->selection_size);
953 if (action == ACTION_STD_CANCEL)
955 info->selection = -1;
956 break;
958 else if ((action == ACTION_REDRAW) ||
959 (list_is_dirty(&lists)) ||
960 (old_line_count != simplelist_line_count))
962 if (info->get_name == NULL)
964 gui_synclist_set_nb_items(&lists,
965 simplelist_line_count*info->selection_size);
967 gui_synclist_draw(&lists);
968 old_line_count = simplelist_line_count;
970 else if(default_event_handler(action) == SYS_USB_CONNECTED)
971 return true;
973 talk_shutup();
974 FOR_NB_SCREENS(i)
975 viewportmanager_theme_undo(i, false);
976 return false;
979 void simplelist_info_init(struct simplelist_info *info, char* title,
980 int count, void* data)
982 info->title = title;
983 info->count = count;
984 info->selection_size = 1;
985 info->hide_selection = false;
986 info->scroll_all = false;
987 info->timeout = HZ/10;
988 info->selection = 0;
989 info->action_callback = NULL;
990 info->title_icon = Icon_NOICON;
991 info->get_icon = NULL;
992 info->get_name = NULL;
993 info->get_talk = NULL;
994 #ifdef HAVE_LCD_COLOR
995 info->get_color = NULL;
996 #endif
997 info->callback_data = data;
998 simplelist_line_count = 0;