New skin tags: %Vs() to set the text style and %Vg() to get the viewports gradient...
[maemo-rb.git] / apps / gui / skin_engine / skin_render.c
blob27e6747c2921ea53f732fb220981c67f120e2c1a
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;
58 unsigned text_style;
60 char* cur_align_start;
61 struct align_pos align;
62 bool no_line_break;
63 bool line_scrolls;
64 bool force_redraw;
66 char *buf;
67 size_t buf_size;
69 int offset; /* used by the playlist viewer */
72 typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
73 bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
75 #ifdef HAVE_LCD_BITMAP
76 static void skin_render_playlistviewer(struct playlistviewer* viewer,
77 struct gui_wps *gwps,
78 struct skin_viewport* skin_viewport,
79 unsigned long refresh_type);
80 #endif
82 static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
83 struct skin_element *element, struct viewport* vp)
85 #ifndef HAVE_LCD_BITMAP
86 (void)vp; /* silence warnings */
87 (void)info;
88 #endif
89 struct wps_token *token = (struct wps_token *)element->data;
91 #ifdef HAVE_LCD_BITMAP
92 struct wps_data *data = gwps->data;
93 bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
94 #endif
95 switch (token->type)
97 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
98 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
100 struct viewport_colour *col = token->value.data;
101 col->vp->fg_pattern = col->colour;
103 break;
104 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
106 struct viewport_colour *col = token->value.data;
107 col->vp->bg_pattern = col->colour;
109 break;
110 case SKIN_TOKEN_VIEWPORT_TEXTSTYLE:
111 info->text_style = token->value.l;
112 break;
113 #endif
114 #ifdef HAVE_LCD_COLOR
115 case SKIN_TOKEN_VIEWPORT_GRADIENT_SETUP:
117 struct gradient_config *cfg = token->value.data;
118 vp->lss_pattern = cfg->start;
119 vp->lse_pattern = cfg->end;
120 vp->lst_pattern = cfg->text;
122 break;
123 #endif
124 case SKIN_TOKEN_VIEWPORT_ENABLE:
126 char *label = token->value.data;
127 char temp = VP_DRAW_HIDEABLE;
128 struct skin_element *viewport = gwps->data->tree;
129 while (viewport)
131 struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data;
132 if (skinvp->label && !skinvp->is_infovp &&
133 !strcmp(skinvp->label, label))
135 if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
137 temp |= VP_DRAW_WASHIDDEN;
139 skinvp->hidden_flags = temp;
141 viewport = viewport->next;
144 break;
145 #ifdef HAVE_LCD_BITMAP
146 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
147 sb_set_info_vp(gwps->display->screen_type,
148 token->value.data);
149 break;
150 case SKIN_TOKEN_PEAKMETER:
151 data->peak_meter_enabled = true;
152 if (do_refresh)
153 draw_peakmeters(gwps, info->line_number, vp);
154 break;
155 #endif
156 #ifdef HAVE_LCD_BITMAP
157 case SKIN_TOKEN_PEAKMETER_LEFTBAR:
158 case SKIN_TOKEN_PEAKMETER_RIGHTBAR:
159 data->peak_meter_enabled = true;
160 /* fall through to the progressbar code */
161 #endif
162 case SKIN_TOKEN_VOLUMEBAR:
163 case SKIN_TOKEN_BATTERY_PERCENTBAR:
164 #ifdef HAVE_LCD_BITMAP
165 case SKIN_TOKEN_PROGRESSBAR:
166 case SKIN_TOKEN_TUNER_RSSI_BAR:
168 struct progressbar *bar = (struct progressbar*)token->value.data;
169 if (do_refresh)
170 draw_progressbar(gwps, info->line_number, bar);
172 #endif
173 break;
174 #ifdef HAVE_LCD_BITMAP
175 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
176 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
178 struct image_display *id = token->value.data;
179 const char* label = id->label;
180 struct gui_img *img = skin_find_item(label,SKIN_FIND_IMAGE, data);
181 if (img && img->loaded)
183 if (id->token == NULL)
185 img->display = id->subimage;
187 else
189 char buf[16];
190 const char *out;
191 int a = img->num_subimages;
192 out = get_token_value(gwps, id->token, info->offset,
193 buf, sizeof(buf), &a);
195 /* NOTE: get_token_value() returns values starting at 1! */
196 if (a == -1)
197 a = (out && *out) ? 1 : 2;
198 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
199 a -= 2; /* 2 is added in statusbar-skinned.c! */
200 else
201 a--;
202 a += id->offset;
204 /* Clear the image, as in conditionals */
205 clear_image_pos(gwps, img);
207 /* If the token returned a value which is higher than
208 * the amount of subimages, don't draw it. */
209 if (a >= 0 && a < img->num_subimages)
211 img->display = a;
215 break;
217 #ifdef HAVE_ALBUMART
218 case SKIN_TOKEN_ALBUMART_DISPLAY:
219 /* now draw the AA */
220 if (do_refresh && data->albumart)
222 int handle = playback_current_aa_hid(data->playback_aa_slot);
223 #if CONFIG_TUNER
224 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
226 struct dim dim = {data->albumart->width, data->albumart->height};
227 handle = radio_get_art_hid(&dim);
229 #endif
230 data->albumart->draw_handle = handle;
232 break;
233 #endif
234 case SKIN_TOKEN_DRAW_INBUILTBAR:
235 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
236 info->refresh_type == SKIN_REFRESH_ALL,
237 token->value.data);
238 break;
239 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
240 if (do_refresh)
241 skin_render_playlistviewer(token->value.data, gwps,
242 info->skin_vp, info->refresh_type);
243 break;
245 #endif /* HAVE_LCD_BITMAP */
246 #ifdef HAVE_SKIN_VARIABLES
247 case SKIN_TOKEN_VAR_SET:
248 if (do_refresh)
250 struct skin_var_changer *data = token->value.data;
251 if (data->direct)
252 data->var->value = data->newval;
253 else
255 data->var->value += data->newval;
256 if (data->max)
258 if (data->var->value > data->max)
259 data->var->value = 1;
260 else if (data->var->value < 1)
261 data->var->value = data->max;
264 if (data->var->value < 1)
265 data->var->value = 1;
266 data->var->last_changed = current_tick;
268 break;
269 #endif
270 default:
271 return false;
273 return true;
278 static void do_tags_in_hidden_conditional(struct skin_element* branch,
279 struct skin_draw_info *info)
281 #ifdef HAVE_LCD_BITMAP
282 struct gui_wps *gwps = info->gwps;
283 struct wps_data *data = gwps->data;
284 #endif
285 /* Tags here are ones which need to be "turned off" or cleared
286 * if they are in a conditional branch which isnt being used */
287 if (branch->type == LINE_ALTERNATOR)
289 int i;
290 for (i=0; i<branch->children_count; i++)
292 do_tags_in_hidden_conditional(branch->children[i], info);
295 else if (branch->type == LINE && branch->children_count)
297 struct skin_element *child = branch->children[0];
298 struct wps_token *token;
299 while (child)
301 if (child->type == CONDITIONAL)
303 int i;
304 for (i=0; i<child->children_count; i++)
306 do_tags_in_hidden_conditional(child->children[i], info);
308 child = child->next;
309 continue;
311 else if (child->type != TAG || !child->data)
313 child = child->next;
314 continue;
316 token = (struct wps_token *)child->data;
317 #ifdef HAVE_LCD_BITMAP
318 /* clear all pictures in the conditional and nested ones */
319 if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
321 struct image_display *id = token->value.data;
322 struct gui_img *img = skin_find_item(id->label,
323 SKIN_FIND_IMAGE, data);
324 clear_image_pos(gwps, img);
326 else if (token->type == SKIN_TOKEN_PEAKMETER)
328 data->peak_meter_enabled = false;
330 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
332 char *label = token->value.data;
333 struct skin_element *viewport;
334 for (viewport = data->tree;
335 viewport;
336 viewport = viewport->next)
338 struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data;
339 if (skin_viewport->label && strcmp(skin_viewport->label, label))
340 continue;
341 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
343 continue;
345 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
347 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
348 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
349 else
351 gwps->display->set_viewport(&skin_viewport->vp);
352 gwps->display->clear_viewport();
353 gwps->display->scroll_stop(&skin_viewport->vp);
354 gwps->display->set_viewport(&info->skin_vp->vp);
355 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
360 #endif
361 #ifdef HAVE_ALBUMART
362 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
364 draw_album_art(gwps,
365 playback_current_aa_hid(data->playback_aa_slot), true);
367 #endif
368 child = child->next;
373 static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
375 struct align_pos *align = &info->align;
376 char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
377 switch (element->tag->type)
379 case SKIN_TOKEN_ALIGN_LEFT:
380 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
381 align->left = cur_pos;
382 info->cur_align_start = cur_pos;
383 break;
384 case SKIN_TOKEN_ALIGN_LEFT_RTL:
385 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
386 if (lang_is_rtl())
387 align->right = cur_pos;
388 else
389 align->left = cur_pos;
390 info->cur_align_start = cur_pos;
391 break;
392 case SKIN_TOKEN_ALIGN_CENTER:
393 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
394 align->center = cur_pos;
395 info->cur_align_start = cur_pos;
396 break;
397 case SKIN_TOKEN_ALIGN_RIGHT:
398 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
399 align->right = cur_pos;
400 info->cur_align_start = cur_pos;
401 break;
402 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
403 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
404 if (lang_is_rtl())
405 align->left = cur_pos;
406 else
407 align->right = cur_pos;
408 info->cur_align_start = cur_pos;
409 break;
410 default:
411 break;
415 /* Draw a LINE element onto the display */
416 static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
418 bool needs_update = false;
419 int last_value, value;
421 if (line->children_count == 0)
422 return false; /* empty line, do nothing */
424 struct skin_element *child = line->children[0];
425 struct conditional *conditional;
426 skin_render_func func = skin_render_line;
427 int old_refresh_mode = info->refresh_type;
428 while (child)
430 switch (child->type)
432 case CONDITIONAL:
433 conditional = (struct conditional*)child->data;
434 last_value = conditional->last_value;
435 value = evaluate_conditional(info->gwps, info->offset,
436 conditional, child->children_count);
437 conditional->last_value = value;
438 if (child->children_count == 1)
440 /* special handling so
441 * %?aa<true> and %?<true|false> need special handlng here */
443 if (value == -1) /* tag is false */
445 /* we are in a false branch of a %?aa<true> conditional */
446 if (last_value == 0)
447 do_tags_in_hidden_conditional(child->children[0], info);
448 break;
451 else
453 if (last_value >= 0 && value != last_value && last_value < child->children_count)
454 do_tags_in_hidden_conditional(child->children[last_value], info);
456 if (child->children[value]->type == LINE_ALTERNATOR)
458 func = skin_render_alternator;
460 else if (child->children[value]->type == LINE)
461 func = skin_render_line;
463 if (value != last_value)
465 info->refresh_type = SKIN_REFRESH_ALL;
466 info->force_redraw = true;
469 if (func(child->children[value], info))
470 needs_update = true;
471 else
472 needs_update = needs_update || (last_value != value);
474 info->refresh_type = old_refresh_mode;
475 break;
476 case TAG:
477 if (child->tag->flags & NOBREAK)
478 info->no_line_break = true;
479 if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
480 info->line_scrolls = true;
482 fix_line_alignment(info, child);
484 if (!child->data)
486 break;
488 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
490 static char tempbuf[128];
491 const char *value = get_token_value(info->gwps, child->data,
492 info->offset, tempbuf,
493 sizeof(tempbuf), NULL);
494 if (value)
496 #if CONFIG_RTC
497 if (child->tag->flags&SKIN_RTC_REFRESH)
498 needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
499 #endif
500 needs_update = needs_update ||
501 ((child->tag->flags&info->refresh_type)!=0);
502 strlcat(info->cur_align_start, value,
503 info->buf_size - (info->cur_align_start-info->buf));
506 break;
507 case TEXT:
508 strlcat(info->cur_align_start, child->data,
509 info->buf_size - (info->cur_align_start-info->buf));
510 needs_update = needs_update ||
511 (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
512 break;
513 case COMMENT:
514 default:
515 break;
518 child = child->next;
520 return needs_update;
523 static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
525 struct skin_element *element=line;
526 struct wps_token *token;
527 int retval = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
528 if (element->type == LINE)
530 if (element->children_count == 0)
531 return retval; /* empty line, so force redraw */
532 element = element->children[0];
534 while (element)
536 if (element->type == TAG &&
537 element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
539 token = element->data;
540 return token->value.i;
542 else if (element->type == CONDITIONAL)
544 struct conditional *conditional = element->data;
545 int val = evaluate_conditional(gwps, 0, conditional,
546 element->children_count);
547 if (val >= 0)
549 retval = get_subline_timeout(gwps, element->children[val]);
550 if (retval >= 0)
551 return retval;
554 element = element->next;
556 return retval;
559 bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
561 bool changed_lines = false;
562 struct line_alternator *alternator = (struct line_alternator*)element->data;
563 unsigned old_refresh = info->refresh_type;
564 if (info->refresh_type == SKIN_REFRESH_ALL)
566 alternator->current_line = element->children_count-1;
567 changed_lines = true;
569 else if (TIME_AFTER(current_tick, alternator->next_change_tick))
571 changed_lines = true;
574 if (changed_lines)
576 struct skin_element *current_line;
577 int start = alternator->current_line;
578 int try_line = start;
579 bool suitable = false;
580 int rettimeout = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
582 /* find a subline which has at least one token in it,
583 * and that line doesnt have a timeout set to 0 through conditionals */
584 do {
585 try_line++;
586 if (try_line >= element->children_count)
587 try_line = 0;
588 if (element->children[try_line]->children_count != 0)
590 current_line = element->children[try_line];
591 rettimeout = get_subline_timeout(info->gwps,
592 current_line->children[0]);
593 if (rettimeout > 0)
595 suitable = true;
599 while (try_line != start && !suitable);
601 if (suitable)
603 alternator->current_line = try_line;
604 alternator->next_change_tick = current_tick + rettimeout;
607 info->refresh_type = SKIN_REFRESH_ALL;
608 info->force_redraw = true;
610 bool ret = skin_render_line(element->children[alternator->current_line], info);
611 info->refresh_type = old_refresh;
612 return changed_lines || ret;
615 static void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
616 struct skin_viewport* skin_viewport, unsigned long refresh_type)
618 struct screen *display = gwps->display;
619 char linebuf[MAX_LINE];
620 skin_render_func func = skin_render_line;
621 struct skin_element* line = viewport;
622 struct skin_draw_info info = {
623 .gwps = gwps,
624 .buf = linebuf,
625 .buf_size = sizeof(linebuf),
626 .line_number = 0,
627 .no_line_break = false,
628 .line_scrolls = false,
629 .refresh_type = refresh_type,
630 .skin_vp = skin_viewport,
631 .offset = 0,
632 .text_style = STYLE_DEFAULT
635 struct align_pos * align = &info.align;
636 bool needs_update;
637 #ifdef HAVE_LCD_BITMAP
638 /* Set images to not to be displayed */
639 struct skin_token_list *imglist = gwps->data->images;
640 while (imglist)
642 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
643 img->display = -1;
644 imglist = imglist->next;
646 #endif
648 while (line)
650 linebuf[0] = '\0';
651 info.no_line_break = false;
652 info.line_scrolls = false;
653 info.force_redraw = false;
654 #ifdef HAVE_LCD_COLOR
655 if (info.text_style&STYLE_GRADIENT)
657 int cur = CURLN_UNPACK(info.text_style);
658 int num = NUMLN_UNPACK(info.text_style);
659 if (cur+1 == num)
660 info.text_style = STYLE_DEFAULT;
661 else
662 info.text_style = STYLE_GRADIENT|CURLN_PACK(cur+1)|NUMLN_PACK(num);
664 #endif
665 info.cur_align_start = info.buf;
666 align->left = info.buf;
667 align->center = NULL;
668 align->right = NULL;
671 if (line->type == LINE_ALTERNATOR)
672 func = skin_render_alternator;
673 else if (line->type == LINE)
674 func = skin_render_line;
676 needs_update = func(line, &info);
677 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
678 if (skin_viewport->vp.fg_pattern != skin_viewport->start_fgcolour ||
679 skin_viewport->vp.bg_pattern != skin_viewport->start_bgcolour)
681 /* 2bit lcd drivers need lcd_set_viewport() to be called to change
682 * the colour, 16bit doesnt. But doing this makes static text
683 * get the new colour also */
684 needs_update = true;
685 display->set_viewport(&skin_viewport->vp);
687 #endif
688 /* only update if the line needs to be, and there is something to write */
689 if (refresh_type && needs_update)
691 if (info.line_scrolls)
693 /* if the line is a scrolling one we don't want to update
694 too often, so that it has the time to scroll */
695 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
696 write_line(display, align, info.line_number, true, info.text_style);
698 else
699 write_line(display, align, info.line_number, false, info.text_style);
701 if (!info.no_line_break)
702 info.line_number++;
703 line = line->next;
705 #ifdef HAVE_LCD_BITMAP
706 wps_display_images(gwps, &skin_viewport->vp);
707 #endif
710 void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
712 struct wps_data *data = gwps->data;
713 struct screen *display = gwps->display;
715 struct skin_element* viewport;
716 struct skin_viewport* skin_viewport;
718 int old_refresh_mode = refresh_mode;
720 #ifdef HAVE_LCD_CHARCELLS
721 int i;
722 for (i = 0; i < 8; i++)
724 if (data->wps_progress_pat[i] == 0)
725 data->wps_progress_pat[i] = display->get_locked_pattern();
727 #endif
728 viewport = data->tree;
729 skin_viewport = (struct skin_viewport *)viewport->data;
730 if (skin_viewport->label && viewport->next &&
731 !strcmp(skin_viewport->label,VP_DEFAULT_LABEL))
732 refresh_mode = 0;
734 for (viewport = data->tree;
735 viewport;
736 viewport = viewport->next)
738 /* SETUP */
739 skin_viewport = (struct skin_viewport*)viewport->data;
740 unsigned vp_refresh_mode = refresh_mode;
741 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
742 skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
743 skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
744 #endif
745 #ifdef HAVE_LCD_COLOR
746 skin_viewport->vp.lss_pattern = skin_viewport->start_gradient.start;
747 skin_viewport->vp.lse_pattern = skin_viewport->start_gradient.end;
748 skin_viewport->vp.lst_pattern = skin_viewport->start_gradient.text;
749 #endif
751 /* dont redraw the viewport if its disabled */
752 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
753 { /* don't draw anything into this one */
754 vp_refresh_mode = 0;
756 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
758 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
759 continue;
761 else if (((skin_viewport->hidden_flags&
762 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
763 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
765 vp_refresh_mode = SKIN_REFRESH_ALL;
766 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
769 display->set_viewport(&skin_viewport->vp);
770 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
772 display->clear_viewport();
774 /* render */
775 if (viewport->children_count)
776 skin_render_viewport(viewport->children[0], gwps,
777 skin_viewport, vp_refresh_mode);
778 refresh_mode = old_refresh_mode;
781 /* Restore the default viewport */
782 display->set_viewport(NULL);
783 display->update();
786 #ifdef HAVE_LCD_BITMAP
787 static __attribute__((noinline)) void skin_render_playlistviewer(struct playlistviewer* viewer,
788 struct gui_wps *gwps,
789 struct skin_viewport* skin_viewport,
790 unsigned long refresh_type)
792 struct screen *display = gwps->display;
793 char linebuf[MAX_LINE];
794 skin_render_func func = skin_render_line;
795 struct skin_element* line;
796 struct skin_draw_info info = {
797 .gwps = gwps,
798 .buf = linebuf,
799 .buf_size = sizeof(linebuf),
800 .line_number = 0,
801 .no_line_break = false,
802 .line_scrolls = false,
803 .refresh_type = refresh_type,
804 .skin_vp = skin_viewport,
805 .offset = viewer->start_offset,
806 .text_style = STYLE_DEFAULT
809 struct align_pos * align = &info.align;
810 bool needs_update;
811 int cur_pos, start_item, max;
812 int nb_lines = viewport_get_nb_lines(viewer->vp);
813 #if CONFIG_TUNER
814 if (get_current_activity() == ACTIVITY_FM)
816 cur_pos = radio_current_preset();
817 start_item = cur_pos + viewer->start_offset;
818 max = start_item+radio_preset_count();
820 else
821 #endif
823 struct cuesheet *cue = skin_get_global_state()->id3 ?
824 skin_get_global_state()->id3->cuesheet : NULL;
825 cur_pos = playlist_get_display_index();
826 max = playlist_amount()+1;
827 if (cue)
828 max += cue->track_count;
829 start_item = MAX(0, cur_pos + viewer->start_offset);
831 if (max-start_item > nb_lines)
832 max = start_item + nb_lines;
834 line = viewer->line;
835 while (start_item < max)
837 linebuf[0] = '\0';
838 info.no_line_break = false;
839 info.line_scrolls = false;
840 info.force_redraw = false;
842 info.cur_align_start = info.buf;
843 align->left = info.buf;
844 align->center = NULL;
845 align->right = NULL;
848 if (line->type == LINE_ALTERNATOR)
849 func = skin_render_alternator;
850 else if (line->type == LINE)
851 func = skin_render_line;
853 needs_update = func(line, &info);
855 /* only update if the line needs to be, and there is something to write */
856 if (refresh_type && needs_update)
858 if (info.line_scrolls)
860 /* if the line is a scrolling one we don't want to update
861 too often, so that it has the time to scroll */
862 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
863 write_line(display, align, info.line_number, true, info.text_style);
865 else
866 write_line(display, align, info.line_number, false, info.text_style);
868 info.line_number++;
869 info.offset++;
870 start_item++;
873 #endif