Add optional icon to list title, current path display shows a dir icon, and list...
[Rockbox.git] / apps / gui / list.c
blob94150176dc3832f02839610ad96c98f4728ab934
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Kevin Ferrare
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "config.h"
21 #include "lcd.h"
22 #include "font.h"
23 #include "button.h"
24 #include "sprintf.h"
25 #include "string.h"
26 #include "settings.h"
27 #include "kernel.h"
29 #include "action.h"
30 #include "screen_access.h"
31 #include "list.h"
32 #include "scrollbar.h"
33 #include "statusbar.h"
34 #include "textarea.h"
36 #ifdef HAVE_LCD_CHARCELLS
37 #define SCROLL_LIMIT 1
38 #else
39 #define SCROLL_LIMIT 2
40 #endif
42 #ifdef HAVE_LCD_BITMAP
43 static int offset_step = 16; /* pixels per screen scroll step */
44 /* should lines scroll out of the screen */
45 static bool offset_out_of_view = false;
46 #endif
50 void gui_list_init(struct gui_list * gui_list,
51 list_get_name callback_get_item_name,
52 void * data,
53 bool scroll_all,
54 int selected_size
57 gui_list->callback_get_item_icon = NULL;
58 gui_list->callback_get_item_name = callback_get_item_name;
59 gui_list->display = NULL;
60 gui_list_set_nb_items(gui_list, 0);
61 gui_list->selected_item = 0;
62 gui_list->start_item = 0;
63 gui_list->limit_scroll = false;
64 gui_list->data=data;
65 gui_list->cursor_flash_state=false;
66 #ifdef HAVE_LCD_BITMAP
67 gui_list->offset_position = 0;
68 #endif
69 gui_list->scroll_all=scroll_all;
70 gui_list->selected_size=selected_size;
71 gui_list->title = NULL;
72 gui_list->title_width = 0;
73 gui_list->title_icon = NOICON;
76 void gui_list_set_display(struct gui_list * gui_list, struct screen * display)
78 if(gui_list->display != 0) /* we switched from a previous display */
79 gui_list->display->stop_scroll();
80 gui_list->display = display;
81 #ifdef HAVE_LCD_CHARCELLS
82 display->double_height(false);
83 #endif
84 gui_list_put_selection_in_screen(gui_list, false);
87 void gui_list_flash(struct gui_list * gui_list)
89 struct screen * display=gui_list->display;
90 gui_list->cursor_flash_state=!gui_list->cursor_flash_state;
91 int selected_line=gui_list->selected_item-gui_list->start_item;
92 #ifdef HAVE_LCD_BITMAP
93 int line_ypos=display->getymargin()+display->char_height*selected_line;
94 if (global_settings.invert_cursor)
96 int line_xpos=display->getxmargin();
97 display->set_drawmode(DRMODE_COMPLEMENT);
98 display->fillrect(line_xpos, line_ypos, display->width,
99 display->char_height);
100 display->set_drawmode(DRMODE_SOLID);
101 display->invertscroll(0, selected_line);
103 else
105 int cursor_xpos=(global_settings.scrollbar &&
106 display->nb_lines < gui_list->nb_items)?1:0;
107 screen_put_cursorxy(display, cursor_xpos, selected_line, gui_list->cursor_flash_state);
109 display->update_rect(0, line_ypos,display->width,
110 display->char_height);
111 #else
112 screen_put_cursorxy(display, 0, selected_line, gui_list->cursor_flash_state);
113 gui_textarea_update(display);
114 #endif
117 void gui_list_put_selection_in_screen(struct gui_list * gui_list,
118 bool put_from_end)
120 #ifdef HAVE_LCD_BITMAP
121 gui_list->display->setfont(FONT_UI);
122 #endif
123 gui_textarea_update_nblines(gui_list->display);
124 int nb_lines=gui_list->display->nb_lines;
125 if (gui_list->title)
126 nb_lines--;
127 if(put_from_end)
129 int list_end = gui_list->selected_item + SCROLL_LIMIT;
131 if(list_end-1 == gui_list->nb_items)
132 list_end--;
133 if(list_end > gui_list->nb_items)
134 list_end = nb_lines;
135 gui_list->start_item = list_end - nb_lines;
137 else
139 int list_start = gui_list->selected_item - SCROLL_LIMIT + 1;
140 if(list_start + nb_lines > gui_list->nb_items)
141 list_start = gui_list->nb_items - nb_lines;
142 gui_list->start_item = list_start;
144 if(gui_list->start_item < 0)
145 gui_list->start_item = 0;
148 #ifdef HAVE_LCD_BITMAP
149 int gui_list_get_item_offset(struct gui_list * gui_list, int item_width,
150 int text_pos)
152 struct screen * display=gui_list->display;
153 int item_offset;
155 if (offset_out_of_view)
157 item_offset = gui_list->offset_position;
159 else
161 /* if text is smaller then view */
162 if (item_width <= display->width - text_pos)
164 item_offset = 0;
166 else
168 /* if text got out of view */
169 if (gui_list->offset_position >
170 item_width - (display->width - text_pos))
171 item_offset = item_width - (display->width - text_pos);
172 else
173 item_offset = gui_list->offset_position;
177 return item_offset;
179 #endif
181 void gui_list_draw(struct gui_list * gui_list)
183 struct screen * display=gui_list->display;
184 int cursor_pos = 0;
185 int icon_pos = 1;
186 int text_pos;
187 bool draw_icons = (gui_list->callback_get_item_icon != NULL ) ;
188 bool draw_cursor;
189 int i;
190 int lines;
191 #ifdef HAVE_LCD_BITMAP
192 int item_offset;
193 #endif
195 gui_textarea_clear(display);
197 /* position and draw the list title & icon */
198 if (gui_list->title)
200 i = 1;
201 lines = display->nb_lines - 1;
203 if (gui_list->title_icon != NOICON && draw_icons)
205 screen_put_iconxy(display, 0, 0, gui_list->title_icon);
206 #ifdef HAVE_LCD_BITMAP
207 text_pos = 8; /* pixels */
208 #else
209 text_pos = 1; /* chars */
210 #endif
212 else
214 text_pos = 0;
217 #ifdef HAVE_LCD_BITMAP
218 screen_set_xmargin(display, text_pos); /* margin for title */
219 item_offset = gui_list_get_item_offset(gui_list, gui_list->title_width,
220 text_pos);
221 if (item_offset > gui_list->title_width - (display->width - text_pos))
222 display->puts_offset(0, 0, gui_list->title, item_offset);
223 else
224 display->puts_scroll_offset(0, 0, gui_list->title, item_offset);
225 #else
226 display->puts_scroll(text_pos, 0, gui_list->title);
227 #endif
229 else
231 i = 0;
232 lines = display->nb_lines;
235 /* Adjust the position of icon, cursor, text for the list */
236 #ifdef HAVE_LCD_BITMAP
237 display->setfont(FONT_UI);
238 gui_textarea_update_nblines(display);
239 bool draw_scrollbar;
241 draw_scrollbar = (global_settings.scrollbar &&
242 lines < gui_list->nb_items);
244 draw_cursor = !global_settings.invert_cursor;
245 text_pos = 0; /* here it's in pixels */
246 if(draw_scrollbar || gui_list->title) /* indent if there's a title */
248 cursor_pos++;
249 icon_pos++;
250 text_pos += SCROLLBAR_WIDTH;
252 if(!draw_cursor)
253 icon_pos--;
254 else
255 text_pos += CURSOR_WIDTH;
257 if(draw_icons)
258 text_pos += 8;
259 #else
260 draw_cursor = true;
261 if(draw_icons)
262 text_pos = 2; /* here it's in chars */
263 else
264 text_pos = 1;
265 #endif
267 #ifdef HAVE_LCD_BITMAP
268 screen_set_xmargin(display, text_pos); /* margin for list */
269 #endif
271 while (i < display->nb_lines)
273 char entry_buffer[MAX_PATH];
274 unsigned char *entry_name;
275 int current_item = gui_list->start_item + (gui_list->title?i-1:i);
277 /* When there are less items to display than the
278 * current available space on the screen, we stop*/
279 if(current_item >= gui_list->nb_items)
280 break;
281 entry_name = gui_list->callback_get_item_name(current_item,
282 gui_list->data,
283 entry_buffer);
284 #ifdef HAVE_LCD_BITMAP
285 /* position the string at the correct offset place */
286 int item_width,h;
287 display->getstringsize(entry_name, &item_width, &h);
288 item_offset = gui_list_get_item_offset(gui_list, item_width, text_pos);
289 #endif
291 if(current_item >= gui_list->selected_item &&
292 current_item < gui_list->selected_item + gui_list->selected_size)
293 {/* The selected item must be displayed scrolling */
294 #ifdef HAVE_LCD_BITMAP
295 if (global_settings.invert_cursor)/* Display inverted-line-style*/
296 /* if text got out of view */
297 if (item_offset > item_width - (display->width - text_pos))
298 /* don't scroll */
299 display->puts_style_offset(0, i, entry_name, STYLE_INVERT,item_offset);
300 else
301 display->puts_scroll_style_offset(0, i, entry_name, STYLE_INVERT,item_offset);
303 else /* if (!global_settings.invert_cursor) */
304 if (item_offset > item_width - (display->width - text_pos))
305 display->puts_offset(0, i, entry_name,item_offset);
306 else
307 display->puts_scroll_offset(0, i, entry_name,item_offset);
308 #else
309 display->puts_scroll(text_pos, i, entry_name);
310 #endif
312 if(draw_cursor)
313 screen_put_cursorxy(display, cursor_pos, i, true);
315 else
316 {/* normal item */
317 if(gui_list->scroll_all)
319 #ifdef HAVE_LCD_BITMAP
320 display->puts_scroll_offset(0, i, entry_name,item_offset);
321 #else
322 display->puts_scroll(text_pos, i, entry_name);
323 #endif
325 else
327 #ifdef HAVE_LCD_BITMAP
328 display->puts_offset(0, i, entry_name,item_offset);
329 #else
330 display->puts(text_pos, i, entry_name);
331 #endif
334 /* Icons display */
335 if(draw_icons)
337 ICON icon;
338 gui_list->callback_get_item_icon(current_item,
339 gui_list->data,
340 &icon);
341 if(icon)
342 screen_put_iconxy(display, icon_pos, i, icon);
344 i++;
347 #ifdef HAVE_LCD_BITMAP
348 /* Draw the scrollbar if needed*/
349 if(draw_scrollbar)
351 int y_start = gui_textarea_get_ystart(display);
352 if (gui_list->title)
353 y_start += display->char_height;
354 int scrollbar_y_end = display->char_height *
355 lines + y_start;
356 gui_scrollbar_draw(display, 0, y_start, SCROLLBAR_WIDTH-1,
357 scrollbar_y_end - y_start, gui_list->nb_items,
358 gui_list->start_item,
359 gui_list->start_item + display->nb_lines, VERTICAL);
361 #endif
363 gui_textarea_update(display);
366 void gui_list_select_item(struct gui_list * gui_list, int item_number)
368 if( item_number > gui_list->nb_items-1 || item_number < 0 )
369 return;
370 gui_list->selected_item = item_number;
371 gui_list_put_selection_in_screen(gui_list, false);
374 void gui_list_select_next(struct gui_list * gui_list)
376 if( gui_list->selected_item+gui_list->selected_size >= gui_list->nb_items )
378 if(gui_list->limit_scroll)
379 return;
380 /* we have already reached the bottom of the list */
381 gui_list->selected_item = 0;
382 gui_list->start_item = 0;
384 else
386 gui_list->selected_item+=gui_list->selected_size;
387 int nb_lines = gui_list->display->nb_lines;
388 if (gui_list->title)
389 nb_lines--;
390 int item_pos = gui_list->selected_item - gui_list->start_item;
391 int end_item = gui_list->start_item + nb_lines;
393 if (global_settings.scroll_paginated)
395 /* When we reach the bottom of the list
396 * we jump to a new page if there are more items*/
397 if( item_pos > nb_lines-gui_list->selected_size && end_item < gui_list->nb_items )
399 gui_list->start_item = gui_list->selected_item;
400 if ( gui_list->start_item > gui_list->nb_items-nb_lines )
401 gui_list->start_item = gui_list->nb_items-nb_lines;
404 else
406 /* we start scrolling vertically when reaching the line
407 * (nb_lines-SCROLL_LIMIT)
408 * and when we are not in the last part of the list*/
409 if( item_pos > nb_lines-SCROLL_LIMIT && end_item < gui_list->nb_items )
410 gui_list->start_item+=gui_list->selected_size;
415 void gui_list_select_previous(struct gui_list * gui_list)
417 int nb_lines = gui_list->display->nb_lines;
418 if (gui_list->title)
419 nb_lines--;
420 if( gui_list->selected_item-gui_list->selected_size < 0 )
422 if(gui_list->limit_scroll)
423 return;
424 /* we have aleady reached the top of the list */
425 int start;
426 gui_list->selected_item = gui_list->nb_items-gui_list->selected_size;
427 start = gui_list->nb_items-nb_lines;
428 if( start < 0 )
429 gui_list->start_item = 0;
430 else
431 gui_list->start_item = start;
433 else
435 int item_pos;
436 gui_list->selected_item-=gui_list->selected_size;
437 item_pos = gui_list->selected_item - gui_list->start_item;
438 if (global_settings.scroll_paginated)
440 /* When we reach the top of the list
441 * we jump to a new page if there are more items*/
442 if( item_pos < 0)
443 gui_list->start_item = gui_list->selected_item-nb_lines+gui_list->selected_size;
445 else
447 /* we start scrolling vertically when reaching the line
448 * (nb_lines-SCROLL_LIMIT)
449 * and when we are not in the last part of the list*/
450 if( item_pos < SCROLL_LIMIT-1)
451 gui_list->start_item-=gui_list->selected_size;
453 if( gui_list->start_item < 0 )
454 gui_list->start_item = 0;
458 void gui_list_select_next_page(struct gui_list * gui_list, int nb_lines)
460 if(gui_list->selected_item == gui_list->nb_items-gui_list->selected_size)
462 if(gui_list->limit_scroll)
463 return;
464 gui_list->selected_item = 0;
466 else
468 if (gui_list->title)
469 nb_lines--;
470 nb_lines-=nb_lines%gui_list->selected_size;
471 gui_list->selected_item += nb_lines;
472 if(gui_list->selected_item > gui_list->nb_items-1)
473 gui_list->selected_item = gui_list->nb_items-1;
475 gui_list_put_selection_in_screen(gui_list, true);
478 void gui_list_select_previous_page(struct gui_list * gui_list, int nb_lines)
480 if(gui_list->selected_item == 0)
482 if(gui_list->limit_scroll)
483 return;
484 gui_list->selected_item = gui_list->nb_items - gui_list->selected_size;
486 else
488 if (gui_list->title)
489 nb_lines--;
490 nb_lines-=nb_lines%gui_list->selected_size;
491 gui_list->selected_item -= nb_lines;
492 if(gui_list->selected_item < 0)
493 gui_list->selected_item = 0;
495 gui_list_put_selection_in_screen(gui_list, false);
498 void gui_list_add_item(struct gui_list * gui_list)
500 gui_list->nb_items++;
501 /* if only one item in the list, select it */
502 if(gui_list->nb_items == 1)
503 gui_list->selected_item = 0;
506 void gui_list_del_item(struct gui_list * gui_list)
508 if(gui_list->nb_items > 0)
510 gui_textarea_update_nblines(gui_list->display);
511 int nb_lines = gui_list->display->nb_lines;
513 int dist_selected_from_end = gui_list->nb_items
514 - gui_list->selected_item - 1;
515 int dist_start_from_end = gui_list->nb_items
516 - gui_list->start_item - 1;
517 if(dist_selected_from_end == 0)
519 /* Oops we are removing the selected item,
520 select the previous one */
521 gui_list->selected_item--;
523 gui_list->nb_items--;
525 /* scroll the list if needed */
526 if( (dist_start_from_end < nb_lines) && (gui_list->start_item != 0) )
527 gui_list->start_item--;
531 #ifdef HAVE_LCD_BITMAP
532 void gui_list_scroll_right(struct gui_list * gui_list)
534 /* FIXME: This is a fake right boundry limiter. there should be some
535 * callback function to find the longest item on the list in pixels,
536 * to stop the list from scrolling past that point */
537 gui_list->offset_position+=offset_step;
538 if (gui_list->offset_position > 1000)
539 gui_list->offset_position = 1000;
542 void gui_list_scroll_left(struct gui_list * gui_list)
544 gui_list->offset_position-=offset_step;
545 if (gui_list->offset_position < 0)
546 gui_list->offset_position = 0;
548 void gui_list_screen_scroll_step(int ofs)
550 offset_step = ofs;
553 void gui_list_screen_scroll_out_of_view(bool enable)
555 if (enable)
556 offset_out_of_view = true;
557 else
558 offset_out_of_view = false;
560 #endif /* HAVE_LCD_BITMAP */
562 void gui_list_set_title(struct gui_list * gui_list, char * title, ICON icon)
564 gui_list->title = title;
565 gui_list->title_icon = icon;
566 if (title) {
567 #ifdef HAVE_LCD_BITMAP
568 gui_list->display->getstringsize(title, &gui_list->title_width, NULL);
569 #else
570 gui_list->title_width = strlen(title);
571 #endif
572 } else {
573 gui_list->title_width = 0;
578 * Synchronized lists stuffs
580 void gui_synclist_init(
581 struct gui_synclist * lists,
582 list_get_name callback_get_item_name,
583 void * data,
584 bool scroll_all,
585 int selected_size
588 int i;
589 FOR_NB_SCREENS(i)
591 gui_list_init(&(lists->gui_list[i]),
592 callback_get_item_name,
593 data, scroll_all, selected_size);
594 gui_list_set_display(&(lists->gui_list[i]), &(screens[i]));
598 void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items)
600 int i;
601 FOR_NB_SCREENS(i)
603 gui_list_set_nb_items(&(lists->gui_list[i]), nb_items);
604 #ifdef HAVE_LCD_BITMAP
605 lists->gui_list[i].offset_position = 0;
606 #endif
609 int gui_synclist_get_nb_items(struct gui_synclist * lists)
611 return gui_list_get_nb_items(&((lists)->gui_list[0]));
613 int gui_synclist_get_sel_pos(struct gui_synclist * lists)
615 return gui_list_get_sel_pos(&((lists)->gui_list[0]));
617 void gui_synclist_set_icon_callback(struct gui_synclist * lists, list_get_icon icon_callback)
619 int i;
620 FOR_NB_SCREENS(i)
622 gui_list_set_icon_callback(&(lists->gui_list[i]), icon_callback);
626 void gui_synclist_draw(struct gui_synclist * lists)
628 int i;
629 FOR_NB_SCREENS(i)
630 gui_list_draw(&(lists->gui_list[i]));
633 void gui_synclist_select_item(struct gui_synclist * lists, int item_number)
635 int i;
636 FOR_NB_SCREENS(i)
637 gui_list_select_item(&(lists->gui_list[i]), item_number);
640 void gui_synclist_select_next(struct gui_synclist * lists)
642 int i;
643 FOR_NB_SCREENS(i)
644 gui_list_select_next(&(lists->gui_list[i]));
647 void gui_synclist_select_previous(struct gui_synclist * lists)
649 int i;
650 FOR_NB_SCREENS(i)
651 gui_list_select_previous(&(lists->gui_list[i]));
654 void gui_synclist_select_next_page(struct gui_synclist * lists,
655 enum screen_type screen)
657 int i;
658 FOR_NB_SCREENS(i)
659 gui_list_select_next_page(&(lists->gui_list[i]),
660 screens[screen].nb_lines);
663 void gui_synclist_select_previous_page(struct gui_synclist * lists,
664 enum screen_type screen)
666 int i;
667 FOR_NB_SCREENS(i)
668 gui_list_select_previous_page(&(lists->gui_list[i]),
669 screens[screen].nb_lines);
672 void gui_synclist_add_item(struct gui_synclist * lists)
674 int i;
675 FOR_NB_SCREENS(i)
676 gui_list_add_item(&(lists->gui_list[i]));
679 void gui_synclist_del_item(struct gui_synclist * lists)
681 int i;
682 FOR_NB_SCREENS(i)
683 gui_list_del_item(&(lists->gui_list[i]));
686 void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll)
688 int i;
689 FOR_NB_SCREENS(i)
690 gui_list_limit_scroll(&(lists->gui_list[i]), scroll);
693 void gui_synclist_set_title(struct gui_synclist * lists, char * title, ICON icon)
695 int i;
696 FOR_NB_SCREENS(i)
697 gui_list_set_title(&(lists->gui_list[i]), title, icon);
700 void gui_synclist_flash(struct gui_synclist * lists)
702 int i;
703 FOR_NB_SCREENS(i)
704 gui_list_flash(&(lists->gui_list[i]));
707 #ifdef HAVE_LCD_BITMAP
708 void gui_synclist_scroll_right(struct gui_synclist * lists)
710 int i;
711 FOR_NB_SCREENS(i)
712 gui_list_scroll_right(&(lists->gui_list[i]));
715 void gui_synclist_scroll_left(struct gui_synclist * lists)
717 int i;
718 FOR_NB_SCREENS(i)
719 gui_list_scroll_left(&(lists->gui_list[i]));
721 #endif /* HAVE_LCD_BITMAP */
723 unsigned gui_synclist_do_button(struct gui_synclist * lists, unsigned button)
725 gui_synclist_limit_scroll(lists, true);
726 switch(button)
728 case ACTION_STD_PREV:
729 gui_synclist_limit_scroll(lists, false);
731 case ACTION_STD_PREVREPEAT:
732 gui_synclist_select_previous(lists);
733 gui_synclist_draw(lists);
734 yield();
735 return ACTION_STD_PREV;
737 case ACTION_STD_NEXT:
738 gui_synclist_limit_scroll(lists, false);
740 case ACTION_STD_NEXTREPEAT:
741 gui_synclist_select_next(lists);
742 gui_synclist_draw(lists);
743 yield();
744 return ACTION_STD_NEXT;
746 #ifdef HAVE_LCD_BITMAP
747 case ACTION_TREE_PGRIGHT:
748 gui_synclist_scroll_right(lists);
749 gui_synclist_draw(lists);
750 return ACTION_TREE_PGRIGHT;
751 case ACTION_TREE_PGLEFT:
752 gui_synclist_scroll_left(lists);
753 gui_synclist_draw(lists);
754 return ACTION_TREE_PGLEFT;
755 #endif
757 /* for pgup / pgdown, we are obliged to have a different behaviour depending on the screen
758 * for which the user pressed the key since for example, remote and main screen doesn't
759 * have the same number of lines*/
760 case ACTION_LISTTREE_PGUP:
761 gui_synclist_select_previous_page(lists, SCREEN_MAIN);
762 gui_synclist_draw(lists);
763 yield();
764 return ACTION_STD_NEXT;
766 case ACTION_LISTTREE_PGDOWN:
767 gui_synclist_select_next_page(lists, SCREEN_MAIN);
768 gui_synclist_draw(lists);
769 yield();
770 return ACTION_STD_PREV;
771 #if (REMOTE_BUTTON != 0 )
772 case ACTION_LISTTREE_RC_PGUP:
773 gui_synclist_select_previous_page(lists, SCREEN_REMOTE);
774 gui_synclist_draw(lists);
775 yield();
776 return ACTION_STD_NEXT;
778 case ACTION_LISTTREE_RC_PGDOWN:
779 gui_synclist_select_next_page(lists, SCREEN_REMOTE);
780 gui_synclist_draw(lists);
781 yield();
782 return ACTION_STD_PREV;
783 #endif
785 return 0;