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 #ifdef HAVE_LCD_BITMAP
75 static void skin_render_playlistviewer(struct playlistviewer
* viewer
,
77 struct skin_viewport
* skin_viewport
,
78 unsigned long refresh_type
);
81 static __attribute__((noinline
)) bool do_non_text_tags(struct gui_wps
*gwps
, struct skin_draw_info
*info
,
82 struct skin_element
*element
, struct viewport
* vp
)
84 #ifndef HAVE_LCD_BITMAP
85 (void)vp
; /* silence warnings */
87 struct wps_token
*token
= (struct wps_token
*)element
->data
;
88 struct wps_data
*data
= gwps
->data
;
89 bool do_refresh
= (element
->tag
->flags
& info
->refresh_type
) > 0;
92 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
93 case SKIN_TOKEN_VIEWPORT_FGCOLOUR
:
95 struct viewport_colour
*col
= token
->value
.data
;
96 col
->vp
->fg_pattern
= col
->colour
;
99 case SKIN_TOKEN_VIEWPORT_BGCOLOUR
:
101 struct viewport_colour
*col
= token
->value
.data
;
102 col
->vp
->bg_pattern
= col
->colour
;
106 case SKIN_TOKEN_VIEWPORT_ENABLE
:
108 char *label
= token
->value
.data
;
109 char temp
= VP_DRAW_HIDEABLE
;
110 struct skin_element
*viewport
= gwps
->data
->tree
;
113 struct skin_viewport
*skinvp
= (struct skin_viewport
*)viewport
->data
;
114 if (skinvp
->label
&& !skinvp
->is_infovp
&&
115 !strcmp(skinvp
->label
, label
))
117 if (skinvp
->hidden_flags
&VP_DRAW_HIDDEN
)
119 temp
|= VP_DRAW_WASHIDDEN
;
121 skinvp
->hidden_flags
= temp
;
123 viewport
= viewport
->next
;
127 #ifdef HAVE_LCD_BITMAP
128 case SKIN_TOKEN_UIVIEWPORT_ENABLE
:
129 sb_set_info_vp(gwps
->display
->screen_type
,
132 case SKIN_TOKEN_PEAKMETER
:
133 data
->peak_meter_enabled
= true;
135 draw_peakmeters(gwps
, info
->line_number
, vp
);
138 case SKIN_TOKEN_VOLUMEBAR
:
139 case SKIN_TOKEN_BATTERY_PERCENTBAR
:
140 case SKIN_TOKEN_PROGRESSBAR
:
142 #ifdef HAVE_LCD_BITMAP
143 struct progressbar
*bar
= (struct progressbar
*)token
->value
.data
;
145 draw_progressbar(gwps
, info
->line_number
, bar
);
146 #else /* HAVE_LCD_CHARCELL */
149 if (data
->full_line_progressbar
)
150 draw_player_fullbar(gwps
, info
->buf
, info
->buf_size
);
152 draw_player_progress(gwps
);
157 #ifdef HAVE_LCD_BITMAP
158 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
:
159 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
:
161 struct image_display
*id
= token
->value
.data
;
162 const char* label
= id
->label
;
163 struct gui_img
*img
= find_image(label
, data
);
164 if (img
&& img
->loaded
)
166 if (id
->token
== NULL
)
168 img
->display
= id
->subimage
;
174 int a
= img
->num_subimages
;
175 out
= get_token_value(gwps
, id
->token
, info
->offset
,
176 buf
, sizeof(buf
), &a
);
178 /* NOTE: get_token_value() returns values starting at 1! */
180 a
= (out
&& *out
) ? 1 : 2;
181 if (token
->type
== SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
)
182 a
-= 2; /* 2 is added in statusbar-skinned.c! */
187 /* Clear the image, as in conditionals */
188 clear_image_pos(gwps
, img
);
190 /* If the token returned a value which is higher than
191 * the amount of subimages, don't draw it. */
192 if (a
>= 0 && a
< img
->num_subimages
)
201 case SKIN_TOKEN_ALBUMART_DISPLAY
:
202 /* now draw the AA */
203 if (do_refresh
&& data
->albumart
)
205 int handle
= playback_current_aa_hid(data
->playback_aa_slot
);
207 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
209 struct dim dim
= {data
->albumart
->width
, data
->albumart
->height
};
210 handle
= radio_get_art_hid(&dim
);
213 data
->albumart
->draw_handle
= handle
;
217 case SKIN_TOKEN_DRAW_INBUILTBAR
:
218 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
219 info
->refresh_type
== SKIN_REFRESH_ALL
,
222 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST
:
224 skin_render_playlistviewer(token
->value
.data
, gwps
,
225 info
->skin_vp
, info
->refresh_type
);
228 #endif /* HAVE_LCD_BITMAP */
237 static __attribute__((noinline
)) void do_tags_in_hidden_conditional(struct skin_element
* branch
,
238 struct skin_draw_info
*info
)
240 #ifdef HAVE_LCD_BITMAP
241 struct gui_wps
*gwps
= info
->gwps
;
242 struct wps_data
*data
= gwps
->data
;
244 /* Tags here are ones which need to be "turned off" or cleared
245 * if they are in a conditional branch which isnt being used */
246 if (branch
->type
== LINE_ALTERNATOR
)
249 for (i
=0; i
<branch
->children_count
; i
++)
251 do_tags_in_hidden_conditional(branch
->children
[i
], info
);
254 else if (branch
->type
== LINE
&& branch
->children_count
)
256 struct skin_element
*child
= branch
->children
[0];
257 struct wps_token
*token
;
260 if (child
->type
== CONDITIONAL
)
263 for (i
=0; i
<child
->children_count
; i
++)
265 do_tags_in_hidden_conditional(child
->children
[i
], info
);
270 else if (child
->type
!= TAG
|| !child
->data
)
275 token
= (struct wps_token
*)child
->data
;
276 #ifdef HAVE_LCD_BITMAP
277 /* clear all pictures in the conditional and nested ones */
278 if (token
->type
== SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
)
280 struct image_display
*id
= token
->value
.data
;
281 struct gui_img
*img
= find_image(id
->label
, data
);
282 clear_image_pos(gwps
, img
);
284 else if (token
->type
== SKIN_TOKEN_PEAKMETER
)
286 data
->peak_meter_enabled
= false;
288 else if (token
->type
== SKIN_TOKEN_VIEWPORT_ENABLE
)
290 char *label
= token
->value
.data
;
291 struct skin_element
*viewport
;
292 for (viewport
= data
->tree
;
294 viewport
= viewport
->next
)
296 struct skin_viewport
*skin_viewport
= (struct skin_viewport
*)viewport
->data
;
297 if (skin_viewport
->label
&& strcmp(skin_viewport
->label
, label
))
299 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
303 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
305 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
306 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
309 gwps
->display
->set_viewport(&skin_viewport
->vp
);
310 gwps
->display
->clear_viewport();
311 gwps
->display
->scroll_stop(&skin_viewport
->vp
);
312 gwps
->display
->set_viewport(&info
->skin_vp
->vp
);
313 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
320 else if (data
->albumart
&& token
->type
== SKIN_TOKEN_ALBUMART_DISPLAY
)
323 playback_current_aa_hid(data
->playback_aa_slot
), true);
331 static __attribute__((noinline
)) void fix_line_alignment(struct skin_draw_info
*info
, struct skin_element
*element
)
333 struct align_pos
*align
= &info
->align
;
334 char *cur_pos
= info
->cur_align_start
+ strlen(info
->cur_align_start
);
335 switch (element
->tag
->type
)
337 case SKIN_TOKEN_ALIGN_LEFT
:
338 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
339 align
->left
= cur_pos
;
340 info
->cur_align_start
= cur_pos
;
342 case SKIN_TOKEN_ALIGN_LEFT_RTL
:
343 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
345 align
->right
= cur_pos
;
347 align
->left
= cur_pos
;
348 info
->cur_align_start
= cur_pos
;
350 case SKIN_TOKEN_ALIGN_CENTER
:
351 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
352 align
->center
= cur_pos
;
353 info
->cur_align_start
= cur_pos
;
355 case SKIN_TOKEN_ALIGN_RIGHT
:
356 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
357 align
->right
= cur_pos
;
358 info
->cur_align_start
= cur_pos
;
360 case SKIN_TOKEN_ALIGN_RIGHT_RTL
:
361 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
363 align
->left
= cur_pos
;
365 align
->right
= cur_pos
;
366 info
->cur_align_start
= cur_pos
;
373 /* Draw a LINE element onto the display */
374 static __attribute__((noinline
)) bool skin_render_line(struct skin_element
* line
, struct skin_draw_info
*info
)
376 bool needs_update
= false;
377 int last_value
, value
;
379 if (line
->children_count
== 0)
380 return false; /* empty line, do nothing */
382 struct skin_element
*child
= line
->children
[0];
383 struct conditional
*conditional
;
384 skin_render_func func
= skin_render_line
;
385 int old_refresh_mode
= info
->refresh_type
;
391 conditional
= (struct conditional
*)child
->data
;
392 last_value
= conditional
->last_value
;
393 value
= evaluate_conditional(info
->gwps
, info
->offset
,
394 conditional
, child
->children_count
);
395 conditional
->last_value
= value
;
396 if (child
->children_count
== 1)
398 /* special handling so
399 * %?aa<true> and %?<true|false> need special handlng here */
401 if (value
== -1) /* tag is false */
403 /* we are in a false branch of a %?aa<true> conditional */
405 do_tags_in_hidden_conditional(child
->children
[0], info
);
411 if (last_value
>= 0 && value
!= last_value
&& last_value
< child
->children_count
)
412 do_tags_in_hidden_conditional(child
->children
[last_value
], info
);
414 if (child
->children
[value
]->type
== LINE_ALTERNATOR
)
416 func
= skin_render_alternator
;
418 else if (child
->children
[value
]->type
== LINE
)
419 func
= skin_render_line
;
421 if (value
!= last_value
)
423 info
->refresh_type
= SKIN_REFRESH_ALL
;
424 info
->force_redraw
= true;
427 if (func(child
->children
[value
], info
))
430 needs_update
= needs_update
|| (last_value
!= value
);
432 info
->refresh_type
= old_refresh_mode
;
435 if (child
->tag
->flags
& NOBREAK
)
436 info
->no_line_break
= true;
437 if (child
->tag
->type
== SKIN_TOKEN_SUBLINE_SCROLL
)
438 info
->line_scrolls
= true;
440 fix_line_alignment(info
, child
);
446 if (!do_non_text_tags(info
->gwps
, info
, child
, &info
->skin_vp
->vp
))
448 static char tempbuf
[128];
449 const char *value
= get_token_value(info
->gwps
, child
->data
,
450 info
->offset
, tempbuf
,
451 sizeof(tempbuf
), NULL
);
455 if (child
->tag
->flags
&SKIN_RTC_REFRESH
)
456 needs_update
= needs_update
|| info
->refresh_type
&SKIN_REFRESH_DYNAMIC
;
458 needs_update
= needs_update
||
459 ((child
->tag
->flags
&info
->refresh_type
)!=0);
460 strlcat(info
->cur_align_start
, value
,
461 info
->buf_size
- (info
->cur_align_start
-info
->buf
));
466 strlcat(info
->cur_align_start
, child
->data
,
467 info
->buf_size
- (info
->cur_align_start
-info
->buf
));
468 needs_update
= needs_update
||
469 (info
->refresh_type
&SKIN_REFRESH_STATIC
) != 0;
481 static __attribute__((noinline
)) int get_subline_timeout(struct gui_wps
*gwps
, struct skin_element
* line
)
483 struct skin_element
*element
=line
;
484 struct wps_token
*token
;
485 int retval
= DEFAULT_SUBLINE_TIME_MULTIPLIER
*TIMEOUT_UNIT
;
486 if (element
->type
== LINE
)
488 if (element
->children_count
== 0)
489 return retval
; /* empty line, so force redraw */
490 element
= element
->children
[0];
494 if (element
->type
== TAG
&&
495 element
->tag
->type
== SKIN_TOKEN_SUBLINE_TIMEOUT
)
497 token
= element
->data
;
498 return token
->value
.i
;
500 else if (element
->type
== CONDITIONAL
)
502 struct conditional
*conditional
= element
->data
;
503 int val
= evaluate_conditional(gwps
, 0, conditional
,
504 element
->children_count
);
507 retval
= get_subline_timeout(gwps
, element
->children
[val
]);
512 element
= element
->next
;
517 bool __attribute__((noinline
)) skin_render_alternator(struct skin_element
* element
, struct skin_draw_info
*info
)
519 bool changed_lines
= false;
520 struct line_alternator
*alternator
= (struct line_alternator
*)element
->data
;
521 unsigned old_refresh
= info
->refresh_type
;
522 if (info
->refresh_type
== SKIN_REFRESH_ALL
)
524 alternator
->current_line
= element
->children_count
-1;
525 changed_lines
= true;
527 else if (TIME_AFTER(current_tick
, alternator
->next_change_tick
))
529 changed_lines
= true;
534 struct skin_element
*current_line
= element
->children
[alternator
->current_line
];
535 int start
= alternator
->current_line
;
536 int try_line
= start
;
537 bool suitable
= false;
538 int rettimeout
= DEFAULT_SUBLINE_TIME_MULTIPLIER
*TIMEOUT_UNIT
;
540 /* find a subline which has at least one token in it,
541 * and that line doesnt have a timeout set to 0 through conditionals */
544 if (try_line
>= element
->children_count
)
546 if (element
->children
[try_line
]->children_count
!= 0)
548 current_line
= element
->children
[try_line
];
549 rettimeout
= get_subline_timeout(info
->gwps
,
550 current_line
->children
[0]);
557 while (try_line
!= start
&& !suitable
);
561 alternator
->current_line
= try_line
;
562 alternator
->next_change_tick
= current_tick
+ rettimeout
;
565 info
->refresh_type
= SKIN_REFRESH_ALL
;
566 info
->force_redraw
= true;
568 bool ret
= skin_render_line(element
->children
[alternator
->current_line
], info
);
569 info
->refresh_type
= old_refresh
;
570 return changed_lines
|| ret
;
573 static __attribute__((noinline
)) void skin_render_viewport(struct skin_element
* viewport
, struct gui_wps
*gwps
,
574 struct skin_viewport
* skin_viewport
, unsigned long refresh_type
)
576 struct screen
*display
= gwps
->display
;
577 char linebuf
[MAX_LINE
];
578 skin_render_func func
= skin_render_line
;
579 struct skin_element
* line
= viewport
;
580 struct skin_draw_info info
= {
583 .buf_size
= sizeof(linebuf
),
585 .no_line_break
= false,
586 .line_scrolls
= false,
587 .refresh_type
= refresh_type
,
588 .skin_vp
= skin_viewport
,
592 struct align_pos
* align
= &info
.align
;
594 #ifdef HAVE_LCD_BITMAP
595 /* Set images to not to be displayed */
596 struct skin_token_list
*imglist
= gwps
->data
->images
;
599 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
601 imglist
= imglist
->next
;
608 info
.no_line_break
= false;
609 info
.line_scrolls
= false;
610 info
.force_redraw
= false;
612 info
.cur_align_start
= info
.buf
;
613 align
->left
= info
.buf
;
614 align
->center
= NULL
;
618 if (line
->type
== LINE_ALTERNATOR
)
619 func
= skin_render_alternator
;
620 else if (line
->type
== LINE
)
621 func
= skin_render_line
;
623 needs_update
= func(line
, &info
);
625 /* only update if the line needs to be, and there is something to write */
626 if (refresh_type
&& needs_update
)
628 if (info
.line_scrolls
)
630 /* if the line is a scrolling one we don't want to update
631 too often, so that it has the time to scroll */
632 if ((refresh_type
& SKIN_REFRESH_SCROLL
) || info
.force_redraw
)
633 write_line(display
, align
, info
.line_number
, true);
636 write_line(display
, align
, info
.line_number
, false);
638 if (!info
.no_line_break
)
642 #ifdef HAVE_LCD_BITMAP
643 wps_display_images(gwps
, &skin_viewport
->vp
);
647 void skin_render(struct gui_wps
*gwps
, unsigned refresh_mode
)
649 struct wps_data
*data
= gwps
->data
;
650 struct screen
*display
= gwps
->display
;
652 struct skin_element
* viewport
= data
->tree
;
653 struct skin_viewport
* skin_viewport
;
655 int old_refresh_mode
= refresh_mode
;
657 #ifdef HAVE_LCD_CHARCELLS
659 for (i
= 0; i
< 8; i
++)
661 if (data
->wps_progress_pat
[i
] == 0)
662 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
665 viewport
= data
->tree
;
666 skin_viewport
= (struct skin_viewport
*)viewport
->data
;
667 if (skin_viewport
->label
&& viewport
->next
&&
668 !strcmp(skin_viewport
->label
,VP_DEFAULT_LABEL
))
671 for (viewport
= data
->tree
;
673 viewport
= viewport
->next
)
676 skin_viewport
= (struct skin_viewport
*)viewport
->data
;
677 unsigned vp_refresh_mode
= refresh_mode
;
678 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
679 skin_viewport
->vp
.fg_pattern
= skin_viewport
->start_fgcolour
;
680 skin_viewport
->vp
.bg_pattern
= skin_viewport
->start_bgcolour
;
683 /* dont redraw the viewport if its disabled */
684 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
685 { /* don't draw anything into this one */
688 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
690 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
693 else if (((skin_viewport
->hidden_flags
&
694 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
695 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
697 vp_refresh_mode
= SKIN_REFRESH_ALL
;
698 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
701 display
->set_viewport(&skin_viewport
->vp
);
702 if ((vp_refresh_mode
&SKIN_REFRESH_ALL
) == SKIN_REFRESH_ALL
)
704 display
->clear_viewport();
707 skin_render_viewport(viewport
->children
[0], gwps
,
708 skin_viewport
, vp_refresh_mode
);
709 refresh_mode
= old_refresh_mode
;
712 /* Restore the default viewport */
713 display
->set_viewport(NULL
);
717 #ifdef HAVE_LCD_BITMAP
718 static __attribute__((noinline
)) void skin_render_playlistviewer(struct playlistviewer
* viewer
,
719 struct gui_wps
*gwps
,
720 struct skin_viewport
* skin_viewport
,
721 unsigned long refresh_type
)
723 struct screen
*display
= gwps
->display
;
724 char linebuf
[MAX_LINE
];
725 skin_render_func func
= skin_render_line
;
726 struct skin_element
* line
;
727 struct skin_draw_info info
= {
730 .buf_size
= sizeof(linebuf
),
732 .no_line_break
= false,
733 .line_scrolls
= false,
734 .refresh_type
= refresh_type
,
735 .skin_vp
= skin_viewport
,
736 .offset
= viewer
->start_offset
739 struct align_pos
* align
= &info
.align
;
741 int cur_pos
, start_item
, max
;
742 int nb_lines
= viewport_get_nb_lines(viewer
->vp
);
744 if (current_screen() == GO_TO_FM
)
746 cur_pos
= radio_current_preset();
747 start_item
= cur_pos
+ viewer
->start_offset
;
748 max
= start_item
+radio_preset_count();
753 struct cuesheet
*cue
= skin_get_global_state()->id3
?
754 skin_get_global_state()->id3
->cuesheet
: NULL
;
755 cur_pos
= playlist_get_display_index();
756 max
= playlist_amount()+1;
758 max
+= cue
->track_count
;
759 start_item
= MAX(0, cur_pos
+ viewer
->start_offset
);
761 if (max
-start_item
> nb_lines
)
762 max
= start_item
+ nb_lines
;
765 while (start_item
< max
)
768 info
.no_line_break
= false;
769 info
.line_scrolls
= false;
770 info
.force_redraw
= false;
772 info
.cur_align_start
= info
.buf
;
773 align
->left
= info
.buf
;
774 align
->center
= NULL
;
778 if (line
->type
== LINE_ALTERNATOR
)
779 func
= skin_render_alternator
;
780 else if (line
->type
== LINE
)
781 func
= skin_render_line
;
783 needs_update
= func(line
, &info
);
785 /* only update if the line needs to be, and there is something to write */
786 if (refresh_type
&& needs_update
)
788 if (info
.line_scrolls
)
790 /* if the line is a scrolling one we don't want to update
791 too often, so that it has the time to scroll */
792 if ((refresh_type
& SKIN_REFRESH_SCROLL
) || info
.force_redraw
)
793 write_line(display
, align
, info
.line_number
, true);
796 write_line(display
, align
, info
.line_number
, false);