fix warnings and errors
[kugel-rb.git] / apps / gui / skin_engine / skin_render.c
blobef2bfd57b971b4a3c63691ecf89284c39341ad02
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 "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
42 #include "viewport.h"
43 #include "cuesheet.h"
44 #include "language.h"
45 #include "playback.h"
46 #include "playlist.h"
47 #include "root_menu.h"
48 #include "misc.h"
51 #define MAX_LINE 1024
53 struct skin_draw_info {
54 struct gui_wps *gwps;
55 struct skin_viewport *skin_vp;
56 int line_number;
57 unsigned long refresh_type;
59 char* cur_align_start;
60 struct align_pos align;
61 bool no_line_break;
62 bool line_scrolls;
63 bool force_redraw;
65 char *buf;
66 size_t buf_size;
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,
75 struct gui_wps *gwps,
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 */
85 #endif
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;
89 switch (token->type)
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;
97 break;
98 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
100 struct viewport_colour *col = token->value.data;
101 col->vp->bg_pattern = col->colour;
103 break;
104 #endif
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;
110 while (viewport)
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;
125 break;
126 #ifdef HAVE_LCD_BITMAP
127 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
128 sb_set_info_vp(gwps->display->screen_type,
129 token->value.data);
130 break;
131 case SKIN_TOKEN_PEAKMETER:
132 data->peak_meter_enabled = true;
133 if (do_refresh)
134 draw_peakmeters(gwps, info->line_number, vp);
135 break;
136 #endif
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;
143 if (do_refresh)
144 draw_progressbar(gwps, info->line_number, bar);
145 #else /* HAVE_LCD_CHARCELL */
146 if (do_refresh)
148 if (data->full_line_progressbar)
149 draw_player_fullbar(gwps, info->buf, info->buf_size);
150 else
151 draw_player_progress(gwps);
153 #endif
155 break;
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;
169 else
171 char buf[16];
172 const char *out;
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! */
178 if (a == -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! */
182 else
183 a--;
184 a += id->offset;
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)
193 img->display = a;
197 break;
199 #ifdef HAVE_ALBUMART
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);
205 #if CONFIG_TUNER
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);
211 #endif
212 data->albumart->draw_handle = handle;
214 break;
215 #endif
216 case SKIN_TOKEN_DRAW_INBUILTBAR:
217 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
218 info->refresh_type == SKIN_REFRESH_ALL,
219 token->value.data);
220 break;
221 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
222 if (do_refresh)
223 skin_render_playlistviewer(token->value.data, gwps,
224 info->skin_vp, info->refresh_type);
225 break;
227 #endif /* HAVE_LCD_BITMAP */
228 default:
229 return false;
231 return true;
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;
242 #endif
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)
247 int i;
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;
257 while (child)
259 if (child->type == CONDITIONAL)
261 int i;
262 for (i=0; i<child->children_count; i++)
264 do_tags_in_hidden_conditional(child->children[i], info);
266 child = child->next;
267 continue;
269 else if (child->type != TAG || !child->data)
271 child = child->next;
272 continue;
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;
292 viewport;
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))
297 continue;
298 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
300 continue;
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;
306 else
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;
317 #endif
318 #ifdef HAVE_ALBUMART
319 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
321 draw_album_art(gwps,
322 playback_current_aa_hid(data->playback_aa_slot), true);
324 #endif
325 child = child->next;
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;
340 break;
341 case SKIN_TOKEN_ALIGN_LEFT_RTL:
342 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
343 if (lang_is_rtl())
344 align->right = cur_pos;
345 else
346 align->left = cur_pos;
347 info->cur_align_start = cur_pos;
348 break;
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;
353 break;
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;
358 break;
359 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
360 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
361 if (lang_is_rtl())
362 align->left = cur_pos;
363 else
364 align->right = cur_pos;
365 info->cur_align_start = cur_pos;
366 break;
367 default:
368 break;
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;
384 char tempbuf[128];
385 int old_refresh_mode = info->refresh_type;
386 while (child)
388 tempbuf[0] = '\0';
389 switch (child->type)
391 case CONDITIONAL:
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 */
407 if (last_value == 0)
408 do_tags_in_hidden_conditional(child->children[0], info);
409 break;
411 value = 0;
413 else
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))
432 needs_update = true;
433 else
434 needs_update = needs_update || (last_value != value);
436 info->refresh_type = old_refresh_mode;
437 break;
438 case TAG:
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);
446 if (!child->data)
448 break;
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);
455 if (value)
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));
463 break;
464 case TEXT:
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;
469 break;
470 case COMMENT:
471 default:
472 break;
475 child = child->next;
477 return needs_update;
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;
491 else
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;
514 if (changed_lines)
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 = {
532 .gwps = gwps,
533 .buf = linebuf,
534 .buf_size = sizeof(linebuf),
535 .line_number = 0,
536 .no_line_break = false,
537 .line_scrolls = false,
538 .refresh_type = refresh_type,
539 .skin_vp = skin_viewport,
540 .offset = 0
543 struct align_pos * align = &info.align;
544 bool needs_update;
545 #ifdef HAVE_LCD_BITMAP
546 /* Set images to not to be displayed */
547 struct skin_token_list *imglist = gwps->data->images;
548 while (imglist)
550 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
551 img->display = -1;
552 imglist = imglist->next;
554 #endif
556 while (line)
558 linebuf[0] = '\0';
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;
566 align->right = 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);
586 else
587 write_line(display, align, info.line_number, false);
589 if (!info.no_line_break)
590 info.line_number++;
591 line = line->next;
593 #ifdef HAVE_LCD_BITMAP
594 wps_display_images(gwps, &skin_viewport->vp);
595 #endif
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
609 int i;
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();
615 #endif
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))
620 refresh_mode = 0;
622 for (viewport = data->tree;
623 viewport;
624 viewport = viewport->next)
626 /* SETUP */
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;
632 #endif
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 */
637 vp_refresh_mode = 0;
639 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
641 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
642 continue;
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();
657 /* render */
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);
665 display->update();
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 = {
679 .gwps = gwps,
680 .buf = linebuf,
681 .buf_size = sizeof(linebuf),
682 .line_number = 0,
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;
691 bool needs_update;
692 int cur_pos, start_item, max;
693 int nb_lines = viewport_get_nb_lines(viewer->vp);
694 #if CONFIG_TUNER
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();
701 else
702 #endif
704 struct cuesheet *cue = gwps->state->id3 ? gwps->state->id3->cuesheet:NULL;
705 cur_pos = playlist_get_display_index();
706 max = playlist_amount()+1;
707 if (cue)
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;
714 line = viewer->line;
715 while (start_item < max)
717 linebuf[0] = '\0';
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;
725 align->right = 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);
745 else
746 write_line(display, align, info.line_number, false);
748 info.line_number++;
749 info.offset++;
750 start_item++;