1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
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"
47 #include "root_menu.h"
53 struct skin_draw_info
{
55 struct skin_viewport
*skin_vp
;
57 unsigned long refresh_type
;
59 char* cur_align_start
;
60 struct align_pos align
;
68 int offset
; /* used by the playlist viewer */
71 typedef bool (*skin_render_func
)(struct skin_element
* alternator
, struct skin_draw_info
*info
);
72 bool skin_render_alternator(struct skin_element
* alternator
, struct skin_draw_info
*info
);
74 static void skin_render_playlistviewer(struct playlistviewer
* viewer
,
76 struct skin_viewport
* skin_viewport
,
77 unsigned long refresh_type
);
80 static bool do_non_text_tags(struct gui_wps
*gwps
, struct skin_draw_info
*info
,
81 struct skin_element
*element
, struct viewport
* vp
)
83 #ifndef HAVE_LCD_BITMAP
84 (void)vp
; /* silence warnings */
86 struct wps_token
*token
= (struct wps_token
*)element
->data
;
87 struct wps_data
*data
= gwps
->data
;
88 bool do_refresh
= (element
->tag
->flags
& info
->refresh_type
) > 0;
91 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
92 case SKIN_TOKEN_VIEWPORT_FGCOLOUR
:
94 struct viewport_colour
*col
= token
->value
.data
;
95 col
->vp
->fg_pattern
= col
->colour
;
98 case SKIN_TOKEN_VIEWPORT_BGCOLOUR
:
100 struct viewport_colour
*col
= token
->value
.data
;
101 col
->vp
->bg_pattern
= col
->colour
;
105 case SKIN_TOKEN_VIEWPORT_ENABLE
:
107 char *label
= token
->value
.data
;
108 char temp
= VP_DRAW_HIDEABLE
;
109 struct skin_element
*viewport
= gwps
->data
->tree
;
112 struct skin_viewport
*skinvp
= (struct skin_viewport
*)viewport
->data
;
113 if (skinvp
->label
&& !skinvp
->is_infovp
&&
114 !strcmp(skinvp
->label
, label
))
116 if (skinvp
->hidden_flags
&VP_DRAW_HIDDEN
)
118 temp
|= VP_DRAW_WASHIDDEN
;
120 skinvp
->hidden_flags
= temp
;
122 viewport
= viewport
->next
;
126 #ifdef HAVE_LCD_BITMAP
127 case SKIN_TOKEN_UIVIEWPORT_ENABLE
:
128 sb_set_info_vp(gwps
->display
->screen_type
,
131 case SKIN_TOKEN_PEAKMETER
:
132 data
->peak_meter_enabled
= true;
134 draw_peakmeters(gwps
, info
->line_number
, vp
);
137 case SKIN_TOKEN_VOLUMEBAR
:
138 case SKIN_TOKEN_BATTERY_PERCENTBAR
:
139 case SKIN_TOKEN_PROGRESSBAR
:
141 #ifdef HAVE_LCD_BITMAP
142 struct progressbar
*bar
= (struct progressbar
*)token
->value
.data
;
144 draw_progressbar(gwps
, info
->line_number
, bar
);
145 #else /* HAVE_LCD_CHARCELL */
148 if (data
->full_line_progressbar
)
149 draw_player_fullbar(gwps
, info
->buf
, info
->buf_size
);
151 draw_player_progress(gwps
);
156 #ifdef HAVE_LCD_BITMAP
157 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
:
158 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
:
160 struct image_display
*id
= token
->value
.data
;
161 const char* label
= id
->label
;
162 struct gui_img
*img
= find_image(label
, data
);
163 if (img
&& img
->loaded
)
165 if (id
->token
== NULL
)
167 img
->display
= id
->subimage
;
173 int a
= img
->num_subimages
;
174 out
= get_token_value(gwps
, id
->token
, info
->offset
,
175 buf
, sizeof(buf
), &a
);
177 /* NOTE: get_token_value() returns values starting at 1! */
179 a
= (out
&& *out
) ? 1 : 2;
180 if (token
->type
== SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
)
181 a
-= 2; /* 2 is added in statusbar-skinned.c! */
186 /* Clear the image, as in conditionals */
187 clear_image_pos(gwps
, img
);
189 /* If the token returned a value which is higher than
190 * the amount of subimages, don't draw it. */
191 if (a
>= 0 && a
< img
->num_subimages
)
200 case SKIN_TOKEN_ALBUMART_DISPLAY
:
201 /* now draw the AA */
202 if (do_refresh
&& data
->albumart
)
204 int handle
= playback_current_aa_hid(data
->playback_aa_slot
);
206 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
208 struct dim dim
= {data
->albumart
->width
, data
->albumart
->height
};
209 handle
= radio_get_art_hid(&dim
);
212 data
->albumart
->draw_handle
= handle
;
216 case SKIN_TOKEN_DRAW_INBUILTBAR
:
217 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
218 info
->refresh_type
== SKIN_REFRESH_ALL
,
221 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST
:
223 skin_render_playlistviewer(token
->value
.data
, gwps
,
224 info
->skin_vp
, info
->refresh_type
);
227 #endif /* HAVE_LCD_BITMAP */
236 static void do_tags_in_hidden_conditional(struct skin_element
* branch
,
237 struct skin_draw_info
*info
)
239 #ifdef HAVE_LCD_BITMAP
240 struct gui_wps
*gwps
= info
->gwps
;
241 struct wps_data
*data
= gwps
->data
;
243 /* Tags here are ones which need to be "turned off" or cleared
244 * if they are in a conditional branch which isnt being used */
245 if (branch
->type
== LINE_ALTERNATOR
)
248 for (i
=0; i
<branch
->children_count
; i
++)
250 do_tags_in_hidden_conditional(branch
->children
[i
], info
);
253 else if (branch
->type
== LINE
&& branch
->children_count
)
255 struct skin_element
*child
= branch
->children
[0];
256 struct wps_token
*token
;
259 if (child
->type
== CONDITIONAL
)
262 for (i
=0; i
<child
->children_count
; i
++)
264 do_tags_in_hidden_conditional(child
->children
[i
], info
);
269 else if (child
->type
!= TAG
|| !child
->data
)
274 token
= (struct wps_token
*)child
->data
;
275 #ifdef HAVE_LCD_BITMAP
276 /* clear all pictures in the conditional and nested ones */
277 if (token
->type
== SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
)
279 struct image_display
*id
= token
->value
.data
;
280 struct gui_img
*img
= find_image(id
->label
, data
);
281 clear_image_pos(gwps
, img
);
283 else if (token
->type
== SKIN_TOKEN_PEAKMETER
)
285 data
->peak_meter_enabled
= false;
287 else if (token
->type
== SKIN_TOKEN_VIEWPORT_ENABLE
)
289 char *label
= token
->value
.data
;
290 struct skin_element
*viewport
;
291 for (viewport
= data
->tree
;
293 viewport
= viewport
->next
)
295 struct skin_viewport
*skin_viewport
= (struct skin_viewport
*)viewport
->data
;
296 if (skin_viewport
->label
&& strcmp(skin_viewport
->label
, label
))
298 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
302 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
304 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
305 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
308 gwps
->display
->set_viewport(&skin_viewport
->vp
);
309 gwps
->display
->clear_viewport();
310 gwps
->display
->scroll_stop(&skin_viewport
->vp
);
311 gwps
->display
->set_viewport(&info
->skin_vp
->vp
);
312 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
319 else if (data
->albumart
&& token
->type
== SKIN_TOKEN_ALBUMART_DISPLAY
)
322 playback_current_aa_hid(data
->playback_aa_slot
), true);
330 static void fix_line_alignment(struct skin_draw_info
*info
, struct skin_element
*element
)
332 struct align_pos
*align
= &info
->align
;
333 char *cur_pos
= info
->cur_align_start
+ strlen(info
->cur_align_start
);
334 switch (element
->tag
->type
)
336 case SKIN_TOKEN_ALIGN_LEFT
:
337 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
338 align
->left
= cur_pos
;
339 info
->cur_align_start
= cur_pos
;
341 case SKIN_TOKEN_ALIGN_LEFT_RTL
:
342 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
344 align
->right
= cur_pos
;
346 align
->left
= cur_pos
;
347 info
->cur_align_start
= cur_pos
;
349 case SKIN_TOKEN_ALIGN_CENTER
:
350 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
351 align
->center
= cur_pos
;
352 info
->cur_align_start
= cur_pos
;
354 case SKIN_TOKEN_ALIGN_RIGHT
:
355 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
356 align
->right
= cur_pos
;
357 info
->cur_align_start
= cur_pos
;
359 case SKIN_TOKEN_ALIGN_RIGHT_RTL
:
360 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
362 align
->left
= cur_pos
;
364 align
->right
= cur_pos
;
365 info
->cur_align_start
= cur_pos
;
372 /* Draw a LINE element onto the display */
373 static bool skin_render_line(struct skin_element
* line
, struct skin_draw_info
*info
)
375 bool needs_update
= false;
376 int last_value
, value
;
378 if (line
->children_count
== 0)
379 return false; /* empty line, do nothing */
381 struct skin_element
*child
= line
->children
[0];
382 struct conditional
*conditional
;
383 skin_render_func func
= skin_render_line
;
385 int old_refresh_mode
= info
->refresh_type
;
392 conditional
= (struct conditional
*)child
->data
;
393 last_value
= conditional
->last_value
;
394 value
= evaluate_conditional(info
->gwps
, info
->offset
,
395 conditional
, child
->children_count
);
397 if (value
!= 1 && value
>= child
->children_count
)
398 value
= child
->children_count
-1;
399 if (child
->children_count
== 1)
401 /* special handling so
402 * %?aa<true> and %?<true|false> need special handlng here */
404 if (value
== 1) /* tag is false */
406 /* we are in a false branch of a %?aa<true> conditional */
408 do_tags_in_hidden_conditional(child
->children
[0], info
);
415 if (last_value
>= 0 && value
!= last_value
&& last_value
< child
->children_count
)
416 do_tags_in_hidden_conditional(child
->children
[last_value
], info
);
418 if (child
->children
[value
]->type
== LINE_ALTERNATOR
)
420 func
= skin_render_alternator
;
422 else if (child
->children
[value
]->type
== LINE
)
423 func
= skin_render_line
;
425 if (value
!= last_value
)
427 info
->refresh_type
= SKIN_REFRESH_ALL
;
428 info
->force_redraw
= true;
431 if (func(child
->children
[value
], info
))
434 needs_update
= needs_update
|| (last_value
!= value
);
436 info
->refresh_type
= old_refresh_mode
;
439 if (child
->tag
->flags
& NOBREAK
)
440 info
->no_line_break
= true;
441 if (child
->tag
->type
== SKIN_TOKEN_SUBLINE_SCROLL
)
442 info
->line_scrolls
= true;
444 fix_line_alignment(info
, child
);
450 if (!do_non_text_tags(info
->gwps
, info
, child
, &info
->skin_vp
->vp
))
452 const char *value
= get_token_value(info
->gwps
, child
->data
,
453 info
->offset
, tempbuf
,
454 sizeof(tempbuf
), NULL
);
457 needs_update
= needs_update
||
458 ((child
->tag
->flags
&info
->refresh_type
)!=0);
459 strlcat(info
->cur_align_start
, value
,
460 info
->buf_size
- (info
->cur_align_start
-info
->buf
));
465 strlcat(info
->cur_align_start
, child
->data
,
466 info
->buf_size
- (info
->cur_align_start
-info
->buf
));
467 needs_update
= needs_update
||
468 (info
->refresh_type
&SKIN_REFRESH_STATIC
) != 0;
480 bool skin_render_alternator(struct skin_element
* element
, struct skin_draw_info
*info
)
482 bool changed_lines
= false;
483 struct line_alternator
*alternator
= (struct line_alternator
*)element
->data
;
484 unsigned old_refresh
= info
->refresh_type
;
485 if (info
->refresh_type
== SKIN_REFRESH_ALL
)
487 alternator
->current_line
= 0;
488 alternator
->last_change_tick
= current_tick
;
489 changed_lines
= true;
493 struct skin_element
*current_line
= element
->children
[alternator
->current_line
];
494 struct line
*line
= (struct line
*)current_line
->data
;
495 int next_change
= alternator
->last_change_tick
+ line
->timeout
;
496 if (TIME_AFTER(current_tick
, next_change
))
498 alternator
->current_line
++;
499 if (alternator
->current_line
>= element
->children_count
)
500 alternator
->current_line
= 0;
501 alternator
->last_change_tick
= current_tick
;
502 changed_lines
= true;
505 if (element
->children
[alternator
->current_line
]->children_count
== 0)
507 /* skip empty sublines */
508 alternator
->current_line
++;
509 if (alternator
->current_line
>= element
->children_count
)
510 alternator
->current_line
= 0;
511 changed_lines
= true;
516 info
->refresh_type
= SKIN_REFRESH_ALL
;
517 info
->force_redraw
= true;
519 bool ret
= skin_render_line(element
->children
[alternator
->current_line
], info
);
520 info
->refresh_type
= old_refresh
;
521 return changed_lines
|| ret
;
524 static void skin_render_viewport(struct skin_element
* viewport
, struct gui_wps
*gwps
,
525 struct skin_viewport
* skin_viewport
, unsigned long refresh_type
)
527 struct screen
*display
= gwps
->display
;
528 char linebuf
[MAX_LINE
];
529 skin_render_func func
= skin_render_line
;
530 struct skin_element
* line
= viewport
;
531 struct skin_draw_info info
= {
534 .buf_size
= sizeof(linebuf
),
536 .no_line_break
= false,
537 .line_scrolls
= false,
538 .refresh_type
= refresh_type
,
539 .skin_vp
= skin_viewport
,
543 struct align_pos
* align
= &info
.align
;
545 #ifdef HAVE_LCD_BITMAP
546 /* Set images to not to be displayed */
547 struct skin_token_list
*imglist
= gwps
->data
->images
;
550 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
552 imglist
= imglist
->next
;
559 info
.no_line_break
= false;
560 info
.line_scrolls
= false;
561 info
.force_redraw
= false;
563 info
.cur_align_start
= info
.buf
;
564 align
->left
= info
.buf
;
565 align
->center
= NULL
;
569 if (line
->type
== LINE_ALTERNATOR
)
570 func
= skin_render_alternator
;
571 else if (line
->type
== LINE
)
572 func
= skin_render_line
;
574 needs_update
= func(line
, &info
);
576 /* only update if the line needs to be, and there is something to write */
577 if (refresh_type
&& needs_update
)
579 if (info
.line_scrolls
)
581 /* if the line is a scrolling one we don't want to update
582 too often, so that it has the time to scroll */
583 if ((refresh_type
& SKIN_REFRESH_SCROLL
) || info
.force_redraw
)
584 write_line(display
, align
, info
.line_number
, true);
587 write_line(display
, align
, info
.line_number
, false);
589 if (!info
.no_line_break
)
593 #ifdef HAVE_LCD_BITMAP
594 wps_display_images(gwps
, &skin_viewport
->vp
);
598 void skin_render(struct gui_wps
*gwps
, unsigned refresh_mode
)
600 struct wps_data
*data
= gwps
->data
;
601 struct screen
*display
= gwps
->display
;
603 struct skin_element
* viewport
= data
->tree
;
604 struct skin_viewport
* skin_viewport
;
606 int old_refresh_mode
= refresh_mode
;
608 #ifdef HAVE_LCD_CHARCELLS
610 for (i
= 0; i
< 8; i
++)
612 if (data
->wps_progress_pat
[i
] == 0)
613 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
616 viewport
= data
->tree
;
617 skin_viewport
= (struct skin_viewport
*)viewport
->data
;
618 if (skin_viewport
->label
&& viewport
->next
&&
619 !strcmp(skin_viewport
->label
,VP_DEFAULT_LABEL
))
622 for (viewport
= data
->tree
;
624 viewport
= viewport
->next
)
627 skin_viewport
= (struct skin_viewport
*)viewport
->data
;
628 unsigned vp_refresh_mode
= refresh_mode
;
629 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
630 skin_viewport
->vp
.fg_pattern
= skin_viewport
->start_fgcolour
;
631 skin_viewport
->vp
.bg_pattern
= skin_viewport
->start_bgcolour
;
634 /* dont redraw the viewport if its disabled */
635 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
636 { /* don't draw anything into this one */
639 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
641 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
644 else if (((skin_viewport
->hidden_flags
&
645 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
646 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
648 vp_refresh_mode
= SKIN_REFRESH_ALL
;
649 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
652 display
->set_viewport(&skin_viewport
->vp
);
653 if ((vp_refresh_mode
&SKIN_REFRESH_ALL
) == SKIN_REFRESH_ALL
)
655 display
->clear_viewport();
658 skin_render_viewport(viewport
->children
[0], gwps
,
659 skin_viewport
, vp_refresh_mode
);
660 refresh_mode
= old_refresh_mode
;
663 /* Restore the default viewport */
664 display
->set_viewport(NULL
);
669 static void skin_render_playlistviewer(struct playlistviewer
* viewer
,
670 struct gui_wps
*gwps
,
671 struct skin_viewport
* skin_viewport
,
672 unsigned long refresh_type
)
674 struct screen
*display
= gwps
->display
;
675 char linebuf
[MAX_LINE
];
676 skin_render_func func
= skin_render_line
;
677 struct skin_element
* line
;
678 struct skin_draw_info info
= {
681 .buf_size
= sizeof(linebuf
),
683 .no_line_break
= false,
684 .line_scrolls
= false,
685 .refresh_type
= refresh_type
,
686 .skin_vp
= skin_viewport
,
687 .offset
= viewer
->start_offset
690 struct align_pos
* align
= &info
.align
;
692 int cur_pos
, start_item
, max
;
693 int nb_lines
= viewport_get_nb_lines(viewer
->vp
);
695 if (current_screen() == GO_TO_FM
)
697 cur_pos
= radio_current_preset();
698 start_item
= cur_pos
+ viewer
->start_offset
;
699 max
= start_item
+radio_preset_count();
704 struct cuesheet
*cue
= gwps
->state
->id3
? gwps
->state
->id3
->cuesheet
:NULL
;
705 cur_pos
= playlist_get_display_index();
706 max
= playlist_amount()+1;
708 max
+= cue
->track_count
;
709 start_item
= MAX(0, cur_pos
+ viewer
->start_offset
);
711 if (max
-start_item
> nb_lines
)
712 max
= start_item
+ nb_lines
;
715 while (start_item
< max
)
718 info
.no_line_break
= false;
719 info
.line_scrolls
= false;
720 info
.force_redraw
= false;
722 info
.cur_align_start
= info
.buf
;
723 align
->left
= info
.buf
;
724 align
->center
= NULL
;
728 if (line
->type
== LINE_ALTERNATOR
)
729 func
= skin_render_alternator
;
730 else if (line
->type
== LINE
)
731 func
= skin_render_line
;
733 needs_update
= func(line
, &info
);
735 /* only update if the line needs to be, and there is something to write */
736 if (refresh_type
&& needs_update
)
738 if (info
.line_scrolls
)
740 /* if the line is a scrolling one we don't want to update
741 too often, so that it has the time to scroll */
742 if ((refresh_type
& SKIN_REFRESH_SCROLL
) || info
.force_redraw
)
743 write_line(display
, align
, info
.line_number
, true);
746 write_line(display
, align
, info
.line_number
, false);