blob: 425cab9a0f3242a54a0d66c6fc1e8406b8029664 [file] [log] [blame]
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
Kevin Ferrarea3ed62b2005-10-30 01:24:35 +000010 * Copyright (C) 2005 by Kevin Ferrare
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +000011 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
Thomas Martitz50a6ca32010-05-06 21:04:40 +000022#include <stdarg.h>
Thomas Martitz55e4fe72010-05-06 22:33:57 +000023#include <stdio.h>
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +000024#include "config.h"
25#include "lcd.h"
26#include "font.h"
27#include "button.h"
Dave Chapman079ad112006-08-15 08:14:46 +000028#include "string.h"
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +000029#include "settings.h"
30#include "kernel.h"
Jonathan Gordonfea88882007-06-05 02:57:43 +000031#include "system.h"
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +000032
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +000033#include "action.h"
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +000034#include "screen_access.h"
35#include "list.h"
36#include "scrollbar.h"
Linus Nielsen Feltzing298c2bb2007-03-05 00:32:33 +000037#include "lang.h"
Jonathan Gordona053e582007-04-08 01:33:01 +000038#include "sound.h"
Nils Wallméniusfbdbd212007-05-30 18:08:29 +000039#include "misc.h"
Stéphane Doyon8486c042007-10-21 00:20:10 +000040#include "talk.h"
Jonathan Gordon0e5cec22008-03-05 09:58:30 +000041#include "viewport.h"
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000042#include "appevents.h"
Jonathan Gordon466e5d92010-02-26 03:45:41 +000043#include "statusbar-skinned.h"
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +000044
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +000045/* The minimum number of pending button events in queue before starting
46 * to limit list drawing interval.
47 */
48#define FRAMEDROP_TRIGGER 6
49
Brandon Lowd3a03b62006-01-22 04:24:26 +000050#ifdef HAVE_LCD_BITMAP
51static int offset_step = 16; /* pixels per screen scroll step */
52/* should lines scroll out of the screen */
53static bool offset_out_of_view = false;
54#endif
55
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +000056static void gui_list_select_at_offset(struct gui_synclist * gui_list,
57 int offset);
Jonathan Gordone385ee12008-12-31 05:59:26 +000058void list_draw(struct screen *display, struct gui_synclist *list);
Jonathan Gordon0e5cec22008-03-05 09:58:30 +000059
60#ifdef HAVE_LCD_BITMAP
Thomas Martitz0acdc872011-10-07 17:22:49 +000061static long last_dirty_tick;
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000062static struct viewport parent[NB_SCREENS];
63
Thomas Martitzf8650662011-10-07 17:28:27 +000064static bool list_is_dirty(struct gui_synclist *list)
65{
66 return TIME_BEFORE(list->dirty_tick, last_dirty_tick);
67}
68
Thomas Martitz470989b2014-03-14 23:15:16 +010069static void list_force_reinit(unsigned short id, void *param, void *last_dirty_tick)
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000070{
Thomas Martitz470989b2014-03-14 23:15:16 +010071 (void)id;
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000072 (void)param;
Thomas Martitz470989b2014-03-14 23:15:16 +010073 *(int *)last_dirty_tick = current_tick;
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000074}
75
76void list_init(void)
77{
Thomas Martitz0acdc872011-10-07 17:22:49 +000078 last_dirty_tick = current_tick;
Thomas Martitz470989b2014-03-14 23:15:16 +010079 add_event_ex(GUI_EVENT_THEME_CHANGED, false, list_force_reinit, &last_dirty_tick);
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000080}
81
Thomas Martitz4c48b592009-08-16 22:20:11 +000082static void list_init_viewports(struct gui_synclist *list)
Jonathan Gordon0e5cec22008-03-05 09:58:30 +000083{
Thomas Martitz09d0b6f2011-10-22 10:06:48 +000084 bool parent_used = (*list->parent == &parent[SCREEN_MAIN]);
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000085
Thomas Martitz09d0b6f2011-10-22 10:06:48 +000086 if (parent_used)
Jonathan Gordon0e5cec22008-03-05 09:58:30 +000087 {
Thomas Martitz4c48b592009-08-16 22:20:11 +000088 FOR_NB_SCREENS(i)
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000089 {
Thomas Martitz5c509d12011-10-22 10:09:23 +000090 gui_synclist_set_viewport_defaults(list->parent[i], i);
Thomas Martitz2eb1cb62009-09-07 17:37:06 +000091 }
92 }
Thomas Martitz42a33a72011-10-07 17:31:15 +000093 list->dirty_tick = current_tick;
Thomas Martitz4c48b592009-08-16 22:20:11 +000094}
95#else
Teruaki Kawashima67470c82010-01-07 12:51:57 +000096static struct viewport parent[NB_SCREENS] =
97{
Solomon Peachy39b64f72018-12-15 19:15:28 -050098 [SCREEN_MAIN] =
Teruaki Kawashima67470c82010-01-07 12:51:57 +000099 {
100 .x = 0,
101 .y = 0,
102 .width = LCD_WIDTH,
103 .height = LCD_HEIGHT
104 },
105};
106
Thomas Martitz4c48b592009-08-16 22:20:11 +0000107#define list_init_viewports(a)
Thomas Martitzf8650662011-10-07 17:28:27 +0000108#define list_is_dirty(a) false
Thomas Martitz4c48b592009-08-16 22:20:11 +0000109#endif
110
111#ifdef HAVE_LCD_BITMAP
Thomas Martitzeec89a92013-12-20 23:34:28 +0100112static int list_nb_lines(struct gui_synclist *list, enum screen_type screen)
113{
114 struct viewport *vp = list->parent[screen];
115 return vp->height / list->line_height[screen];
116}
117
Thomas Martitz4c48b592009-08-16 22:20:11 +0000118bool list_display_title(struct gui_synclist *list, enum screen_type screen)
119{
Solomon Peachy39b64f72018-12-15 19:15:28 -0500120 return list->title != NULL &&
Jonathan Gordon32114812010-02-26 06:45:50 +0000121 !sb_set_title_text(list->title, list->title_icon, screen) &&
Thomas Martitzeec89a92013-12-20 23:34:28 +0100122 list_nb_lines(list, screen) > 2;
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000123}
124
Thomas Martitzeec89a92013-12-20 23:34:28 +0100125int list_get_nb_lines(struct gui_synclist *list, enum screen_type screen)
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000126{
Thomas Martitz3b126342011-10-17 17:38:10 +0000127 int lines = skinlist_get_line_count(screen, list);
128 if (lines < 0)
129 {
Thomas Martitzeec89a92013-12-20 23:34:28 +0100130 lines = list_nb_lines(list, screen);
Thomas Martitz3b126342011-10-17 17:38:10 +0000131 if (list_display_title(list, screen))
132 lines -= 1;
133 }
134 return lines;
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000135}
Thomas Martitzeec89a92013-12-20 23:34:28 +0100136
137void list_init_item_height(struct gui_synclist *list, enum screen_type screen)
138{
139 struct viewport *vp = list->parent[screen];
140#ifdef HAVE_TOUCHSCREEN
141 /* the 4/12 factor is designed for reasonable item size on a 160dpi screen */
142 if (global_settings.list_line_padding == -1)
143 list->line_height[screen] = MAX(lcd_get_dpi()*4/12, (int)font_get(vp->font)->height);
144 else
145 list->line_height[screen] = font_get(vp->font)->height + global_settings.list_line_padding;
146#else
147 list->line_height[screen] = font_get(vp->font)->height;
148#endif
149}
150
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000151#else
Jonathan Gordone385ee12008-12-31 05:59:26 +0000152#define list_display_title(l, i) false
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000153#define list_get_nb_lines(list, screen) \
154 viewport_get_nb_lines((list)->parent[(screen)]);
Thomas Martitzeec89a92013-12-20 23:34:28 +0100155#define list_init_item_height(l, i)
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000156#endif
Nils Wallménius7fdee102007-02-23 21:52:45 +0000157
158/*
159 * Initializes a scrolling list
160 * - gui_list : the list structure to initialize
Nils Wallménius7fdee102007-02-23 21:52:45 +0000161 * - callback_get_item_name : pointer to a function that associates a label
162 * to a given item number
163 * - data : extra data passed to the list callback
Solomon Peachy39b64f72018-12-15 19:15:28 -0500164 * - scroll_all :
165 * - selected_size :
Jonathan Gordon5ca15392008-03-26 03:35:24 +0000166 * - parent : the parent viewports to use. NULL means the full screen minus
167 * statusbar if enabled. NOTE: new screens should NOT set this to NULL.
Nils Wallménius7fdee102007-02-23 21:52:45 +0000168 */
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000169void gui_synclist_init(struct gui_synclist * gui_list,
Kevin Ferrare8517ed82005-11-16 02:12:25 +0000170 list_get_name callback_get_item_name,
Kevin Ferraref77ac7a2006-07-02 12:28:27 +0000171 void * data,
172 bool scroll_all,
Jonathan Gordon5ca15392008-03-26 03:35:24 +0000173 int selected_size, struct viewport list_parent[NB_SCREENS]
Kevin Ferraree75cbdd2005-10-29 02:33:19 +0000174 )
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000175{
Kevin Ferrare15046f92005-11-16 17:23:49 +0000176 gui_list->callback_get_item_icon = NULL;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000177 gui_list->callback_get_item_name = callback_get_item_name;
Stéphane Doyon8486c042007-10-21 00:20:10 +0000178 gui_list->callback_speak_item = NULL;
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000179 gui_list->nb_items = 0;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000180 gui_list->selected_item = 0;
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000181 FOR_NB_SCREENS(i)
182 {
183 gui_list->start_item[i] = 0;
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000184#ifdef HAVE_LCD_BITMAP
185 gui_list->offset_position[i] = 0;
186#endif
Jonathan Gordon5ca15392008-03-26 03:35:24 +0000187 if (list_parent)
188 gui_list->parent[i] = &list_parent[i];
189 else
Thomas Martitz2eb1cb62009-09-07 17:37:06 +0000190 gui_list->parent[i] = &parent[i];
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000191 }
Jonathan Gordonbe185a12008-03-26 09:05:42 +0000192 list_init_viewports(gui_list);
Thomas Martitzeec89a92013-12-20 23:34:28 +0100193 FOR_NB_SCREENS(i)
194 list_init_item_height(gui_list, i);
Kevin Ferraree75cbdd2005-10-29 02:33:19 +0000195 gui_list->limit_scroll = false;
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000196 gui_list->data = data;
197 gui_list->scroll_all = scroll_all;
198 gui_list->selected_size = selected_size;
Dave Chapman079ad112006-08-15 08:14:46 +0000199 gui_list->title = NULL;
Jonathan Gordon6a5cc0b2007-04-16 09:14:36 +0000200 gui_list->title_icon = Icon_NOICON;
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000201
Stéphane Doyon8486c042007-10-21 00:20:10 +0000202 gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0;
Thomas Martitz0acdc872011-10-07 17:22:49 +0000203 gui_list->dirty_tick = current_tick;
Jonathan Gordon38234862007-06-07 12:14:31 +0000204 gui_list->show_selection_marker = true;
Brandon Low74cbb0a2007-06-17 21:16:34 +0000205
206#ifdef HAVE_LCD_COLOR
207 gui_list->title_color = -1;
208 gui_list->callback_get_item_color = NULL;
209#endif
Jonathan Gordon38234862007-06-07 12:14:31 +0000210}
211
212/* this toggles the selection bar or cursor */
213void gui_synclist_hide_selection_marker(struct gui_synclist * lists, bool hide)
214{
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000215 lists->show_selection_marker = !hide;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000216}
217
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000218
Mark Arigo34301bb2006-08-23 20:02:06 +0000219#ifdef HAVE_LCD_BITMAP
Jonathan Gordone385ee12008-12-31 05:59:26 +0000220int gui_list_get_item_offset(struct gui_synclist * gui_list,
221 int item_width,
222 int text_pos,
223 struct screen * display,
224 struct viewport *vp)
Mark Arigo34301bb2006-08-23 20:02:06 +0000225{
Mark Arigo34301bb2006-08-23 20:02:06 +0000226 int item_offset;
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000227
Mark Arigo34301bb2006-08-23 20:02:06 +0000228 if (offset_out_of_view)
229 {
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000230 item_offset = gui_list->offset_position[display->screen_type];
Mark Arigo34301bb2006-08-23 20:02:06 +0000231 }
232 else
233 {
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000234 /* if text is smaller than view */
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000235 if (item_width <= vp->width - text_pos)
Mark Arigo34301bb2006-08-23 20:02:06 +0000236 {
237 item_offset = 0;
238 }
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000239 /* if text got out of view */
240 else if (gui_list->offset_position[display->screen_type] >
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000241 item_width - (vp->width - text_pos))
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000242 {
243 item_offset = item_width - (vp->width - text_pos);
Mark Arigo34301bb2006-08-23 20:02:06 +0000244 }
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000245 else
246 item_offset = gui_list->offset_position[display->screen_type];
Mark Arigo34301bb2006-08-23 20:02:06 +0000247 }
248
249 return item_offset;
250}
251#endif
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000252
Nils Wallménius7fdee102007-02-23 21:52:45 +0000253/*
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000254 * Force a full screen update.
255 */
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000256void gui_synclist_draw(struct gui_synclist *gui_list)
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000257{
Thomas Martitzf8650662011-10-07 17:28:27 +0000258 if (list_is_dirty(gui_list))
Jonathan Gordon885283c2008-03-09 09:37:39 +0000259 {
Jonathan Gordonbe185a12008-03-26 09:05:42 +0000260 list_init_viewports(gui_list);
Thomas Martitzeec89a92013-12-20 23:34:28 +0100261 FOR_NB_SCREENS(i)
262 list_init_item_height(gui_list, i);
Nils Wallménius68f6e832008-10-13 12:16:06 +0000263 gui_synclist_select_item(gui_list, gui_list->selected_item);
Jonathan Gordon885283c2008-03-09 09:37:39 +0000264 }
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000265 FOR_NB_SCREENS(i)
266 {
Solomon Peachy39b64f72018-12-15 19:15:28 -0500267#ifdef HAVE_LCD_BITMAP
Jonathan Gordon9b6ac012011-09-06 13:49:41 +0000268 if (!skinlist_draw(&screens[i], gui_list))
269#endif
270 list_draw(&screens[i], gui_list);
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000271 }
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000272}
273
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000274/* sets up the list so the selection is shown correctly on the screen */
275static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list,
276 enum screen_type screen)
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000277{
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000278 int nb_lines = list_get_nb_lines(gui_list, screen);
279 int bottom = MAX(0, gui_list->nb_items - nb_lines);
280 int new_start_item = gui_list->start_item[screen];
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000281 int difference = gui_list->selected_item - gui_list->start_item[screen];
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000282#ifdef HAVE_LCD_CHARCELLS
283 const int scroll_limit_up = 0;
284 const int scroll_limit_down = 1;
285#else
286 const int scroll_limit_up = (nb_lines < gui_list->selected_size+2 ? 0:1);
287 const int scroll_limit_down = (scroll_limit_up+gui_list->selected_size);
288#endif
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000289
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000290 if (gui_list->show_selection_marker == false)
Jonathan Gordon38234862007-06-07 12:14:31 +0000291 {
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000292 new_start_item = gui_list->selected_item;
Jonathan Gordon38234862007-06-07 12:14:31 +0000293 }
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000294 else if (gui_list->selected_size >= nb_lines)
Jonathan Gordonfea88882007-06-05 02:57:43 +0000295 {
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000296 new_start_item = gui_list->selected_item;
Jonathan Gordonfea88882007-06-05 02:57:43 +0000297 }
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000298 else if (global_settings.scroll_paginated)
Jonathan Gordonfea88882007-06-05 02:57:43 +0000299 {
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000300 nb_lines -= nb_lines%gui_list->selected_size;
301 if (difference < 0 || difference >= nb_lines)
Jonathan Gordonfea88882007-06-05 02:57:43 +0000302 {
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000303 new_start_item = gui_list->selected_item -
304 (gui_list->selected_item%nb_lines);
Jonathan Gordonfea88882007-06-05 02:57:43 +0000305 }
306 }
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000307 else if (difference <= scroll_limit_up) /* list moved up */
Jonathan Gordonfea88882007-06-05 02:57:43 +0000308 {
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000309 new_start_item = gui_list->selected_item - scroll_limit_up;
310 }
311 else if (difference > nb_lines - scroll_limit_down) /* list moved down */
312 {
313 new_start_item = gui_list->selected_item + scroll_limit_down - nb_lines;
Jonathan Gordonfea88882007-06-05 02:57:43 +0000314 }
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000315 if (new_start_item < 0)
316 gui_list->start_item[screen] = 0;
317 else if (new_start_item > bottom)
318 gui_list->start_item[screen] = bottom;
319 else
320 gui_list->start_item[screen] = new_start_item;
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000321}
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000322
Solomon Peachy39b64f72018-12-15 19:15:28 -0500323static void edge_beep(struct gui_synclist * gui_list, bool wrap)
324{
325#if CONFIG_CODEC != SWCODEC
326 (void)gui_list;
327 (void)wrap;
328#else
329 if (global_settings.keyclick)
330 {
331 list_speak_item *cb = gui_list->callback_speak_item;
332 if (!wrap) /* a bounce */
333 {
334 static long last_bounce_tick = 0;
335 if(TIME_BEFORE(current_tick, last_bounce_tick+HZ/4))
336 return;
337 last_bounce_tick = current_tick;
338 }
339 /* Next thing the list code will do is go speak the item, doing
340 a talk_shutup() first. Shutup now so the beep is clearer, and
341 make sure the subsequent shutup is skipped because otherwise
342 it'd kill the pcm buffer. */
343 if (cb) {
344 talk_shutup();
345 talk_force_enqueue_next();
346 }
347 system_sound_play(wrap ? SOUND_LIST_EDGE_BEEP_WRAP : SOUND_LIST_EDGE_BEEP_NOWRAP);
348 if (cb) {
349 /* On at least x5: if, instead of the above shutup, I insert a
350 sleep just after the beep_play() call, to delay the subsequent
351 shutup and talk, then in some cases the beep is not played: if
352 the end of a previous utterance is still playing from the pcm buf,
353 the beep fails, even if there would seem to remain enough time
354 to the utterance to mix in the beep. */
355
356 /* Somehow, the following voice utterance is suppressed on e200,
357 but not on x5. Work around... */
358 sleep((40*HZ +999)/1000); // FIXME: Is this really needed?
359 talk_force_shutup();
360 }
361 }
362#endif
363}
364
Steve Bavindddd1572010-06-15 07:08:35 +0000365static void _gui_synclist_speak_item(struct gui_synclist *lists)
366{
367 list_speak_item *cb = lists->callback_speak_item;
368 if (cb && lists->nb_items != 0)
369 {
370 talk_shutup();
371 /* If we have just very recently started talking, then we want
372 to stay silent for a while until things settle. Likewise if
373 we already had a pending scheduled announcement not yet due
374 we need to reschedule it. */
Solomon Peachy39b64f72018-12-15 19:15:28 -0500375 if ((lists->scheduled_talk_tick &&
Steve Bavindddd1572010-06-15 07:08:35 +0000376 TIME_BEFORE(current_tick, lists->scheduled_talk_tick)) ||
Solomon Peachy39b64f72018-12-15 19:15:28 -0500377 (lists->last_talked_tick &&
Steve Bavindddd1572010-06-15 07:08:35 +0000378 TIME_BEFORE(current_tick, lists->last_talked_tick + HZ/5)))
379 {
380 lists->scheduled_talk_tick = current_tick + HZ/5;
Solomon Peachy39b64f72018-12-15 19:15:28 -0500381 }
Steve Bavindddd1572010-06-15 07:08:35 +0000382 else
383 {
384 lists->scheduled_talk_tick = 0; /* work done */
385 cb(lists->selected_item, lists->data);
386 lists->last_talked_tick = current_tick;
387 }
388 }
389}
390
391void gui_synclist_speak_item(struct gui_synclist *lists)
392{
393 if (global_settings.talk_menu)
394 {
395 if (lists->nb_items == 0)
396 talk_id(VOICE_EMPTY_LIST, true);
397 else
398 _gui_synclist_speak_item(lists);
399 }
400}
401
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000402/*
403 * Selects an item in the list
404 * - gui_list : the list structure
405 * - item_number : the number of the item which will be selected
406 */
407void gui_synclist_select_item(struct gui_synclist * gui_list, int item_number)
408{
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000409 if (item_number >= gui_list->nb_items || item_number < 0)
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000410 return;
Steve Bavindddd1572010-06-15 07:08:35 +0000411 if (item_number != gui_list->selected_item)
412 {
413 gui_list->selected_item = item_number;
414 _gui_synclist_speak_item(gui_list);
415 }
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000416 FOR_NB_SCREENS(i)
417 gui_list_put_selection_on_screen(gui_list, i);
Jonathan Gordonfea88882007-06-05 02:57:43 +0000418}
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000419
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000420static void gui_list_select_at_offset(struct gui_synclist * gui_list,
421 int offset)
Jonathan Gordonfea88882007-06-05 02:57:43 +0000422{
Peter D'Hoyec4c678f2008-01-21 22:28:25 +0000423 int new_selection;
Jonathan Gordon195ac122007-05-29 12:16:31 +0000424 if (gui_list->selected_size > 1)
425 {
426 offset *= gui_list->selected_size;
Jonathan Gordon195ac122007-05-29 12:16:31 +0000427 }
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000428
Peter D'Hoyec4c678f2008-01-21 22:28:25 +0000429 new_selection = gui_list->selected_item + offset;
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000430
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000431 if (new_selection >= gui_list->nb_items)
Jonathan Gordonf3145ba2007-06-09 09:41:13 +0000432 {
Steve Bavindddd1572010-06-15 07:08:35 +0000433 new_selection = gui_list->limit_scroll ?
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000434 gui_list->nb_items - gui_list->selected_size : 0;
Solomon Peachy39b64f72018-12-15 19:15:28 -0500435 edge_beep(gui_list, !gui_list->limit_scroll);
Jonathan Gordonf3145ba2007-06-09 09:41:13 +0000436 }
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000437 else if (new_selection < 0)
438 {
Steve Bavindddd1572010-06-15 07:08:35 +0000439 new_selection = gui_list->limit_scroll ?
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000440 0 : gui_list->nb_items - gui_list->selected_size;
Solomon Peachy39b64f72018-12-15 19:15:28 -0500441 edge_beep(gui_list, !gui_list->limit_scroll);
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000442 }
443 else if (gui_list->show_selection_marker == false)
444 {
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000445 FOR_NB_SCREENS(i)
446 {
Björn Stenberg0942e2a2011-10-15 19:35:02 +0000447 int nb_lines = list_get_nb_lines(gui_list, i);
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000448 if (offset > 0)
449 {
Björn Stenberg0942e2a2011-10-15 19:35:02 +0000450 int screen_top = MAX(0, gui_list->nb_items - nb_lines);
Solomon Peachy39b64f72018-12-15 19:15:28 -0500451 gui_list->start_item[i] = MIN(screen_top, gui_list->start_item[i] +
Jonathan Gordon8703cca2008-10-12 10:21:44 +0000452 gui_list->selected_size);
453 gui_list->selected_item = gui_list->start_item[i];
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000454 }
455 else
456 {
Solomon Peachy39b64f72018-12-15 19:15:28 -0500457 gui_list->start_item[i] = MAX(0, gui_list->start_item[i] -
Jonathan Gordon8703cca2008-10-12 10:21:44 +0000458 gui_list->selected_size);
459 gui_list->selected_item = gui_list->start_item[i] + nb_lines;
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000460 }
461 }
462 return;
463 }
Steve Bavindddd1572010-06-15 07:08:35 +0000464 gui_synclist_select_item(gui_list, new_selection);
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000465}
466
Nils Wallménius7fdee102007-02-23 21:52:45 +0000467/*
468 * Adds an item to the list (the callback will be asked for one more item)
469 * - gui_list : the list structure
470 */
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000471void gui_synclist_add_item(struct gui_synclist * gui_list)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000472{
Kevin Ferraree75cbdd2005-10-29 02:33:19 +0000473 gui_list->nb_items++;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000474 /* if only one item in the list, select it */
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000475 if (gui_list->nb_items == 1)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000476 gui_list->selected_item = 0;
477}
478
Nils Wallménius7fdee102007-02-23 21:52:45 +0000479/*
480 * Removes an item to the list (the callback will be asked for one less item)
481 * - gui_list : the list structure
482 */
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000483void gui_synclist_del_item(struct gui_synclist * gui_list)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000484{
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000485 if (gui_list->nb_items > 0)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000486 {
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000487 if (gui_list->selected_item == gui_list->nb_items-1)
Kevin Ferraree75cbdd2005-10-29 02:33:19 +0000488 gui_list->selected_item--;
Kevin Ferraree75cbdd2005-10-29 02:33:19 +0000489 gui_list->nb_items--;
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000490 gui_synclist_select_item(gui_list, gui_list->selected_item);
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000491 }
492}
493
Brandon Lowd3a03b62006-01-22 04:24:26 +0000494#ifdef HAVE_LCD_BITMAP
Brandon Lowd3a03b62006-01-22 04:24:26 +0000495void gui_list_screen_scroll_step(int ofs)
496{
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000497 offset_step = ofs;
Brandon Lowd3a03b62006-01-22 04:24:26 +0000498}
499
500void gui_list_screen_scroll_out_of_view(bool enable)
501{
Nils Wallménius8598b572009-10-09 06:52:20 +0000502 offset_out_of_view = enable;
Brandon Lowd3a03b62006-01-22 04:24:26 +0000503}
504#endif /* HAVE_LCD_BITMAP */
Dan Evertonc0f8b182006-08-15 12:23:55 +0000505
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000506/*
Nils Wallménius7fdee102007-02-23 21:52:45 +0000507 * Set the title and title icon of the list. Setting title to NULL disables
508 * both the title and icon. Use NOICON if there is no icon.
509 */
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000510void gui_synclist_set_title(struct gui_synclist * gui_list,
Nils Wallménius8e33c2f2009-10-11 09:40:47 +0000511 char * title, enum themable_icons icon)
Dave Chapman079ad112006-08-15 08:14:46 +0000512{
513 gui_list->title = title;
Mark Arigo34301bb2006-08-23 20:02:06 +0000514 gui_list->title_icon = icon;
Jonathan Gordon466e5d92010-02-26 03:45:41 +0000515#ifdef HAVE_LCD_BITMAP
Jonathan Gordon466e5d92010-02-26 03:45:41 +0000516 FOR_NB_SCREENS(i)
517 sb_set_title_text(title, icon, i);
518#endif
519 send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
Dave Chapman079ad112006-08-15 08:14:46 +0000520}
Dan Evertonc0f8b182006-08-15 12:23:55 +0000521
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000522void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items)
523{
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000524 lists->nb_items = nb_items;
525#ifdef HAVE_LCD_BITMAP
Jens Arnoldedf5a702005-11-09 22:47:15 +0000526 FOR_NB_SCREENS(i)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000527 {
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000528 lists->offset_position[i] = 0;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000529 }
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000530#endif
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000531}
Hristo Kovachev38deb8f2006-04-03 08:51:08 +0000532int gui_synclist_get_nb_items(struct gui_synclist * lists)
533{
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000534 return lists->nb_items;
Hristo Kovachev38deb8f2006-04-03 08:51:08 +0000535}
536int gui_synclist_get_sel_pos(struct gui_synclist * lists)
537{
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000538 return lists->selected_item;
Hristo Kovachev38deb8f2006-04-03 08:51:08 +0000539}
Peter D'Hoye552ac5a2006-12-09 10:02:09 +0000540void gui_synclist_set_icon_callback(struct gui_synclist * lists,
541 list_get_icon icon_callback)
Kevin Ferrare15046f92005-11-16 17:23:49 +0000542{
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000543 lists->callback_get_item_icon = icon_callback;
Kevin Ferrare15046f92005-11-16 17:23:49 +0000544}
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000545
Stéphane Doyon8486c042007-10-21 00:20:10 +0000546void gui_synclist_set_voice_callback(struct gui_synclist * lists,
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000547 list_speak_item voice_callback)
Stéphane Doyon8486c042007-10-21 00:20:10 +0000548{
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000549 lists->callback_speak_item = voice_callback;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000550}
551
Thomas Martitz5c509d12011-10-22 10:09:23 +0000552void gui_synclist_set_viewport_defaults(struct viewport *vp,
553 enum screen_type screen)
554{
Thomas Martitz155a9f82011-10-23 11:05:27 +0000555 viewport_set_defaults(vp, screen);
Thomas Martitz5c509d12011-10-22 10:09:23 +0000556#ifdef HAVE_BUTTONBAR
557 if (screens[screen].has_buttonbar)
558 vp->height -= BUTTONBAR_HEIGHT;
559#endif
560}
561
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000562#ifdef HAVE_LCD_COLOR
563void gui_synclist_set_color_callback(struct gui_synclist * lists,
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000564 list_get_color color_callback)
Jonathan Gordon0e5cec22008-03-05 09:58:30 +0000565{
566 lists->callback_get_item_color = color_callback;
567}
568#endif
569
Nils Wallménius7fdee102007-02-23 21:52:45 +0000570static void gui_synclist_select_next_page(struct gui_synclist * lists,
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000571 enum screen_type screen)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000572{
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000573 int nb_lines = list_get_nb_lines(lists, screen);
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000574 if (lists->selected_size > 1)
575 nb_lines = MAX(1, nb_lines/lists->selected_size);
Jonathan Gordon95be4fe2008-05-11 14:08:46 +0000576 gui_list_select_at_offset(lists, nb_lines);
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000577}
578
Nils Wallménius7fdee102007-02-23 21:52:45 +0000579static void gui_synclist_select_previous_page(struct gui_synclist * lists,
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000580 enum screen_type screen)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000581{
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000582 int nb_lines = list_get_nb_lines(lists, screen);
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000583 if (lists->selected_size > 1)
584 nb_lines = MAX(1, nb_lines/lists->selected_size);
Jonathan Gordon95be4fe2008-05-11 14:08:46 +0000585 gui_list_select_at_offset(lists, -nb_lines);
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000586}
587
Kevin Ferrared452d262005-10-28 23:52:49 +0000588void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll)
589{
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000590 lists->limit_scroll = scroll;
Dave Chapman079ad112006-08-15 08:14:46 +0000591}
592
Brandon Lowd3a03b62006-01-22 04:24:26 +0000593#ifdef HAVE_LCD_BITMAP
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000594/*
595 * Makes all the item in the list scroll by one step to the right.
Solomon Peachy39b64f72018-12-15 19:15:28 -0500596 * Should stop increasing the value when reaching the widest item value
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000597 * in the list.
598 */
Nils Wallménius7fdee102007-02-23 21:52:45 +0000599static void gui_synclist_scroll_right(struct gui_synclist * lists)
Brandon Lowd3a03b62006-01-22 04:24:26 +0000600{
Brandon Lowd3a03b62006-01-22 04:24:26 +0000601 FOR_NB_SCREENS(i)
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000602 {
603 /* FIXME: This is a fake right boundry limiter. there should be some
604 * callback function to find the longest item on the list in pixels,
605 * to stop the list from scrolling past that point */
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000606 lists->offset_position[i] += offset_step;
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000607 if (lists->offset_position[i] > 1000)
608 lists->offset_position[i] = 1000;
609 }
Brandon Lowd3a03b62006-01-22 04:24:26 +0000610}
611
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000612/*
613 * Makes all the item in the list scroll by one step to the left.
614 * stops at starting position.
615 */
Nils Wallménius7fdee102007-02-23 21:52:45 +0000616static void gui_synclist_scroll_left(struct gui_synclist * lists)
Brandon Lowd3a03b62006-01-22 04:24:26 +0000617{
Brandon Lowd3a03b62006-01-22 04:24:26 +0000618 FOR_NB_SCREENS(i)
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000619 {
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000620 lists->offset_position[i] -= offset_step;
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000621 if (lists->offset_position[i] < 0)
622 lists->offset_position[i] = 0;
623 }
Brandon Lowd3a03b62006-01-22 04:24:26 +0000624}
625#endif /* HAVE_LCD_BITMAP */
626
Jonathan Gordoneb2ea7f2012-01-12 22:28:36 +1100627#if CONFIG_CODEC == SWCODEC
628bool gui_synclist_keyclick_callback(int action, void* data)
629{
630 struct gui_synclist *lists = (struct gui_synclist *)data;
631
632 /* block the beep if we are at the end of the list and we are not wrapping.
633 * CAVEAT: mosts lists don't set limit_scroll untill it sees a repeat
634 * press at the end of the list so this can cause an extra beep.
635 */
636 if (lists->limit_scroll == false)
637 return true;
638 if (lists->selected_item == 0)
639 return (action != ACTION_STD_PREV && action != ACTION_STD_PREVREPEAT);
640 if (lists->selected_item == lists->nb_items - lists->selected_size)
641 return (action != ACTION_STD_NEXT && action != ACTION_STD_NEXTREPEAT);
642
643 return action != ACTION_NONE;
644}
645#endif
Thomas Martitzb673ae22010-10-31 11:11:46 +0000646
Jonathan Gordon94139ac2012-03-20 21:27:33 +1100647/*
648 * Magic to make sure the list gets updated correctly if the skin does
649 * something naughty like a full screen update when we are in a button
650 * loop.
651 *
652 * The GUI_EVENT_NEED_UI_UPDATE event is registered for in list_do_action_timeout()
653 * and unregistered in gui_synclict_do_button(). This is done because
654 * if something is using the list UI they *must* be calling those
655 * two functions in the correct order or the list wont work.
656 */
657static struct gui_synclist *current_lists;
658static bool ui_update_event_registered = false;
Thomas Martitz470989b2014-03-14 23:15:16 +0100659static void _lists_uiviewport_update_callback(unsigned short id, void *data)
Jonathan Gordon94139ac2012-03-20 21:27:33 +1100660{
Thomas Martitz470989b2014-03-14 23:15:16 +0100661 (void)id;
Jonathan Gordon94139ac2012-03-20 21:27:33 +1100662 (void)data;
663 if (current_lists)
664 gui_synclist_draw(current_lists);
665}
666
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000667bool gui_synclist_do_button(struct gui_synclist * lists,
Jonathan Gordone385ee12008-12-31 05:59:26 +0000668 int *actionptr, enum list_wrap wrap)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000669{
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000670 int action = *actionptr;
Martin Scarratt4a1d8d22006-09-25 11:06:54 +0000671#ifdef HAVE_LCD_BITMAP
Sebastian Leonhardta8758c92015-10-21 00:11:44 +0200672 static bool pgleft_allow_cancel = false;
Martin Scarratt4a1d8d22006-09-25 11:06:54 +0000673#endif
Michael Sevakis873e0fd2007-07-22 21:02:24 +0000674
Thomas Martitzfd14cac2009-03-02 19:25:50 +0000675#ifdef HAVE_WHEEL_ACCELERATION
Linus Nielsen Feltzinge75327b2007-11-19 11:05:54 +0000676 int next_item_modifier = button_apply_acceleration(get_action_data());
Michael Sevakis873e0fd2007-07-22 21:02:24 +0000677#else
Jonathan Gordon42698e42007-05-27 15:08:56 +0000678 static int next_item_modifier = 1;
679 static int last_accel_tick = 0;
Jonathan Gordon42698e42007-05-27 15:08:56 +0000680
Thomas Martitzb673ae22010-10-31 11:11:46 +0000681 if (action != ACTION_TOUCHSCREEN)
682 {
683 if (global_settings.list_accel_start_delay)
Jonathan Gordon42698e42007-05-27 15:08:56 +0000684 {
Andree Buschmann04bd51f2011-03-22 21:29:05 +0000685 int start_delay = global_settings.list_accel_start_delay * HZ;
686 int accel_wait = global_settings.list_accel_wait * HZ;
Thomas Martitzb673ae22010-10-31 11:11:46 +0000687
688 if (get_action_statuscode(NULL)&ACTION_REPEAT)
Jonathan Gordon42698e42007-05-27 15:08:56 +0000689 {
Thomas Martitzb673ae22010-10-31 11:11:46 +0000690 if (!last_accel_tick)
691 last_accel_tick = current_tick + start_delay;
692 else if (TIME_AFTER(current_tick, last_accel_tick + accel_wait))
693 {
694 last_accel_tick = current_tick;
695 next_item_modifier++;
696 }
Jonathan Gordon42698e42007-05-27 15:08:56 +0000697 }
Thomas Martitzb673ae22010-10-31 11:11:46 +0000698 else if (last_accel_tick)
699 {
700 next_item_modifier = 1;
701 last_accel_tick = 0;
702 }
Jonathan Gordon42698e42007-05-27 15:08:56 +0000703 }
704 }
Michael Sevakis873e0fd2007-07-22 21:02:24 +0000705#endif
Maurus Cuelenaere1392dc22008-08-23 09:46:38 +0000706#if defined(HAVE_TOUCHSCREEN)
707 if (action == ACTION_TOUCHSCREEN)
Jonathan Gordone385ee12008-12-31 05:59:26 +0000708 action = *actionptr = gui_synclist_do_touchscreen(lists);
Thomas Martitzb673ae22010-10-31 11:11:46 +0000709 else if (action > ACTION_TOUCHSCREEN_MODE)
710 /* cancel kinetic if we got a normal button event */
711 _gui_synclist_stop_kinetic_scrolling();
Jonathan Gordonfa13cbe2007-10-22 12:26:53 +0000712#endif
Martin Scarratt1ab1f022006-09-25 10:56:38 +0000713
Jonathan Gordon94139ac2012-03-20 21:27:33 +1100714 /* Disable the skin redraw callback */
715 current_lists = NULL;
716
Jonathan Gordon6a1161b2006-11-06 10:11:51 +0000717 switch (wrap)
718 {
719 case LIST_WRAP_ON:
720 gui_synclist_limit_scroll(lists, false);
721 break;
722 case LIST_WRAP_OFF:
723 gui_synclist_limit_scroll(lists, true);
724 break;
725 case LIST_WRAP_UNLESS_HELD:
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000726 if (action == ACTION_STD_PREVREPEAT ||
727 action == ACTION_STD_NEXTREPEAT ||
728 action == ACTION_LISTTREE_PGUP ||
729 action == ACTION_LISTTREE_PGDOWN)
Jonathan Gordon6a1161b2006-11-06 10:11:51 +0000730 gui_synclist_limit_scroll(lists, true);
731 else gui_synclist_limit_scroll(lists, false);
732 break;
733 };
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000734
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000735 switch (action)
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000736 {
Jonathan Gordonfa13cbe2007-10-22 12:26:53 +0000737 case ACTION_REDRAW:
738 gui_synclist_draw(lists);
739 return true;
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000740
Jonathan Gordona053e582007-04-08 01:33:01 +0000741#ifdef HAVE_VOLUME_IN_LIST
742 case ACTION_LIST_VOLUP:
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000743 global_settings.volume += 2;
Jonathan Gordona053e582007-04-08 01:33:01 +0000744 /* up two because the falthrough brings it down one */
745 case ACTION_LIST_VOLDOWN:
746 global_settings.volume--;
Nils Wallméniusf46657e2007-05-30 17:57:32 +0000747 setvol();
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000748 return true;
Jonathan Gordona053e582007-04-08 01:33:01 +0000749#endif
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +0000750 case ACTION_STD_PREV:
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +0000751 case ACTION_STD_PREVREPEAT:
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000752 gui_list_select_at_offset(lists, -next_item_modifier);
Thomas Martitzfd14cac2009-03-02 19:25:50 +0000753#ifndef HAVE_WHEEL_ACCELERATION
Bertrik Sikken92442592008-05-20 19:41:48 +0000754 if (button_queue_count() < FRAMEDROP_TRIGGER)
Michael Sevakis873e0fd2007-07-22 21:02:24 +0000755#endif
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000756 gui_synclist_draw(lists);
Jens Arnold237d3c42006-02-19 13:34:12 +0000757 yield();
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000758 *actionptr = ACTION_STD_PREV;
759 return true;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000760
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +0000761 case ACTION_STD_NEXT:
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +0000762 case ACTION_STD_NEXTREPEAT:
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000763 gui_list_select_at_offset(lists, next_item_modifier);
Thomas Martitzfd14cac2009-03-02 19:25:50 +0000764#ifndef HAVE_WHEEL_ACCELERATION
Bertrik Sikken92442592008-05-20 19:41:48 +0000765 if (button_queue_count() < FRAMEDROP_TRIGGER)
Michael Sevakis873e0fd2007-07-22 21:02:24 +0000766#endif
Miika Pekkarinen2eefb5a2007-03-11 10:52:36 +0000767 gui_synclist_draw(lists);
Jens Arnold237d3c42006-02-19 13:34:12 +0000768 yield();
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000769 *actionptr = ACTION_STD_NEXT;
770 return true;
Brandon Lowd3a03b62006-01-22 04:24:26 +0000771
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +0000772#ifdef HAVE_LCD_BITMAP
Jonathan Gordonffeccda2007-07-25 12:37:09 +0000773 case ACTION_TREE_PGRIGHT:
774 gui_synclist_scroll_right(lists);
775 gui_synclist_draw(lists);
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000776 return true;
Martin Scarratt1ab1f022006-09-25 10:56:38 +0000777 case ACTION_TREE_ROOT_INIT:
Peter D'Hoye552ac5a2006-12-09 10:02:09 +0000778 /* After this button press ACTION_TREE_PGLEFT is allowed
779 to skip to root. ACTION_TREE_ROOT_INIT must be defined in the
780 keymaps as a repeated button press (the same as the repeated
781 ACTION_TREE_PGLEFT) with the pre condition being the non-repeated
Sebastian Leonhardta8758c92015-10-21 00:11:44 +0200782 button press. Leave out ACTION_TREE_ROOT_INIT in your keymaps to
783 disable cancel action by PGLEFT key (e.g. if PGLEFT and CANCEL
784 are mapped to different keys) */
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000785 if (lists->offset_position[0] == 0)
Martin Scarratt1ab1f022006-09-25 10:56:38 +0000786 {
Sebastian Leonhardta8758c92015-10-21 00:11:44 +0200787 pgleft_allow_cancel = true;
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000788 *actionptr = ACTION_STD_CANCEL;
789 return true;
Martin Scarratt1ab1f022006-09-25 10:56:38 +0000790 }
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000791 *actionptr = ACTION_TREE_PGLEFT;
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +0000792 case ACTION_TREE_PGLEFT:
Sebastian Leonhardta8758c92015-10-21 00:11:44 +0200793 if(pgleft_allow_cancel && (lists->offset_position[0] == 0))
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000794 {
795 *actionptr = ACTION_STD_CANCEL;
796 return false;
797 }
Kevin Ferraref77ac7a2006-07-02 12:28:27 +0000798 gui_synclist_scroll_left(lists);
799 gui_synclist_draw(lists);
Sebastian Leonhardta8758c92015-10-21 00:11:44 +0200800 pgleft_allow_cancel = false; /* stop ACTION_TREE_PAGE_LEFT
801 skipping to root */
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000802 return true;
Tomas Salfischberger7fa39df2006-01-22 01:42:05 +0000803#endif
Peter D'Hoye552ac5a2006-12-09 10:02:09 +0000804/* for pgup / pgdown, we are obliged to have a different behaviour depending
805 * on the screen for which the user pressed the key since for example, remote
806 * and main screen doesn't have the same number of lines */
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +0000807 case ACTION_LISTTREE_PGUP:
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000808 {
809 int screen =
Jonathan Gordon5ea43ce2007-03-04 08:35:20 +0000810#ifdef HAVE_REMOTE_LCD
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000811 get_action_statuscode(NULL)&ACTION_REMOTE ?
Solomon Peachy39b64f72018-12-15 19:15:28 -0500812 SCREEN_REMOTE :
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000813#endif
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000814 SCREEN_MAIN;
815 gui_synclist_select_previous_page(lists, screen);
816 gui_synclist_draw(lists);
817 yield();
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000818 *actionptr = ACTION_STD_NEXT;
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000819 }
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000820 return true;
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000821
822 case ACTION_LISTTREE_PGDOWN:
823 {
824 int screen =
Jonathan Gordon5ea43ce2007-03-04 08:35:20 +0000825#ifdef HAVE_REMOTE_LCD
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000826 get_action_statuscode(NULL)&ACTION_REMOTE ?
Solomon Peachy39b64f72018-12-15 19:15:28 -0500827 SCREEN_REMOTE :
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000828#endif
829 SCREEN_MAIN;
830 gui_synclist_select_next_page(lists, screen);
831 gui_synclist_draw(lists);
832 yield();
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000833 *actionptr = ACTION_STD_PREV;
Jonathan Gordonca701bf2007-03-04 07:45:12 +0000834 }
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000835 return true;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000836 }
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000837 if(lists->scheduled_talk_tick
838 && TIME_AFTER(current_tick, lists->scheduled_talk_tick))
Stéphane Doyon8486c042007-10-21 00:20:10 +0000839 /* scheduled postponed item announcement is due */
Steve Bavindddd1572010-06-15 07:08:35 +0000840 _gui_synclist_speak_item(lists);
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000841 return false;
Linus Nielsen Feltzing7da94772005-10-28 00:00:00 +0000842}
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000843
Stéphane Doyon8486c042007-10-21 00:20:10 +0000844int list_do_action_timeout(struct gui_synclist *lists, int timeout)
845/* Returns the lowest of timeout or the delay until a postponed
846 scheduled announcement is due (if any). */
847{
Jonathan Gordon94139ac2012-03-20 21:27:33 +1100848 if (lists != current_lists)
849 {
850 if (!ui_update_event_registered)
Solomon Peachy39b64f72018-12-15 19:15:28 -0500851 ui_update_event_registered =
Thomas Martitz470989b2014-03-14 23:15:16 +0100852 add_event(GUI_EVENT_NEED_UI_UPDATE, _lists_uiviewport_update_callback);
Jonathan Gordon94139ac2012-03-20 21:27:33 +1100853 current_lists = lists;
854 }
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000855 if(lists->scheduled_talk_tick)
Stéphane Doyon8486c042007-10-21 00:20:10 +0000856 {
Jonathan Gordonf5ec3e42007-12-17 02:37:21 +0000857 long delay = lists->scheduled_talk_tick -current_tick +1;
Stéphane Doyon8486c042007-10-21 00:20:10 +0000858 /* +1 because the trigger condition uses TIME_AFTER(), which
859 is implemented as strictly greater than. */
860 if(delay < 0)
861 delay = 0;
862 if(timeout > delay || timeout == TIMEOUT_BLOCK)
863 timeout = delay;
864 }
865 return timeout;
866}
867
868bool list_do_action(int context, int timeout,
869 struct gui_synclist *lists, int *action,
870 enum list_wrap wrap)
871/* Combines the get_action() (with possibly overridden timeout) and
872 gui_synclist_do_button() calls. Returns the list action from
873 do_button, and places the action from get_action in *action. */
874{
875 timeout = list_do_action_timeout(lists, timeout);
Jonathan Gordoneb2ea7f2012-01-12 22:28:36 +1100876#if CONFIG_CODEC == SWCODEC
877 keyclick_set_callback(gui_synclist_keyclick_callback, lists);
878#endif
Stéphane Doyon8486c042007-10-21 00:20:10 +0000879 *action = get_action(context, timeout);
880 return gui_synclist_do_button(lists, action, wrap);
881}
882
Solomon Peachy4beafe12019-02-08 19:59:57 -0500883bool gui_synclist_item_is_onscreen(struct gui_synclist *lists,
884 enum screen_type screen, int item)
885{
886 int nb_lines = list_get_nb_lines(lists, screen);
887 return (unsigned)(item - lists->start_item[screen]) < (unsigned) nb_lines;
888}
889
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000890/* Simple use list implementation */
Jonathan Gordon69228f92013-02-12 20:35:11 +1100891static int simplelist_line_count = 0, simplelist_line_remaining;
892static int simplelist_line_pos;
893static char simplelist_buffer[SIMPLELIST_MAX_LINES * SIMPLELIST_MAX_LINELENGTH];
894static char *simplelist_text[SIMPLELIST_MAX_LINES];
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000895/* set the amount of lines shown in the list */
896void simplelist_set_line_count(int lines)
897{
Jonathan Gordon69228f92013-02-12 20:35:11 +1100898 if (lines <= 0) {
899 simplelist_line_pos = 0;
900 simplelist_line_remaining = sizeof(simplelist_buffer);
Jonathan Gordonfa64dbb2008-04-21 13:32:10 +0000901 simplelist_line_count = 0;
Jonathan Gordon69228f92013-02-12 20:35:11 +1100902 }
Jonathan Gordon83d3f1d2013-03-27 22:43:40 +1100903 else if (lines < simplelist_line_count) {
904 char *end = simplelist_text[lines];
905 simplelist_line_pos = end - simplelist_buffer;
906 simplelist_line_remaining = sizeof(simplelist_buffer) - simplelist_line_pos;
907 simplelist_line_count = lines;
908 }
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000909}
910/* get the current amount of lines shown */
911int simplelist_get_line_count(void)
912{
913 return simplelist_line_count;
914}
915/* add/edit a line in the list.
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000916 if line_number > number of lines shown it adds the line,
917 else it edits the line */
Jonathan Gordon69228f92013-02-12 20:35:11 +1100918void simplelist_addline(const char *fmt, ...)
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000919{
920 va_list ap;
Jonathan Gordon69228f92013-02-12 20:35:11 +1100921 size_t len = simplelist_line_remaining;
922 int line_number = simplelist_line_count++;
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000923
Jonathan Gordon69228f92013-02-12 20:35:11 +1100924 simplelist_text[line_number] = &simplelist_buffer[simplelist_line_pos];
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000925 va_start(ap, fmt);
Jonathan Gordon69228f92013-02-12 20:35:11 +1100926 len = vsnprintf(simplelist_text[line_number], simplelist_line_remaining, fmt, ap);
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000927 va_end(ap);
Jonathan Gordon69228f92013-02-12 20:35:11 +1100928 len++;
929 simplelist_line_remaining -= len;
930 simplelist_line_pos += len;
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000931}
932
Nils Wallménius3200d042009-08-20 16:47:44 +0000933static const char* simplelist_static_getname(int item,
934 void * data,
935 char *buffer,
936 size_t buffer_len)
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000937{
Nils Wallménius68489612008-04-09 15:25:17 +0000938 (void)data; (void)buffer; (void)buffer_len;
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000939 return simplelist_text[item];
940}
Nils Wallménius68489612008-04-09 15:25:17 +0000941
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000942bool simplelist_show_list(struct simplelist_info *info)
943{
944 struct gui_synclist lists;
Björn Stenberg0942e2a2011-10-15 19:35:02 +0000945 int action, old_line_count = simplelist_line_count;
Alexander Levin47027702010-04-10 21:11:19 +0000946 list_get_name *getname;
Jonathan Gordon8703cca2008-10-12 10:21:44 +0000947 int wrap = LIST_WRAP_UNLESS_HELD;
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000948 if (info->get_name)
949 getname = info->get_name;
950 else
951 getname = simplelist_static_getname;
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000952
Jonathan Gordonb2eb44c2009-12-09 07:25:46 +0000953 FOR_NB_SCREENS(i)
Teruaki Kawashima67470c82010-01-07 12:51:57 +0000954 viewportmanager_theme_enable(i, true, NULL);
955
Solomon Peachy39b64f72018-12-15 19:15:28 -0500956 gui_synclist_init(&lists, getname, info->callback_data,
Jonathan Gordonf9329e42009-01-07 07:52:32 +0000957 info->scroll_all, info->selection_size, NULL);
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000958
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000959 if (info->title)
Jonathan Gordon573f2202011-09-03 13:24:56 +0000960 gui_synclist_set_title(&lists, info->title, info->title_icon);
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000961 if (info->get_icon)
962 gui_synclist_set_icon_callback(&lists, info->get_icon);
Jonathan Gordon5eac0102007-10-21 01:27:17 +0000963 if (info->get_talk)
964 gui_synclist_set_voice_callback(&lists, info->get_talk);
Jonathan Gordon573f2202011-09-03 13:24:56 +0000965#ifdef HAVE_LCD_COLOR
966 if (info->get_color)
967 gui_synclist_set_color_callback(&lists, info->get_color);
968#endif
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000969
Jonathan Gordon8703cca2008-10-12 10:21:44 +0000970 if (info->hide_selection)
971 {
972 gui_synclist_hide_selection_marker(&lists, true);
973 wrap = LIST_WRAP_OFF;
974 }
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000975
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000976 if (info->action_callback)
977 info->action_callback(ACTION_REDRAW, &lists);
978
979 if (info->get_name == NULL)
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000980 gui_synclist_set_nb_items(&lists,
981 simplelist_line_count*info->selection_size);
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000982 else
983 gui_synclist_set_nb_items(&lists, info->count*info->selection_size);
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000984
Jonathan Gordon67df5f22008-09-25 07:56:34 +0000985 gui_synclist_select_item(&lists, info->selection);
Teruaki Kawashima478afc32009-10-27 13:18:54 +0000986
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000987 gui_synclist_draw(&lists);
Stéphane Doyona82a8602007-10-23 05:19:03 +0000988 gui_synclist_speak_item(&lists);
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000989
990 while(1)
991 {
Teruaki Kawashima2d864172010-10-07 12:27:26 +0000992 list_do_action(CONTEXT_LIST, info->timeout,
Jonathan Gordon8703cca2008-10-12 10:21:44 +0000993 &lists, &action, wrap);
Michael Sevakisd6af2872007-10-26 23:11:18 +0000994
995 /* We must yield in this case or no other thread can run */
Steve Bavinab784642007-10-26 18:04:42 +0000996 if (info->timeout == TIMEOUT_NOBLOCK)
Michael Sevakisd6af2872007-10-26 23:11:18 +0000997 yield();
998
Jonathan Gordon5f893be2007-10-20 12:32:55 +0000999 if (info->action_callback)
1000 {
Jonathan Gordon2438d782008-09-25 08:12:25 +00001001 bool stdok = action==ACTION_STD_OK;
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001002 action = info->action_callback(action, &lists);
Jonathan Gordone385ee12008-12-31 05:59:26 +00001003 if (stdok && action == ACTION_STD_CANCEL)
Jonathan Gordon2438d782008-09-25 08:12:25 +00001004 {
Jonathan Gordone385ee12008-12-31 05:59:26 +00001005 /* callback asked us to exit */
Jonathan Gordon2438d782008-09-25 08:12:25 +00001006 info->selection = gui_synclist_get_sel_pos(&lists);
1007 break;
1008 }
Teruaki Kawashima478afc32009-10-27 13:18:54 +00001009
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001010 if (info->get_name == NULL)
Jonathan Gordone385ee12008-12-31 05:59:26 +00001011 gui_synclist_set_nb_items(&lists,
1012 simplelist_line_count*info->selection_size);
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001013 }
1014 if (action == ACTION_STD_CANCEL)
Jonathan Gordon2438d782008-09-25 08:12:25 +00001015 {
1016 info->selection = -1;
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001017 break;
Jonathan Gordon2438d782008-09-25 08:12:25 +00001018 }
Jens Arnoldbf9ae1f2008-02-25 07:41:30 +00001019 else if ((action == ACTION_REDRAW) ||
Thomas Martitz6e6f0c62012-03-22 20:59:01 +01001020 (list_is_dirty(&lists)) ||
Jonathan Gordone2472302007-11-04 01:04:10 +00001021 (old_line_count != simplelist_line_count))
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001022 {
1023 if (info->get_name == NULL)
Jonathan Gordone385ee12008-12-31 05:59:26 +00001024 {
1025 gui_synclist_set_nb_items(&lists,
1026 simplelist_line_count*info->selection_size);
1027 }
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001028 gui_synclist_draw(&lists);
Miika Pekkarinencaff8352007-10-21 11:06:30 +00001029 old_line_count = simplelist_line_count;
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001030 }
1031 else if(default_event_handler(action) == SYS_USB_CONNECTED)
1032 return true;
1033 }
Stéphane Doyona82a8602007-10-23 05:19:03 +00001034 talk_shutup();
Jonathan Gordonb2eb44c2009-12-09 07:25:46 +00001035 FOR_NB_SCREENS(i)
Teruaki Kawashima67470c82010-01-07 12:51:57 +00001036 viewportmanager_theme_undo(i, false);
Jonathan Gordon5f893be2007-10-20 12:32:55 +00001037 return false;
1038}
1039
Jonathan Gordon5eac0102007-10-21 01:27:17 +00001040void simplelist_info_init(struct simplelist_info *info, char* title,
Jonathan Gordon616971c2007-10-21 06:42:52 +00001041 int count, void* data)
Jonathan Gordon5eac0102007-10-21 01:27:17 +00001042{
1043 info->title = title;
1044 info->count = count;
Jonathan Gordon616971c2007-10-21 06:42:52 +00001045 info->selection_size = 1;
Jonathan Gordon5eac0102007-10-21 01:27:17 +00001046 info->hide_selection = false;
1047 info->scroll_all = false;
Jonathan Gordon04651012007-10-21 13:47:43 +00001048 info->timeout = HZ/10;
Jonathan Gordon67df5f22008-09-25 07:56:34 +00001049 info->selection = 0;
Jonathan Gordon5eac0102007-10-21 01:27:17 +00001050 info->action_callback = NULL;
Jonathan Gordon573f2202011-09-03 13:24:56 +00001051 info->title_icon = Icon_NOICON;
Jonathan Gordon5eac0102007-10-21 01:27:17 +00001052 info->get_icon = NULL;
1053 info->get_name = NULL;
1054 info->get_talk = NULL;
Jonathan Gordon573f2202011-09-03 13:24:56 +00001055#ifdef HAVE_LCD_COLOR
1056 info->get_color = NULL;
1057#endif
Jonathan Gordon5eac0102007-10-21 01:27:17 +00001058 info->callback_data = data;
Alexander Levinf2325192010-04-10 20:25:23 +00001059 simplelist_line_count = 0;
Jonathan Gordon5eac0102007-10-21 01:27:17 +00001060}