Use buflib for all skin engine allocations.
[maemo-rb.git] / apps / gui / skin_engine / skin_render.c
blobe408caaa1ed60cf3fc718ac85e44a6f3968fef90
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 if (do_refresh)
169 skinlist_set_cfg(gwps->display->screen_type,
170 SKINOFFSETTOPTR(skin_buffer, token->value.data));
171 break;
172 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
173 sb_set_info_vp(gwps->display->screen_type, token->value.data);
174 break;
175 case SKIN_TOKEN_PEAKMETER:
176 data->peak_meter_enabled = true;
177 if (do_refresh)
178 draw_peakmeters(gwps, info->line_number, vp);
179 break;
180 #endif
181 #ifdef HAVE_LCD_BITMAP
182 case SKIN_TOKEN_PEAKMETER_LEFTBAR:
183 case SKIN_TOKEN_PEAKMETER_RIGHTBAR:
184 data->peak_meter_enabled = true;
185 /* fall through to the progressbar code */
186 #endif
187 case SKIN_TOKEN_VOLUMEBAR:
188 case SKIN_TOKEN_BATTERY_PERCENTBAR:
189 #ifdef HAVE_LCD_BITMAP
190 case SKIN_TOKEN_PROGRESSBAR:
191 case SKIN_TOKEN_TUNER_RSSI_BAR:
192 case SKIN_TOKEN_LIST_SCROLLBAR:
194 struct progressbar *bar = (struct progressbar*)SKINOFFSETTOPTR(skin_buffer, token->value.data);
195 if (do_refresh)
196 draw_progressbar(gwps, info->line_number, bar);
198 #endif
199 break;
200 #ifdef HAVE_LCD_BITMAP
201 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
202 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
204 struct image_display *id = SKINOFFSETTOPTR(skin_buffer, token->value.data);
205 const char* label = SKINOFFSETTOPTR(skin_buffer, id->label);
206 struct gui_img *img = skin_find_item(label,SKIN_FIND_IMAGE, data);
207 if (img && img->loaded)
209 if (SKINOFFSETTOPTR(skin_buffer, id->token) == NULL)
211 img->display = id->subimage;
213 else
215 char buf[16];
216 const char *out;
217 int a = img->num_subimages;
218 out = get_token_value(gwps, SKINOFFSETTOPTR(skin_buffer, id->token),
219 info->offset, buf, sizeof(buf), &a);
221 /* NOTE: get_token_value() returns values starting at 1! */
222 if (a == -1)
223 a = (out && *out) ? 1 : 2;
224 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
225 a -= 2; /* 2 is added in statusbar-skinned.c! */
226 else
227 a--;
228 a += id->offset;
230 /* Clear the image, as in conditionals */
231 clear_image_pos(gwps, img);
233 /* If the token returned a value which is higher than
234 * the amount of subimages, don't draw it. */
235 if (a >= 0 && a < img->num_subimages)
237 img->display = a;
241 break;
243 #ifdef HAVE_ALBUMART
244 case SKIN_TOKEN_ALBUMART_DISPLAY:
246 struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, data->albumart);
247 /* now draw the AA */
248 if (do_refresh && aa)
250 int handle = playback_current_aa_hid(data->playback_aa_slot);
251 #if CONFIG_TUNER
252 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
254 struct dim dim = {aa->width, aa->height};
255 handle = radio_get_art_hid(&dim);
257 #endif
258 aa->draw_handle = handle;
260 break;
262 #endif
263 case SKIN_TOKEN_DRAW_INBUILTBAR:
264 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
265 info->refresh_type == SKIN_REFRESH_ALL,
266 SKINOFFSETTOPTR(skin_buffer, token->value.data));
267 break;
268 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
269 if (do_refresh)
270 skin_render_playlistviewer(SKINOFFSETTOPTR(skin_buffer, token->value.data), gwps,
271 info->skin_vp, info->refresh_type);
272 break;
274 #endif /* HAVE_LCD_BITMAP */
275 #ifdef HAVE_SKIN_VARIABLES
276 case SKIN_TOKEN_VAR_SET:
277 if (do_refresh)
279 struct skin_var_changer *data = SKINOFFSETTOPTR(skin_buffer, token->value.data);
280 struct skin_var *var = SKINOFFSETTOPTR(skin_buffer, data->var);
281 if (data->direct)
282 var->value = data->newval;
283 else
285 var->value += data->newval;
286 if (data->max)
288 if (var->value > data->max)
289 var->value = 1;
290 else if (var->value < 1)
291 var->value = data->max;
294 if (var->value < 1)
295 var->value = 1;
296 var->last_changed = current_tick;
298 break;
299 #endif
300 default:
301 return false;
303 return true;
308 static void do_tags_in_hidden_conditional(struct skin_element* branch,
309 struct skin_draw_info *info)
311 #ifdef HAVE_LCD_BITMAP
312 struct gui_wps *gwps = info->gwps;
313 struct wps_data *data = gwps->data;
314 #endif
315 /* Tags here are ones which need to be "turned off" or cleared
316 * if they are in a conditional branch which isnt being used */
317 if (branch->type == LINE_ALTERNATOR)
319 int i;
320 for (i=0; i<branch->children_count; i++)
322 do_tags_in_hidden_conditional(get_child(branch->children, i), info);
325 else if (branch->type == LINE && branch->children_count)
327 struct skin_element *child = get_child(branch->children, 0);
328 #if defined(HAVE_LCD_BITMAP) || defined(HAVE_ALBUMART)
329 struct wps_token *token;
330 #endif
331 while (child)
333 if (child->type == CONDITIONAL)
335 int i;
336 for (i=0; i<child->children_count; i++)
338 do_tags_in_hidden_conditional(get_child(child->children, i), info);
340 child = SKINOFFSETTOPTR(skin_buffer, child->next);
341 continue;
343 else if (child->type != TAG || !SKINOFFSETTOPTR(skin_buffer, child->data))
345 child = SKINOFFSETTOPTR(skin_buffer, child->next);
346 continue;
348 #if defined(HAVE_LCD_BITMAP) || defined(HAVE_ALBUMART)
349 token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, child->data);
350 #endif
351 #ifdef HAVE_LCD_BITMAP
352 /* clear all pictures in the conditional and nested ones */
353 if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
355 struct image_display *id = SKINOFFSETTOPTR(skin_buffer, token->value.data);
356 struct gui_img *img = skin_find_item(SKINOFFSETTOPTR(skin_buffer, id->label),
357 SKIN_FIND_IMAGE, data);
358 clear_image_pos(gwps, img);
360 else if (token->type == SKIN_TOKEN_PEAKMETER)
362 data->peak_meter_enabled = false;
364 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
366 char *label = SKINOFFSETTOPTR(skin_buffer, token->value.data);
367 struct skin_element *viewport;
368 for (viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
369 viewport;
370 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next))
372 struct skin_viewport *skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
374 char *vplabel = SKINOFFSETTOPTR(skin_buffer, skin_viewport->label);
375 if (skin_viewport->label == VP_DEFAULT_LABEL)
376 vplabel = VP_DEFAULT_LABEL_STRING;
377 if (vplabel && strcmp(vplabel, label))
378 continue;
379 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
381 continue;
383 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
385 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
386 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
387 else
389 gwps->display->set_viewport(&skin_viewport->vp);
390 gwps->display->clear_viewport();
391 gwps->display->scroll_stop(&skin_viewport->vp);
392 gwps->display->set_viewport(&info->skin_vp->vp);
393 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
398 #endif
399 #ifdef HAVE_ALBUMART
400 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
402 draw_album_art(gwps,
403 playback_current_aa_hid(data->playback_aa_slot), true);
405 #endif
406 child = SKINOFFSETTOPTR(skin_buffer, child->next);
411 static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
413 struct align_pos *align = &info->align;
414 char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
415 switch (element->tag->type)
417 case SKIN_TOKEN_ALIGN_LEFT:
418 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
419 align->left = cur_pos;
420 info->cur_align_start = cur_pos;
421 break;
422 case SKIN_TOKEN_ALIGN_LEFT_RTL:
423 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
424 if (lang_is_rtl())
425 align->right = cur_pos;
426 else
427 align->left = cur_pos;
428 info->cur_align_start = cur_pos;
429 break;
430 case SKIN_TOKEN_ALIGN_CENTER:
431 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
432 align->center = cur_pos;
433 info->cur_align_start = cur_pos;
434 break;
435 case SKIN_TOKEN_ALIGN_RIGHT:
436 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
437 align->right = cur_pos;
438 info->cur_align_start = cur_pos;
439 break;
440 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
441 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
442 if (lang_is_rtl())
443 align->left = cur_pos;
444 else
445 align->right = cur_pos;
446 info->cur_align_start = cur_pos;
447 break;
448 default:
449 break;
453 /* Draw a LINE element onto the display */
454 static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
456 bool needs_update = false;
457 int last_value, value;
459 if (line->children_count == 0)
460 return false; /* empty line, do nothing */
462 struct skin_element *child = get_child(line->children, 0);
463 struct conditional *conditional;
464 skin_render_func func = skin_render_line;
465 int old_refresh_mode = info->refresh_type;
466 while (child)
468 switch (child->type)
470 case CONDITIONAL:
471 conditional = SKINOFFSETTOPTR(skin_buffer, child->data);
472 last_value = conditional->last_value;
473 value = evaluate_conditional(info->gwps, info->offset,
474 conditional, child->children_count);
475 conditional->last_value = value;
476 if (child->children_count == 1)
478 /* special handling so
479 * %?aa<true> and %?<true|false> need special handlng here */
481 if (value == -1) /* tag is false */
483 /* we are in a false branch of a %?aa<true> conditional */
484 if (last_value == 0)
485 do_tags_in_hidden_conditional(get_child(child->children, 0), info);
486 break;
489 else
491 if (last_value >= 0 && value != last_value && last_value < child->children_count)
492 do_tags_in_hidden_conditional(get_child(child->children, last_value), info);
494 if (get_child(child->children, value)->type == LINE_ALTERNATOR)
496 func = skin_render_alternator;
498 else if (get_child(child->children, value)->type == LINE)
499 func = skin_render_line;
501 if (value != last_value)
503 info->refresh_type = SKIN_REFRESH_ALL;
504 info->force_redraw = true;
507 if (func(get_child(child->children, value), info))
508 needs_update = true;
509 else
510 needs_update = needs_update || (last_value != value);
512 info->refresh_type = old_refresh_mode;
513 break;
514 case TAG:
515 if (child->tag->flags & NOBREAK)
516 info->no_line_break = true;
517 if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
518 info->line_scrolls = true;
520 fix_line_alignment(info, child);
522 if (!SKINOFFSETTOPTR(skin_buffer, child->data))
524 break;
526 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
528 static char tempbuf[128];
529 const char *valuestr = get_token_value(info->gwps, SKINOFFSETTOPTR(skin_buffer, child->data),
530 info->offset, tempbuf,
531 sizeof(tempbuf), NULL);
532 if (valuestr)
534 #if CONFIG_RTC
535 if (child->tag->flags&SKIN_RTC_REFRESH)
536 needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
537 #endif
538 needs_update = needs_update ||
539 ((child->tag->flags&info->refresh_type)!=0);
540 strlcat(info->cur_align_start, valuestr,
541 info->buf_size - (info->cur_align_start-info->buf));
544 break;
545 case TEXT:
546 strlcat(info->cur_align_start, SKINOFFSETTOPTR(skin_buffer, child->data),
547 info->buf_size - (info->cur_align_start-info->buf));
548 needs_update = needs_update ||
549 (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
550 break;
551 case COMMENT:
552 default:
553 break;
556 child = SKINOFFSETTOPTR(skin_buffer, child->next);
558 return needs_update;
561 static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
563 struct skin_element *element=line;
564 struct wps_token *token;
565 int retval = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
566 if (element->type == LINE)
568 if (element->children_count == 0)
569 return retval; /* empty line, so force redraw */
570 element = get_child(element->children, 0);
572 while (element)
574 if (element->type == TAG &&
575 element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
577 token = SKINOFFSETTOPTR(skin_buffer, element->data);
578 return token->value.i;
580 else if (element->type == CONDITIONAL)
582 struct conditional *conditional = SKINOFFSETTOPTR(skin_buffer, element->data);
583 int val = evaluate_conditional(gwps, 0, conditional,
584 element->children_count);
585 if (val >= 0)
587 retval = get_subline_timeout(gwps, get_child(element->children, val));
588 if (retval >= 0)
589 return retval;
592 element = SKINOFFSETTOPTR(skin_buffer, element->next);
594 return retval;
597 bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
599 bool changed_lines = false;
600 struct line_alternator *alternator = SKINOFFSETTOPTR(skin_buffer, element->data);
601 unsigned old_refresh = info->refresh_type;
602 if (info->refresh_type == SKIN_REFRESH_ALL)
604 alternator->current_line = element->children_count-1;
605 changed_lines = true;
607 else if (TIME_AFTER(current_tick, alternator->next_change_tick))
609 changed_lines = true;
612 if (changed_lines)
614 struct skin_element *current_line;
615 int start = alternator->current_line;
616 int try_line = start;
617 bool suitable = false;
618 int rettimeout = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
620 /* find a subline which has at least one token in it,
621 * and that line doesnt have a timeout set to 0 through conditionals */
622 do {
623 try_line++;
624 if (try_line >= element->children_count)
625 try_line = 0;
626 if (get_child(element->children, try_line)->children_count != 0)
628 current_line = get_child(element->children, try_line);
629 rettimeout = get_subline_timeout(info->gwps,
630 get_child(current_line->children, 0));
631 if (rettimeout > 0)
633 suitable = true;
637 while (try_line != start && !suitable);
639 if (suitable)
641 alternator->current_line = try_line;
642 alternator->next_change_tick = current_tick + rettimeout;
645 info->refresh_type = SKIN_REFRESH_ALL;
646 info->force_redraw = true;
648 bool ret = skin_render_line(get_child(element->children, alternator->current_line), info);
649 info->refresh_type = old_refresh;
650 return changed_lines || ret;
653 void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
654 struct skin_viewport* skin_viewport, unsigned long refresh_type)
656 struct screen *display = gwps->display;
657 char linebuf[MAX_LINE];
658 skin_render_func func = skin_render_line;
659 struct skin_element* line = viewport;
660 struct skin_draw_info info = {
661 .gwps = gwps,
662 .buf = linebuf,
663 .buf_size = sizeof(linebuf),
664 .line_number = 0,
665 .no_line_break = false,
666 .line_scrolls = false,
667 .refresh_type = refresh_type,
668 .skin_vp = skin_viewport,
669 .offset = 0,
670 .text_style = STYLE_DEFAULT
673 struct align_pos * align = &info.align;
674 bool needs_update;
675 skin_buffer = get_skin_buffer(gwps->data);
676 skinparser_set_buffer(skin_buffer);
677 #ifdef HAVE_LCD_BITMAP
678 /* Set images to not to be displayed */
679 struct skin_token_list *imglist = SKINOFFSETTOPTR(skin_buffer, gwps->data->images);
680 while (imglist)
682 struct wps_token *token = SKINOFFSETTOPTR(skin_buffer, imglist->token);
683 struct gui_img *img = (struct gui_img *)SKINOFFSETTOPTR(skin_buffer, token->value.data);
684 img->display = -1;
685 imglist = SKINOFFSETTOPTR(skin_buffer, imglist->next);
688 /* fix font ID's */
689 if (skin_viewport->parsed_fontid == 1)
690 skin_viewport->vp.font = display->getuifont();
691 #endif
693 while (line)
695 linebuf[0] = '\0';
696 info.no_line_break = false;
697 info.line_scrolls = false;
698 info.force_redraw = false;
699 #ifdef HAVE_LCD_COLOR
700 if (info.text_style&STYLE_GRADIENT)
702 int cur = CURLN_UNPACK(info.text_style);
703 int num = NUMLN_UNPACK(info.text_style);
704 if (cur+1 == num)
705 info.text_style = STYLE_DEFAULT;
706 else
707 info.text_style = STYLE_GRADIENT|CURLN_PACK(cur+1)|NUMLN_PACK(num);
709 #endif
710 info.cur_align_start = info.buf;
711 align->left = info.buf;
712 align->center = NULL;
713 align->right = NULL;
716 if (line->type == LINE_ALTERNATOR)
717 func = skin_render_alternator;
718 else if (line->type == LINE)
719 func = skin_render_line;
721 needs_update = func(line, &info);
722 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
723 if (skin_viewport->vp.fg_pattern != skin_viewport->start_fgcolour ||
724 skin_viewport->vp.bg_pattern != skin_viewport->start_bgcolour)
726 /* 2bit lcd drivers need lcd_set_viewport() to be called to change
727 * the colour, 16bit doesnt. But doing this makes static text
728 * get the new colour also */
729 needs_update = true;
730 display->set_viewport(&skin_viewport->vp);
732 #endif
733 /* only update if the line needs to be, and there is something to write */
734 if (refresh_type && needs_update)
736 if (info.line_scrolls)
738 /* if the line is a scrolling one we don't want to update
739 too often, so that it has the time to scroll */
740 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
741 write_line(display, align, info.line_number, true, info.text_style);
743 else
744 write_line(display, align, info.line_number, false, info.text_style);
746 if (!info.no_line_break)
747 info.line_number++;
748 line = SKINOFFSETTOPTR(skin_buffer, line->next);
750 #ifdef HAVE_LCD_BITMAP
751 wps_display_images(gwps, &skin_viewport->vp);
752 #endif
755 void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
757 struct wps_data *data = gwps->data;
758 struct screen *display = gwps->display;
760 struct skin_element* viewport;
761 struct skin_viewport* skin_viewport;
762 char *label;
764 int old_refresh_mode = refresh_mode;
765 skin_buffer = get_skin_buffer(gwps->data);
766 skinparser_set_buffer(skin_buffer);
768 #ifdef HAVE_LCD_CHARCELLS
769 int i;
770 for (i = 0; i < 8; i++)
772 if (data->wps_progress_pat[i] == 0)
773 data->wps_progress_pat[i] = display->get_locked_pattern();
775 #endif
776 viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
777 skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
778 label = SKINOFFSETTOPTR(skin_buffer, skin_viewport->label);
779 if (skin_viewport->label == VP_DEFAULT_LABEL)
780 label = VP_DEFAULT_LABEL_STRING;
781 if (label && SKINOFFSETTOPTR(skin_buffer, viewport->next) &&
782 !strcmp(label,VP_DEFAULT_LABEL_STRING))
783 refresh_mode = 0;
785 for (viewport = SKINOFFSETTOPTR(skin_buffer, data->tree);
786 viewport;
787 viewport = SKINOFFSETTOPTR(skin_buffer, viewport->next))
789 /* SETUP */
790 skin_viewport = SKINOFFSETTOPTR(skin_buffer, viewport->data);
791 unsigned vp_refresh_mode = refresh_mode;
792 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
793 skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
794 skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
795 #endif
796 #ifdef HAVE_LCD_COLOR
797 skin_viewport->vp.lss_pattern = skin_viewport->start_gradient.start;
798 skin_viewport->vp.lse_pattern = skin_viewport->start_gradient.end;
799 skin_viewport->vp.lst_pattern = skin_viewport->start_gradient.text;
800 #endif
802 /* dont redraw the viewport if its disabled */
803 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
804 { /* don't draw anything into this one */
805 vp_refresh_mode = 0;
807 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
809 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
810 continue;
812 else if (((skin_viewport->hidden_flags&
813 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
814 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
816 vp_refresh_mode = SKIN_REFRESH_ALL;
817 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
820 display->set_viewport(&skin_viewport->vp);
821 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
823 display->clear_viewport();
825 /* render */
826 if (viewport->children_count)
827 skin_render_viewport(get_child(viewport->children, 0), gwps,
828 skin_viewport, vp_refresh_mode);
829 refresh_mode = old_refresh_mode;
832 /* Restore the default viewport */
833 display->set_viewport(NULL);
834 display->update();
837 #ifdef HAVE_LCD_BITMAP
838 static __attribute__((noinline))
839 void skin_render_playlistviewer(struct playlistviewer* viewer,
840 struct gui_wps *gwps,
841 struct skin_viewport* skin_viewport,
842 unsigned long refresh_type)
844 struct screen *display = gwps->display;
845 char linebuf[MAX_LINE];
846 skin_render_func func = skin_render_line;
847 struct skin_element* line;
848 struct skin_draw_info info = {
849 .gwps = gwps,
850 .buf = linebuf,
851 .buf_size = sizeof(linebuf),
852 .line_number = 0,
853 .no_line_break = false,
854 .line_scrolls = false,
855 .refresh_type = refresh_type,
856 .skin_vp = skin_viewport,
857 .offset = viewer->start_offset,
858 .text_style = STYLE_DEFAULT
861 struct align_pos * align = &info.align;
862 bool needs_update;
863 int cur_pos, start_item, max;
864 int nb_lines = viewport_get_nb_lines(SKINOFFSETTOPTR(skin_buffer, viewer->vp));
865 #if CONFIG_TUNER
866 if (get_current_activity() == ACTIVITY_FM)
868 cur_pos = radio_current_preset();
869 start_item = cur_pos + viewer->start_offset;
870 max = start_item+radio_preset_count();
872 else
873 #endif
875 struct cuesheet *cue = skin_get_global_state()->id3 ?
876 skin_get_global_state()->id3->cuesheet : NULL;
877 cur_pos = playlist_get_display_index();
878 max = playlist_amount()+1;
879 if (cue)
880 max += cue->track_count;
881 start_item = MAX(0, cur_pos + viewer->start_offset);
883 if (max-start_item > nb_lines)
884 max = start_item + nb_lines;
886 line = SKINOFFSETTOPTR(skin_buffer, viewer->line);
887 while (start_item < max)
889 linebuf[0] = '\0';
890 info.no_line_break = false;
891 info.line_scrolls = false;
892 info.force_redraw = false;
894 info.cur_align_start = info.buf;
895 align->left = info.buf;
896 align->center = NULL;
897 align->right = NULL;
900 if (line->type == LINE_ALTERNATOR)
901 func = skin_render_alternator;
902 else if (line->type == LINE)
903 func = skin_render_line;
905 needs_update = func(line, &info);
907 /* only update if the line needs to be, and there is something to write */
908 if (refresh_type && needs_update)
910 if (info.line_scrolls)
912 /* if the line is a scrolling one we don't want to update
913 too often, so that it has the time to scroll */
914 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
915 write_line(display, align, info.line_number, true, info.text_style);
917 else
918 write_line(display, align, info.line_number, false, info.text_style);
920 info.line_number++;
921 info.offset++;
922 start_item++;
925 #endif