FS#11925 - Add a proper system to keep track of the current screen/activity to make...
[maemo-rb.git] / apps / gui / skin_engine / skin_render.c
blob349dc07d0acc92649e6c76dc4691efb39e880f98
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
10 * Copyright (C) 2010 Jonathan Gordon
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 <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdbool.h>
26 #include <ctype.h>
27 #include "strlcat.h"
29 #include "config.h"
30 #include "kernel.h"
31 #ifdef HAVE_ALBUMART
32 #include "albumart.h"
33 #endif
34 #include "skin_display.h"
35 #include "skin_engine.h"
36 #include "skin_parser.h"
37 #include "tag_table.h"
38 #include "skin_scan.h"
39 #if CONFIG_TUNER
40 #include "radio.h"
41 #endif
42 #include "viewport.h"
43 #include "cuesheet.h"
44 #include "language.h"
45 #include "playback.h"
46 #include "playlist.h"
47 #include "root_menu.h"
48 #include "misc.h"
51 #define MAX_LINE 1024
53 struct skin_draw_info {
54 struct gui_wps *gwps;
55 struct skin_viewport *skin_vp;
56 int line_number;
57 unsigned long refresh_type;
59 char* cur_align_start;
60 struct align_pos align;
61 bool no_line_break;
62 bool line_scrolls;
63 bool force_redraw;
65 char *buf;
66 size_t buf_size;
68 int offset; /* used by the playlist viewer */
71 typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
72 bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
74 #ifdef HAVE_LCD_BITMAP
75 static void skin_render_playlistviewer(struct playlistviewer* viewer,
76 struct gui_wps *gwps,
77 struct skin_viewport* skin_viewport,
78 unsigned long refresh_type);
79 #endif
81 static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
82 struct skin_element *element, struct viewport* vp)
84 #ifndef HAVE_LCD_BITMAP
85 (void)vp; /* silence warnings */
86 (void)info;
87 #endif
88 struct wps_token *token = (struct wps_token *)element->data;
90 #ifdef HAVE_LCD_BITMAP
91 struct wps_data *data = gwps->data;
92 bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
93 #endif
94 switch (token->type)
96 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
97 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
99 struct viewport_colour *col = token->value.data;
100 col->vp->fg_pattern = col->colour;
102 break;
103 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
105 struct viewport_colour *col = token->value.data;
106 col->vp->bg_pattern = col->colour;
108 break;
109 #endif
110 case SKIN_TOKEN_VIEWPORT_ENABLE:
112 char *label = token->value.data;
113 char temp = VP_DRAW_HIDEABLE;
114 struct skin_element *viewport = gwps->data->tree;
115 while (viewport)
117 struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data;
118 if (skinvp->label && !skinvp->is_infovp &&
119 !strcmp(skinvp->label, label))
121 if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
123 temp |= VP_DRAW_WASHIDDEN;
125 skinvp->hidden_flags = temp;
127 viewport = viewport->next;
130 break;
131 #ifdef HAVE_LCD_BITMAP
132 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
133 sb_set_info_vp(gwps->display->screen_type,
134 token->value.data);
135 break;
136 case SKIN_TOKEN_PEAKMETER:
137 data->peak_meter_enabled = true;
138 if (do_refresh)
139 draw_peakmeters(gwps, info->line_number, vp);
140 break;
141 #endif
142 #ifdef HAVE_LCD_BITMAP
143 case SKIN_TOKEN_PEAKMETER_LEFTBAR:
144 case SKIN_TOKEN_PEAKMETER_RIGHTBAR:
145 data->peak_meter_enabled = true;
146 /* fall through to the progressbar code */
147 #endif
148 case SKIN_TOKEN_VOLUMEBAR:
149 case SKIN_TOKEN_BATTERY_PERCENTBAR:
150 #ifdef HAVE_LCD_BITMAP
151 case SKIN_TOKEN_PROGRESSBAR:
152 case SKIN_TOKEN_TUNER_RSSI_BAR:
154 struct progressbar *bar = (struct progressbar*)token->value.data;
155 if (do_refresh)
156 draw_progressbar(gwps, info->line_number, bar);
158 #endif
159 break;
160 #ifdef HAVE_LCD_BITMAP
161 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
162 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
164 struct image_display *id = token->value.data;
165 const char* label = id->label;
166 struct gui_img *img = skin_find_item(label,SKIN_FIND_IMAGE, data);
167 if (img && img->loaded)
169 if (id->token == NULL)
171 img->display = id->subimage;
173 else
175 char buf[16];
176 const char *out;
177 int a = img->num_subimages;
178 out = get_token_value(gwps, id->token, info->offset,
179 buf, sizeof(buf), &a);
181 /* NOTE: get_token_value() returns values starting at 1! */
182 if (a == -1)
183 a = (out && *out) ? 1 : 2;
184 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
185 a -= 2; /* 2 is added in statusbar-skinned.c! */
186 else
187 a--;
188 a += id->offset;
190 /* Clear the image, as in conditionals */
191 clear_image_pos(gwps, img);
193 /* If the token returned a value which is higher than
194 * the amount of subimages, don't draw it. */
195 if (a >= 0 && a < img->num_subimages)
197 img->display = a;
201 break;
203 #ifdef HAVE_ALBUMART
204 case SKIN_TOKEN_ALBUMART_DISPLAY:
205 /* now draw the AA */
206 if (do_refresh && data->albumart)
208 int handle = playback_current_aa_hid(data->playback_aa_slot);
209 #if CONFIG_TUNER
210 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
212 struct dim dim = {data->albumart->width, data->albumart->height};
213 handle = radio_get_art_hid(&dim);
215 #endif
216 data->albumart->draw_handle = handle;
218 break;
219 #endif
220 case SKIN_TOKEN_DRAW_INBUILTBAR:
221 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
222 info->refresh_type == SKIN_REFRESH_ALL,
223 token->value.data);
224 break;
225 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
226 if (do_refresh)
227 skin_render_playlistviewer(token->value.data, gwps,
228 info->skin_vp, info->refresh_type);
229 break;
231 #endif /* HAVE_LCD_BITMAP */
232 #ifdef HAVE_SKIN_VARIABLES
233 case SKIN_TOKEN_VAR_SET:
234 if (do_refresh)
236 struct skin_var_changer *data = token->value.data;
237 if (data->direct)
238 data->var->value = data->newval;
239 else
241 data->var->value += data->newval;
242 if (data->max)
244 if (data->var->value > data->max)
245 data->var->value = 1;
246 else if (data->var->value < 1)
247 data->var->value = data->max;
250 if (data->var->value < 1)
251 data->var->value = 1;
252 data->var->last_changed = current_tick;
254 break;
255 #endif
256 default:
257 return false;
259 return true;
264 static void do_tags_in_hidden_conditional(struct skin_element* branch,
265 struct skin_draw_info *info)
267 #ifdef HAVE_LCD_BITMAP
268 struct gui_wps *gwps = info->gwps;
269 struct wps_data *data = gwps->data;
270 #endif
271 /* Tags here are ones which need to be "turned off" or cleared
272 * if they are in a conditional branch which isnt being used */
273 if (branch->type == LINE_ALTERNATOR)
275 int i;
276 for (i=0; i<branch->children_count; i++)
278 do_tags_in_hidden_conditional(branch->children[i], info);
281 else if (branch->type == LINE && branch->children_count)
283 struct skin_element *child = branch->children[0];
284 struct wps_token *token;
285 while (child)
287 if (child->type == CONDITIONAL)
289 int i;
290 for (i=0; i<child->children_count; i++)
292 do_tags_in_hidden_conditional(child->children[i], info);
294 child = child->next;
295 continue;
297 else if (child->type != TAG || !child->data)
299 child = child->next;
300 continue;
302 token = (struct wps_token *)child->data;
303 #ifdef HAVE_LCD_BITMAP
304 /* clear all pictures in the conditional and nested ones */
305 if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
307 struct image_display *id = token->value.data;
308 struct gui_img *img = skin_find_item(id->label,
309 SKIN_FIND_IMAGE, data);
310 clear_image_pos(gwps, img);
312 else if (token->type == SKIN_TOKEN_PEAKMETER)
314 data->peak_meter_enabled = false;
316 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
318 char *label = token->value.data;
319 struct skin_element *viewport;
320 for (viewport = data->tree;
321 viewport;
322 viewport = viewport->next)
324 struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data;
325 if (skin_viewport->label && strcmp(skin_viewport->label, label))
326 continue;
327 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
329 continue;
331 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
333 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
334 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
335 else
337 gwps->display->set_viewport(&skin_viewport->vp);
338 gwps->display->clear_viewport();
339 gwps->display->scroll_stop(&skin_viewport->vp);
340 gwps->display->set_viewport(&info->skin_vp->vp);
341 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
346 #endif
347 #ifdef HAVE_ALBUMART
348 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
350 draw_album_art(gwps,
351 playback_current_aa_hid(data->playback_aa_slot), true);
353 #endif
354 child = child->next;
359 static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
361 struct align_pos *align = &info->align;
362 char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
363 switch (element->tag->type)
365 case SKIN_TOKEN_ALIGN_LEFT:
366 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
367 align->left = cur_pos;
368 info->cur_align_start = cur_pos;
369 break;
370 case SKIN_TOKEN_ALIGN_LEFT_RTL:
371 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
372 if (lang_is_rtl())
373 align->right = cur_pos;
374 else
375 align->left = cur_pos;
376 info->cur_align_start = cur_pos;
377 break;
378 case SKIN_TOKEN_ALIGN_CENTER:
379 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
380 align->center = cur_pos;
381 info->cur_align_start = cur_pos;
382 break;
383 case SKIN_TOKEN_ALIGN_RIGHT:
384 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
385 align->right = cur_pos;
386 info->cur_align_start = cur_pos;
387 break;
388 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
389 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
390 if (lang_is_rtl())
391 align->left = cur_pos;
392 else
393 align->right = cur_pos;
394 info->cur_align_start = cur_pos;
395 break;
396 default:
397 break;
401 /* Draw a LINE element onto the display */
402 static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
404 bool needs_update = false;
405 int last_value, value;
407 if (line->children_count == 0)
408 return false; /* empty line, do nothing */
410 struct skin_element *child = line->children[0];
411 struct conditional *conditional;
412 skin_render_func func = skin_render_line;
413 int old_refresh_mode = info->refresh_type;
414 while (child)
416 switch (child->type)
418 case CONDITIONAL:
419 conditional = (struct conditional*)child->data;
420 last_value = conditional->last_value;
421 value = evaluate_conditional(info->gwps, info->offset,
422 conditional, child->children_count);
423 conditional->last_value = value;
424 if (child->children_count == 1)
426 /* special handling so
427 * %?aa<true> and %?<true|false> need special handlng here */
429 if (value == -1) /* tag is false */
431 /* we are in a false branch of a %?aa<true> conditional */
432 if (last_value == 0)
433 do_tags_in_hidden_conditional(child->children[0], info);
434 break;
437 else
439 if (last_value >= 0 && value != last_value && last_value < child->children_count)
440 do_tags_in_hidden_conditional(child->children[last_value], info);
442 if (child->children[value]->type == LINE_ALTERNATOR)
444 func = skin_render_alternator;
446 else if (child->children[value]->type == LINE)
447 func = skin_render_line;
449 if (value != last_value)
451 info->refresh_type = SKIN_REFRESH_ALL;
452 info->force_redraw = true;
455 if (func(child->children[value], info))
456 needs_update = true;
457 else
458 needs_update = needs_update || (last_value != value);
460 info->refresh_type = old_refresh_mode;
461 break;
462 case TAG:
463 if (child->tag->flags & NOBREAK)
464 info->no_line_break = true;
465 if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
466 info->line_scrolls = true;
468 fix_line_alignment(info, child);
470 if (!child->data)
472 break;
474 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
476 static char tempbuf[128];
477 const char *value = get_token_value(info->gwps, child->data,
478 info->offset, tempbuf,
479 sizeof(tempbuf), NULL);
480 if (value)
482 #if CONFIG_RTC
483 if (child->tag->flags&SKIN_RTC_REFRESH)
484 needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
485 #endif
486 needs_update = needs_update ||
487 ((child->tag->flags&info->refresh_type)!=0);
488 strlcat(info->cur_align_start, value,
489 info->buf_size - (info->cur_align_start-info->buf));
492 break;
493 case TEXT:
494 strlcat(info->cur_align_start, child->data,
495 info->buf_size - (info->cur_align_start-info->buf));
496 needs_update = needs_update ||
497 (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
498 break;
499 case COMMENT:
500 default:
501 break;
504 child = child->next;
506 return needs_update;
509 static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
511 struct skin_element *element=line;
512 struct wps_token *token;
513 int retval = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
514 if (element->type == LINE)
516 if (element->children_count == 0)
517 return retval; /* empty line, so force redraw */
518 element = element->children[0];
520 while (element)
522 if (element->type == TAG &&
523 element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
525 token = element->data;
526 return token->value.i;
528 else if (element->type == CONDITIONAL)
530 struct conditional *conditional = element->data;
531 int val = evaluate_conditional(gwps, 0, conditional,
532 element->children_count);
533 if (val >= 0)
535 retval = get_subline_timeout(gwps, element->children[val]);
536 if (retval >= 0)
537 return retval;
540 element = element->next;
542 return retval;
545 bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
547 bool changed_lines = false;
548 struct line_alternator *alternator = (struct line_alternator*)element->data;
549 unsigned old_refresh = info->refresh_type;
550 if (info->refresh_type == SKIN_REFRESH_ALL)
552 alternator->current_line = element->children_count-1;
553 changed_lines = true;
555 else if (TIME_AFTER(current_tick, alternator->next_change_tick))
557 changed_lines = true;
560 if (changed_lines)
562 struct skin_element *current_line = element->children[alternator->current_line];
563 int start = alternator->current_line;
564 int try_line = start;
565 bool suitable = false;
566 int rettimeout = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
568 /* find a subline which has at least one token in it,
569 * and that line doesnt have a timeout set to 0 through conditionals */
570 do {
571 try_line++;
572 if (try_line >= element->children_count)
573 try_line = 0;
574 if (element->children[try_line]->children_count != 0)
576 current_line = element->children[try_line];
577 rettimeout = get_subline_timeout(info->gwps,
578 current_line->children[0]);
579 if (rettimeout > 0)
581 suitable = true;
585 while (try_line != start && !suitable);
587 if (suitable)
589 alternator->current_line = try_line;
590 alternator->next_change_tick = current_tick + rettimeout;
593 info->refresh_type = SKIN_REFRESH_ALL;
594 info->force_redraw = true;
596 bool ret = skin_render_line(element->children[alternator->current_line], info);
597 info->refresh_type = old_refresh;
598 return changed_lines || ret;
601 static void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
602 struct skin_viewport* skin_viewport, unsigned long refresh_type)
604 struct screen *display = gwps->display;
605 char linebuf[MAX_LINE];
606 skin_render_func func = skin_render_line;
607 struct skin_element* line = viewport;
608 struct skin_draw_info info = {
609 .gwps = gwps,
610 .buf = linebuf,
611 .buf_size = sizeof(linebuf),
612 .line_number = 0,
613 .no_line_break = false,
614 .line_scrolls = false,
615 .refresh_type = refresh_type,
616 .skin_vp = skin_viewport,
617 .offset = 0
620 struct align_pos * align = &info.align;
621 bool needs_update;
622 #ifdef HAVE_LCD_BITMAP
623 /* Set images to not to be displayed */
624 struct skin_token_list *imglist = gwps->data->images;
625 while (imglist)
627 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
628 img->display = -1;
629 imglist = imglist->next;
631 #endif
633 while (line)
635 linebuf[0] = '\0';
636 info.no_line_break = false;
637 info.line_scrolls = false;
638 info.force_redraw = false;
640 info.cur_align_start = info.buf;
641 align->left = info.buf;
642 align->center = NULL;
643 align->right = NULL;
646 if (line->type == LINE_ALTERNATOR)
647 func = skin_render_alternator;
648 else if (line->type == LINE)
649 func = skin_render_line;
651 needs_update = func(line, &info);
652 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
653 if (skin_viewport->vp.fg_pattern != skin_viewport->start_fgcolour ||
654 skin_viewport->vp.bg_pattern != skin_viewport->start_bgcolour)
656 /* 2bit lcd drivers need lcd_set_viewport() to be called to change
657 * the colour, 16bit doesnt. But doing this makes static text
658 * get the new colour also */
659 needs_update = true;
660 display->set_viewport(&skin_viewport->vp);
662 #endif
663 /* only update if the line needs to be, and there is something to write */
664 if (refresh_type && needs_update)
666 if (info.line_scrolls)
668 /* if the line is a scrolling one we don't want to update
669 too often, so that it has the time to scroll */
670 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
671 write_line(display, align, info.line_number, true);
673 else
674 write_line(display, align, info.line_number, false);
676 if (!info.no_line_break)
677 info.line_number++;
678 line = line->next;
680 #ifdef HAVE_LCD_BITMAP
681 wps_display_images(gwps, &skin_viewport->vp);
682 #endif
685 void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
687 struct wps_data *data = gwps->data;
688 struct screen *display = gwps->display;
690 struct skin_element* viewport = data->tree;
691 struct skin_viewport* skin_viewport;
693 int old_refresh_mode = refresh_mode;
695 #ifdef HAVE_LCD_CHARCELLS
696 int i;
697 for (i = 0; i < 8; i++)
699 if (data->wps_progress_pat[i] == 0)
700 data->wps_progress_pat[i] = display->get_locked_pattern();
702 #endif
703 viewport = data->tree;
704 skin_viewport = (struct skin_viewport *)viewport->data;
705 if (skin_viewport->label && viewport->next &&
706 !strcmp(skin_viewport->label,VP_DEFAULT_LABEL))
707 refresh_mode = 0;
709 for (viewport = data->tree;
710 viewport;
711 viewport = viewport->next)
713 /* SETUP */
714 skin_viewport = (struct skin_viewport*)viewport->data;
715 unsigned vp_refresh_mode = refresh_mode;
716 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
717 skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
718 skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
719 #endif
721 /* dont redraw the viewport if its disabled */
722 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
723 { /* don't draw anything into this one */
724 vp_refresh_mode = 0;
726 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
728 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
729 continue;
731 else if (((skin_viewport->hidden_flags&
732 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
733 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
735 vp_refresh_mode = SKIN_REFRESH_ALL;
736 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
739 display->set_viewport(&skin_viewport->vp);
740 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
742 display->clear_viewport();
744 /* render */
745 if (viewport->children_count)
746 skin_render_viewport(viewport->children[0], gwps,
747 skin_viewport, vp_refresh_mode);
748 refresh_mode = old_refresh_mode;
751 /* Restore the default viewport */
752 display->set_viewport(NULL);
753 display->update();
756 #ifdef HAVE_LCD_BITMAP
757 static __attribute__((noinline)) void skin_render_playlistviewer(struct playlistviewer* viewer,
758 struct gui_wps *gwps,
759 struct skin_viewport* skin_viewport,
760 unsigned long refresh_type)
762 struct screen *display = gwps->display;
763 char linebuf[MAX_LINE];
764 skin_render_func func = skin_render_line;
765 struct skin_element* line;
766 struct skin_draw_info info = {
767 .gwps = gwps,
768 .buf = linebuf,
769 .buf_size = sizeof(linebuf),
770 .line_number = 0,
771 .no_line_break = false,
772 .line_scrolls = false,
773 .refresh_type = refresh_type,
774 .skin_vp = skin_viewport,
775 .offset = viewer->start_offset
778 struct align_pos * align = &info.align;
779 bool needs_update;
780 int cur_pos, start_item, max;
781 int nb_lines = viewport_get_nb_lines(viewer->vp);
782 #if CONFIG_TUNER
783 if (get_current_activity() == ACTIVITY_FM)
785 cur_pos = radio_current_preset();
786 start_item = cur_pos + viewer->start_offset;
787 max = start_item+radio_preset_count();
789 else
790 #endif
792 struct cuesheet *cue = skin_get_global_state()->id3 ?
793 skin_get_global_state()->id3->cuesheet : NULL;
794 cur_pos = playlist_get_display_index();
795 max = playlist_amount()+1;
796 if (cue)
797 max += cue->track_count;
798 start_item = MAX(0, cur_pos + viewer->start_offset);
800 if (max-start_item > nb_lines)
801 max = start_item + nb_lines;
803 line = viewer->line;
804 while (start_item < max)
806 linebuf[0] = '\0';
807 info.no_line_break = false;
808 info.line_scrolls = false;
809 info.force_redraw = false;
811 info.cur_align_start = info.buf;
812 align->left = info.buf;
813 align->center = NULL;
814 align->right = NULL;
817 if (line->type == LINE_ALTERNATOR)
818 func = skin_render_alternator;
819 else if (line->type == LINE)
820 func = skin_render_line;
822 needs_update = func(line, &info);
824 /* only update if the line needs to be, and there is something to write */
825 if (refresh_type && needs_update)
827 if (info.line_scrolls)
829 /* if the line is a scrolling one we don't want to update
830 too often, so that it has the time to scroll */
831 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
832 write_line(display, align, info.line_number, true);
834 else
835 write_line(display, align, info.line_number, false);
837 info.line_number++;
838 info.offset++;
839 start_item++;
842 #endif