Fix charcell build
[maemo-rb.git] / apps / gui / list.c
blob4f1d3833d692bfee70b51357d5177be56974028c
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 */
580 bool gui_synclist_do_button(struct gui_synclist * lists,
581 int *actionptr, enum list_wrap wrap)
583 int action = *actionptr;
584 #ifdef HAVE_LCD_BITMAP
585 static bool scrolling_left = false;
586 #endif
588 #ifdef HAVE_WHEEL_ACCELERATION
589 int next_item_modifier = button_apply_acceleration(get_action_data());
590 #else
591 static int next_item_modifier = 1;
592 static int last_accel_tick = 0;
594 if (action != ACTION_TOUCHSCREEN)
596 if (global_settings.list_accel_start_delay)
598 int start_delay = global_settings.list_accel_start_delay * HZ;
599 int accel_wait = global_settings.list_accel_wait * HZ;
601 if (get_action_statuscode(NULL)&ACTION_REPEAT)
603 if (!last_accel_tick)
604 last_accel_tick = current_tick + start_delay;
605 else if (TIME_AFTER(current_tick, last_accel_tick + accel_wait))
607 last_accel_tick = current_tick;
608 next_item_modifier++;
611 else if (last_accel_tick)
613 next_item_modifier = 1;
614 last_accel_tick = 0;
618 #endif
619 #if defined(HAVE_TOUCHSCREEN)
620 if (action == ACTION_TOUCHSCREEN)
621 action = *actionptr = gui_synclist_do_touchscreen(lists);
622 else if (action > ACTION_TOUCHSCREEN_MODE)
623 /* cancel kinetic if we got a normal button event */
624 _gui_synclist_stop_kinetic_scrolling();
625 #endif
627 switch (wrap)
629 case LIST_WRAP_ON:
630 gui_synclist_limit_scroll(lists, false);
631 break;
632 case LIST_WRAP_OFF:
633 gui_synclist_limit_scroll(lists, true);
634 break;
635 case LIST_WRAP_UNLESS_HELD:
636 if (action == ACTION_STD_PREVREPEAT ||
637 action == ACTION_STD_NEXTREPEAT ||
638 action == ACTION_LISTTREE_PGUP ||
639 action == ACTION_LISTTREE_PGDOWN)
640 gui_synclist_limit_scroll(lists, true);
641 else gui_synclist_limit_scroll(lists, false);
642 break;
645 switch (action)
647 case ACTION_REDRAW:
648 gui_synclist_draw(lists);
649 return true;
651 #ifdef HAVE_VOLUME_IN_LIST
652 case ACTION_LIST_VOLUP:
653 global_settings.volume += 2;
654 /* up two because the falthrough brings it down one */
655 case ACTION_LIST_VOLDOWN:
656 global_settings.volume--;
657 setvol();
658 return true;
659 #endif
660 case ACTION_STD_PREV:
661 case ACTION_STD_PREVREPEAT:
662 gui_list_select_at_offset(lists, -next_item_modifier);
663 #ifndef HAVE_WHEEL_ACCELERATION
664 if (button_queue_count() < FRAMEDROP_TRIGGER)
665 #endif
666 gui_synclist_draw(lists);
667 yield();
668 *actionptr = ACTION_STD_PREV;
669 return true;
671 case ACTION_STD_NEXT:
672 case ACTION_STD_NEXTREPEAT:
673 gui_list_select_at_offset(lists, next_item_modifier);
674 #ifndef HAVE_WHEEL_ACCELERATION
675 if (button_queue_count() < FRAMEDROP_TRIGGER)
676 #endif
677 gui_synclist_draw(lists);
678 yield();
679 *actionptr = ACTION_STD_NEXT;
680 return true;
682 #ifdef HAVE_LCD_BITMAP
683 case ACTION_TREE_PGRIGHT:
684 gui_synclist_scroll_right(lists);
685 gui_synclist_draw(lists);
686 return true;
687 case ACTION_TREE_ROOT_INIT:
688 /* After this button press ACTION_TREE_PGLEFT is allowed
689 to skip to root. ACTION_TREE_ROOT_INIT must be defined in the
690 keymaps as a repeated button press (the same as the repeated
691 ACTION_TREE_PGLEFT) with the pre condition being the non-repeated
692 button press */
693 if (lists->offset_position[0] == 0)
695 scrolling_left = false;
696 *actionptr = ACTION_STD_CANCEL;
697 return true;
699 *actionptr = ACTION_TREE_PGLEFT;
700 case ACTION_TREE_PGLEFT:
701 if(!scrolling_left && (lists->offset_position[0] == 0))
703 *actionptr = ACTION_STD_CANCEL;
704 return false;
706 gui_synclist_scroll_left(lists);
707 gui_synclist_draw(lists);
708 scrolling_left = true; /* stop ACTION_TREE_PAGE_LEFT
709 skipping to root */
710 return true;
711 #endif
712 /* for pgup / pgdown, we are obliged to have a different behaviour depending
713 * on the screen for which the user pressed the key since for example, remote
714 * and main screen doesn't have the same number of lines */
715 case ACTION_LISTTREE_PGUP:
717 int screen =
718 #ifdef HAVE_REMOTE_LCD
719 get_action_statuscode(NULL)&ACTION_REMOTE ?
720 SCREEN_REMOTE :
721 #endif
722 SCREEN_MAIN;
723 gui_synclist_select_previous_page(lists, screen);
724 gui_synclist_draw(lists);
725 yield();
726 *actionptr = ACTION_STD_NEXT;
728 return true;
730 case ACTION_LISTTREE_PGDOWN:
732 int screen =
733 #ifdef HAVE_REMOTE_LCD
734 get_action_statuscode(NULL)&ACTION_REMOTE ?
735 SCREEN_REMOTE :
736 #endif
737 SCREEN_MAIN;
738 gui_synclist_select_next_page(lists, screen);
739 gui_synclist_draw(lists);
740 yield();
741 *actionptr = ACTION_STD_PREV;
743 return true;
745 if(lists->scheduled_talk_tick
746 && TIME_AFTER(current_tick, lists->scheduled_talk_tick))
747 /* scheduled postponed item announcement is due */
748 _gui_synclist_speak_item(lists);
749 return false;
752 int list_do_action_timeout(struct gui_synclist *lists, int timeout)
753 /* Returns the lowest of timeout or the delay until a postponed
754 scheduled announcement is due (if any). */
756 if(lists->scheduled_talk_tick)
758 long delay = lists->scheduled_talk_tick -current_tick +1;
759 /* +1 because the trigger condition uses TIME_AFTER(), which
760 is implemented as strictly greater than. */
761 if(delay < 0)
762 delay = 0;
763 if(timeout > delay || timeout == TIMEOUT_BLOCK)
764 timeout = delay;
766 return timeout;
769 bool list_do_action(int context, int timeout,
770 struct gui_synclist *lists, int *action,
771 enum list_wrap wrap)
772 /* Combines the get_action() (with possibly overridden timeout) and
773 gui_synclist_do_button() calls. Returns the list action from
774 do_button, and places the action from get_action in *action. */
776 timeout = list_do_action_timeout(lists, timeout);
777 *action = get_action(context, timeout);
778 return gui_synclist_do_button(lists, action, wrap);
781 bool gui_synclist_item_is_onscreen(struct gui_synclist *lists,
782 enum screen_type screen, int item)
784 int nb_lines = list_get_nb_lines(lists, screen);
785 return (unsigned)(item - lists->start_item[screen]) < (unsigned) nb_lines;
788 /* Simple use list implementation */
789 static int simplelist_line_count = 0;
790 static char simplelist_text[SIMPLELIST_MAX_LINES][SIMPLELIST_MAX_LINELENGTH];
791 /* set the amount of lines shown in the list */
792 void simplelist_set_line_count(int lines)
794 if (lines < 0)
795 simplelist_line_count = 0;
796 else if (lines >= SIMPLELIST_MAX_LINES)
797 simplelist_line_count = SIMPLELIST_MAX_LINES;
798 else
799 simplelist_line_count = lines;
801 /* get the current amount of lines shown */
802 int simplelist_get_line_count(void)
804 return simplelist_line_count;
806 /* add/edit a line in the list.
807 if line_number > number of lines shown it adds the line,
808 else it edits the line */
809 void simplelist_addline(int line_number, const char *fmt, ...)
811 va_list ap;
813 if (line_number > simplelist_line_count)
815 if (simplelist_line_count < SIMPLELIST_MAX_LINES)
816 line_number = simplelist_line_count++;
817 else
818 return;
820 va_start(ap, fmt);
821 vsnprintf(simplelist_text[line_number], SIMPLELIST_MAX_LINELENGTH, fmt, ap);
822 va_end(ap);
825 static const char* simplelist_static_getname(int item,
826 void * data,
827 char *buffer,
828 size_t buffer_len)
830 (void)data; (void)buffer; (void)buffer_len;
831 return simplelist_text[item];
834 bool simplelist_show_list(struct simplelist_info *info)
836 struct gui_synclist lists;
837 int action, old_line_count = simplelist_line_count;
838 list_get_name *getname;
839 int wrap = LIST_WRAP_UNLESS_HELD;
840 if (info->get_name)
841 getname = info->get_name;
842 else
843 getname = simplelist_static_getname;
845 FOR_NB_SCREENS(i)
846 viewportmanager_theme_enable(i, true, NULL);
848 gui_synclist_init(&lists, getname, info->callback_data,
849 info->scroll_all, info->selection_size, NULL);
851 if (info->title)
852 gui_synclist_set_title(&lists, info->title, info->title_icon);
853 if (info->get_icon)
854 gui_synclist_set_icon_callback(&lists, info->get_icon);
855 if (info->get_talk)
856 gui_synclist_set_voice_callback(&lists, info->get_talk);
857 #ifdef HAVE_LCD_COLOR
858 if (info->get_color)
859 gui_synclist_set_color_callback(&lists, info->get_color);
860 #endif
862 if (info->hide_selection)
864 gui_synclist_hide_selection_marker(&lists, true);
865 wrap = LIST_WRAP_OFF;
868 if (info->action_callback)
869 info->action_callback(ACTION_REDRAW, &lists);
871 if (info->get_name == NULL)
872 gui_synclist_set_nb_items(&lists,
873 simplelist_line_count*info->selection_size);
874 else
875 gui_synclist_set_nb_items(&lists, info->count*info->selection_size);
877 gui_synclist_select_item(&lists, info->selection);
879 gui_synclist_draw(&lists);
880 gui_synclist_speak_item(&lists);
882 while(1)
884 list_do_action(CONTEXT_LIST, info->timeout,
885 &lists, &action, wrap);
887 /* We must yield in this case or no other thread can run */
888 if (info->timeout == TIMEOUT_NOBLOCK)
889 yield();
891 if (info->action_callback)
893 bool stdok = action==ACTION_STD_OK;
894 action = info->action_callback(action, &lists);
895 if (stdok && action == ACTION_STD_CANCEL)
897 /* callback asked us to exit */
898 info->selection = gui_synclist_get_sel_pos(&lists);
899 break;
902 if (info->get_name == NULL)
903 gui_synclist_set_nb_items(&lists,
904 simplelist_line_count*info->selection_size);
906 if (action == ACTION_STD_CANCEL)
908 info->selection = -1;
909 break;
911 else if ((action == ACTION_REDRAW) ||
912 (old_line_count != simplelist_line_count))
914 if (info->get_name == NULL)
916 gui_synclist_set_nb_items(&lists,
917 simplelist_line_count*info->selection_size);
919 gui_synclist_draw(&lists);
920 old_line_count = simplelist_line_count;
922 else if(default_event_handler(action) == SYS_USB_CONNECTED)
923 return true;
925 talk_shutup();
926 FOR_NB_SCREENS(i)
927 viewportmanager_theme_undo(i, false);
928 return false;
931 void simplelist_info_init(struct simplelist_info *info, char* title,
932 int count, void* data)
934 info->title = title;
935 info->count = count;
936 info->selection_size = 1;
937 info->hide_selection = false;
938 info->scroll_all = false;
939 info->timeout = HZ/10;
940 info->selection = 0;
941 info->action_callback = NULL;
942 info->title_icon = Icon_NOICON;
943 info->get_icon = NULL;
944 info->get_name = NULL;
945 info->get_talk = NULL;
946 #ifdef HAVE_LCD_COLOR
947 info->get_color = NULL;
948 #endif
949 info->callback_data = data;
950 simplelist_line_count = 0;