summaryrefslogtreecommitdiffstats
path: root/apps/gui/wps_engine/wps_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/wps_engine/wps_display.c')
-rw-r--r--apps/gui/wps_engine/wps_display.c1099
1 files changed, 1099 insertions, 0 deletions
diff --git a/apps/gui/wps_engine/wps_display.c b/apps/gui/wps_engine/wps_display.c
new file mode 100644
index 0000000000..c74e2cedfa
--- /dev/null
+++ b/apps/gui/wps_engine/wps_display.c
@@ -0,0 +1,1099 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002-2007 Björn Stenberg
+ * Copyright (C) 2007-2008 Nicolas Pennequin
+ *
+ * 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 "font.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "system.h"
+#include "settings.h"
+#include "settings_list.h"
+#include "rbunicode.h"
+#include "rtc.h"
+#include "audio.h"
+#include "status.h"
+#include "power.h"
+#include "powermgmt.h"
+#include "sound.h"
+#include "debug.h"
+#ifdef HAVE_LCD_CHARCELLS
+#include "hwcompat.h"
+#endif
+#include "abrepeat.h"
+#include "mp3_playback.h"
+#include "lang.h"
+#include "misc.h"
+#include "splash.h"
+#include "scrollbar.h"
+#include "led.h"
+#include "lcd.h"
+#ifdef HAVE_LCD_BITMAP
+#include "peakmeter.h"
+/* Image stuff */
+#include "bmp.h"
+#include "albumart.h"
+#endif
+#include "dsp.h"
+#include "action.h"
+#include "cuesheet.h"
+#include "playlist.h"
+#if CONFIG_CODEC == SWCODEC
+#include "playback.h"
+#endif
+#include "backdrop.h"
+#include "viewport.h"
+
+
+#include "wps_internals.h"
+#include "wps_engine.h"
+
+bool gui_wps_display(struct gui_wps *gwps)
+{
+ struct screen *display = gwps->display;
+ struct wps_data *data = gwps->data;
+ int screen = display->screen_type;
+
+ /* Update the values in the first (default) viewport - in case the user
+ has modified the statusbar or colour settings */
+#if LCD_DEPTH > 1
+ if (display->depth > 1)
+ {
+ data->viewports[0].vp.fg_pattern = display->get_foreground();
+ data->viewports[0].vp.bg_pattern = display->get_background();
+ }
+#endif
+ display->clear_display();
+ if (!data->wps_loaded) {
+ if ( !data->num_tokens ) {
+ /* set the default wps for the main-screen */
+ if(screen == SCREEN_MAIN)
+ {
+#if LCD_DEPTH > 1
+ unload_wps_backdrop();
+#endif
+ wps_data_load(data,
+ display,
+#ifdef HAVE_LCD_BITMAP
+ "%s%?it<%?in<%in. |>%it|%fn>\n"
+ "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
+ "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
+ "\n"
+ "%al%pc/%pt%ar[%pp:%pe]\n"
+ "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
+ "%pb\n"
+ "%pm\n", false);
+#else
+ "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
+ "%pc%?ps<*|/>%pt\n", false);
+#endif
+ }
+#ifdef HAVE_REMOTE_LCD
+ /* set the default wps for the remote-screen */
+ else if(screen == SCREEN_REMOTE)
+ {
+#if LCD_REMOTE_DEPTH > 1
+ unload_remote_wps_backdrop();
+#endif
+ wps_data_load(data,
+ display,
+ "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
+ "%s%?it<%?in<%in. |>%it|%fn>\n"
+ "%al%pc/%pt%ar[%pp:%pe]\n"
+ "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
+ "%pb\n", false);
+ }
+#endif
+ }
+ }
+ else
+ {
+#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
+ if (screen == SCREEN_REMOTE)
+ show_remote_wps_backdrop();
+ else if (screen == SCREEN_MAIN)
+#endif
+#if LCD_DEPTH > 1
+ show_wps_backdrop();
+#endif
+ }
+ return gui_wps_redraw(gwps, 0, WPS_REFRESH_ALL);
+}
+
+bool gui_wps_update(struct gui_wps *gwps)
+{
+ struct mp3entry *id3 = gwps->state->id3;
+ bool retval;
+ bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
+ gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
+ retval = gui_wps_redraw(gwps, 0,
+ gwps->state->do_full_update ?
+ WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC);
+ return retval;
+}
+
+
+#ifdef HAVE_LCD_BITMAP
+
+static void draw_progressbar(struct gui_wps *gwps,
+ struct wps_viewport *wps_vp)
+ {
+ struct screen *display = gwps->display;
+ struct wps_state *state = gwps->state;
+ struct progressbar *pb = wps_vp->pb;
+ int y = pb->y;
+
+ if (y < 0)
+ {
+ int line_height = font_get(wps_vp->vp.font)->height;
+ /* center the pb in the line, but only if the line is higher than the pb */
+ int center = (line_height-pb->height)/2;
+ /* if Y was not set calculate by font height,Y is -line_number-1 */
+ y = (-y -1)*line_height + (0 > center ? 0 : center);
+ }
+
+ if (pb->have_bitmap_pb)
+ gui_bitmap_scrollbar_draw(display, pb->bm,
+ pb->x, y, pb->width, pb->bm.height,
+ state->id3->length ? state->id3->length : 1, 0,
+ state->id3->length ? state->id3->elapsed
+ + state->ff_rewind_count : 0,
+ HORIZONTAL);
+ else
+ gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
+ state->id3->length ? state->id3->length : 1, 0,
+ state->id3->length ? state->id3->elapsed
+ + state->ff_rewind_count : 0,
+ HORIZONTAL);
+#ifdef AB_REPEAT_ENABLE
+ if ( ab_repeat_mode_enabled() && state->id3->length != 0 )
+ ab_draw_markers(display, state->id3->length,
+ pb->x, pb->x + pb->width, y, pb->height);
+#endif
+
+ if (state->id3->cuesheet)
+ cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
+ pb->x, pb->x + pb->width, y+1, pb->height-2);
+}
+
+/* clears the area where the image was shown */
+static void clear_image_pos(struct gui_wps *gwps, int n)
+{
+ if(!gwps)
+ return;
+ struct wps_data *data = gwps->data;
+ gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ gwps->display->fillrect(data->img[n].x, data->img[n].y,
+ data->img[n].bm.width, data->img[n].subimage_height);
+ gwps->display->set_drawmode(DRMODE_SOLID);
+}
+
+static void wps_draw_image(struct gui_wps *gwps, int n, int subimage)
+{
+ struct screen *display = gwps->display;
+ struct wps_data *data = gwps->data;
+ if(data->img[n].always_display)
+ display->set_drawmode(DRMODE_FG);
+ else
+ display->set_drawmode(DRMODE_SOLID);
+
+#if LCD_DEPTH > 1
+ if(data->img[n].bm.format == FORMAT_MONO) {
+#endif
+ display->mono_bitmap_part(data->img[n].bm.data,
+ 0, data->img[n].subimage_height * subimage,
+ data->img[n].bm.width, data->img[n].x,
+ data->img[n].y, data->img[n].bm.width,
+ data->img[n].subimage_height);
+#if LCD_DEPTH > 1
+ } else {
+ display->transparent_bitmap_part((fb_data *)data->img[n].bm.data,
+ 0, data->img[n].subimage_height * subimage,
+ data->img[n].bm.width, data->img[n].x,
+ data->img[n].y, data->img[n].bm.width,
+ data->img[n].subimage_height);
+ }
+#endif
+}
+
+static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
+{
+ if(!gwps || !gwps->data || !gwps->display)
+ return;
+
+ int n;
+ struct wps_data *data = gwps->data;
+ struct screen *display = gwps->display;
+
+ for (n = 0; n < MAX_IMAGES; n++)
+ {
+ if (data->img[n].loaded)
+ {
+ if (data->img[n].display >= 0)
+ {
+ wps_draw_image(gwps, n, data->img[n].display);
+ } else if (data->img[n].always_display && data->img[n].vp == vp)
+ {
+ wps_draw_image(gwps, n, 0);
+ }
+ }
+ }
+ display->set_drawmode(DRMODE_SOLID);
+}
+
+#else /* HAVE_LCD_CHARCELL */
+
+static bool draw_player_progress(struct gui_wps *gwps)
+{
+ struct wps_state *state = gwps->state;
+ struct screen *display = gwps->display;
+ unsigned char progress_pattern[7];
+ int pos = 0;
+ int i;
+
+ if (!state->id3)
+ return false;
+
+ if (state->id3->length)
+ pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
+ / state->id3->length;
+
+ for (i = 0; i < 7; i++, pos -= 5)
+ {
+ if (pos <= 0)
+ progress_pattern[i] = 0x1fu;
+ else if (pos >= 5)
+ progress_pattern[i] = 0x00u;
+ else
+ progress_pattern[i] = 0x1fu >> pos;
+ }
+
+ display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
+ return true;
+}
+
+static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
+{
+ static const unsigned char numbers[10][4] = {
+ {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
+ {0x04, 0x0c, 0x04, 0x04}, /* 1 */
+ {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
+ {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
+ {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
+ {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
+ {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
+ {0x0e, 0x02, 0x04, 0x08}, /* 7 */
+ {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
+ {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
+ };
+
+ struct wps_state *state = gwps->state;
+ struct screen *display = gwps->display;
+ struct wps_data *data = gwps->data;
+ unsigned char progress_pattern[7];
+ char timestr[10];
+ int time;
+ int time_idx = 0;
+ int pos = 0;
+ int pat_idx = 1;
+ int digit, i, j;
+ bool softchar;
+
+ if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
+ return;
+
+ time = state->id3->elapsed + state->ff_rewind_count;
+ if (state->id3->length)
+ pos = 55 * time / state->id3->length;
+
+ memset(timestr, 0, sizeof(timestr));
+ format_time(timestr, sizeof(timestr)-2, time);
+ timestr[strlen(timestr)] = ':'; /* always safe */
+
+ for (i = 0; i < 11; i++, pos -= 5)
+ {
+ softchar = false;
+ memset(progress_pattern, 0, sizeof(progress_pattern));
+
+ if ((digit = timestr[time_idx]))
+ {
+ softchar = true;
+ digit -= '0';
+
+ if (timestr[time_idx + 1] == ':') /* ones, left aligned */
+ {
+ memcpy(progress_pattern, numbers[digit], 4);
+ time_idx += 2;
+ }
+ else /* tens, shifted right */
+ {
+ for (j = 0; j < 4; j++)
+ progress_pattern[j] = numbers[digit][j] >> 1;
+
+ if (time_idx > 0) /* not the first group, add colon in front */
+ {
+ progress_pattern[1] |= 0x10u;
+ progress_pattern[3] |= 0x10u;
+ }
+ time_idx++;
+ }
+
+ if (pos >= 5)
+ progress_pattern[5] = progress_pattern[6] = 0x1fu;
+ }
+
+ if (pos > 0 && pos < 5)
+ {
+ softchar = true;
+ progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
+ }
+
+ if (softchar && pat_idx < 8)
+ {
+ display->define_pattern(data->wps_progress_pat[pat_idx],
+ progress_pattern);
+ buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
+ pat_idx++;
+ }
+ else if (pos <= 0)
+ buf = utf8encode(' ', buf);
+ else
+ buf = utf8encode(0xe115, buf); /* 2/7 _ */
+ }
+ *buf = '\0';
+}
+
+#endif /* HAVE_LCD_CHARCELL */
+
+/* Return the index to the end token for the conditional token at index.
+ The conditional token can be either a start token or a separator
+ (i.e. option) token.
+*/
+static int find_conditional_end(struct wps_data *data, int index)
+{
+ int ret = index;
+ while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
+ ret = data->tokens[ret].value.i;
+
+ /* ret now is the index to the end token for the conditional. */
+ return ret;
+}
+
+/* Evaluate the conditional that is at *token_index and return whether a skip
+ has ocurred. *token_index is updated with the new position.
+*/
+static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
+{
+ if (!gwps)
+ return false;
+
+ struct wps_data *data = gwps->data;
+
+ int i, cond_end;
+ int cond_index = *token_index;
+ char result[128];
+ const char *value;
+ unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
+ unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
+
+ /* treat ?xx<true> constructs as if they had 2 options. */
+ if (num_options < 2)
+ num_options = 2;
+
+ int intval = num_options;
+ /* get_token_value needs to know the number of options in the enum */
+ value = get_token_value(gwps, &data->tokens[cond_index + 1],
+ result, sizeof(result), &intval);
+
+ /* intval is now the number of the enum option we want to read,
+ starting from 1. If intval is -1, we check if value is empty. */
+ if (intval == -1)
+ intval = (value && *value) ? 1 : num_options;
+ else if (intval > num_options || intval < 1)
+ intval = num_options;
+
+ data->tokens[cond_index].value.i = (intval << 8) + num_options;
+
+ /* skip to the appropriate enum case */
+ int next = cond_index + 2;
+ for (i = 1; i < intval; i++)
+ {
+ next = data->tokens[next].value.i;
+ }
+ *token_index = next;
+
+ if (prev_val == intval)
+ {
+ /* Same conditional case as previously. Return without clearing the
+ pictures */
+ return false;
+ }
+
+ cond_end = find_conditional_end(data, cond_index + 2);
+ for (i = cond_index + 3; i < cond_end; i++)
+ {
+#ifdef HAVE_LCD_BITMAP
+ /* clear all pictures in the conditional and nested ones */
+ if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
+ clear_image_pos(gwps, data->tokens[i].value.i & 0xFF);
+#endif
+#ifdef HAVE_ALBUMART
+ if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
+ draw_album_art(gwps, audio_current_aa_hid(), true);
+#endif
+ }
+
+ return true;
+}
+
+/* Read a (sub)line to the given alignment format buffer.
+ linebuf is the buffer where the data is actually stored.
+ align is the alignment format that'll be used to display the text.
+ The return value indicates whether the line needs to be updated.
+*/
+static bool get_line(struct gui_wps *gwps,
+ int line, int subline,
+ struct align_pos *align,
+ char *linebuf,
+ int linebuf_size)
+{
+ struct wps_data *data = gwps->data;
+
+ char temp_buf[128];
+ char *buf = linebuf; /* will always point to the writing position */
+ char *linebuf_end = linebuf + linebuf_size - 1;
+ int i, last_token_idx;
+ bool update = false;
+
+ /* alignment-related variables */
+ int cur_align;
+ char* cur_align_start;
+ cur_align_start = buf;
+ cur_align = WPS_ALIGN_LEFT;
+ align->left = NULL;
+ align->center = NULL;
+ align->right = NULL;
+
+ /* Process all tokens of the desired subline */
+ last_token_idx = wps_last_token_index(data, line, subline);
+ for (i = wps_first_token_index(data, line, subline);
+ i <= last_token_idx; i++)
+ {
+ switch(data->tokens[i].type)
+ {
+ case WPS_TOKEN_CONDITIONAL:
+ /* place ourselves in the right conditional case */
+ update |= evaluate_conditional(gwps, &i);
+ break;
+
+ case WPS_TOKEN_CONDITIONAL_OPTION:
+ /* we've finished in the curent conditional case,
+ skip to the end of the conditional structure */
+ i = find_conditional_end(data, i);
+ break;
+
+#ifdef HAVE_LCD_BITMAP
+ case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
+ {
+ struct gui_img *img = data->img;
+ int n = data->tokens[i].value.i & 0xFF;
+ int subimage = data->tokens[i].value.i >> 8;
+
+ if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
+ img[n].display = subimage;
+ break;
+ }
+#endif
+
+ case WPS_TOKEN_ALIGN_LEFT:
+ case WPS_TOKEN_ALIGN_CENTER:
+ case WPS_TOKEN_ALIGN_RIGHT:
+ /* remember where the current aligned text started */
+ switch (cur_align)
+ {
+ case WPS_ALIGN_LEFT:
+ align->left = cur_align_start;
+ break;
+
+ case WPS_ALIGN_CENTER:
+ align->center = cur_align_start;
+ break;
+
+ case WPS_ALIGN_RIGHT:
+ align->right = cur_align_start;
+ break;
+ }
+ /* start a new alignment */
+ switch (data->tokens[i].type)
+ {
+ case WPS_TOKEN_ALIGN_LEFT:
+ cur_align = WPS_ALIGN_LEFT;
+ break;
+ case WPS_TOKEN_ALIGN_CENTER:
+ cur_align = WPS_ALIGN_CENTER;
+ break;
+ case WPS_TOKEN_ALIGN_RIGHT:
+ cur_align = WPS_ALIGN_RIGHT;
+ break;
+ default:
+ break;
+ }
+ *buf++ = 0;
+ cur_align_start = buf;
+ break;
+ case WPS_VIEWPORT_ENABLE:
+ {
+ char label = data->tokens[i].value.i;
+ int j;
+ char temp = VP_DRAW_HIDEABLE;
+ for(j=0;j<data->num_viewports;j++)
+ {
+ temp = VP_DRAW_HIDEABLE;
+ if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) &&
+ (data->viewports[j].label == label))
+ {
+ if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN)
+ temp |= VP_DRAW_WASHIDDEN;
+ data->viewports[j].hidden_flags = temp;
+ }
+ }
+ }
+ break;
+ default:
+ {
+ /* get the value of the tag and copy it to the buffer */
+ const char *value = get_token_value(gwps, &data->tokens[i],
+ temp_buf, sizeof(temp_buf), NULL);
+ if (value)
+ {
+ update = true;
+ while (*value && (buf < linebuf_end))
+ *buf++ = *value++;
+ }
+ break;
+ }
+ }
+ }
+
+ /* close the current alignment */
+ switch (cur_align)
+ {
+ case WPS_ALIGN_LEFT:
+ align->left = cur_align_start;
+ break;
+
+ case WPS_ALIGN_CENTER:
+ align->center = cur_align_start;
+ break;
+
+ case WPS_ALIGN_RIGHT:
+ align->right = cur_align_start;
+ break;
+ }
+
+ return update;
+}
+
+static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
+{
+ struct wps_data *data = gwps->data;
+ int i;
+ int subline_idx = wps_subline_index(data, line, subline);
+ int last_token_idx = wps_last_token_index(data, line, subline);
+
+ data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
+
+ for (i = wps_first_token_index(data, line, subline);
+ i <= last_token_idx; i++)
+ {
+ switch(data->tokens[i].type)
+ {
+ case WPS_TOKEN_CONDITIONAL:
+ /* place ourselves in the right conditional case */
+ evaluate_conditional(gwps, &i);
+ break;
+
+ case WPS_TOKEN_CONDITIONAL_OPTION:
+ /* we've finished in the curent conditional case,
+ skip to the end of the conditional structure */
+ i = find_conditional_end(data, i);
+ break;
+
+ case WPS_TOKEN_SUBLINE_TIMEOUT:
+ data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/* Calculates which subline should be displayed for the specified line
+ Returns true iff the subline must be refreshed */
+static bool update_curr_subline(struct gui_wps *gwps, int line)
+{
+ struct wps_data *data = gwps->data;
+
+ int search, search_start, num_sublines;
+ bool reset_subline;
+ bool new_subline_refresh;
+ bool only_one_subline;
+
+ num_sublines = data->lines[line].num_sublines;
+ reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
+ new_subline_refresh = false;
+ only_one_subline = false;
+
+ /* if time to advance to next sub-line */
+ if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) ||
+ reset_subline)
+ {
+ /* search all sublines until the next subline with time > 0
+ is found or we get back to the subline we started with */
+ if (reset_subline)
+ search_start = 0;
+ else
+ search_start = data->lines[line].curr_subline;
+
+ for (search = 0; search < num_sublines; search++)
+ {
+ data->lines[line].curr_subline++;
+
+ /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
+ if (data->lines[line].curr_subline == num_sublines)
+ {
+ if (data->lines[line].curr_subline == 1)
+ only_one_subline = true;
+ data->lines[line].curr_subline = 0;
+ }
+
+ /* if back where we started after search or
+ only one subline is defined on the line */
+ if (((search > 0) &&
+ (data->lines[line].curr_subline == search_start)) ||
+ only_one_subline)
+ {
+ /* no other subline with a time > 0 exists */
+ data->lines[line].subline_expire_time = (reset_subline ?
+ current_tick :
+ data->lines[line].subline_expire_time) + 100 * HZ;
+ break;
+ }
+ else
+ {
+ /* get initial time multiplier for this subline */
+ get_subline_timeout(gwps, line, data->lines[line].curr_subline);
+
+ int subline_idx = wps_subline_index(data, line,
+ data->lines[line].curr_subline);
+
+ /* only use this subline if subline time > 0 */
+ if (data->sublines[subline_idx].time_mult > 0)
+ {
+ new_subline_refresh = true;
+ data->lines[line].subline_expire_time = (reset_subline ?
+ current_tick : data->lines[line].subline_expire_time) +
+ TIMEOUT_UNIT*data->sublines[subline_idx].time_mult;
+ break;
+ }
+ }
+ }
+ }
+
+ return new_subline_refresh;
+}
+
+/* Display a line appropriately according to its alignment format.
+ format_align contains the text, separated between left, center and right.
+ line is the index of the line on the screen.
+ scroll indicates whether the line is a scrolling one or not.
+*/
+static void write_line(struct screen *display,
+ struct align_pos *format_align,
+ int line,
+ bool scroll)
+{
+ int left_width = 0, left_xpos;
+ int center_width = 0, center_xpos;
+ int right_width = 0, right_xpos;
+ int ypos;
+ int space_width;
+ int string_height;
+ int scroll_width;
+
+ /* calculate different string sizes and positions */
+ display->getstringsize((unsigned char *)" ", &space_width, &string_height);
+ if (format_align->left != 0) {
+ display->getstringsize((unsigned char *)format_align->left,
+ &left_width, &string_height);
+ }
+
+ if (format_align->right != 0) {
+ display->getstringsize((unsigned char *)format_align->right,
+ &right_width, &string_height);
+ }
+
+ if (format_align->center != 0) {
+ display->getstringsize((unsigned char *)format_align->center,
+ &center_width, &string_height);
+ }
+
+ left_xpos = 0;
+ right_xpos = (display->getwidth() - right_width);
+ center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
+
+ scroll_width = display->getwidth() - left_xpos;
+
+ /* Checks for overlapping strings.
+ If needed the overlapping strings will be merged, separated by a
+ space */
+
+ /* CASE 1: left and centered string overlap */
+ /* there is a left string, need to merge left and center */
+ if ((left_width != 0 && center_width != 0) &&
+ (left_xpos + left_width + space_width > center_xpos)) {
+ /* replace the former separator '\0' of left and
+ center string with a space */
+ *(--format_align->center) = ' ';
+ /* calculate the new width and position of the merged string */
+ left_width = left_width + space_width + center_width;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no left string, move center to left */
+ if ((left_width == 0 && center_width != 0) &&
+ (left_xpos + left_width > center_xpos)) {
+ /* move the center string to the left string */
+ format_align->left = format_align->center;
+ /* calculate the new width and position of the string */
+ left_width = center_width;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+
+ /* CASE 2: centered and right string overlap */
+ /* there is a right string, need to merge center and right */
+ if ((center_width != 0 && right_width != 0) &&
+ (center_xpos + center_width + space_width > right_xpos)) {
+ /* replace the former separator '\0' of center and
+ right string with a space */
+ *(--format_align->right) = ' ';
+ /* move the center string to the right after merge */
+ format_align->right = format_align->center;
+ /* calculate the new width and position of the merged string */
+ right_width = center_width + space_width + right_width;
+ right_xpos = (display->getwidth() - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no right string, move center to right */
+ if ((center_width != 0 && right_width == 0) &&
+ (center_xpos + center_width > right_xpos)) {
+ /* move the center string to the right string */
+ format_align->right = format_align->center;
+ /* calculate the new width and position of the string */
+ right_width = center_width;
+ right_xpos = (display->getwidth() - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+
+ /* CASE 3: left and right overlap
+ There is no center string anymore, either there never
+ was one or it has been merged in case 1 or 2 */
+ /* there is a left string, need to merge left and right */
+ if ((left_width != 0 && center_width == 0 && right_width != 0) &&
+ (left_xpos + left_width + space_width > right_xpos)) {
+ /* replace the former separator '\0' of left and
+ right string with a space */
+ *(--format_align->right) = ' ';
+ /* calculate the new width and position of the string */
+ left_width = left_width + space_width + right_width;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+ /* there is no left string, move right to left */
+ if ((left_width == 0 && center_width == 0 && right_width != 0) &&
+ (left_width > right_xpos)) {
+ /* move the right string to the left string */
+ format_align->left = format_align->right;
+ /* calculate the new width and position of the string */
+ left_width = right_width;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+
+ ypos = (line * string_height);
+
+
+ if (scroll && ((left_width > scroll_width) ||
+ (center_width > scroll_width) ||
+ (right_width > scroll_width)))
+ {
+ display->puts_scroll(0, line,
+ (unsigned char *)format_align->left);
+ }
+ else
+ {
+#ifdef HAVE_LCD_BITMAP
+ /* clear the line first */
+ display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
+ display->set_drawmode(DRMODE_SOLID);
+#endif
+
+ /* Nasty hack: we output an empty scrolling string,
+ which will reset the scroller for that line */
+ display->puts_scroll(0, line, (unsigned char *)"");
+
+ /* print aligned strings */
+ if (left_width != 0)
+ {
+ display->putsxy(left_xpos, ypos,
+ (unsigned char *)format_align->left);
+ }
+ if (center_width != 0)
+ {
+ display->putsxy(center_xpos, ypos,
+ (unsigned char *)format_align->center);
+ }
+ if (right_width != 0)
+ {
+ display->putsxy(right_xpos, ypos,
+ (unsigned char *)format_align->right);
+ }
+ }
+}
+
+bool gui_wps_redraw(struct gui_wps *gwps,
+ int ffwd_offset,
+ unsigned refresh_mode)
+{
+ struct wps_data *data = gwps->data;
+ struct screen *display = gwps->display;
+ struct wps_state *state = gwps->state;
+
+ if (!data || !state || !display)
+ return false;
+
+ struct mp3entry *id3 = state->id3;
+
+ if (!id3)
+ return false;
+
+ int v, line, i, subline_idx;
+ unsigned flags;
+ char linebuf[MAX_PATH];
+
+ struct align_pos align;
+ align.left = NULL;
+ align.center = NULL;
+ align.right = NULL;
+
+ bool update_line, new_subline_refresh;
+
+#ifdef HAVE_LCD_BITMAP
+
+ /* to find out wether the peak meter is enabled we
+ assume it wasn't until we find a line that contains
+ the peak meter. We can't use peak_meter_enabled itself
+ because that would mean to turn off the meter thread
+ temporarily. (That shouldn't matter unless yield
+ or sleep is called but who knows...)
+ */
+ bool enable_pm = false;
+
+#endif
+
+ /* reset to first subline if refresh all flag is set */
+ if (refresh_mode == WPS_REFRESH_ALL)
+ {
+ display->set_viewport(&data->viewports[0].vp);
+ display->clear_viewport();
+
+ for (i = 0; i <= data->num_lines; i++)
+ {
+ data->lines[i].curr_subline = SUBLINE_RESET;
+ }
+ }
+
+#ifdef HAVE_LCD_CHARCELLS
+ for (i = 0; i < 8; i++)
+ {
+ if (data->wps_progress_pat[i] == 0)
+ data->wps_progress_pat[i] = display->get_locked_pattern();
+ }
+#endif
+
+ state->ff_rewind_count = ffwd_offset;
+
+ /* disable any viewports which are conditionally displayed */
+ for (v = 0; v < data->num_viewports; v++)
+ {
+ if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE)
+ {
+ if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN)
+ data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN;
+ else
+ data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN;
+ }
+ }
+ for (v = 0; v < data->num_viewports; v++)
+ {
+ struct wps_viewport *wps_vp = &(data->viewports[v]);
+ unsigned vp_refresh_mode = refresh_mode;
+ display->set_viewport(&wps_vp->vp);
+
+#ifdef HAVE_LCD_BITMAP
+ /* Set images to not to be displayed */
+ for (i = 0; i < MAX_IMAGES; i++)
+ {
+ data->img[i].display = -1;
+ }
+#endif
+ /* dont redraw the viewport if its disabled */
+ if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN))
+ {
+ if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN))
+ display->scroll_stop(&wps_vp->vp);
+ wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN;
+ continue;
+ }
+ else if (((wps_vp->hidden_flags&
+ (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
+ == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
+ {
+ vp_refresh_mode = WPS_REFRESH_ALL;
+ wps_vp->hidden_flags = VP_DRAW_HIDEABLE;
+ }
+ if (vp_refresh_mode == WPS_REFRESH_ALL)
+ {
+ display->clear_viewport();
+ }
+
+ for (line = wps_vp->first_line;
+ line <= wps_vp->last_line; line++)
+ {
+ memset(linebuf, 0, sizeof(linebuf));
+ update_line = false;
+
+ /* get current subline for the line */
+ new_subline_refresh = update_curr_subline(gwps, line);
+
+ subline_idx = wps_subline_index(data, line,
+ data->lines[line].curr_subline);
+ flags = data->sublines[subline_idx].line_type;
+
+ if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
+ || new_subline_refresh)
+ {
+ /* get_line tells us if we need to update the line */
+ update_line = get_line(gwps, line, data->lines[line].curr_subline,
+ &align, linebuf, sizeof(linebuf));
+ }
+#ifdef HAVE_LCD_BITMAP
+ /* peakmeter */
+ if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
+ {
+ /* the peakmeter should be alone on its line */
+ update_line = false;
+
+ int h = font_get(wps_vp->vp.font)->height;
+ int peak_meter_y = (line - wps_vp->first_line)* h;
+
+ /* The user might decide to have the peak meter in the last
+ line so that it is only displayed if no status bar is
+ visible. If so we neither want do draw nor enable the
+ peak meter. */
+ if (peak_meter_y + h <= display->getheight()) {
+ /* found a line with a peak meter -> remember that we must
+ enable it later */
+ enable_pm = true;
+ peak_meter_enabled = true;
+ peak_meter_screen(gwps->display, 0, peak_meter_y,
+ MIN(h, display->getheight() - peak_meter_y));
+ }
+ else
+ {
+ peak_meter_enabled = false;
+ }
+ }
+
+#else /* HAVE_LCD_CHARCELL */
+
+ /* progressbar */
+ if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+ {
+ if (data->full_line_progressbar)
+ draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
+ else
+ draw_player_progress(gwps);
+ }
+#endif
+
+ if (update_line &&
+ /* conditionals clear the line which means if the %Vd is put into the default
+ viewport there will be a blank line.
+ To get around this we dont allow any actual drawing to happen in the
+ deault vp if other vp's are defined */
+ ((data->num_viewports>1 && v!=0) || data->num_viewports == 1))
+ {
+ if (flags & WPS_REFRESH_SCROLL)
+ {
+ /* if the line is a scrolling one we don't want to update
+ too often, so that it has the time to scroll */
+ if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
+ write_line(display, &align, line - wps_vp->first_line, true);
+ }
+ else
+ write_line(display, &align, line - wps_vp->first_line, false);
+ }
+ }
+
+#ifdef HAVE_LCD_BITMAP
+ /* progressbar */
+ if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+ {
+ if (wps_vp->pb)
+ {
+ draw_progressbar(gwps, wps_vp);
+ }
+ }
+ /* Now display any images in this viewport */
+ wps_display_images(gwps, &wps_vp->vp);
+#endif
+ }
+
+#ifdef HAVE_LCD_BITMAP
+ data->peak_meter_enabled = enable_pm;
+#endif
+
+ if (refresh_mode & WPS_REFRESH_STATUSBAR)
+ {
+ gwps_draw_statusbars();
+ }
+ /* Restore the default viewport */
+ display->set_viewport(NULL);
+
+ display->update();
+
+ return true;
+}