9928e3418f
Skins are now more self contained in the skin manager which in the future might allow on demand skin loading (i.e smaller skin buffers) Skin backdrops are also managed more intelegently (fixes a bug where you can get a crazy backdrop loaded if a .sbs fails to load) the rockbox_default rescue theme is now called rockbox_failsafe to better express what it actually is. This commit hopefully/maybe fixes the heavily reported data aborts, so please check if you are getting them git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28073 a1c6a512-1295-4272-9138-f99709370657
802 lines
27 KiB
C
802 lines
27 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
|
|
*
|
|
* Copyright (C) 2010 Jonathan Gordon
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <ctype.h>
|
|
#include "strlcat.h"
|
|
|
|
#include "config.h"
|
|
#include "kernel.h"
|
|
#ifdef HAVE_ALBUMART
|
|
#include "albumart.h"
|
|
#endif
|
|
#include "skin_display.h"
|
|
#include "skin_engine.h"
|
|
#include "skin_parser.h"
|
|
#include "tag_table.h"
|
|
#include "skin_scan.h"
|
|
#if CONFIG_TUNER
|
|
#include "radio.h"
|
|
#endif
|
|
#include "viewport.h"
|
|
#include "cuesheet.h"
|
|
#include "language.h"
|
|
#include "playback.h"
|
|
#include "playlist.h"
|
|
#include "root_menu.h"
|
|
#include "misc.h"
|
|
|
|
|
|
#define MAX_LINE 1024
|
|
|
|
struct skin_draw_info {
|
|
struct gui_wps *gwps;
|
|
struct skin_viewport *skin_vp;
|
|
int line_number;
|
|
unsigned long refresh_type;
|
|
|
|
char* cur_align_start;
|
|
struct align_pos align;
|
|
bool no_line_break;
|
|
bool line_scrolls;
|
|
bool force_redraw;
|
|
|
|
char *buf;
|
|
size_t buf_size;
|
|
|
|
int offset; /* used by the playlist viewer */
|
|
};
|
|
|
|
typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
|
|
bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
static void skin_render_playlistviewer(struct playlistviewer* viewer,
|
|
struct gui_wps *gwps,
|
|
struct skin_viewport* skin_viewport,
|
|
unsigned long refresh_type);
|
|
#endif
|
|
|
|
static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
|
|
struct skin_element *element, struct viewport* vp)
|
|
{
|
|
#ifndef HAVE_LCD_BITMAP
|
|
(void)vp; /* silence warnings */
|
|
#endif
|
|
struct wps_token *token = (struct wps_token *)element->data;
|
|
struct wps_data *data = gwps->data;
|
|
bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
|
|
switch (token->type)
|
|
{
|
|
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
|
|
case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
|
|
{
|
|
struct viewport_colour *col = token->value.data;
|
|
col->vp->fg_pattern = col->colour;
|
|
}
|
|
break;
|
|
case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
|
|
{
|
|
struct viewport_colour *col = token->value.data;
|
|
col->vp->bg_pattern = col->colour;
|
|
}
|
|
break;
|
|
#endif
|
|
case SKIN_TOKEN_VIEWPORT_ENABLE:
|
|
{
|
|
char *label = token->value.data;
|
|
char temp = VP_DRAW_HIDEABLE;
|
|
struct skin_element *viewport = gwps->data->tree;
|
|
while (viewport)
|
|
{
|
|
struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data;
|
|
if (skinvp->label && !skinvp->is_infovp &&
|
|
!strcmp(skinvp->label, label))
|
|
{
|
|
if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
|
|
{
|
|
temp |= VP_DRAW_WASHIDDEN;
|
|
}
|
|
skinvp->hidden_flags = temp;
|
|
}
|
|
viewport = viewport->next;
|
|
}
|
|
}
|
|
break;
|
|
#ifdef HAVE_LCD_BITMAP
|
|
case SKIN_TOKEN_UIVIEWPORT_ENABLE:
|
|
sb_set_info_vp(gwps->display->screen_type,
|
|
token->value.data);
|
|
break;
|
|
case SKIN_TOKEN_PEAKMETER:
|
|
data->peak_meter_enabled = true;
|
|
if (do_refresh)
|
|
draw_peakmeters(gwps, info->line_number, vp);
|
|
break;
|
|
#endif
|
|
case SKIN_TOKEN_VOLUMEBAR:
|
|
case SKIN_TOKEN_BATTERY_PERCENTBAR:
|
|
case SKIN_TOKEN_PROGRESSBAR:
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
struct progressbar *bar = (struct progressbar*)token->value.data;
|
|
if (do_refresh)
|
|
draw_progressbar(gwps, info->line_number, bar);
|
|
#else /* HAVE_LCD_CHARCELL */
|
|
if (do_refresh)
|
|
{
|
|
if (data->full_line_progressbar)
|
|
draw_player_fullbar(gwps, info->buf, info->buf_size);
|
|
else
|
|
draw_player_progress(gwps);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
#ifdef HAVE_LCD_BITMAP
|
|
case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
|
|
case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
|
|
{
|
|
struct image_display *id = token->value.data;
|
|
const char* label = id->label;
|
|
struct gui_img *img = find_image(label, data);
|
|
if (img && img->loaded)
|
|
{
|
|
if (id->token == NULL)
|
|
{
|
|
img->display = id->subimage;
|
|
}
|
|
else
|
|
{
|
|
char buf[16];
|
|
const char *out;
|
|
int a = img->num_subimages;
|
|
out = get_token_value(gwps, id->token, info->offset,
|
|
buf, sizeof(buf), &a);
|
|
|
|
/* NOTE: get_token_value() returns values starting at 1! */
|
|
if (a == -1)
|
|
a = (out && *out) ? 1 : 2;
|
|
if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
|
|
a -= 2; /* 2 is added in statusbar-skinned.c! */
|
|
else
|
|
a--;
|
|
a += id->offset;
|
|
|
|
/* Clear the image, as in conditionals */
|
|
clear_image_pos(gwps, img);
|
|
|
|
/* If the token returned a value which is higher than
|
|
* the amount of subimages, don't draw it. */
|
|
if (a >= 0 && a < img->num_subimages)
|
|
{
|
|
img->display = a;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#ifdef HAVE_ALBUMART
|
|
case SKIN_TOKEN_ALBUMART_DISPLAY:
|
|
/* now draw the AA */
|
|
if (do_refresh && data->albumart)
|
|
{
|
|
int handle = playback_current_aa_hid(data->playback_aa_slot);
|
|
#if CONFIG_TUNER
|
|
if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
|
|
{
|
|
struct dim dim = {data->albumart->width, data->albumart->height};
|
|
handle = radio_get_art_hid(&dim);
|
|
}
|
|
#endif
|
|
data->albumart->draw_handle = handle;
|
|
}
|
|
break;
|
|
#endif
|
|
case SKIN_TOKEN_DRAW_INBUILTBAR:
|
|
gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
|
|
info->refresh_type == SKIN_REFRESH_ALL,
|
|
token->value.data);
|
|
break;
|
|
case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
|
|
if (do_refresh)
|
|
skin_render_playlistviewer(token->value.data, gwps,
|
|
info->skin_vp, info->refresh_type);
|
|
break;
|
|
|
|
#endif /* HAVE_LCD_BITMAP */
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
static void do_tags_in_hidden_conditional(struct skin_element* branch,
|
|
struct skin_draw_info *info)
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
struct gui_wps *gwps = info->gwps;
|
|
struct wps_data *data = gwps->data;
|
|
#endif
|
|
/* Tags here are ones which need to be "turned off" or cleared
|
|
* if they are in a conditional branch which isnt being used */
|
|
if (branch->type == LINE_ALTERNATOR)
|
|
{
|
|
int i;
|
|
for (i=0; i<branch->children_count; i++)
|
|
{
|
|
do_tags_in_hidden_conditional(branch->children[i], info);
|
|
}
|
|
}
|
|
else if (branch->type == LINE && branch->children_count)
|
|
{
|
|
struct skin_element *child = branch->children[0];
|
|
struct wps_token *token;
|
|
while (child)
|
|
{
|
|
if (child->type == CONDITIONAL)
|
|
{
|
|
int i;
|
|
for (i=0; i<child->children_count; i++)
|
|
{
|
|
do_tags_in_hidden_conditional(child->children[i], info);
|
|
}
|
|
child = child->next;
|
|
continue;
|
|
}
|
|
else if (child->type != TAG || !child->data)
|
|
{
|
|
child = child->next;
|
|
continue;
|
|
}
|
|
token = (struct wps_token *)child->data;
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* clear all pictures in the conditional and nested ones */
|
|
if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
|
|
{
|
|
struct image_display *id = token->value.data;
|
|
struct gui_img *img = find_image(id->label, data);
|
|
clear_image_pos(gwps, img);
|
|
}
|
|
else if (token->type == SKIN_TOKEN_PEAKMETER)
|
|
{
|
|
data->peak_meter_enabled = false;
|
|
}
|
|
else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
|
|
{
|
|
char *label = token->value.data;
|
|
struct skin_element *viewport;
|
|
for (viewport = data->tree;
|
|
viewport;
|
|
viewport = viewport->next)
|
|
{
|
|
struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data;
|
|
if (skin_viewport->label && strcmp(skin_viewport->label, label))
|
|
continue;
|
|
if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
|
|
{
|
|
continue;
|
|
}
|
|
if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
|
|
{
|
|
if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
|
|
skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
|
|
else
|
|
{
|
|
gwps->display->set_viewport(&skin_viewport->vp);
|
|
gwps->display->clear_viewport();
|
|
gwps->display->scroll_stop(&skin_viewport->vp);
|
|
gwps->display->set_viewport(&info->skin_vp->vp);
|
|
skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HAVE_ALBUMART
|
|
else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
|
|
{
|
|
draw_album_art(gwps,
|
|
playback_current_aa_hid(data->playback_aa_slot), true);
|
|
}
|
|
#endif
|
|
child = child->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
|
|
{
|
|
struct align_pos *align = &info->align;
|
|
char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
|
|
switch (element->tag->type)
|
|
{
|
|
case SKIN_TOKEN_ALIGN_LEFT:
|
|
*cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
|
|
align->left = cur_pos;
|
|
info->cur_align_start = cur_pos;
|
|
break;
|
|
case SKIN_TOKEN_ALIGN_LEFT_RTL:
|
|
*cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
|
|
if (lang_is_rtl())
|
|
align->right = cur_pos;
|
|
else
|
|
align->left = cur_pos;
|
|
info->cur_align_start = cur_pos;
|
|
break;
|
|
case SKIN_TOKEN_ALIGN_CENTER:
|
|
*cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
|
|
align->center = cur_pos;
|
|
info->cur_align_start = cur_pos;
|
|
break;
|
|
case SKIN_TOKEN_ALIGN_RIGHT:
|
|
*cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
|
|
align->right = cur_pos;
|
|
info->cur_align_start = cur_pos;
|
|
break;
|
|
case SKIN_TOKEN_ALIGN_RIGHT_RTL:
|
|
*cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
|
|
if (lang_is_rtl())
|
|
align->left = cur_pos;
|
|
else
|
|
align->right = cur_pos;
|
|
info->cur_align_start = cur_pos;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Draw a LINE element onto the display */
|
|
static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
|
|
{
|
|
bool needs_update = false;
|
|
int last_value, value;
|
|
|
|
if (line->children_count == 0)
|
|
return false; /* empty line, do nothing */
|
|
|
|
struct skin_element *child = line->children[0];
|
|
struct conditional *conditional;
|
|
skin_render_func func = skin_render_line;
|
|
char tempbuf[128];
|
|
int old_refresh_mode = info->refresh_type;
|
|
while (child)
|
|
{
|
|
tempbuf[0] = '\0';
|
|
switch (child->type)
|
|
{
|
|
case CONDITIONAL:
|
|
conditional = (struct conditional*)child->data;
|
|
last_value = conditional->last_value;
|
|
value = evaluate_conditional(info->gwps, info->offset,
|
|
conditional, child->children_count);
|
|
conditional->last_value = value;
|
|
if (child->children_count == 1)
|
|
{
|
|
/* special handling so
|
|
* %?aa<true> and %?<true|false> need special handlng here */
|
|
|
|
if (value == -1) /* tag is false */
|
|
{
|
|
/* we are in a false branch of a %?aa<true> conditional */
|
|
if (last_value == 0)
|
|
do_tags_in_hidden_conditional(child->children[0], info);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (last_value >= 0 && value != last_value && last_value < child->children_count)
|
|
do_tags_in_hidden_conditional(child->children[last_value], info);
|
|
}
|
|
if (child->children[value]->type == LINE_ALTERNATOR)
|
|
{
|
|
func = skin_render_alternator;
|
|
}
|
|
else if (child->children[value]->type == LINE)
|
|
func = skin_render_line;
|
|
|
|
if (value != last_value)
|
|
{
|
|
info->refresh_type = SKIN_REFRESH_ALL;
|
|
info->force_redraw = true;
|
|
}
|
|
|
|
if (func(child->children[value], info))
|
|
needs_update = true;
|
|
else
|
|
needs_update = needs_update || (last_value != value);
|
|
|
|
info->refresh_type = old_refresh_mode;
|
|
break;
|
|
case TAG:
|
|
if (child->tag->flags & NOBREAK)
|
|
info->no_line_break = true;
|
|
if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
|
|
info->line_scrolls = true;
|
|
|
|
fix_line_alignment(info, child);
|
|
|
|
if (!child->data)
|
|
{
|
|
break;
|
|
}
|
|
if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
|
|
{
|
|
const char *value = get_token_value(info->gwps, child->data,
|
|
info->offset, tempbuf,
|
|
sizeof(tempbuf), NULL);
|
|
if (value)
|
|
{
|
|
#if CONFIG_RTC
|
|
if (child->tag->flags&SKIN_RTC_REFRESH)
|
|
needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
|
|
#endif
|
|
needs_update = needs_update ||
|
|
((child->tag->flags&info->refresh_type)!=0);
|
|
strlcat(info->cur_align_start, value,
|
|
info->buf_size - (info->cur_align_start-info->buf));
|
|
}
|
|
}
|
|
break;
|
|
case TEXT:
|
|
strlcat(info->cur_align_start, child->data,
|
|
info->buf_size - (info->cur_align_start-info->buf));
|
|
needs_update = needs_update ||
|
|
(info->refresh_type&SKIN_REFRESH_STATIC) != 0;
|
|
break;
|
|
case COMMENT:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
child = child->next;
|
|
}
|
|
return needs_update;
|
|
}
|
|
|
|
static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
|
|
{
|
|
struct skin_element *element=line;
|
|
struct wps_token *token;
|
|
int retval = -1;
|
|
if (element->type == LINE)
|
|
element = element->children[0];
|
|
while (element)
|
|
{
|
|
if (element->type == TAG &&
|
|
element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
|
|
{
|
|
token = element->data;
|
|
return token->value.i;
|
|
}
|
|
else if (element->type == CONDITIONAL)
|
|
{
|
|
struct conditional *conditional = element->data;
|
|
int val = evaluate_conditional(gwps, 0, conditional,
|
|
element->children_count);
|
|
if (val >= 0)
|
|
{
|
|
retval = get_subline_timeout(gwps, element->children[val]);
|
|
if (retval >= 0)
|
|
return retval;
|
|
}
|
|
}
|
|
element = element->next;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
|
|
{
|
|
bool changed_lines = false;
|
|
struct line_alternator *alternator = (struct line_alternator*)element->data;
|
|
unsigned old_refresh = info->refresh_type;
|
|
if (info->refresh_type == SKIN_REFRESH_ALL)
|
|
{
|
|
alternator->current_line = 0;
|
|
alternator->last_change_tick = current_tick;
|
|
changed_lines = true;
|
|
}
|
|
else
|
|
{
|
|
struct skin_element *current_line = element->children[alternator->current_line];
|
|
struct line *line = (struct line *)current_line->data;
|
|
int next_change = alternator->last_change_tick + line->timeout;
|
|
if (TIME_AFTER(current_tick, next_change))
|
|
{
|
|
alternator->last_change_tick = current_tick;
|
|
changed_lines = true;
|
|
}
|
|
}
|
|
if (changed_lines)
|
|
{
|
|
struct skin_element *current_line = element->children[alternator->current_line];
|
|
int start = alternator->current_line;
|
|
int try_line = start;
|
|
bool suitable = false;
|
|
|
|
/* find a subline which has at least one token in it,
|
|
* and that line doesnt have a timeout set to 0 through conditionals */
|
|
do {
|
|
try_line++;
|
|
if (try_line >= element->children_count)
|
|
try_line = 0;
|
|
if (element->children[try_line]->children_count != 0)
|
|
{
|
|
current_line = element->children[try_line];
|
|
if ((current_line->children[0]->type != CONDITIONAL) ||
|
|
get_subline_timeout(info->gwps, current_line->children[0]) > 0)
|
|
{
|
|
suitable = true;
|
|
}
|
|
}
|
|
}
|
|
while (try_line != start && !suitable);
|
|
|
|
if (suitable)
|
|
alternator->current_line = try_line;
|
|
|
|
info->refresh_type = SKIN_REFRESH_ALL;
|
|
info->force_redraw = true;
|
|
}
|
|
bool ret = skin_render_line(element->children[alternator->current_line], info);
|
|
info->refresh_type = old_refresh;
|
|
return changed_lines || ret;
|
|
}
|
|
|
|
static void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
|
|
struct skin_viewport* skin_viewport, unsigned long refresh_type)
|
|
{
|
|
struct screen *display = gwps->display;
|
|
char linebuf[MAX_LINE];
|
|
skin_render_func func = skin_render_line;
|
|
struct skin_element* line = viewport;
|
|
struct skin_draw_info info = {
|
|
.gwps = gwps,
|
|
.buf = linebuf,
|
|
.buf_size = sizeof(linebuf),
|
|
.line_number = 0,
|
|
.no_line_break = false,
|
|
.line_scrolls = false,
|
|
.refresh_type = refresh_type,
|
|
.skin_vp = skin_viewport,
|
|
.offset = 0
|
|
};
|
|
|
|
struct align_pos * align = &info.align;
|
|
bool needs_update;
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* Set images to not to be displayed */
|
|
struct skin_token_list *imglist = gwps->data->images;
|
|
while (imglist)
|
|
{
|
|
struct gui_img *img = (struct gui_img *)imglist->token->value.data;
|
|
img->display = -1;
|
|
imglist = imglist->next;
|
|
}
|
|
#endif
|
|
|
|
while (line)
|
|
{
|
|
linebuf[0] = '\0';
|
|
info.no_line_break = false;
|
|
info.line_scrolls = false;
|
|
info.force_redraw = false;
|
|
|
|
info.cur_align_start = info.buf;
|
|
align->left = info.buf;
|
|
align->center = NULL;
|
|
align->right = NULL;
|
|
|
|
|
|
if (line->type == LINE_ALTERNATOR)
|
|
func = skin_render_alternator;
|
|
else if (line->type == LINE)
|
|
func = skin_render_line;
|
|
|
|
needs_update = func(line, &info);
|
|
|
|
/* only update if the line needs to be, and there is something to write */
|
|
if (refresh_type && needs_update)
|
|
{
|
|
if (info.line_scrolls)
|
|
{
|
|
/* if the line is a scrolling one we don't want to update
|
|
too often, so that it has the time to scroll */
|
|
if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
|
|
write_line(display, align, info.line_number, true);
|
|
}
|
|
else
|
|
write_line(display, align, info.line_number, false);
|
|
}
|
|
if (!info.no_line_break)
|
|
info.line_number++;
|
|
line = line->next;
|
|
}
|
|
#ifdef HAVE_LCD_BITMAP
|
|
wps_display_images(gwps, &skin_viewport->vp);
|
|
#endif
|
|
}
|
|
|
|
void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
|
|
{
|
|
struct wps_data *data = gwps->data;
|
|
struct screen *display = gwps->display;
|
|
|
|
struct skin_element* viewport = data->tree;
|
|
struct skin_viewport* skin_viewport;
|
|
|
|
int old_refresh_mode = refresh_mode;
|
|
|
|
#ifdef HAVE_LCD_CHARCELLS
|
|
int i;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (data->wps_progress_pat[i] == 0)
|
|
data->wps_progress_pat[i] = display->get_locked_pattern();
|
|
}
|
|
#endif
|
|
viewport = data->tree;
|
|
skin_viewport = (struct skin_viewport *)viewport->data;
|
|
if (skin_viewport->label && viewport->next &&
|
|
!strcmp(skin_viewport->label,VP_DEFAULT_LABEL))
|
|
refresh_mode = 0;
|
|
|
|
for (viewport = data->tree;
|
|
viewport;
|
|
viewport = viewport->next)
|
|
{
|
|
/* SETUP */
|
|
skin_viewport = (struct skin_viewport*)viewport->data;
|
|
unsigned vp_refresh_mode = refresh_mode;
|
|
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
|
|
skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
|
|
skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
|
|
#endif
|
|
|
|
/* dont redraw the viewport if its disabled */
|
|
if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
|
|
{ /* don't draw anything into this one */
|
|
vp_refresh_mode = 0;
|
|
}
|
|
else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
|
|
{
|
|
skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
|
|
continue;
|
|
}
|
|
else if (((skin_viewport->hidden_flags&
|
|
(VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
|
|
== (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
|
|
{
|
|
vp_refresh_mode = SKIN_REFRESH_ALL;
|
|
skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
|
|
}
|
|
|
|
display->set_viewport(&skin_viewport->vp);
|
|
if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
|
|
{
|
|
display->clear_viewport();
|
|
}
|
|
/* render */
|
|
skin_render_viewport(viewport->children[0], gwps,
|
|
skin_viewport, vp_refresh_mode);
|
|
refresh_mode = old_refresh_mode;
|
|
}
|
|
|
|
/* Restore the default viewport */
|
|
display->set_viewport(NULL);
|
|
display->update();
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
static void skin_render_playlistviewer(struct playlistviewer* viewer,
|
|
struct gui_wps *gwps,
|
|
struct skin_viewport* skin_viewport,
|
|
unsigned long refresh_type)
|
|
{
|
|
struct screen *display = gwps->display;
|
|
char linebuf[MAX_LINE];
|
|
skin_render_func func = skin_render_line;
|
|
struct skin_element* line;
|
|
struct skin_draw_info info = {
|
|
.gwps = gwps,
|
|
.buf = linebuf,
|
|
.buf_size = sizeof(linebuf),
|
|
.line_number = 0,
|
|
.no_line_break = false,
|
|
.line_scrolls = false,
|
|
.refresh_type = refresh_type,
|
|
.skin_vp = skin_viewport,
|
|
.offset = viewer->start_offset
|
|
};
|
|
|
|
struct align_pos * align = &info.align;
|
|
bool needs_update;
|
|
int cur_pos, start_item, max;
|
|
int nb_lines = viewport_get_nb_lines(viewer->vp);
|
|
#if CONFIG_TUNER
|
|
if (current_screen() == GO_TO_FM)
|
|
{
|
|
cur_pos = radio_current_preset();
|
|
start_item = cur_pos + viewer->start_offset;
|
|
max = start_item+radio_preset_count();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
struct cuesheet *cue = skin_get_global_state()->id3 ?
|
|
skin_get_global_state()->id3->cuesheet : NULL;
|
|
cur_pos = playlist_get_display_index();
|
|
max = playlist_amount()+1;
|
|
if (cue)
|
|
max += cue->track_count;
|
|
start_item = MAX(0, cur_pos + viewer->start_offset);
|
|
}
|
|
if (max-start_item > nb_lines)
|
|
max = start_item + nb_lines;
|
|
|
|
line = viewer->line;
|
|
while (start_item < max)
|
|
{
|
|
linebuf[0] = '\0';
|
|
info.no_line_break = false;
|
|
info.line_scrolls = false;
|
|
info.force_redraw = false;
|
|
|
|
info.cur_align_start = info.buf;
|
|
align->left = info.buf;
|
|
align->center = NULL;
|
|
align->right = NULL;
|
|
|
|
|
|
if (line->type == LINE_ALTERNATOR)
|
|
func = skin_render_alternator;
|
|
else if (line->type == LINE)
|
|
func = skin_render_line;
|
|
|
|
needs_update = func(line, &info);
|
|
|
|
/* only update if the line needs to be, and there is something to write */
|
|
if (refresh_type && needs_update)
|
|
{
|
|
if (info.line_scrolls)
|
|
{
|
|
/* if the line is a scrolling one we don't want to update
|
|
too often, so that it has the time to scroll */
|
|
if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
|
|
write_line(display, align, info.line_number, true);
|
|
}
|
|
else
|
|
write_line(display, align, info.line_number, false);
|
|
}
|
|
info.line_number++;
|
|
info.offset++;
|
|
start_item++;
|
|
}
|
|
}
|
|
#endif
|