blob: 27e6747c2921ea53f732fb220981c67f120e2c1a [file] [log] [blame]
Jonathan Gordon2d31d772010-07-29 12:37:48 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
9 *
10 * Copyright (C) 2010 Jonathan Gordon
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <stdbool.h>
26#include <ctype.h>
27#include "strlcat.h"
28
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
Jonathan Gordondc3778a2010-08-14 15:17:59 +000042#include "viewport.h"
43#include "cuesheet.h"
Jonathan Gordon2d31d772010-07-29 12:37:48 +000044#include "language.h"
45#include "playback.h"
Jonathan Gordondc3778a2010-08-14 15:17:59 +000046#include "playlist.h"
Jonathan Gordonb9093f22010-08-14 15:23:07 +000047#include "root_menu.h"
Jonathan Gordondc3778a2010-08-14 15:17:59 +000048#include "misc.h"
Jonathan Gordon2d31d772010-07-29 12:37:48 +000049
50
51#define MAX_LINE 1024
52
53struct skin_draw_info {
54 struct gui_wps *gwps;
55 struct skin_viewport *skin_vp;
56 int line_number;
57 unsigned long refresh_type;
Jonathan Gordon281f1a12011-08-14 13:50:07 +000058 unsigned text_style;
Jonathan Gordon2d31d772010-07-29 12:37:48 +000059
60 char* cur_align_start;
61 struct align_pos align;
62 bool no_line_break;
63 bool line_scrolls;
64 bool force_redraw;
65
66 char *buf;
67 size_t buf_size;
Jonathan Gordondc3778a2010-08-14 15:17:59 +000068
69 int offset; /* used by the playlist viewer */
Jonathan Gordon2d31d772010-07-29 12:37:48 +000070};
71
72typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
73bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
74
Jonathan Gordond23d7a12010-08-14 15:27:46 +000075#ifdef HAVE_LCD_BITMAP
Jonathan Gordondc3778a2010-08-14 15:17:59 +000076static void skin_render_playlistviewer(struct playlistviewer* viewer,
77 struct gui_wps *gwps,
78 struct skin_viewport* skin_viewport,
79 unsigned long refresh_type);
Jonathan Gordond23d7a12010-08-14 15:27:46 +000080#endif
Jonathan Gordon2d31d772010-07-29 12:37:48 +000081
Frank Gevaertsd0bf13b2010-10-10 13:21:49 +000082static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
Jonathan Gordon2d31d772010-07-29 12:37:48 +000083 struct skin_element *element, struct viewport* vp)
84{
85#ifndef HAVE_LCD_BITMAP
86 (void)vp; /* silence warnings */
Frank Gevaerts1f0ab7c2010-10-10 23:46:15 +000087 (void)info;
Jonathan Gordon2d31d772010-07-29 12:37:48 +000088#endif
89 struct wps_token *token = (struct wps_token *)element->data;
Frank Gevaerts1f0ab7c2010-10-10 23:46:15 +000090
91#ifdef HAVE_LCD_BITMAP
Jonathan Gordon2d31d772010-07-29 12:37:48 +000092 struct wps_data *data = gwps->data;
93 bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
Frank Gevaerts1f0ab7c2010-10-10 23:46:15 +000094#endif
Jonathan Gordon2d31d772010-07-29 12:37:48 +000095 switch (token->type)
96 {
97#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
98 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
99 {
100 struct viewport_colour *col = token->value.data;
101 col->vp->fg_pattern = col->colour;
102 }
103 break;
104 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
105 {
106 struct viewport_colour *col = token->value.data;
107 col->vp->bg_pattern = col->colour;
108 }
109 break;
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000110 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:
116 {
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;
121 }
122 break;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000123#endif
124 case SKIN_TOKEN_VIEWPORT_ENABLE:
125 {
Jonathan Gordonee4f8a92010-08-02 12:50:23 +0000126 char *label = token->value.data;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000127 char temp = VP_DRAW_HIDEABLE;
128 struct skin_element *viewport = gwps->data->tree;
129 while (viewport)
130 {
131 struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data;
Jonathan Gordonee4f8a92010-08-02 12:50:23 +0000132 if (skinvp->label && !skinvp->is_infovp &&
133 !strcmp(skinvp->label, label))
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000134 {
135 if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
136 {
137 temp |= VP_DRAW_WASHIDDEN;
138 }
139 skinvp->hidden_flags = temp;
140 }
141 viewport = viewport->next;
142 }
143 }
144 break;
145#ifdef HAVE_LCD_BITMAP
146 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
147 sb_set_info_vp(gwps->display->screen_type,
Jonathan Gordonee4f8a92010-08-02 12:50:23 +0000148 token->value.data);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000149 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
Jonathan Gordon261c56b2011-01-13 06:48:39 +0000156#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
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000162 case SKIN_TOKEN_VOLUMEBAR:
163 case SKIN_TOKEN_BATTERY_PERCENTBAR:
Frank Gevaerts1f0ab7c2010-10-10 23:46:15 +0000164#ifdef HAVE_LCD_BITMAP
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000165 case SKIN_TOKEN_PROGRESSBAR:
Jonathan Gordon1ce7ba42010-11-18 11:47:42 +0000166 case SKIN_TOKEN_TUNER_RSSI_BAR:
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000167 {
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000168 struct progressbar *bar = (struct progressbar*)token->value.data;
169 if (do_refresh)
170 draw_progressbar(gwps, info->line_number, bar);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000171 }
Frank Gevaerts1f0ab7c2010-10-10 23:46:15 +0000172#endif
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000173 break;
174#ifdef HAVE_LCD_BITMAP
Jonathan Gordon70ebe462010-08-12 13:27:10 +0000175 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000176 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
177 {
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000178 struct image_display *id = token->value.data;
Jonathan Gordonff8d43d2010-08-14 11:40:20 +0000179 const char* label = id->label;
Jonathan Gordon343001b2011-03-07 12:45:45 +0000180 struct gui_img *img = skin_find_item(label,SKIN_FIND_IMAGE, data);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000181 if (img && img->loaded)
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000182 {
183 if (id->token == NULL)
184 {
185 img->display = id->subimage;
186 }
187 else
188 {
189 char buf[16];
190 const char *out;
Magnus Holmgren6d7900e2010-08-12 10:01:46 +0000191 int a = img->num_subimages;
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000192 out = get_token_value(gwps, id->token, info->offset,
193 buf, sizeof(buf), &a);
Magnus Holmgren6d7900e2010-08-12 10:01:46 +0000194
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000195 /* NOTE: get_token_value() returns values starting at 1! */
196 if (a == -1)
197 a = (out && *out) ? 1 : 2;
Jonathan Gordon70ebe462010-08-12 13:27:10 +0000198 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
199 a -= 2; /* 2 is added in statusbar-skinned.c! */
200 else
201 a--;
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000202 a += id->offset;
Magnus Holmgren6d7900e2010-08-12 10:01:46 +0000203
204 /* Clear the image, as in conditionals */
205 clear_image_pos(gwps, img);
206
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000207 /* If the token returned a value which is higher than
Magnus Holmgren6d7900e2010-08-12 10:01:46 +0000208 * the amount of subimages, don't draw it. */
209 if (a >= 0 && a < img->num_subimages)
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000210 {
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000211 img->display = a;
Magnus Holmgren6d7900e2010-08-12 10:01:46 +0000212 }
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000213 }
214 }
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000215 break;
216 }
217#ifdef HAVE_ALBUMART
218 case SKIN_TOKEN_ALBUMART_DISPLAY:
219 /* now draw the AA */
Jonathan Gordonf3a6d242010-08-05 12:45:46 +0000220 if (do_refresh && data->albumart)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000221 {
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))
225 {
226 struct dim dim = {data->albumart->width, data->albumart->height};
227 handle = radio_get_art_hid(&dim);
228 }
229#endif
230 data->albumart->draw_handle = handle;
231 }
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)
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000241 skin_render_playlistviewer(token->value.data, gwps,
242 info->skin_vp, info->refresh_type);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000243 break;
244
245#endif /* HAVE_LCD_BITMAP */
Jonathan Gordon87aa86c2011-03-27 08:01:58 +0000246#ifdef HAVE_SKIN_VARIABLES
247 case SKIN_TOKEN_VAR_SET:
248 if (do_refresh)
249 {
250 struct skin_var_changer *data = token->value.data;
251 if (data->direct)
252 data->var->value = data->newval;
253 else
254 {
255 data->var->value += data->newval;
256 if (data->max)
257 {
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;
262 }
263 }
264 if (data->var->value < 1)
265 data->var->value = 1;
266 data->var->last_changed = current_tick;
267 }
268 break;
269#endif
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000270 default:
271 return false;
272 }
273 return true;
274}
275
276
277
Frank Gevaertsd0bf13b2010-10-10 13:21:49 +0000278static void do_tags_in_hidden_conditional(struct skin_element* branch,
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000279 struct skin_draw_info *info)
280{
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)
288 {
289 int i;
290 for (i=0; i<branch->children_count; i++)
291 {
292 do_tags_in_hidden_conditional(branch->children[i], info);
293 }
294 }
295 else if (branch->type == LINE && branch->children_count)
296 {
297 struct skin_element *child = branch->children[0];
298 struct wps_token *token;
299 while (child)
300 {
301 if (child->type == CONDITIONAL)
302 {
303 int i;
304 for (i=0; i<child->children_count; i++)
305 {
306 do_tags_in_hidden_conditional(child->children[i], info);
307 }
308 child = child->next;
309 continue;
310 }
311 else if (child->type != TAG || !child->data)
312 {
313 child = child->next;
314 continue;
315 }
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)
320 {
Jonathan Gordon3f8e7fc2010-08-05 11:28:48 +0000321 struct image_display *id = token->value.data;
Jonathan Gordon343001b2011-03-07 12:45:45 +0000322 struct gui_img *img = skin_find_item(id->label,
323 SKIN_FIND_IMAGE, data);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000324 clear_image_pos(gwps, img);
325 }
326 else if (token->type == SKIN_TOKEN_PEAKMETER)
327 {
328 data->peak_meter_enabled = false;
329 }
330 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
331 {
Jonathan Gordonee4f8a92010-08-02 12:50:23 +0000332 char *label = token->value.data;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000333 struct skin_element *viewport;
334 for (viewport = data->tree;
335 viewport;
336 viewport = viewport->next)
337 {
338 struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data;
Jonathan Gordonee4f8a92010-08-02 12:50:23 +0000339 if (skin_viewport->label && strcmp(skin_viewport->label, label))
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000340 continue;
341 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
342 {
343 continue;
344 }
345 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
346 {
347 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
348 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
349 else
350 {
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;
356 }
357 }
358 }
359 }
360#endif
361#ifdef HAVE_ALBUMART
362 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
363 {
364 draw_album_art(gwps,
365 playback_current_aa_hid(data->playback_aa_slot), true);
366 }
367#endif
368 child = child->next;
369 }
370 }
371}
372
Frank Gevaertsd0bf13b2010-10-10 13:21:49 +0000373static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000374{
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)
378 {
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;
412 }
413}
414
415/* Draw a LINE element onto the display */
Frank Gevaertsd0bf13b2010-10-10 13:21:49 +0000416static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000417{
418 bool needs_update = false;
419 int last_value, value;
420
421 if (line->children_count == 0)
422 return false; /* empty line, do nothing */
423
424 struct skin_element *child = line->children[0];
425 struct conditional *conditional;
426 skin_render_func func = skin_render_line;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000427 int old_refresh_mode = info->refresh_type;
428 while (child)
429 {
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000430 switch (child->type)
431 {
432 case CONDITIONAL:
433 conditional = (struct conditional*)child->data;
434 last_value = conditional->last_value;
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000435 value = evaluate_conditional(info->gwps, info->offset,
436 conditional, child->children_count);
Jonathan Gordon216ed292010-09-02 11:43:33 +0000437 conditional->last_value = value;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000438 if (child->children_count == 1)
439 {
440 /* special handling so
441 * %?aa<true> and %?<true|false> need special handlng here */
442
Jonathan Gordon216ed292010-09-02 11:43:33 +0000443 if (value == -1) /* tag is false */
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000444 {
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;
449 }
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000450 }
451 else
452 {
453 if (last_value >= 0 && value != last_value && last_value < child->children_count)
454 do_tags_in_hidden_conditional(child->children[last_value], info);
455 }
456 if (child->children[value]->type == LINE_ALTERNATOR)
457 {
458 func = skin_render_alternator;
459 }
460 else if (child->children[value]->type == LINE)
461 func = skin_render_line;
462
463 if (value != last_value)
464 {
465 info->refresh_type = SKIN_REFRESH_ALL;
466 info->force_redraw = true;
467 }
468
469 if (func(child->children[value], info))
470 needs_update = true;
471 else
472 needs_update = needs_update || (last_value != value);
473
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;
481
482 fix_line_alignment(info, child);
483
484 if (!child->data)
485 {
486 break;
487 }
488 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
489 {
Jonathan Gordon562437b2010-09-26 06:43:33 +0000490 static char tempbuf[128];
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000491 const char *value = get_token_value(info->gwps, child->data,
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000492 info->offset, tempbuf,
493 sizeof(tempbuf), NULL);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000494 if (value)
495 {
Jonathan Gordone8bbbdf2010-08-15 14:35:34 +0000496#if CONFIG_RTC
497 if (child->tag->flags&SKIN_RTC_REFRESH)
498 needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
499#endif
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000500 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));
504 }
505 }
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;
516 }
517
518 child = child->next;
519 }
520 return needs_update;
521}
522
Frank Gevaertsd0bf13b2010-10-10 13:21:49 +0000523static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
Jonathan Gordon216ed292010-09-02 11:43:33 +0000524{
525 struct skin_element *element=line;
526 struct wps_token *token;
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000527 int retval = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
Jonathan Gordon216ed292010-09-02 11:43:33 +0000528 if (element->type == LINE)
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000529 {
530 if (element->children_count == 0)
531 return retval; /* empty line, so force redraw */
Jonathan Gordon216ed292010-09-02 11:43:33 +0000532 element = element->children[0];
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000533 }
Jonathan Gordon216ed292010-09-02 11:43:33 +0000534 while (element)
535 {
536 if (element->type == TAG &&
537 element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
538 {
539 token = element->data;
540 return token->value.i;
541 }
542 else if (element->type == CONDITIONAL)
543 {
544 struct conditional *conditional = element->data;
545 int val = evaluate_conditional(gwps, 0, conditional,
546 element->children_count);
547 if (val >= 0)
548 {
549 retval = get_subline_timeout(gwps, element->children[val]);
550 if (retval >= 0)
551 return retval;
552 }
553 }
554 element = element->next;
555 }
556 return retval;
557}
558
Frank Gevaertsd0bf13b2010-10-10 13:21:49 +0000559bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000560{
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)
565 {
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000566 alternator->current_line = element->children_count-1;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000567 changed_lines = true;
568 }
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000569 else if (TIME_AFTER(current_tick, alternator->next_change_tick))
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000570 {
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000571 changed_lines = true;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000572 }
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000573
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000574 if (changed_lines)
575 {
Nils Wallménius3b04a852011-06-12 22:17:45 +0000576 struct skin_element *current_line;
Jonathan Gordon216ed292010-09-02 11:43:33 +0000577 int start = alternator->current_line;
578 int try_line = start;
579 bool suitable = false;
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000580 int rettimeout = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
Jonathan Gordon216ed292010-09-02 11:43:33 +0000581
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)
589 {
590 current_line = element->children[try_line];
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000591 rettimeout = get_subline_timeout(info->gwps,
592 current_line->children[0]);
593 if (rettimeout > 0)
Jonathan Gordon216ed292010-09-02 11:43:33 +0000594 {
595 suitable = true;
596 }
597 }
598 }
599 while (try_line != start && !suitable);
600
601 if (suitable)
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000602 {
Jonathan Gordon216ed292010-09-02 11:43:33 +0000603 alternator->current_line = try_line;
Jonathan Gordon0b824da2010-10-10 06:47:52 +0000604 alternator->next_change_tick = current_tick + rettimeout;
605 }
Jonathan Gordon216ed292010-09-02 11:43:33 +0000606
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000607 info->refresh_type = SKIN_REFRESH_ALL;
608 info->force_redraw = true;
609 }
610 bool ret = skin_render_line(element->children[alternator->current_line], info);
611 info->refresh_type = old_refresh;
612 return changed_lines || ret;
613}
614
Frank Gevaertsd0bf13b2010-10-10 13:21:49 +0000615static void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
Bertrik Sikken57933f22010-07-30 23:47:49 +0000616 struct skin_viewport* skin_viewport, unsigned long refresh_type)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000617{
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,
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000630 .skin_vp = skin_viewport,
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000631 .offset = 0,
632 .text_style = STYLE_DEFAULT
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000633 };
634
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)
641 {
642 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
643 img->display = -1;
644 imglist = imglist->next;
645 }
646#endif
647
648 while (line)
649 {
650 linebuf[0] = '\0';
651 info.no_line_break = false;
652 info.line_scrolls = false;
653 info.force_redraw = false;
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000654#ifdef HAVE_LCD_COLOR
655 if (info.text_style&STYLE_GRADIENT)
656 {
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);
663 }
664#endif
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000665 info.cur_align_start = info.buf;
666 align->left = info.buf;
667 align->center = NULL;
668 align->right = NULL;
669
670
671 if (line->type == LINE_ALTERNATOR)
672 func = skin_render_alternator;
673 else if (line->type == LINE)
674 func = skin_render_line;
675
676 needs_update = func(line, &info);
Jonathan Gordon04321472010-10-13 09:46:01 +0000677#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)
680 {
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);
686 }
687#endif
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000688 /* only update if the line needs to be, and there is something to write */
689 if (refresh_type && needs_update)
690 {
691 if (info.line_scrolls)
692 {
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)
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000696 write_line(display, align, info.line_number, true, info.text_style);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000697 }
698 else
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000699 write_line(display, align, info.line_number, false, info.text_style);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000700 }
701 if (!info.no_line_break)
702 info.line_number++;
703 line = line->next;
704 }
705#ifdef HAVE_LCD_BITMAP
706 wps_display_images(gwps, &skin_viewport->vp);
707#endif
708}
709
710void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
711{
712 struct wps_data *data = gwps->data;
713 struct screen *display = gwps->display;
714
Nils Wallménius3b04a852011-06-12 22:17:45 +0000715 struct skin_element* viewport;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000716 struct skin_viewport* skin_viewport;
717
718 int old_refresh_mode = refresh_mode;
719
720#ifdef HAVE_LCD_CHARCELLS
721 int i;
722 for (i = 0; i < 8; i++)
723 {
724 if (data->wps_progress_pat[i] == 0)
725 data->wps_progress_pat[i] = display->get_locked_pattern();
726 }
727#endif
728 viewport = data->tree;
729 skin_viewport = (struct skin_viewport *)viewport->data;
Jonathan Gordonee4f8a92010-08-02 12:50:23 +0000730 if (skin_viewport->label && viewport->next &&
731 !strcmp(skin_viewport->label,VP_DEFAULT_LABEL))
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000732 refresh_mode = 0;
733
734 for (viewport = data->tree;
735 viewport;
736 viewport = viewport->next)
737 {
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
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000745#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
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000750
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;
755 }
756 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
757 {
758 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
759 continue;
760 }
761 else if (((skin_viewport->hidden_flags&
762 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
763 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
764 {
765 vp_refresh_mode = SKIN_REFRESH_ALL;
766 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
767 }
768
769 display->set_viewport(&skin_viewport->vp);
770 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
771 {
772 display->clear_viewport();
773 }
774 /* render */
Jonathan Gordon39cf6dd2010-10-12 09:36:59 +0000775 if (viewport->children_count)
776 skin_render_viewport(viewport->children[0], gwps,
777 skin_viewport, vp_refresh_mode);
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000778 refresh_mode = old_refresh_mode;
779 }
780
781 /* Restore the default viewport */
782 display->set_viewport(NULL);
783 display->update();
784}
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000785
Jonathan Gordona9d752b2010-08-14 15:31:04 +0000786#ifdef HAVE_LCD_BITMAP
Frank Gevaerts561999e2010-10-10 13:26:51 +0000787static __attribute__((noinline)) void skin_render_playlistviewer(struct playlistviewer* viewer,
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000788 struct gui_wps *gwps,
789 struct skin_viewport* skin_viewport,
790 unsigned long refresh_type)
791{
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,
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000805 .offset = viewer->start_offset,
806 .text_style = STYLE_DEFAULT
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000807 };
808
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
Jonathan Gordonb58d3652011-06-01 14:41:49 +0000814 if (get_current_activity() == ACTIVITY_FM)
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000815 {
816 cur_pos = radio_current_preset();
817 start_item = cur_pos + viewer->start_offset;
818 max = start_item+radio_preset_count();
819 }
820 else
821#endif
822 {
Jonathan Gordon9928e342010-09-14 11:56:50 +0000823 struct cuesheet *cue = skin_get_global_state()->id3 ?
824 skin_get_global_state()->id3->cuesheet : NULL;
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000825 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);
830 }
831 if (max-start_item > nb_lines)
832 max = start_item + nb_lines;
833
834 line = viewer->line;
835 while (start_item < max)
836 {
837 linebuf[0] = '\0';
838 info.no_line_break = false;
839 info.line_scrolls = false;
840 info.force_redraw = false;
841
842 info.cur_align_start = info.buf;
843 align->left = info.buf;
844 align->center = NULL;
845 align->right = NULL;
846
847
848 if (line->type == LINE_ALTERNATOR)
849 func = skin_render_alternator;
850 else if (line->type == LINE)
851 func = skin_render_line;
852
853 needs_update = func(line, &info);
854
855 /* only update if the line needs to be, and there is something to write */
856 if (refresh_type && needs_update)
857 {
858 if (info.line_scrolls)
859 {
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)
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000863 write_line(display, align, info.line_number, true, info.text_style);
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000864 }
865 else
Jonathan Gordon281f1a12011-08-14 13:50:07 +0000866 write_line(display, align, info.line_number, false, info.text_style);
Jonathan Gordondc3778a2010-08-14 15:17:59 +0000867 }
868 info.line_number++;
869 info.offset++;
870 start_item++;
871 }
872}
Jonathan Gordona9d752b2010-08-14 15:31:04 +0000873#endif