skin_engine: New tag to draw a rectangle (optionally with a gradient)
[maemo-rb.git] / apps / gui / skin_engine / skin_render.c
blob7ceb0bce179e98e7b05b2bb87e734dbcf067f7cf
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 "core_alloc.h"
31 #include "kernel.h"
32 #ifdef HAVE_ALBUMART
33 #include "albumart.h"
34 #endif
35 #include "settings.h"
36 #include "skin_display.h"
37 #include "skin_engine.h"
38 #include "skin_parser.h"
39 #include "tag_table.h"
40 #include "skin_scan.h"
41 #if CONFIG_TUNER
42 #include "radio.h"
43 #endif
44 #include "viewport.h"
45 #include "cuesheet.h"
46 #include "language.h"
47 #include "playback.h"
48 #include "playlist.h"
49 #include "root_menu.h"
50 #include "misc.h"
51 #include "list.h"
54 #define MAX_LINE 1024
56 struct skin_draw_info {
57 struct gui_wps *gwps;
58 struct skin_viewport *skin_vp;
59 int line_number;
60 unsigned long refresh_type;
61 unsigned text_style;
63 char* cur_align_start;
64 struct align_pos align;
65 bool no_line_break;
66 bool line_scrolls;
67 bool force_redraw;
69 char *buf;
70 size_t buf_size;
72 int offset; /* used by the playlist viewer */
75 typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
76 bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
78 #ifdef HAVE_LCD_BITMAP
79 static void skin_render_playlistviewer(struct playlistviewer* viewer,
80 struct gui_wps *gwps,
81 struct skin_viewport* skin_viewport,
82 unsigned long refresh_type);
83 #endif
85 static char* skin_buffer;
86 /* hack alert: fix skin_parser.c's skin_buffer pointer */
87 void skinparser_set_buffer(char* pointer);
89 static inline struct skin_element*
90 get_child(OFFSETTYPE(struct skin_element**) children, int child)
92 OFFSETTYPE(struct skin_element*) *kids = SKINOFFSETTOPTR(skin_buffer, children);
93 return SKINOFFSETTOPTR(skin_buffer, kids[child]);
97 static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
98 struct skin_element *element, struct viewport* vp)
100 #ifndef HAVE_LCD_BITMAP
101 (void)vp; /* silence warnings */
102 (void)info;
103 #endif
104 struct wps_token *token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, element->data);
106 #ifdef HAVE_LCD_BITMAP
107 struct wps_data *data = gwps->data;
108 bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
109 #endif
110 switch (token->type)
112 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
113 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
115 struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
116 struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
117 vp->fg_pattern = col->colour;
119 break;
120 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
122 struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
123 struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
124 vp->bg_pattern = col->colour;
126 break;
127 case SKIN_TOKEN_VIEWPORT_TEXTSTYLE:
128 info->text_style = token->value.l;
129 break;
130 #endif
131 #ifdef HAVE_LCD_COLOR
132 case SKIN_TOKEN_VIEWPORT_GRADIENT_SETUP:
134 struct gradient_config *cfg = SKINOFFSETTOPTR(skin_buffer, token->value.data);
135 vp->lss_pattern = cfg->start;
136 vp->lse_pattern = cfg->end;
137 vp->lst_pattern = cfg->text;
139 break;
140 #endif
141 case SKIN_TOKEN_VIEWPORT_ENABLE:
143 char *label = SKINOFFSETTOPTR(skin_buffer, token->value.data);
144 char temp = VP_DRAW_HIDEABLE;
145 struct skin_element *viewport = SKINOFFSETTOPTR(skin_buffer, gwps->data->tree);
146 while (viewport)
148 struct skin_viewport *skinvp = SKINOFFSETTOPTR(skin_buffer, viewport->data);
150 char *vplabel = SKINOFFSETTOPTR(skin_buffer, skinvp->label);
151 if (skinvp->label == VP_DEFAULT_LABEL)
152 vplabel = VP_DEFAULT_LABEL_STRING;
153 if (vplabel && !skinvp->is_infovp &&
154 !strcmp(vplabel, label))
156 if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
158 temp |= VP_DRAW_WASHIDDEN;
160 skinvp->hidden_flags = temp;
162 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next);
165 break;
166 #ifdef HAVE_LCD_BITMAP
167 case SKIN_TOKEN_LIST_ITEM_CFG:
168 skinlist_set_cfg(gwps->display->screen_type,
169 SKINOFFSETTOPTR(skin_buffer, token->value.data));
170 break;
171 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
172 sb_set_info_vp(gwps->display->screen_type, token->value.data);
173 break;
174 case SKIN_TOKEN_PEAKMETER:
175 data->peak_meter_enabled = true;
176 if (do_refresh)
177 draw_peakmeters(gwps, info->line_number, vp);
178 break;
179 case SKIN_TOKEN_DRAWRECTANGLE:
180 if (do_refresh)
182 struct draw_rectangle *rect =
183 SKINOFFSETTOPTR(skin_buffer, token->value.data);
184 #ifdef HAVE_LCD_COLOR
185 if (rect->start_colour != rect->end_colour &&
186 gwps->display->screen_type == SCREEN_MAIN)
188 gwps->display->gradient_fillrect(rect->x, rect->y, rect->width,
189 rect->height, rect->start_colour, rect->end_colour);
191 else
192 #endif
194 unsigned backup = vp->fg_pattern;
195 vp->fg_pattern = rect->start_colour;
196 gwps->display->fillrect(rect->x, rect->y, rect->width,
197 rect->height);
198 vp->fg_pattern = backup;
201 break;
202 case SKIN_TOKEN_PEAKMETER_LEFTBAR:
203 case SKIN_TOKEN_PEAKMETER_RIGHTBAR:
204 data->peak_meter_enabled = true;
205 /* fall through to the progressbar code */
206 #endif
207 case SKIN_TOKEN_VOLUMEBAR:
208 case SKIN_TOKEN_BATTERY_PERCENTBAR:
209 #ifdef HAVE_LCD_BITMAP
210 case SKIN_TOKEN_PROGRESSBAR:
211 case SKIN_TOKEN_TUNER_RSSI_BAR:
212 case SKIN_TOKEN_LIST_SCROLLBAR:
214 struct progressbar *bar = (struct progressbar*)SKINOFFSETTOPTR(skin_buffer, token->value.data);
215 if (do_refresh)
216 draw_progressbar(gwps, info->line_number, bar);
218 #endif
219 break;
220 #ifdef HAVE_LCD_BITMAP
221 case SKIN_TOKEN_IMAGE_DISPLAY:
223 struct gui_img *img = SKINOFFSETTOPTR(skin_buffer, token->value.data);
224 if (img && img->loaded && do_refresh)
225 img->display = 0;
227 break;
228 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
229 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
231 struct image_display *id = SKINOFFSETTOPTR(skin_buffer, token->value.data);
232 const char* label = SKINOFFSETTOPTR(skin_buffer, id->label);
233 struct gui_img *img = skin_find_item(label,SKIN_FIND_IMAGE, data);
234 if (img && img->loaded)
236 if (SKINOFFSETTOPTR(skin_buffer, id->token) == NULL)
238 img->display = id->subimage;
240 else
242 char buf[16];
243 const char *out;
244 int a = img->num_subimages;
245 out = get_token_value(gwps, SKINOFFSETTOPTR(skin_buffer, id->token),
246 info->offset, buf, sizeof(buf), &a);
248 /* NOTE: get_token_value() returns values starting at 1! */
249 if (a == -1)
250 a = (out && *out) ? 1 : 2;
251 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
252 a -= 2; /* 2 is added in statusbar-skinned.c! */
253 else
254 a--;
255 a += id->offset;
257 /* Clear the image, as in conditionals */
258 clear_image_pos(gwps, img);
260 /* If the token returned a value which is higher than
261 * the amount of subimages, don't draw it. */
262 if (a >= 0 && a < img->num_subimages)
264 img->display = a;
268 break;
270 #ifdef HAVE_ALBUMART
271 case SKIN_TOKEN_ALBUMART_DISPLAY:
273 struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, data->albumart);
274 /* now draw the AA */
275 if (do_refresh && aa)
277 int handle = playback_current_aa_hid(data->playback_aa_slot);
278 #if CONFIG_TUNER
279 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
281 struct dim dim = {aa->width, aa->height};
282 handle = radio_get_art_hid(&dim);
284 #endif
285 aa->draw_handle = handle;
287 break;
289 #endif
290 case SKIN_TOKEN_DRAW_INBUILTBAR:
291 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
292 info->refresh_type == SKIN_REFRESH_ALL,
293 SKINOFFSETTOPTR(skin_buffer, token->value.data));
294 break;
295 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
296 if (do_refresh)
297 skin_render_playlistviewer(SKINOFFSETTOPTR(skin_buffer, token->value.data), gwps,
298 info->skin_vp, info->refresh_type);
299 break;
301 #endif /* HAVE_LCD_BITMAP */
302 #ifdef HAVE_SKIN_VARIABLES
303 case SKIN_TOKEN_VAR_SET:
305 struct skin_var_changer *data = SKINOFFSETTOPTR(skin_buffer, token->value.data);
306 struct skin_var *var = SKINOFFSETTOPTR(skin_buffer, data->var);
307 if (data->direct)
308 var->value = data->newval;
309 else
311 var->value += data->newval;
312 if (data->max)
314 if (var->value > data->max)
315 var->value = 1;
316 else if (var->value < 1)
317 var->value = data->max;
320 if (var->value < 1)
321 var->value = 1;
322 var->last_changed = current_tick;
324 break;
325 #endif
326 default:
327 return false;
329 return true;
334 static void do_tags_in_hidden_conditional(struct skin_element* branch,
335 struct skin_draw_info *info)
337 #ifdef HAVE_LCD_BITMAP
338 struct gui_wps *gwps = info->gwps;
339 struct wps_data *data = gwps->data;
340 #endif
341 /* Tags here are ones which need to be "turned off" or cleared
342 * if they are in a conditional branch which isnt being used */
343 if (branch->type == LINE_ALTERNATOR)
345 int i;
346 for (i=0; i<branch->children_count; i++)
348 do_tags_in_hidden_conditional(get_child(branch->children, i), info);
351 else if (branch->type == LINE && branch->children_count)
353 struct skin_element *child = get_child(branch->children, 0);
354 #if defined(HAVE_LCD_BITMAP) || defined(HAVE_ALBUMART)
355 struct wps_token *token;
356 #endif
357 while (child)
359 if (child->type == CONDITIONAL)
361 int i;
362 for (i=0; i<child->children_count; i++)
364 do_tags_in_hidden_conditional(get_child(child->children, i), info);
366 child = SKINOFFSETTOPTR(skin_buffer, child->next);
367 continue;
369 else if (child->type != TAG || !SKINOFFSETTOPTR(skin_buffer, child->data))
371 child = SKINOFFSETTOPTR(skin_buffer, child->next);
372 continue;
374 #if defined(HAVE_LCD_BITMAP) || defined(HAVE_ALBUMART)
375 token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, child->data);
376 #endif
377 #ifdef HAVE_LCD_BITMAP
378 /* clear all pictures in the conditional and nested ones */
379 if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
381 struct image_display *id = SKINOFFSETTOPTR(skin_buffer, token->value.data);
382 struct gui_img *img = skin_find_item(SKINOFFSETTOPTR(skin_buffer, id->label),
383 SKIN_FIND_IMAGE, data);
384 clear_image_pos(gwps, img);
386 else if (token->type == SKIN_TOKEN_PEAKMETER)
388 data->peak_meter_enabled = false;
390 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
392 char *label = SKINOFFSETTOPTR(skin_buffer, token->value.data);
393 struct skin_element *viewport;
394 for (viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
395 viewport;
396 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next))
398 struct skin_viewport *skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
400 char *vplabel = SKINOFFSETTOPTR(skin_buffer, skin_viewport->label);
401 if (skin_viewport->label == VP_DEFAULT_LABEL)
402 vplabel = VP_DEFAULT_LABEL_STRING;
403 if (vplabel && strcmp(vplabel, label))
404 continue;
405 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
407 continue;
409 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
411 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
412 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
413 else
415 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
416 if (skin_viewport->output_to_backdrop_buffer)
418 void *backdrop = skin_backdrop_get_buffer(data->backdrop_id);
419 gwps->display->set_framebuffer(backdrop);
420 skin_backdrop_show(-1);
422 #endif
423 gwps->display->set_viewport(&skin_viewport->vp);
424 gwps->display->clear_viewport();
425 gwps->display->scroll_stop(&skin_viewport->vp);
426 gwps->display->set_viewport(&info->skin_vp->vp);
427 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
429 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
430 if (skin_viewport->output_to_backdrop_buffer)
432 gwps->display->set_framebuffer(NULL);
433 skin_backdrop_show(data->backdrop_id);
435 #endif
440 #endif
441 #ifdef HAVE_ALBUMART
442 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
444 draw_album_art(gwps,
445 playback_current_aa_hid(data->playback_aa_slot), true);
447 #endif
448 child = SKINOFFSETTOPTR(skin_buffer, child->next);
453 static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
455 struct align_pos *align = &info->align;
456 char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
457 switch (element->tag->type)
459 case SKIN_TOKEN_ALIGN_LEFT:
460 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
461 align->left = cur_pos;
462 info->cur_align_start = cur_pos;
463 break;
464 case SKIN_TOKEN_ALIGN_LEFT_RTL:
465 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
466 if (lang_is_rtl())
467 align->right = cur_pos;
468 else
469 align->left = cur_pos;
470 info->cur_align_start = cur_pos;
471 break;
472 case SKIN_TOKEN_ALIGN_CENTER:
473 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
474 align->center = cur_pos;
475 info->cur_align_start = cur_pos;
476 break;
477 case SKIN_TOKEN_ALIGN_RIGHT:
478 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
479 align->right = cur_pos;
480 info->cur_align_start = cur_pos;
481 break;
482 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
483 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
484 if (lang_is_rtl())
485 align->left = cur_pos;
486 else
487 align->right = cur_pos;
488 info->cur_align_start = cur_pos;
489 break;
490 default:
491 break;
495 /* Draw a LINE element onto the display */
496 static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
498 bool needs_update = false;
499 int last_value, value;
501 if (line->children_count == 0)
502 return false; /* empty line, do nothing */
504 struct skin_element *child = get_child(line->children, 0);
505 struct conditional *conditional;
506 skin_render_func func = skin_render_line;
507 int old_refresh_mode = info->refresh_type;
508 while (child)
510 switch (child->type)
512 case CONDITIONAL:
513 conditional = SKINOFFSETTOPTR(skin_buffer, child->data);
514 last_value = conditional->last_value;
515 value = evaluate_conditional(info->gwps, info->offset,
516 conditional, child->children_count);
517 conditional->last_value = value;
518 if (child->children_count == 1)
520 /* special handling so
521 * %?aa<true> and %?<true|false> need special handlng here */
523 if (value == -1) /* tag is false */
525 /* we are in a false branch of a %?aa<true> conditional */
526 if (last_value == 0)
527 do_tags_in_hidden_conditional(get_child(child->children, 0), info);
528 break;
531 else
533 if (last_value >= 0 && value != last_value && last_value < child->children_count)
534 do_tags_in_hidden_conditional(get_child(child->children, last_value), info);
536 if (get_child(child->children, value)->type == LINE_ALTERNATOR)
538 func = skin_render_alternator;
540 else if (get_child(child->children, value)->type == LINE)
541 func = skin_render_line;
543 if (value != last_value)
545 info->refresh_type = SKIN_REFRESH_ALL;
546 info->force_redraw = true;
549 if (func(get_child(child->children, value), info))
550 needs_update = true;
551 else
552 needs_update = needs_update || (last_value != value);
554 info->refresh_type = old_refresh_mode;
555 break;
556 case TAG:
557 if (child->tag->flags & NOBREAK)
558 info->no_line_break = true;
559 if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
560 info->line_scrolls = true;
562 fix_line_alignment(info, child);
564 if (!SKINOFFSETTOPTR(skin_buffer, child->data))
566 break;
568 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
570 static char tempbuf[128];
571 const char *valuestr = get_token_value(info->gwps, SKINOFFSETTOPTR(skin_buffer, child->data),
572 info->offset, tempbuf,
573 sizeof(tempbuf), NULL);
574 if (valuestr)
576 #if CONFIG_RTC
577 if (child->tag->flags&SKIN_RTC_REFRESH)
578 needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
579 #endif
580 needs_update = needs_update ||
581 ((child->tag->flags&info->refresh_type)!=0);
582 strlcat(info->cur_align_start, valuestr,
583 info->buf_size - (info->cur_align_start-info->buf));
586 break;
587 case TEXT:
588 strlcat(info->cur_align_start, SKINOFFSETTOPTR(skin_buffer, child->data),
589 info->buf_size - (info->cur_align_start-info->buf));
590 needs_update = needs_update ||
591 (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
592 break;
593 case COMMENT:
594 default:
595 break;
598 child = SKINOFFSETTOPTR(skin_buffer, child->next);
600 return needs_update;
603 static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
605 struct skin_element *element=line;
606 struct wps_token *token;
607 int retval = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
608 if (element->type == LINE)
610 if (element->children_count == 0)
611 return retval; /* empty line, so force redraw */
612 element = get_child(element->children, 0);
614 while (element)
616 if (element->type == TAG &&
617 element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
619 token = SKINOFFSETTOPTR(skin_buffer, element->data);
620 return token->value.i;
622 else if (element->type == CONDITIONAL)
624 struct conditional *conditional = SKINOFFSETTOPTR(skin_buffer, element->data);
625 int val = evaluate_conditional(gwps, 0, conditional,
626 element->children_count);
627 if (val >= 0)
629 retval = get_subline_timeout(gwps, get_child(element->children, val));
630 if (retval >= 0)
631 return retval;
634 element = SKINOFFSETTOPTR(skin_buffer, element->next);
636 return retval;
639 bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
641 bool changed_lines = false;
642 struct line_alternator *alternator = SKINOFFSETTOPTR(skin_buffer, element->data);
643 unsigned old_refresh = info->refresh_type;
644 if (info->refresh_type == SKIN_REFRESH_ALL)
646 alternator->current_line = element->children_count-1;
647 changed_lines = true;
649 else if (TIME_AFTER(current_tick, alternator->next_change_tick))
651 changed_lines = true;
654 if (changed_lines)
656 struct skin_element *current_line;
657 int start = alternator->current_line;
658 int try_line = start;
659 bool suitable = false;
660 int rettimeout = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
662 /* find a subline which has at least one token in it,
663 * and that line doesnt have a timeout set to 0 through conditionals */
664 do {
665 try_line++;
666 if (try_line >= element->children_count)
667 try_line = 0;
668 if (get_child(element->children, try_line)->children_count != 0)
670 current_line = get_child(element->children, try_line);
671 rettimeout = get_subline_timeout(info->gwps,
672 get_child(current_line->children, 0));
673 if (rettimeout > 0)
675 suitable = true;
679 while (try_line != start && !suitable);
681 if (suitable)
683 alternator->current_line = try_line;
684 alternator->next_change_tick = current_tick + rettimeout;
687 info->refresh_type = SKIN_REFRESH_ALL;
688 info->force_redraw = true;
690 bool ret = skin_render_line(get_child(element->children, alternator->current_line), info);
691 info->refresh_type = old_refresh;
692 return changed_lines || ret;
695 void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
696 struct skin_viewport* skin_viewport, unsigned long refresh_type)
698 struct screen *display = gwps->display;
699 char linebuf[MAX_LINE];
700 skin_render_func func = skin_render_line;
701 struct skin_element* line = viewport;
702 struct skin_draw_info info = {
703 .gwps = gwps,
704 .buf = linebuf,
705 .buf_size = sizeof(linebuf),
706 .line_number = 0,
707 .no_line_break = false,
708 .line_scrolls = false,
709 .refresh_type = refresh_type,
710 .skin_vp = skin_viewport,
711 .offset = 0,
712 .text_style = STYLE_DEFAULT
715 struct align_pos * align = &info.align;
716 bool needs_update;
717 skin_buffer = get_skin_buffer(gwps->data);
718 skinparser_set_buffer(skin_buffer);
719 #ifdef HAVE_LCD_BITMAP
720 /* Set images to not to be displayed */
721 struct skin_token_list *imglist = SKINOFFSETTOPTR(skin_buffer, gwps->data->images);
722 while (imglist)
724 struct wps_token *token = SKINOFFSETTOPTR(skin_buffer, imglist->token);
725 struct gui_img *img = (struct gui_img *)SKINOFFSETTOPTR(skin_buffer, token->value.data);
726 img->display = -1;
727 imglist = SKINOFFSETTOPTR(skin_buffer, imglist->next);
730 /* fix font ID's */
731 if (skin_viewport->parsed_fontid == 1)
732 skin_viewport->vp.font = display->getuifont();
733 #endif
735 while (line)
737 linebuf[0] = '\0';
738 info.no_line_break = false;
739 info.line_scrolls = false;
740 info.force_redraw = false;
741 #ifdef HAVE_LCD_COLOR
742 if (info.text_style&STYLE_GRADIENT)
744 int cur = CURLN_UNPACK(info.text_style);
745 int num = NUMLN_UNPACK(info.text_style);
746 if (cur+1 == num)
747 info.text_style = STYLE_DEFAULT;
748 else
749 info.text_style = STYLE_GRADIENT|CURLN_PACK(cur+1)|NUMLN_PACK(num);
751 #endif
752 info.cur_align_start = info.buf;
753 align->left = info.buf;
754 align->center = NULL;
755 align->right = NULL;
758 if (line->type == LINE_ALTERNATOR)
759 func = skin_render_alternator;
760 else if (line->type == LINE)
761 func = skin_render_line;
763 needs_update = func(line, &info);
764 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
765 if (skin_viewport->vp.fg_pattern != skin_viewport->start_fgcolour ||
766 skin_viewport->vp.bg_pattern != skin_viewport->start_bgcolour)
768 /* 2bit lcd drivers need lcd_set_viewport() to be called to change
769 * the colour, 16bit doesnt. But doing this makes static text
770 * get the new colour also */
771 needs_update = true;
772 display->set_viewport(&skin_viewport->vp);
774 #endif
775 /* only update if the line needs to be, and there is something to write */
776 if (refresh_type && needs_update)
778 if (!info.force_redraw)
779 display->scroll_stop_line(&skin_viewport->vp, info.line_number);
780 write_line(display, align, info.line_number,
781 info.line_scrolls, info.text_style);
783 if (!info.no_line_break)
784 info.line_number++;
785 line = SKINOFFSETTOPTR(skin_buffer, line->next);
787 #ifdef HAVE_LCD_BITMAP
788 wps_display_images(gwps, &skin_viewport->vp);
789 #endif
792 void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
794 struct wps_data *data = gwps->data;
795 struct screen *display = gwps->display;
797 struct skin_element* viewport;
798 struct skin_viewport* skin_viewport;
799 char *label;
801 int old_refresh_mode = refresh_mode;
802 skin_buffer = get_skin_buffer(gwps->data);
803 skinparser_set_buffer(skin_buffer);
805 #ifdef HAVE_LCD_CHARCELLS
806 int i;
807 for (i = 0; i < 8; i++)
809 if (data->wps_progress_pat[i] == 0)
810 data->wps_progress_pat[i] = display->get_locked_pattern();
812 #endif
814 viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
815 skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
816 label = SKINOFFSETTOPTR(skin_buffer, skin_viewport->label);
817 if (skin_viewport->label == VP_DEFAULT_LABEL)
818 label = VP_DEFAULT_LABEL_STRING;
819 if (label && SKINOFFSETTOPTR(skin_buffer, viewport->next) &&
820 !strcmp(label,VP_DEFAULT_LABEL_STRING))
821 refresh_mode = 0;
823 for (viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
824 viewport;
825 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next))
827 /* SETUP */
828 skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
829 unsigned vp_refresh_mode = refresh_mode;
830 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
831 skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
832 skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
833 if (skin_viewport->output_to_backdrop_buffer)
835 display->set_framebuffer(skin_backdrop_get_buffer(data->backdrop_id));
836 skin_backdrop_show(-1);
838 else
840 display->set_framebuffer(NULL);
841 skin_backdrop_show(data->backdrop_id);
843 #endif
844 #ifdef HAVE_LCD_COLOR
845 skin_viewport->vp.lss_pattern = skin_viewport->start_gradient.start;
846 skin_viewport->vp.lse_pattern = skin_viewport->start_gradient.end;
847 skin_viewport->vp.lst_pattern = skin_viewport->start_gradient.text;
848 #endif
850 /* dont redraw the viewport if its disabled */
851 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
852 { /* don't draw anything into this one */
853 vp_refresh_mode = 0;
855 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
857 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
858 continue;
860 else if (((skin_viewport->hidden_flags&
861 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
862 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
864 vp_refresh_mode = SKIN_REFRESH_ALL;
865 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
868 display->set_viewport(&skin_viewport->vp);
869 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
871 display->clear_viewport();
873 /* render */
874 if (viewport->children_count)
875 skin_render_viewport(get_child(viewport->children, 0), gwps,
876 skin_viewport, vp_refresh_mode);
877 refresh_mode = old_refresh_mode;
879 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
880 display->set_framebuffer(NULL);
881 skin_backdrop_show(data->backdrop_id);
882 #endif
883 /* Restore the default viewport */
884 display->set_viewport(NULL);
885 display->update();
888 #ifdef HAVE_LCD_BITMAP
889 static __attribute__((noinline))
890 void skin_render_playlistviewer(struct playlistviewer* viewer,
891 struct gui_wps *gwps,
892 struct skin_viewport* skin_viewport,
893 unsigned long refresh_type)
895 struct screen *display = gwps->display;
896 char linebuf[MAX_LINE];
897 skin_render_func func = skin_render_line;
898 struct skin_element* line;
899 struct skin_draw_info info = {
900 .gwps = gwps,
901 .buf = linebuf,
902 .buf_size = sizeof(linebuf),
903 .line_number = 0,
904 .no_line_break = false,
905 .line_scrolls = false,
906 .refresh_type = refresh_type,
907 .skin_vp = skin_viewport,
908 .offset = viewer->start_offset,
909 .text_style = STYLE_DEFAULT
912 struct align_pos * align = &info.align;
913 bool needs_update;
914 int cur_pos, start_item, max;
915 int nb_lines = viewport_get_nb_lines(SKINOFFSETTOPTR(skin_buffer, viewer->vp));
916 #if CONFIG_TUNER
917 if (get_current_activity() == ACTIVITY_FM)
919 cur_pos = radio_current_preset();
920 start_item = cur_pos + viewer->start_offset;
921 max = start_item+radio_preset_count();
923 else
924 #endif
926 struct cuesheet *cue = skin_get_global_state()->id3 ?
927 skin_get_global_state()->id3->cuesheet : NULL;
928 cur_pos = playlist_get_display_index();
929 max = playlist_amount()+1;
930 if (cue)
931 max += cue->track_count;
932 start_item = MAX(0, cur_pos + viewer->start_offset);
934 if (max-start_item > nb_lines)
935 max = start_item + nb_lines;
937 line = SKINOFFSETTOPTR(skin_buffer, viewer->line);
938 while (start_item < max)
940 linebuf[0] = '\0';
941 info.no_line_break = false;
942 info.line_scrolls = false;
943 info.force_redraw = false;
945 info.cur_align_start = info.buf;
946 align->left = info.buf;
947 align->center = NULL;
948 align->right = NULL;
951 if (line->type == LINE_ALTERNATOR)
952 func = skin_render_alternator;
953 else if (line->type == LINE)
954 func = skin_render_line;
956 needs_update = func(line, &info);
958 /* only update if the line needs to be, and there is something to write */
959 if (refresh_type && needs_update)
961 if (!info.force_redraw)
962 display->scroll_stop_line(&skin_viewport->vp, info.line_number);
963 write_line(display, align, info.line_number,
964 info.line_scrolls, info.text_style);
966 info.line_number++;
967 info.offset++;
968 start_item++;
971 #endif