summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES2
-rw-r--r--apps/gui/gwps-common.c3644
-rw-r--r--apps/gui/gwps-common.h12
-rw-r--r--apps/gui/gwps.c141
-rw-r--r--apps/gui/gwps.h206
-rw-r--r--apps/gui/wps_debug.c407
-rw-r--r--apps/gui/wps_parser.c957
-rw-r--r--apps/lang/english.lang8
8 files changed, 3042 insertions, 2335 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 4a4d83d..7e6f84b 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -58,6 +58,8 @@ gui/splash.c
gui/statusbar.c
gui/textarea.c
gui/yesno.c
+gui/wps_debug.c
+gui/wps_parser.c
#ifdef HAVE_LCD_CHARCELLS
player/icons.c
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
index bce213a..2bd2e4c 100644
--- a/apps/gui/gwps-common.c
+++ b/apps/gui/gwps-common.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 Björn Stenberg
+ * Copyright (C) 2007 Nicolas Pennequin
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
@@ -56,16 +56,11 @@
#include "action.h"
#include "cuesheet.h"
-#ifdef HAVE_LCD_CHARCELLS
-static bool draw_player_progress(struct gui_wps *gwps);
-static void draw_player_fullbar(struct gui_wps *gwps,
- char* buf, int buf_size);
-#endif
-
#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
/* 3% of 30min file == 54s step size */
#define MIN_FF_REWIND_STEP 500
+#if 0
/* Skip leading UTF-8 BOM, if present. */
static char* skip_utf8_bom(char* buf)
{
@@ -78,287 +73,14 @@ static char* skip_utf8_bom(char* buf)
return buf;
}
-
-/*
- * returns the image_id between
- * a..z and A..Z
- */
-#ifdef HAVE_LCD_BITMAP
-static int get_image_id(int c)
-{
- if(c >= 'a' && c <= 'z')
- return c - 'a';
- else if(c >= 'A' && c <= 'Z')
- return c - 'A' + 26;
- else
- return -1;
-}
-#endif
-
-/*
- * parse the given buffer for following static tags:
- * %x - load image for always display
- * %X - load backdrop image
- * %xl - preload image
- * %we - enable statusbar on wps regardless of the global setting
- * %wd - disable statusbar on wps regardless of the global setting
- * and also for:
- * # - a comment line
- *
- * it returns true if one of these tags is found and handled
- * false otherwise
- */
-bool wps_data_preload_tags(struct wps_data *data, char *buf,
- const char *bmpdir, size_t bmpdirlen)
-{
- if(!data || !buf) return false;
-
- char c;
-#ifndef HAVE_LCD_BITMAP
- /* no bitmap-lcd == no bitmap loading */
- (void)bmpdir;
- (void)bmpdirlen;
-#endif
- buf = skip_utf8_bom(buf);
-
- if(*buf == '#')
- return true;
- if('%' != *buf)
- return false;
- buf++;
-
- c = *buf;
- switch (c)
- {
-#ifdef HAVE_LCD_BITMAP
- case 'w':
- /*
- * if tag found then return because these two tags must be on
- * must be on their own line
- */
- if(*(buf+1) == 'd' || *(buf+1) == 'e')
- {
- data->wps_sb_tag = true;
- if( *(buf+1) == 'e' )
- data->show_sb_on_wps = true;
- return true;
- }
- break;
-
-#if LCD_DEPTH > 1
- case 'X':
- /* Backdrop image - must be the same size as the LCD */
- {
- char *ptr = buf+2;
- char *pos = NULL;
- char imgname[MAX_PATH];
-
- /* format: %X|filename.bmp| */
-
- /* get filename */
- pos = strchr(ptr, '|');
- if ((pos - ptr) <
- (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
- {
- memcpy(imgname, bmpdir, bmpdirlen);
- imgname[bmpdirlen] = '/';
- memcpy(&imgname[bmpdirlen+1],
- ptr, pos - ptr);
- imgname[bmpdirlen+1+pos-ptr] = 0;
- }
- else
- {
- /* filename too long */
- imgname[0] = 0;
- }
-
- /* load the image */
- return load_wps_backdrop(imgname);
- }
-
- break;
-#endif
-
- case 'P':
- /* progress bar image */
- {
- int ret = 0;
- char *ptr = buf+2;
- char *pos = NULL;
- char imgname[MAX_PATH];
-
- /* format: %P|filename.bmp| */
- {
- /* get filename */
- pos = strchr(ptr, '|');
- if ((pos - ptr) <
- (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
- {
- memcpy(imgname, bmpdir, bmpdirlen);
- imgname[bmpdirlen] = '/';
- memcpy(&imgname[bmpdirlen+1],
- ptr, pos - ptr);
- imgname[bmpdirlen+1+pos-ptr] = 0;
- }
- else
- /* filename too long */
- imgname[0] = 0;
-
- ptr = pos+1;
-
- /* load the image */
- data->progressbar.bm.data=data->img_buf_ptr;
- ret = read_bmp_file(imgname, &data->progressbar.bm,
- data->img_buf_free,
- FORMAT_ANY|FORMAT_TRANSPARENT);
-
- if (ret > 0)
- {
-#if LCD_DEPTH == 16
- if (ret % 2) ret++;
- /* Always consume an even number of bytes */
-#endif
-
- data->img_buf_ptr += ret;
- data->img_buf_free -= ret;
-
- if (data->progressbar.bm.width <= LCD_WIDTH) {
- data->progressbar.have_bitmap_pb=true;
- return true;
- } else
- return false;
- }
-
- }
- }
-
- break;
-
- case 'x':
- /* Preload images so the %xd# tag can display it */
- {
- int ret = 0;
- int n;
- char *ptr = buf+1;
- char *pos = NULL;
- char imgname[MAX_PATH];
- char qual = *ptr;
-
- if (qual == 'l' || qual == '|') /* format:
- %x|n|filename.bmp|x|y|
- or
- %xl|n|filename.bmp|x|y|
- */
- {
- ptr = strchr(ptr, '|') + 1;
- pos = strchr(ptr, '|');
- if (pos)
- {
- /* get the image ID */
- n = get_image_id(*ptr);
-
- if(n < 0 || n >= MAX_IMAGES)
- {
- /* Skip the rest of the line */
- while(*buf != '\n')
- buf++;
- return false;
- }
- ptr = pos+1;
-
- /* check the image number and load state */
- if (data->img[n].loaded)
- {
- /* Skip the rest of the line */
- while(*buf != '\n')
- buf++;
- return false;
- }
- else
- {
- /* get filename */
- pos = strchr(ptr, '|');
-
- if (pos == NULL)
- return false;
-
- if ((pos - ptr) <
- (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
- {
- memcpy(imgname, bmpdir, bmpdirlen);
- imgname[bmpdirlen] = '/';
- memcpy(&imgname[bmpdirlen+1],
- ptr, pos - ptr);
- imgname[bmpdirlen+1+pos-ptr] = 0;
- }
- else
- /* filename too long */
- imgname[0] = 0;
-
- ptr = pos+1;
-
- /* get x-position */
- pos = strchr(ptr, '|');
- if (pos)
- data->img[n].x = atoi(ptr);
- else
- {
- /* weird syntax, bail out */
- buf++;
- return false;
- }
-
- /* get y-position */
- ptr = pos+1;
- pos = strchr(ptr, '|');
- if (pos)
- data->img[n].y = atoi(ptr);
- else
- {
- /* weird syntax, bail out */
- buf++;
- return false;
- }
-
- /* load the image */
- data->img[n].bm.data = data->img_buf_ptr;
- ret = read_bmp_file(imgname, &data->img[n].bm,
- data->img_buf_free,
- FORMAT_ANY|FORMAT_TRANSPARENT);
-
- if (ret > 0)
- {
-#if LCD_DEPTH == 16
- if (ret % 2) ret++;
- /* Always consume an even number of bytes */
#endif
- data->img_buf_ptr += ret;
- data->img_buf_free -= ret;
- data->img[n].loaded = true;
- if(qual == '|')
- data->img[n].always_display = true;
- }
- return true;
- }
- }
- }
- }
-
- break;
-#endif
- }
- /* no of these tags found */
- return false;
-}
-
-
/* draws the statusbar on the given wps-screen */
#ifdef HAVE_LCD_BITMAP
-static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
+void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
{
bool draw = global_settings.statusbar;
-
+
if(wps->data->wps_sb_tag
&& wps->data->show_sb_on_wps)
draw = true;
@@ -372,1235 +94,449 @@ static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
gui_statusbar_draw((wps)->statusbar, (force))
#endif
-/* Extract a part from a path.
- *
- * buf - buffer extract part to.
- * buf_size - size of buffer.
- * path - path to extract from.
- * level - what to extract. 0 is file name, 1 is parent of file, 2 is
- * parent of parent, etc.
- *
- * Returns buf if the desired level was found, NULL otherwise.
- */
-static char* get_dir(char* buf, int buf_size, const char* path, int level)
+/* fades the volume */
+void fade(bool fade_in)
{
- const char* sep;
- const char* last_sep;
- int len;
+ int fp_global_vol = global_settings.volume << 8;
+ int fp_min_vol = sound_min(SOUND_VOLUME) << 8;
+ int fp_step = (fp_global_vol - fp_min_vol) / 30;
- sep = path + strlen(path);
- last_sep = sep;
+ if (fade_in) {
+ /* fade in */
+ int fp_volume = fp_min_vol;
- while (sep > path)
- {
- if ('/' == *(--sep))
- {
- if (!level)
- {
- break;
- }
+ /* zero out the sound */
+ sound_set_volume(fp_min_vol >> 8);
- level--;
- last_sep = sep - 1;
+ sleep(HZ/10); /* let audio thread run */
+ audio_resume();
+
+ while (fp_volume < fp_global_vol - fp_step) {
+ fp_volume += fp_step;
+ sound_set_volume(fp_volume >> 8);
+ sleep(1);
}
+ sound_set_volume(global_settings.volume);
}
+ else {
+ /* fade out */
+ int fp_volume = fp_global_vol;
- if (level || (last_sep <= sep))
- {
- return NULL;
- }
+ while (fp_volume > fp_min_vol + fp_step) {
+ fp_volume -= fp_step;
+ sound_set_volume(fp_volume >> 8);
+ sleep(1);
+ }
+ audio_pause();
+#ifndef SIMULATOR
+ /* let audio thread run and wait for the mas to run out of data */
+ while (!mp3_pause_done())
+#endif
+ sleep(HZ/10);
- len = MIN(last_sep - sep, buf_size - 1);
- strncpy(buf, sep + 1, len);
- buf[len] = 0;
- return buf;
+ /* reset volume to what it was before the fade */
+ sound_set_volume(global_settings.volume);
+ }
}
-/* Get the tag specified by the two characters at fmt.
- *
- * cid3 - ID3 data to get tag values from.
- * nid3 - next-song ID3 data to get tag values from.
- * tag - string (of two characters) specifying the tag to get.
- * buf - buffer to certain tags, such as track number, play time or
- * directory name.
- * buf_size - size of buffer.
- * flags - returns the type of the line. See constants i wps-display.h
- *
- * Returns the tag. NULL indicates the tag wasn't available.
- */
-static char* get_tag(struct wps_data* wps_data,
- struct mp3entry* cid3,
- struct mp3entry* nid3,
- const char* tag,
- char* buf,
- int buf_size,
- unsigned char* tag_len,
- unsigned short* subline_time_mult,
- unsigned char* flags,
- int *intval)
+/* set volume */
+void setvol(void)
{
- struct mp3entry *id3 = cid3; /* default to current song */
- int limit = *intval;
-#ifndef HAVE_LCD_CHARCELLS
- (void)wps_data;
-#endif
- if ((0 == tag[0]) || (0 == tag[1]))
- {
- *tag_len = 0;
- return NULL;
- }
-
- *tag_len = 2;
-
- *intval = 0;
-
- switch (tag[0])
- {
- case 'I': /* ID3 Information */
- id3 = nid3; /* display next-song data */
- *flags |= WPS_REFRESH_DYNAMIC;
- if(!id3)
- return NULL; /* no such info (yet) */
- /* fall-through */
- case 'i': /* ID3 Information */
- *flags |= WPS_REFRESH_STATIC;
- switch (tag[1])
- {
- case 't': /* ID3 Title */
- return id3->title;
-
- case 'a': /* ID3 Artist */
- return id3->artist;
-
- case 'n': /* ID3 Track Number */
- if (id3->track_string)
- return id3->track_string;
-
- if (id3->tracknum) {
- snprintf(buf, buf_size, "%d", id3->tracknum);
- return buf;
- }
- return NULL;
-
- case 'd': /* ID3 Album/Disc */
- return id3->album;
-
- case 'c': /* ID3 Composer */
- return id3->composer;
-
- case 'C': /* ID3 Comment */
- return id3->comment;
-
- case 'A': /* ID3 Albumartist */
- return id3->albumartist;
-
- case 'y': /* year */
- if( id3->year_string )
- return id3->year_string;
-
- if (id3->year) {
- snprintf(buf, buf_size, "%d", id3->year);
- return buf;
- }
- return NULL;
-
- case 'g': /* genre */
- return id3->genre_string;
-
- case 'v': /* id3 version */
- switch (id3->id3version)
- {
- case ID3_VER_1_0:
- return "1";
-
- case ID3_VER_1_1:
- return "1.1";
-
- case ID3_VER_2_2:
- return "2.2";
-
- case ID3_VER_2_3:
- return "2.3";
-
- case ID3_VER_2_4:
- return "2.4";
-
- default:
- return NULL;
- }
- }
- break;
-
- case 'F': /* File Information */
- id3 = nid3;
- *flags |= WPS_REFRESH_DYNAMIC;
- if(!id3)
- return NULL; /* no such info (yet) */
- /* fall-through */
- case 'f': /* File Information */
- *flags |= WPS_REFRESH_STATIC;
- switch(tag[1])
- {
- case 'v': /* VBR file? */
- return id3->vbr ? "(avg)" : NULL;
-
- case 'b': /* File Bitrate */
- if(id3->bitrate)
- snprintf(buf, buf_size, "%d", id3->bitrate);
- else
- snprintf(buf, buf_size, "?");
- return buf;
-
- case 'f': /* File Frequency */
- snprintf(buf, buf_size, "%ld", id3->frequency);
- return buf;
-
- case 'p': /* File Path */
- return id3->path;
-
- case 'm': /* File Name - With Extension */
- return get_dir(buf, buf_size, id3->path, 0);
-
- case 'n': /* File Name */
- if (get_dir(buf, buf_size, id3->path, 0))
- {
- /* Remove extension */
- char* sep = strrchr(buf, '.');
-
- if (NULL != sep)
- {
- *sep = 0;
- }
-
- return buf;
- }
- else
- {
- return NULL;
- }
-
- case 's': /* File Size (in kilobytes) */
- snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
- return buf;
-
- case 'c': /* File Codec */
- if(id3->codectype == AFMT_UNKNOWN)
- *intval = AFMT_NUM_CODECS;
- else
- *intval = id3->codectype;
- return id3_get_codec(id3);
- }
- break;
+ if (global_settings.volume < sound_min(SOUND_VOLUME))
+ global_settings.volume = sound_min(SOUND_VOLUME);
+ if (global_settings.volume > sound_max(SOUND_VOLUME))
+ global_settings.volume = sound_max(SOUND_VOLUME);
+ sound_set_volume(global_settings.volume);
+ settings_save();
+}
+/* return true if screen restore is needed
+ return false otherwise
+*/
+bool update_onvol_change(struct gui_wps * gwps)
+{
+ gui_wps_statusbar_draw(gwps, false);
+ gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
- case 'p': /* Playlist/Song Information */
- switch(tag[1])
- {
- case 'b': /* progress bar */
- *flags |= WPS_REFRESH_PLAYER_PROGRESS;
#ifdef HAVE_LCD_CHARCELLS
- char *end = utf8encode(wps_data->wps_progress_pat[0], buf);
- *end = '\0';
- wps_data->full_line_progressbar=0;
- return buf;
-#else
- /* default values : */
- wps_data->progress_top = -1;
- wps_data->progress_height = 6;
- wps_data->progress_start = 0;
- wps_data->progress_end = 0;
-
- char *prev=strchr(tag, '|');
- if (prev) {
- char *p=strchr(prev+1, '|');
- if (p) {
- wps_data->progress_height=atoi(++prev);
- prev=strchr(prev, '|');
- p=strchr(++p, '|');
- if (p) {
- wps_data->progress_start=atoi(++prev);
- prev=strchr(prev, '|');
- p=strchr(++p, '|');
- if (p) {
- wps_data->progress_end=atoi(++prev);
- prev=strchr(prev, '|');
- p=strchr(++p, '|');
- if(p)
- wps_data->progress_top = atoi(++prev);
- }
-
- if (wps_data->progress_height<3)
- wps_data->progress_height=3;
- if (wps_data->progress_end<wps_data->progress_start+3)
- wps_data->progress_end=0;
- }
- }
- }
- return "\x01";
-#endif
- case 'f': /* full-line progress bar */
-#ifdef HAVE_LCD_CHARCELLS
- if(is_new_player()) {
- *flags |= WPS_REFRESH_PLAYER_PROGRESS;
- *flags |= WPS_REFRESH_DYNAMIC;
- wps_data->full_line_progressbar=1;
- /* we need 11 characters (full line) for
- progress-bar */
- snprintf(buf, buf_size, " ");
- }
- else
- {
- /* Tell the user if we have an OldPlayer */
- snprintf(buf, buf_size, " <Old LCD> ");
- }
- return buf;
-#endif
- case 'p': /* Playlist Position */
- *flags |= WPS_REFRESH_STATIC;
- snprintf(buf, buf_size, "%d",
- playlist_get_display_index());
- return buf;
-
- case 'n': /* Playlist Name (without path) */
- *flags |= WPS_REFRESH_STATIC;
- return playlist_name(NULL, buf, buf_size);
-
- case 'e': /* Playlist Total Entries */
- *flags |= WPS_REFRESH_STATIC;
- snprintf(buf, buf_size, "%d", playlist_amount());
- return buf;
-
- case 'c': /* Current Time in Song */
- *flags |= WPS_REFRESH_DYNAMIC;
- format_time(buf, buf_size,
- id3->elapsed + wps_state.ff_rewind_count);
- return buf;
-
- case 'r': /* Remaining Time in Song */
- *flags |= WPS_REFRESH_DYNAMIC;
- format_time(buf, buf_size,
- id3->length - id3->elapsed -
- wps_state.ff_rewind_count);
- return buf;
-
- case 't': /* Total Time */
- *flags |= WPS_REFRESH_STATIC;
- format_time(buf, buf_size, id3->length);
- return buf;
-
-#ifdef HAVE_LCD_BITMAP
- case 'm': /* Peak Meter */
- *flags |= WPS_REFRESH_PEAK_METER;
- return "\x01";
+ gui_splash(gwps->display, 0, "Vol: %3d dB",
+ sound_val2phys(SOUND_VOLUME, global_settings.volume));
+ return true;
#endif
- case 's': /* shuffle */
- *flags |= WPS_REFRESH_DYNAMIC;
- if ( global_settings.playlist_shuffle )
- return "s";
- else
- return NULL;
- break;
-
- case 'v': /* volume */
- *flags |= WPS_REFRESH_DYNAMIC;
- snprintf(buf, buf_size, "%d", global_settings.volume);
- *intval = limit * (global_settings.volume
- - sound_min(SOUND_VOLUME))
- / (sound_max(SOUND_VOLUME)
- - sound_min(SOUND_VOLUME)) + 1;
- return buf;
-
- }
- break;
+ return false;
+}
-#if (CONFIG_CODEC == SWCODEC)
- case 'S': /* DSP/Equalizer/Sound settings */
- switch (tag[1])
- {
- case 'p': /* pitch */
- *intval = sound_get_pitch();
- snprintf(buf, buf_size, "%d.%d",
- *intval / 10, *intval % 10);
- return buf;
- }
- break;
-#endif
-
- case 'm':
- switch (tag[1])
- {
- case 'm': /* playback repeat mode */
- *flags |= WPS_REFRESH_DYNAMIC;
- *intval = global_settings.repeat_mode + 1;
- snprintf(buf, buf_size, "%d", *intval);
- return buf;
-
- /* playback status */
- case 'p': /* play */
- *flags |= WPS_REFRESH_DYNAMIC;
- int status = audio_status();
- *intval = 1;
- if (status == AUDIO_STATUS_PLAY && \
- !(status & AUDIO_STATUS_PAUSE))
- *intval = 2;
- if (audio_status() & AUDIO_STATUS_PAUSE && \
- (! status_get_ffmode()))
- *intval = 3;
- if (status_get_ffmode() == STATUS_FASTFORWARD)
- *intval = 4;
- if (status_get_ffmode() == STATUS_FASTBACKWARD)
- *intval = 5;
- snprintf(buf, buf_size, "%d", *intval);
- return buf;
+bool ffwd_rew(int button)
+{
+ static const int ff_rew_steps[] = {
+ 1000, 2000, 3000, 4000,
+ 5000, 6000, 8000, 10000,
+ 15000, 20000, 25000, 30000,
+ 45000, 60000
+ };
-#ifdef HAS_BUTTON_HOLD
- case 'h': /* hold */
- *flags |= WPS_REFRESH_DYNAMIC;
- if (button_hold())
- return "h";
- else
- return NULL;
-#endif
-#ifdef HAS_REMOTE_BUTTON_HOLD
- case 'r': /* remote hold */
- *flags |= WPS_REFRESH_DYNAMIC;
- if (remote_button_hold())
- return "r";
- else
- return NULL;
-#endif
- }
- break;
+ unsigned int step = 0; /* current ff/rewind step */
+ unsigned int max_step = 0; /* maximum ff/rewind step */
+ int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
+ int direction = -1; /* forward=1 or backward=-1 */
+ long accel_tick = 0; /* next time at which to bump the step size */
+ bool exit = false;
+ bool usb = false;
+ int i = 0;
- case 'b': /* battery info */
- *flags |= WPS_REFRESH_DYNAMIC;
- switch (tag[1])
- {
- case 'l': /* battery level */
+ if (button == ACTION_NONE)
+ {
+ status_set_ffmode(0);
+ return usb;
+ }
+ while (!exit)
+ {
+ switch ( button )
+ {
+ case ACTION_WPS_SEEKFWD:
+ direction = 1;
+ case ACTION_WPS_SEEKBACK:
+ if (wps_state.ff_rewind)
{
- int l = battery_level();
- limit = MAX(limit, 2);
- if (l > -1)
+ if (direction == 1)
{
- snprintf(buf, buf_size, "%d", l);
- /* First enum is used for "unknown level". */
- *intval = (limit - 1) * l / 100 + 1 + 1;
+ /* fast forwarding, calc max step relative to end */
+ max_step = (wps_state.id3->length -
+ (wps_state.id3->elapsed +
+ ff_rewind_count)) *
+ FF_REWIND_MAX_PERCENT / 100;
}
else
{
- *intval = 1;
- return "?";
+ /* rewinding, calc max step relative to start */
+ max_step = (wps_state.id3->elapsed + ff_rewind_count) *
+ FF_REWIND_MAX_PERCENT / 100;
}
- return buf;
- }
- case 'v': /* battery voltage */
- {
- unsigned int v = battery_voltage();
- snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
- return buf;
- }
+ max_step = MAX(max_step, MIN_FF_REWIND_STEP);
- case 't': /* estimated battery time */
- {
- int t = battery_time();
- if (t >= 0)
- snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
- else
- strncpy(buf, "?h ?m", buf_size);
- return buf;
- }
+ if (step > max_step)
+ step = max_step;
- case 's': /* sleep timer */
- {
- if (get_sleep_timer() == 0)
- {
- return NULL;
- }
- else
- {
- format_time(buf, buf_size, \
- get_sleep_timer() * 1000);
- return buf;
- }
+ ff_rewind_count += step * direction;
+
+ if (global_settings.ff_rewind_accel != 0 &&
+ current_tick >= accel_tick)
+ {
+ step *= 2;
+ accel_tick = current_tick +
+ global_settings.ff_rewind_accel*HZ;
+ }
}
-
-#if CONFIG_CHARGING
- case 'p': /* External power plugged in? */
+ else
{
- if(charger_input_state==CHARGER)
- return "p";
- else
- return NULL;
- }
+ if ( (audio_status() & AUDIO_STATUS_PLAY) &&
+ wps_state.id3 && wps_state.id3->length )
+ {
+ if (!wps_state.paused)
+#if (CONFIG_CODEC == SWCODEC)
+ audio_pre_ff_rewind();
+#else
+ audio_pause();
#endif
-#if CONFIG_CHARGING >= CHARGING_MONITOR
- case 'c': /* Charging */
- {
- if (charge_state == CHARGING || charge_state == TOPOFF) {
- return "c";
- } else {
- return NULL;
- }
- }
+#if CONFIG_KEYPAD == PLAYER_PAD
+ FOR_NB_SCREENS(i)
+ gui_wps[i].display->stop_scroll();
#endif
- }
- break;
+ if (direction > 0)
+ status_set_ffmode(STATUS_FASTFORWARD);
+ else
+ status_set_ffmode(STATUS_FASTBACKWARD);
-#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
- case 'l': /* VIRTUAL_LED */
- {
- switch(tag[1])
- {
- case 'h': /* Only one we have so far HDD LED */
- *flags |= WPS_REFRESH_DYNAMIC;
- if(led_read(HZ/2))
- return "h";
- else
- return NULL;
- }
- }
- break;
-#endif
+ wps_state.ff_rewind = true;
- case 'D': /* Directory path information */
- id3 = nid3; /* next song please! */
- *flags |= WPS_REFRESH_DYNAMIC;
- if(!id3)
- return NULL; /* no such info (yet) */
- /* fall-through */
- case 'd': /* Directory path information */
- {
- int level = tag[1] - '0';
- *flags |= WPS_REFRESH_STATIC;
- /* d1 through d9 */
- if ((0 < level) && (9 > level))
- {
- return get_dir(buf, buf_size, id3->path, level);
- }
- }
- break;
+ step = ff_rew_steps[global_settings.ff_rewind_min_step];
- case 't': /* set sub line time multiplier */
- {
- int d = 1;
- int time_mult = 0;
- bool have_point = false;
- bool have_tenth = false;
-
- while (((tag[d] >= '0') &&
- (tag[d] <= '9')) ||
- (tag[d] == '.'))
- {
- if (tag[d] != '.')
- {
- time_mult = time_mult * 10;
- time_mult = time_mult + tag[d] - '0';
- if (have_point)
- {
- have_tenth = true;
- d++;
- break;
- }
+ accel_tick = current_tick +
+ global_settings.ff_rewind_accel*HZ;
}
else
- {
- have_point = true;
- }
- d++;
+ break;
}
- if (have_tenth == false)
- time_mult *= 10;
+ if (direction > 0) {
+ if ((wps_state.id3->elapsed + ff_rewind_count) >
+ wps_state.id3->length)
+ ff_rewind_count = wps_state.id3->length -
+ wps_state.id3->elapsed;
+ }
+ else {
+ if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
+ ff_rewind_count = -wps_state.id3->elapsed;
+ }
- *subline_time_mult = time_mult;
- *tag_len = d;
+ FOR_NB_SCREENS(i)
+ gui_wps_refresh(&gui_wps[i],
+ (wps_state.wps_time_countup == false)?
+ ff_rewind_count:-ff_rewind_count,
+ WPS_REFRESH_PLAYER_PROGRESS |
+ WPS_REFRESH_DYNAMIC);
- buf[0] = 0;
- return buf;
- }
- break;
- case 'r': /* Runtime database Information and Replaygain */
- switch(tag[1])
- {
- case 'p': /* Playcount */
- *flags |= WPS_REFRESH_DYNAMIC;
- *intval = cid3->playcount+1;
- snprintf(buf, buf_size, "%ld", cid3->playcount);
- return buf;
- case 'r': /* Rating */
- *flags |= WPS_REFRESH_DYNAMIC;
- *intval = cid3->rating+1;
- snprintf(buf, buf_size, "%d", cid3->rating);
- return buf;
-#if CONFIG_CODEC == SWCODEC
- case 'g': /* ReplayGain */
- *flags |= WPS_REFRESH_STATIC;
- if (global_settings.replaygain == 0)
- *intval = 1; /* off */
- else
- {
- int type = get_replaygain_mode(
- id3->track_gain_string != NULL,
- id3->album_gain_string != NULL);
-
- if (type < 0)
- *intval = 6; /* no tag */
- else
- *intval = type + 2;
-
- if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
- *intval += 2;
- }
+ break;
- switch (*intval)
- {
- case 1:
- case 6:
- return "+0.00 dB";
- break;
- case 2:
- case 4:
- strncpy(buf, id3->track_gain_string, buf_size);
- break;
- case 3:
- case 5:
- strncpy(buf, id3->album_gain_string, buf_size);
- break;
- }
- return buf;
+ case ACTION_WPS_STOPSEEK:
+ wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count;
+ audio_ff_rewind(wps_state.id3->elapsed);
+ ff_rewind_count = 0;
+ wps_state.ff_rewind = false;
+ status_set_ffmode(0);
+#if (CONFIG_CODEC != SWCODEC)
+ if (!wps_state.paused)
+ audio_resume();
#endif
- }
- break;
-#if CONFIG_RTC
- case 'c': /* Real Time Clock display */
- *flags |= WPS_REFRESH_DYNAMIC;
- {
- int value;
- char *format = 0;
- char *bufptr = buf;
- struct tm* tm = get_time();
- int i;
- for (i=1;/*break*/;i++) {
- switch(tag[i])
- {
- case 'a': /* abbreviated weekday name (Sun..Sat) */
- value = tm->tm_wday;
- if (value > 6 || value < 0) continue;
- value = snprintf(
- bufptr,buf_size,"%s",str(dayname[value]));
- bufptr += value;
- buf_size -= value;
- continue;
- case 'b': /* abbreviated month name (Jan..Dec) */
- value = tm->tm_mon;
- if (value > 11 || value < 0) continue;
- value = snprintf(
- bufptr,buf_size,"%s",str(monthname[value]));
- bufptr += value;
- buf_size -= value;
- continue;
- case 'd': /* day of month (01..31) */
- value = tm->tm_mday;
- if (value > 31 || value < 1) continue;
- format = "%02d";
- break;
- case 'e': /* day of month, blank padded ( 1..31) */
- value = tm->tm_mday;
- if (value > 31 || value < 1) continue;
- format = "%2d";
- break;
- case 'H': /* hour (00..23) */
- value = tm->tm_hour;
- if (value > 23) continue;
- format = "%02d";
- break;
- case 'k': /* hour ( 0..23) */
- value = tm->tm_hour;
- if (value > 23) continue;
- format = "%2d";
- break;
- case 'I': /* hour (01..12) */
- value = tm->tm_hour;
- if (value > 23) continue;
- value %= 12;
- if (value == 0) value = 12;
- format = "%02d";
- break;
- case 'l': /* hour ( 1..12) */
- value = tm->tm_hour;
- if (value > 23 || value < 0) continue;
- value %= 12;
- if (value == 0) value = 12;
- format = "%2d";
- break;
- case 'm': /* month (01..12) */
- value = tm->tm_mon;
- if (value > 11 || value < 0) continue;
- value++;
- format = "%02d";
- break;
- case 'M': /* minute (00..59) */
- value = tm->tm_min;
- if (value > 59 || value < 0) continue;
- format = "%02d";
- break;
- case 'S': /* second (00..59) */
- value = tm->tm_sec;
- if (value > 59 || value < 0) continue;
- format = "%02d";
- break;
- case 'y': /* last two digits of year (00..99) */
- value = tm->tm_year;
- value %= 100;
- format = "%02d";
- break;
- case 'Y': /* year (1970...) */
- value = tm->tm_year;
- if (value > 199 || value < 100) continue;
- value += 1900;
- format = "%04d";
- break;
- case 'p': /* upper case AM or PM indicator */
- if (tm->tm_hour/12 == 0) format = "AM";
- else format = "PM";
- snprintf(bufptr,buf_size,"%s",format);
- bufptr += 2;
- buf_size -= 2;
- continue;
- case 'P': /* lower case am or pm indicator */
- if (tm->tm_hour/12 == 0) format = "am";
- else format = "pm";
- snprintf(bufptr,buf_size,"%s",format);
- bufptr += 2;
- buf_size -= 2;
- continue;
- case 'u': /* day of week (1..7); 1 is Monday */
- value = tm->tm_wday;
- if (value < 0 || value > 6) continue;
- value++;
- format = "%1d";
- break;
- case 'w': /* day of week (0..6); 0 is Sunday */
- value = tm->tm_wday;
- if (value < 0 || value > 6) continue;
- format = "%1d";
- break;
- default:
- if (tag[i] == 'c') {
- i++;
- value = -1;
- break;
- } else if (tag[i] == '\n') {
- value = -1;
- break;
- }
- snprintf(bufptr,buf_size,"%c",tag[i]);
- bufptr++;
- buf_size--;
- continue;
- } /* switch */
- if (value < 0) break;
-
- value = snprintf(bufptr, buf_size, format, value);
- bufptr += value;
- buf_size -= value;
- } /* while */
- *tag_len = i;
- return buf;
- }
-#endif /* CONFIG_RTC */
-#if CONFIG_CODEC == SWCODEC
- case 'x':
- *flags |= WPS_REFRESH_DYNAMIC;
- switch(tag[1])
- {
- case 'd': /* crossfeed */
- if(global_settings.crossfeed)
- return "d";
- else
- return NULL;
- case 'f': /* crossfade */
- *intval = global_settings.crossfade+1;
- snprintf(buf, buf_size, "%d", global_settings.crossfade);
- return buf;
- }
- break;
+#ifdef HAVE_LCD_CHARCELLS
+ gui_wps_display();
#endif
- }
- return NULL;
-}
+ exit = true;
+ break;
-#ifdef HAVE_LCD_BITMAP
-/* 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].bm.height);
- gwps->display->set_drawmode(DRMODE_SOLID);
+ default:
+ if(default_event_handler(button) == SYS_USB_CONNECTED) {
+ status_set_ffmode(0);
+ usb = true;
+ exit = true;
+ }
+ break;
+ }
+ if (!exit)
+ button = get_action(CONTEXT_WPS,TIMEOUT_BLOCK);
+ }
+ action_signalscreenchange();
+ return usb;
}
-#endif
-/* Skip to the end of the current %? conditional.
- *
- * fmt - string to skip it. Should point to somewhere after the leading
- * "<" char (and before or at the last ">").
- * num - number of |'s to skip, or 0 to skip to the end (the ">").
- * enums - If not NULL, set to the number of |'s found in the current
- * conditional (sub-conditionals are ignored). num should be 0
- * to find all |'s.
- *
- * Returns the new position in fmt.
- */
-static const char* skip_conditional(struct gui_wps *gwps, const char* fmt,
- int num, int *enums)
+bool gui_wps_display(void)
{
- int level = 1;
- int count = num;
- const char *last_alternative = NULL;
+ int i;
+ if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY))
+ {
+ global_status.resume_index = -1;
#ifdef HAVE_LCD_BITMAP
- struct wps_data *data = NULL;
- int last_x=-1, last_y=-1, last_w=-1, last_h=-1;
- if(gwps)
- data = gwps->data;
- if (enums)
- *enums = 0;
-#else
- (void)gwps;
+ gui_syncstatusbar_draw(&statusbars, true);
#endif
- while (*fmt)
+ gui_syncsplash(HZ, str(LANG_END_PLAYLIST_RECORDER));
+ return true;
+ }
+ else
{
- switch (*fmt++)
+ FOR_NB_SCREENS(i)
{
- case '%':
-#ifdef HAVE_LCD_BITMAP
- if(data && *(fmt) == 'x' && *(fmt+1) == 'd' )
- {
- fmt +=2;
- int n = *fmt;
- if(n >= 'a' && n <= 'z')
- n -= 'a';
- if(n >= 'A' && n <= 'Z')
- n = n - 'A' + 26;
- if(last_x != data->img[n].x || last_y != data->img[n].y
- || last_w != data->img[n].bm.width
- || last_h != data->img[n].bm.height)
+ gui_wps[i].display->clear_display();
+ if (!gui_wps[i].data->wps_loaded) {
+ if ( !gui_wps[i].data->num_tokens ) {
+ /* set the default wps for the main-screen */
+ if(i == 0)
{
- last_x = data->img[n].x;
- last_y = data->img[n].y;
- last_w = data->img[n].bm.width;
- last_h = data->img[n].bm.height;
- clear_image_pos(gwps,n);
- }
- }
+#ifdef HAVE_LCD_BITMAP
+#if LCD_DEPTH > 1
+ unload_wps_backdrop();
+#endif
+ wps_data_load(gui_wps[i].data,
+ "%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
+ wps_data_load(gui_wps[i].data,
+ "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
+ "%pc%?ps<*|/>%pt\n", false);
#endif
- break;
-
- case '|':
- if(1 == level) {
- if (enums)
- (*enums)++;
- last_alternative = fmt;
- if(num) {
- count--;
- if(count == 0)
- return fmt;
- continue;
- }
- }
- continue;
-
- case '>':
- if (0 == --level)
- {
- /* We're just skipping to the end */
- if(num == 0)
- return fmt;
-
- /* If we are parsing an enum, we'll return the selected
- item. If there weren't enough items in the enum, we'll
- return the last one found. */
- if(count && last_alternative)
- {
- return last_alternative;
}
- return fmt - 1;
+#if NB_SCREENS == 2
+ /* set the default wps for the remote-screen */
+ else if(i == 1)
+ {
+ wps_data_load(gui_wps[i].data,
+ "%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", false);
+ }
+#endif
}
- continue;
-
- default:
- continue;
- }
-
- switch (*fmt++)
- {
- case 0:
- case '%':
- case '|':
- case '<':
- case '>':
- break;
-
- case '?':
- while (*fmt && ('<' != *fmt))
- fmt++;
-
- if ('<' == *fmt)
- fmt++;
-
- level++;
- break;
-
- default:
- break;
+ }
}
}
-
- return fmt;
+ yield();
+ FOR_NB_SCREENS(i)
+ {
+ gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL);
+ }
+ return false;
}
-/* Generate the display based on id3 information and format string.
- *
- * buf - char buffer to write the display to.
- * buf_size - the size of buffer.
- * id3 - the ID3 data to format with.
- * nid3 - the ID3 data of the next song (might by NULL)
- * fmt - format description.
- * flags - returns the type of the line. See constants i wps-display.h
- */
-static void format_display(struct gui_wps *gwps, char* buf,
- int buf_size,
- struct mp3entry* id3,
- struct mp3entry* nid3, /* next song's id3 */
- const char* fmt,
- struct align_pos* align,
- unsigned short* subline_time_mult,
- unsigned char* flags)
+bool update(struct gui_wps *gwps)
{
- char temp_buf[128];
- char* buf_start = buf;
- char* buf_end = buf + buf_size - 1; /* Leave room for end null */
- char* value = NULL;
- int level = 0;
- unsigned char tag_length;
- int intval;
- int cur_align;
- char* cur_align_start;
-#ifdef HAVE_LCD_BITMAP
- struct gui_img *img = gwps->data->img;
- int n;
-#endif
-
- cur_align_start = buf;
- cur_align = WPS_ALIGN_LEFT;
- *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
-
- align->left = 0;
- align->center = 0;
- align->right = 0;
+ bool track_changed = audio_has_changed_track();
+ bool retcode = false;
- while (fmt && *fmt && buf < buf_end)
+ gwps->state->nid3 = audio_next_track();
+ if (track_changed)
{
- switch (*fmt)
- {
- case '%':
- ++fmt;
- break;
-
- case '|':
- case '>':
- if (level > 0)
- {
- fmt = skip_conditional(NULL, fmt, 0, NULL);
- level--;
- continue;
- }
- /* Else fall through */
-
- default:
- *buf++ = *fmt++;
- continue;
- }
+ gwps->display->stop_scroll();
+ gwps->state->id3 = audio_current_track();
- switch (*fmt)
+ if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
+ && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
{
- case 0:
- *buf++ = '%';
- break;
- case 'a':
- ++fmt;
- /* remember where the current aligned text started */
- switch (cur_align)
- {
- case WPS_ALIGN_LEFT:
- align->left = cur_align_start;
- break;
+ /* the current cuesheet isn't the right one any more */
- case WPS_ALIGN_CENTER:
- align->center = cur_align_start;
- break;
+ if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) {
+ /* We have the new cuesheet in memory (temp_cue),
+ let's make it the current one ! */
+ memcpy(curr_cue, temp_cue, sizeof(struct cuesheet));
+ }
+ else {
+ /* We need to parse the new cuesheet */
- case WPS_ALIGN_RIGHT:
- align->right = cur_align_start;
- break;
- }
- /* start a new alignment */
- switch (*fmt)
- {
- case 'l':
- cur_align = WPS_ALIGN_LEFT;
- break;
- case 'c':
- cur_align = WPS_ALIGN_CENTER;
- break;
- case 'r':
- cur_align = WPS_ALIGN_RIGHT;
- break;
- }
- *buf++=0;
- cur_align_start = buf;
- ++fmt;
- break;
- case 's':
- *flags |= WPS_REFRESH_SCROLL;
- ++fmt;
- break;
+ char cuepath[MAX_PATH];
+ strncpy(cuepath, gwps->state->id3->path, MAX_PATH);
+ char *dot = strrchr(cuepath, '.');
+ strcpy(dot, ".cue");
- case 'x': /* image support */
-#ifdef HAVE_LCD_BITMAP
- if ('d' == *(fmt+1) )
+ if (parse_cuesheet(cuepath, curr_cue))
{
- fmt+=2;
-
- /* get the image ID */
- n = *fmt;
- if(n >= 'a' && n <= 'z')
- n -= 'a';
- if(n >= 'A' && n <= 'Z')
- n = n - 'A' + 26;
- if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
- img[n].display = true;
- }
+ gwps->state->id3->cuesheet_type = 1;
+ strcpy(curr_cue->audio_filename, gwps->state->id3->path);
}
+ }
-#endif
- fmt++;
- break;
-
-
- case '%':
- case '|':
- case '<':
- case '>':
- case ';':
- *buf++ = *fmt++;
- break;
-
- case '?':
- fmt++;
- /* Get number of "|" chars in the current conditional;
- * used by get_tag when calculating levels.
- */
- skip_conditional(gwps, fmt, 0, &intval);
- value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
- sizeof(temp_buf),&tag_length,
- subline_time_mult, flags, &intval);
-
- while (*fmt && ('<' != *fmt))
- fmt++;
-
- if ('<' == *fmt)
- fmt++;
-
- /* No value, so skip to else part, using a sufficiently high
- value to "hit" the last part of the conditional */
- if ((!value) || (!strlen(value)))
- fmt = skip_conditional(NULL, fmt, 1000, NULL);
- else
- if(intval > 1) /* enum */
- fmt = skip_conditional(NULL, fmt, intval - 1, NULL);
-
- level++;
- break;
-
- default:
- intval = 1;
- value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
- sizeof(temp_buf), &tag_length,
- subline_time_mult, flags,&intval);
- fmt += tag_length;
+ cue_spoof_id3(curr_cue, gwps->state->id3);
+ }
- if (value)
- {
- while (*value && (buf < buf_end))
- *buf++ = *value++;
- }
+ if (gui_wps_display())
+ retcode = true;
+ else{
+ gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
}
+
+ if (gwps->state->id3)
+ memcpy(gwps->state->current_track_path, gwps->state->id3->path,
+ sizeof(gwps->state->current_track_path));
}
- /* remember where the current aligned text started */
- switch (cur_align)
+ if (gwps->state->id3)
{
- case WPS_ALIGN_LEFT:
- align->left = cur_align_start;
- break;
+ if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
+ && (gwps->state->id3->elapsed < curr_cue->curr_track->offset
+ || (curr_cue->curr_track_idx < curr_cue->track_count - 1
+ && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset)))
+ {
+ /* We've changed tracks within the cuesheet :
+ we need to update the ID3 info and refresh the WPS */
- case WPS_ALIGN_CENTER:
- align->center = cur_align_start;
- break;
+ cue_find_current_track(curr_cue, gwps->state->id3->elapsed);
+ cue_spoof_id3(curr_cue, gwps->state->id3);
- case WPS_ALIGN_RIGHT:
- align->right = cur_align_start;
- break;
+ gwps->display->stop_scroll();
+ if (gui_wps_display())
+ retcode = true;
+ else
+ gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
+ }
+ else
+ gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
}
- *buf = 0;
-
- /* if resulting line is an empty line, set the subline time to 0 */
- if (buf - buf_start == 0)
- *subline_time_mult = 0;
+ gui_wps_statusbar_draw(gwps, false);
- /* If no flags have been set, the line didn't contain any format codes.
- We still want to refresh it. */
- if(*flags == 0)
- *flags = WPS_REFRESH_STATIC;
+ return retcode;
}
-/* fades the volume */
-void fade(bool fade_in)
+
+void display_keylock_text(bool locked)
{
- int fp_global_vol = global_settings.volume << 8;
- int fp_min_vol = sound_min(SOUND_VOLUME) << 8;
- int fp_step = (fp_global_vol - fp_min_vol) / 30;
+ char* s;
+ int i;
+ FOR_NB_SCREENS(i)
+ gui_wps[i].display->stop_scroll();
- if (fade_in) {
- /* fade in */
- int fp_volume = fp_min_vol;
+#ifdef HAVE_LCD_CHARCELLS
+ if(locked)
+ s = str(LANG_KEYLOCK_ON_PLAYER);
+ else
+ s = str(LANG_KEYLOCK_OFF_PLAYER);
+#else
+ if(locked)
+ s = str(LANG_KEYLOCK_ON_RECORDER);
+ else
+ s = str(LANG_KEYLOCK_OFF_RECORDER);
+#endif
+ gui_syncsplash(HZ, s);
+}
- /* zero out the sound */
- sound_set_volume(fp_min_vol >> 8);
+#ifdef HAVE_LCD_BITMAP
- sleep(HZ/10); /* let audio thread run */
- audio_resume();
-
- while (fp_volume < fp_global_vol - fp_step) {
- fp_volume += fp_step;
- sound_set_volume(fp_volume >> 8);
- sleep(1);
- }
- sound_set_volume(global_settings.volume);
- }
- else {
- /* fade out */
- int fp_volume = fp_global_vol;
+static void draw_progressbar(struct gui_wps *gwps, int line)
+{
+ struct wps_data *data = gwps->data;
+ struct screen *display = gwps->display;
+ struct wps_state *state = gwps->state;
+ int h = font_get(FONT_UI)->height;
- while (fp_volume > fp_min_vol + fp_step) {
- fp_volume -= fp_step;
- sound_set_volume(fp_volume >> 8);
- sleep(1);
- }
- audio_pause();
-#ifndef SIMULATOR
- /* let audio thread run and wait for the mas to run out of data */
- while (!mp3_pause_done())
+ int sb_y;
+ if (data->progress_top < 0)
+ sb_y = line*h + display->getymargin() +
+ ((h > data->progress_height + 1)
+ ? (h - data->progress_height) / 2 : 1);
+ else
+ sb_y = data->progress_top;
+
+ if (!data->progress_end)
+ data->progress_end=display->width;
+
+ if (gwps->data->progressbar.have_bitmap_pb)
+ gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
+ data->progress_start, sb_y,
+ data->progress_end-data->progress_start,
+ data->progressbar.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, data->progress_start, sb_y,
+ data->progress_end-data->progress_start,
+ data->progress_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() )
+ ab_draw_markers(display, state->id3->length,
+ data->progress_start, data->progress_end, sb_y,
+ data->progress_height);
#endif
- sleep(HZ/10);
- /* reset volume to what it was before the fade */
- sound_set_volume(global_settings.volume);
- }
+ if ( cuesheet_is_enabled() && state->id3->cuesheet_type )
+ cue_draw_markers(display, state->id3->length,
+ data->progress_start, data->progress_end,
+ sb_y+1, data->progress_height-2);
}
-/* Set format string to use for WPS, splitting it into lines */
-void gui_wps_format(struct wps_data *data)
+/* clears the area where the image was shown */
+static void clear_image_pos(struct gui_wps *gwps, int n)
{
- char* buf = data->format_buffer;
- char* start_of_line = data->format_buffer;
- int line = 0;
- int subline;
- char c;
- if(!data)
+ if(!gwps)
return;
-
- for (line=0; line<WPS_MAX_LINES; line++)
- {
- for (subline=0; subline<WPS_MAX_SUBLINES; subline++)
- {
- data->format_lines[line][subline] = 0;
- data->time_mult[line][subline] = 0;
- }
- data->subline_expire_time[line] = 0;
- data->curr_subline[line] = SUBLINE_RESET;
- }
-
- line = 0;
- subline = 0;
- buf = skip_utf8_bom(buf);
- data->format_lines[line][subline] = buf;
-
- while ((*buf) && (line < WPS_MAX_LINES))
- {
- c = *buf;
-
- switch (c)
- {
- /*
- * skip % sequences so "%;" doesn't start a new subline
- * don't skip %x lines (pre-load bitmaps)
- */
- case '%':
- buf++;
- break;
-
- case '\r': /* CR */
- *buf = 0;
- break;
-
- case '\n': /* LF */
- *buf = 0;
-
- if (*start_of_line != '#') /* A comment? */
- line++;
-
- if (line < WPS_MAX_LINES)
- {
- /* the next line starts on the next byte */
- subline = 0;
- data->format_lines[line][subline] = buf+1;
- start_of_line = data->format_lines[line][subline];
- }
- break;
-
- case ';': /* start a new subline */
- *buf = 0;
- subline++;
- if (subline < WPS_MAX_SUBLINES)
- {
- data->format_lines[line][subline] = buf+1;
- }
- else /* exceeded max sublines, skip rest of line */
- {
- while (*(++buf))
- {
- if ((*buf == '\r') || (*buf == '\n'))
- {
- break;
- }
- }
- buf--;
- subline = 0;
- }
- break;
- }
- buf++;
- }
+ 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].bm.height);
+ gwps->display->set_drawmode(DRMODE_SOLID);
}
-#ifdef HAVE_LCD_BITMAP
-/* Display images */
static void wps_draw_image(struct gui_wps *gwps, int n)
{
struct screen *display = gwps->display;
@@ -1625,509 +561,29 @@ static void wps_draw_image(struct gui_wps *gwps, int n)
}
#endif
}
-static void wps_display_images(struct gui_wps *gwps, bool always)
-{
- 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( (!always && data->img[n].display)
- || (always && data->img[n].always_display) )
- wps_draw_image(gwps, n);
- }
- }
- display->set_drawmode(DRMODE_SOLID);
-}
-#endif
-#if 0 /* currently unused */
-void gui_wps_reset(struct gui_wps *gui_wps)
+static void wps_display_images(struct gui_wps *gwps)
{
- if(!gui_wps || !gui_wps->data)
+ if(!gwps || !gwps->data || !gwps->display)
return;
- gui_wps->data->wps_loaded = false;
- memset(&gui_wps->data->format_buffer, 0,
- sizeof(gui_wps->data->format_buffer));
-}
-#endif
-bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
- unsigned char refresh_mode)
-{
- char buf[MAX_PATH];
- unsigned char flags;
- int i;
- bool update_line;
- bool only_one_subline;
- bool new_subline_refresh;
- bool reset_subline;
- int search;
- int search_start;
- struct align_pos format_align;
+ int n;
struct wps_data *data = gwps->data;
- struct wps_state *state = gwps->state;
struct screen *display = gwps->display;
- if(!gwps || !data || !state || !display)
- {
- return false;
- }
-#ifdef HAVE_LCD_BITMAP
- int h = font_get(FONT_UI)->height;
- int offset = 0;
- gui_wps_statusbar_draw(gwps, true);
- if(data->wps_sb_tag && data->show_sb_on_wps)
- offset = STATUSBAR_HEIGHT;
- else if ( global_settings.statusbar && !data->wps_sb_tag)
- offset = STATUSBAR_HEIGHT;
-
- /* 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;
-
- /* Set images to not to be displayed */
- for (i = 0; i < MAX_IMAGES; i++) {
- data->img[i].display = false;
- }
-#endif
- /* reset to first subline if refresh all flag is set */
- if (refresh_mode == WPS_REFRESH_ALL)
- {
- for (i=0; i<WPS_MAX_LINES; i++)
- {
- data->curr_subline[i] = 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
-
- if (!state->id3)
- {
- display->stop_scroll();
- return false;
- }
-
- state->ff_rewind_count = ffwd_offset;
- for (i = 0; i < WPS_MAX_LINES; i++)
+ for (n = 0; n < MAX_IMAGES; n++)
{
- reset_subline = (data->curr_subline[i] == SUBLINE_RESET);
- new_subline_refresh = false;
- only_one_subline = false;
-
- /* if time to advance to next sub-line */
- if (TIME_AFTER(current_tick, data->subline_expire_time[i] - 1) ||
- reset_subline)
+ if (data->img[n].loaded &&
+ (data->img[n].display || data->img[n].always_display))
{
- /* 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->curr_subline[i];
- for (search=0; search<WPS_MAX_SUBLINES; search++)
- {
- data->curr_subline[i]++;
-
- /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
- if ((!data->format_lines[i][data->curr_subline[i]]) ||
- (data->curr_subline[i] == WPS_MAX_SUBLINES))
- {
- if (data->curr_subline[i] == 1)
- only_one_subline = true;
- data->curr_subline[i] = 0;
- }
-
- /* if back where we started after search or
- only one subline is defined on the line */
- if (((search > 0) && (data->curr_subline[i] == search_start)) ||
- only_one_subline)
- {
- /* no other subline with a time > 0 exists */
- data->subline_expire_time[i] = (reset_subline?
- current_tick : data->subline_expire_time[i]) + 100 * HZ;
- break;
- }
- else
- {
- /* get initial time multiplier and
- line type flags for this subline */
- format_display(gwps, buf, sizeof(buf),
- state->id3, state->nid3,
- data->format_lines[i][data->curr_subline[i]],
- &format_align,
- &data->time_mult[i][data->curr_subline[i]],
- &data->line_type[i][data->curr_subline[i]]);
-
- /* only use this subline if subline time > 0 */
- if (data->time_mult[i][data->curr_subline[i]] > 0)
- {
- new_subline_refresh = true;
- data->subline_expire_time[i] = (reset_subline?
- current_tick : data->subline_expire_time[i]) +
- BASE_SUBLINE_TIME * data->time_mult[i][data->curr_subline[i]];
- break;
- }
- }
- }
-
- }
-
- update_line = false;
-
- if ( !data->format_lines[i][data->curr_subline[i]] )
- break;
-
- if ((data->line_type[i][data->curr_subline[i]] & refresh_mode) ||
- (refresh_mode == WPS_REFRESH_ALL) ||
- new_subline_refresh)
- {
- flags = 0;
- int left_width, left_xpos;
- int center_width, center_xpos;
- int right_width, right_xpos;
- int space_width;
- int string_height;
- int ypos;
-
- format_display(gwps, buf, sizeof(buf),
- state->id3, state->nid3,
- data->format_lines[i][data->curr_subline[i]],
- &format_align,
- &data->time_mult[i][data->curr_subline[i]],
- &flags);
- data->line_type[i][data->curr_subline[i]] = flags;
-
-#ifdef HAVE_LCD_BITMAP
- /* progress */
- if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
- {
- int sb_y;
- if (data->progress_top == -1)
- sb_y = i*h + offset + ((h > data->progress_height + 1)
- ? (h - data->progress_height) / 2 : 1);
- else
- sb_y = data->progress_top;
-
- if (!data->progress_end)
- data->progress_end=display->width;
-
- if (gwps->data->progressbar.have_bitmap_pb)
- gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
- data->progress_start, sb_y,
- data->progress_end-data->progress_start,
- data->progressbar.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, data->progress_start, sb_y,
- data->progress_end-data->progress_start,
- data->progress_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() )
- ab_draw_markers(display, state->id3->length,
- data->progress_start, data->progress_end, sb_y,
- data->progress_height);
-#endif
-
- if (cuesheet_is_enabled() && state->id3->cuesheet_type)
- {
- cue_draw_markers(display, state->id3->length,
- data->progress_start, data->progress_end,
- sb_y+1, data->progress_height-2);
- }
-
- update_line = true;
- }
- if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
- /* peak meter */
- int peak_meter_y;
-
- update_line = true;
- peak_meter_y = i * h + offset;
-
- /* 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->height) {
- /* found a line with a peak meter -> remember that we must
- enable it later */
- enable_pm = true;
- peak_meter_screen(gwps->display, 0, peak_meter_y,
- MIN(h, display->height - peak_meter_y));
- }
- }
-#else
- /* progress */
- if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
- if (data->full_line_progressbar)
- draw_player_fullbar(gwps, buf, sizeof(buf));
- else
- draw_player_progress(gwps);
- }
-#endif
- /* 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);
- }
- else {
- left_width = 0;
- }
- left_xpos = 0;
-
- if (format_align.center != 0) {
- display->getstringsize((unsigned char *)format_align.center,
- &center_width, &string_height);
- }
- else {
- center_width = 0;
- }
- center_xpos=(display->width - center_width) / 2;
-
- if (format_align.right != 0) {
- display->getstringsize((unsigned char *)format_align.right,
- &right_width, &string_height);
- }
- else {
- right_width = 0;
- }
- right_xpos = (display->width - right_width);
-
- /* 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;
- left_xpos = 0;
- /* 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;
- left_xpos = 0;
- /* 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->width - 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->width - 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;
- left_xpos = 0;
- /* 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_xpos + 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;
- left_xpos = 0;
- /* there is no right string anymore */
- right_width = 0;
- }
-
- if (flags & WPS_REFRESH_SCROLL) {
-
- /* scroll line */
- if ((refresh_mode & WPS_REFRESH_SCROLL) ||
- new_subline_refresh) {
-
- ypos = (i*string_height)+display->getymargin();
- update_line = true;
-
- if (left_width>display->width) {
- display->puts_scroll(0, i,
- (unsigned char *)format_align.left);
- } else {
- /* clear the line first */
-#ifdef HAVE_LCD_BITMAP
- display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
- display->fillrect(0, ypos, display->width, 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, i, (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);
- }
- }
- }
- }
- else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
- {
- /* dynamic / static line */
- if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
- new_subline_refresh)
- {
- ypos = (i*string_height)+display->getymargin();
- update_line = true;
-
-#ifdef HAVE_LCD_BITMAP
- /* clear the line first */
- display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
- display->fillrect(0, ypos, display->width, 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, i, (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);
- }
- }
- }
- }
-#ifdef HAVE_LCD_BITMAP
- if (update_line) {
- wps_display_images(gwps,false);
+ wps_draw_image(gwps, n);
}
-#endif
- }
-
-#ifdef HAVE_LCD_BITMAP
- /* Display all images */
- wps_display_images(gwps,true);
- display->update();
- /* Now we know wether the peak meter is used.
- So we can enable / disable the peak meter thread */
- data->peak_meter_enabled = enable_pm;
-#endif
-
-#if CONFIG_BACKLIGHT
- if (global_settings.caption_backlight && state->id3) {
- /* turn on backlight n seconds before track ends, and turn it off n
- seconds into the new track. n == backlight_timeout, or 5s */
- int n = backlight_timeout_value[global_settings.backlight_timeout]
- * 1000;
-
- if ( n < 1000 )
- n = 5000; /* use 5s if backlight is always on or off */
-
- if (((state->id3->elapsed < 1000) ||
- ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
- (state->paused == false))
- backlight_on();
}
-#endif
-#ifdef HAVE_REMOTE_LCD
- if (global_settings.remote_caption_backlight && state->id3) {
- /* turn on remote backlight n seconds before track ends, and turn it
- off n seconds into the new track. n == remote_backlight_timeout,
- or 5s */
- int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
- * 1000;
-
- if ( n < 1000 )
- n = 5000; /* use 5s if backlight is always on or off */
-
- if (((state->id3->elapsed < 1000) ||
- ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
- (state->paused == false))
- remote_backlight_on();
- }
-#endif
- return true;
+ display->set_drawmode(DRMODE_SOLID);
}
-#ifdef HAVE_LCD_CHARCELLS
+#else /* HAVE_LCD_CHARCELL */
+
static bool draw_player_progress(struct gui_wps *gwps)
{
char player_progressbar[7];
@@ -2150,7 +606,7 @@ static bool draw_player_progress(struct gui_wps *gwps)
songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) /
state->id3->length;
else
- songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) /
+ songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) /
state->id3->length;
}
for (i=0; i < songpos; i++)
@@ -2215,10 +671,10 @@ static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
songpos = 55;
else {
if(state->wps_time_countup == false)
- songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) /
+ songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) /
state->id3->length;
else
- songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) /
+ songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) /
state->id3->length;
}
@@ -2304,342 +760,1190 @@ static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
*buf = '\0';
}
}
-#endif
-/* set volume */
-void setvol(void)
+#endif /* HAVE_LCD_CHARCELL */
+
+/* Extract a part from a path.
+ *
+ * buf - buffer extract part to.
+ * buf_size - size of buffer.
+ * path - path to extract from.
+ * level - what to extract. 0 is file name, 1 is parent of file, 2 is
+ * parent of parent, etc.
+ *
+ * Returns buf if the desired level was found, NULL otherwise.
+ */
+static char* get_dir(char* buf, int buf_size, const char* path, int level)
{
- if (global_settings.volume < sound_min(SOUND_VOLUME))
- global_settings.volume = sound_min(SOUND_VOLUME);
- if (global_settings.volume > sound_max(SOUND_VOLUME))
- global_settings.volume = sound_max(SOUND_VOLUME);
- sound_set_volume(global_settings.volume);
- settings_save();
+ const char* sep;
+ const char* last_sep;
+ int len;
+
+ sep = path + strlen(path);
+ last_sep = sep;
+
+ while (sep > path)
+ {
+ if ('/' == *(--sep))
+ {
+ if (!level)
+ break;
+
+ level--;
+ last_sep = sep - 1;
+ }
+ }
+
+ if (level || (last_sep <= sep))
+ return NULL;
+
+ len = MIN(last_sep - sep, buf_size - 1);
+ strncpy(buf, sep + 1, len);
+ buf[len] = 0;
+ return buf;
}
-/* return true if screen restore is needed
- return false otherwise
+
+/* Return the tag found at index i and write its value in buf.
+ The return value is buf if the tag had a value, or NULL if not.
+
+ intval is used with enums: when this function is called, it should contain
+ the number of options in the enum. When this function returns, it will
+ contain the enum case we are actually in.
+ When not treating an enum, intval should be NULL.
*/
-bool update_onvol_change(struct gui_wps * gwps)
+static char *get_tag(struct gui_wps *gwps,
+ int i,
+ char *buf,
+ int buf_size,
+ int *intval)
{
- gui_wps_statusbar_draw(gwps, false);
- gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
+ if (!gwps)
+ return NULL;
-#ifdef HAVE_LCD_CHARCELLS
- gui_splash(gwps->display, 0, "Vol: %3d dB",
- sound_val2phys(SOUND_VOLUME, global_settings.volume));
- return true;
-#endif
- return false;
-}
+ struct wps_data *data = gwps->data;
+ struct wps_state *state = gwps->state;
-bool ffwd_rew(int button)
-{
- static const int ff_rew_steps[] = {
- 1000, 2000, 3000, 4000,
- 5000, 6000, 8000, 10000,
- 15000, 20000, 25000, 30000,
- 45000, 60000
- };
+ if (!data || !state)
+ return NULL;
- unsigned int step = 0; /* current ff/rewind step */
- unsigned int max_step = 0; /* maximum ff/rewind step */
- int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
- int direction = -1; /* forward=1 or backward=-1 */
- long accel_tick = 0; /* next time at which to bump the step size */
- bool exit = false;
- bool usb = false;
- int i = 0;
+ struct mp3entry *id3;
- if (button == ACTION_NONE)
- {
- status_set_ffmode(0);
- return usb;
- }
- while (!exit)
+ if (data->tokens[i].next)
+ id3 = state->nid3;
+ else
+ id3 = state->id3;
+
+ if (!id3)
+ return NULL;
+
+ int limit = 1;
+ if (intval)
+ limit = *intval;
+
+#if CONFIG_RTC
+ static struct tm* tm;
+#endif
+
+ switch (data->tokens[i].type)
{
- switch ( button )
- {
- case ACTION_WPS_SEEKFWD:
- direction = 1;
- case ACTION_WPS_SEEKBACK:
- if (wps_state.ff_rewind)
- {
- if (direction == 1)
- {
- /* fast forwarding, calc max step relative to end */
- max_step = (wps_state.id3->length -
- (wps_state.id3->elapsed +
- ff_rewind_count)) *
- FF_REWIND_MAX_PERCENT / 100;
- }
- else
- {
- /* rewinding, calc max step relative to start */
- max_step = (wps_state.id3->elapsed + ff_rewind_count) *
- FF_REWIND_MAX_PERCENT / 100;
- }
+ case WPS_TOKEN_CHARACTER:
+ return &(data->tokens[i].value.c);
+
+ case WPS_TOKEN_STRING:
+ return data->strings[data->tokens[i].value.i];
+
+ case WPS_TOKEN_TRACK_TIME_ELAPSED:
+ format_time(buf, buf_size,
+ id3->elapsed + state->ff_rewind_count);
+ return buf;
+
+ case WPS_TOKEN_TRACK_TIME_REMAINING:
+ format_time(buf, buf_size,
+ id3->length - id3->elapsed -
+ state->ff_rewind_count);
+ return buf;
+
+ case WPS_TOKEN_TRACK_LENGTH:
+ format_time(buf, buf_size, id3->length);
+ return buf;
+
+ case WPS_TOKEN_PLAYLIST_ENTRIES:
+ snprintf(buf, buf_size, "%d", playlist_amount());
+ return buf;
+
+ case WPS_TOKEN_PLAYLIST_NAME:
+ return playlist_name(NULL, buf, buf_size);
+
+ case WPS_TOKEN_PLAYLIST_POSITION:
+ snprintf(buf, buf_size, "%d",
+ playlist_get_display_index());
+ return buf;
+
+ case WPS_TOKEN_PLAYLIST_SHUFFLE:
+ if ( global_settings.playlist_shuffle )
+ return "s";
+ else
+ return NULL;
+ break;
- max_step = MAX(max_step, MIN_FF_REWIND_STEP);
+ case WPS_TOKEN_VOLUME:
+ snprintf(buf, buf_size, "%d", global_settings.volume);
+ if (intval)
+ {
+ *intval = limit * (global_settings.volume
+ - sound_min(SOUND_VOLUME))
+ / (sound_max(SOUND_VOLUME)
+ - sound_min(SOUND_VOLUME)) + 1;
+ }
+ return buf;
- if (step > max_step)
- step = max_step;
+ case WPS_TOKEN_METADATA_ARTIST:
+ return id3->artist;
- ff_rewind_count += step * direction;
+ case WPS_TOKEN_METADATA_COMPOSER:
+ return id3->composer;
- if (global_settings.ff_rewind_accel != 0 &&
- current_tick >= accel_tick)
- {
- step *= 2;
- accel_tick = current_tick +
- global_settings.ff_rewind_accel*HZ;
- }
- }
- else
- {
- if ( (audio_status() & AUDIO_STATUS_PLAY) &&
- wps_state.id3 && wps_state.id3->length )
- {
- if (!wps_state.paused)
-#if (CONFIG_CODEC == SWCODEC)
- audio_pre_ff_rewind();
-#else
- audio_pause();
-#endif
-#if CONFIG_KEYPAD == PLAYER_PAD
- FOR_NB_SCREENS(i)
- gui_wps[i].display->stop_scroll();
-#endif
- if (direction > 0)
- status_set_ffmode(STATUS_FASTFORWARD);
- else
- status_set_ffmode(STATUS_FASTBACKWARD);
+ case WPS_TOKEN_METADATA_ALBUM:
+ return id3->album;
- wps_state.ff_rewind = true;
+ case WPS_TOKEN_METADATA_ALBUM_ARTIST:
+ return id3->albumartist;
- step = ff_rew_steps[global_settings.ff_rewind_min_step];
+ case WPS_TOKEN_METADATA_GENRE:
+ return id3->genre_string;
- accel_tick = current_tick +
- global_settings.ff_rewind_accel*HZ;
- }
- else
- break;
- }
+ case WPS_TOKEN_METADATA_TRACK_NUMBER:
+ if (id3->track_string)
+ return id3->track_string;
- if (direction > 0) {
- if ((wps_state.id3->elapsed + ff_rewind_count) >
- wps_state.id3->length)
- ff_rewind_count = wps_state.id3->length -
- wps_state.id3->elapsed;
+ if (id3->tracknum) {
+ snprintf(buf, buf_size, "%d", id3->tracknum);
+ return buf;
+ }
+ return NULL;
+
+ case WPS_TOKEN_METADATA_TRACK_TITLE:
+ return id3->title;
+
+ case WPS_TOKEN_METADATA_VERSION:
+ switch (id3->id3version)
+ {
+ case ID3_VER_1_0:
+ return "1";
+
+ case ID3_VER_1_1:
+ return "1.1";
+
+ case ID3_VER_2_2:
+ return "2.2";
+
+ case ID3_VER_2_3:
+ return "2.3";
+
+ case ID3_VER_2_4:
+ return "2.4";
+
+ default:
+ return NULL;
+ }
+
+ case WPS_TOKEN_METADATA_YEAR:
+ if( id3->year_string )
+ return id3->year_string;
+
+ if (id3->year) {
+ snprintf(buf, buf_size, "%d", id3->year);
+ return buf;
+ }
+ return NULL;
+
+ case WPS_TOKEN_METADATA_COMMENT:
+ return id3->comment;
+
+ case WPS_TOKEN_FILE_BITRATE:
+ if(id3->bitrate)
+ snprintf(buf, buf_size, "%d", id3->bitrate);
+ else
+ snprintf(buf, buf_size, "?");
+ return buf;
+
+ case WPS_TOKEN_FILE_CODEC:
+ if (intval)
+ {
+ if(id3->codectype == AFMT_UNKNOWN)
+ *intval = AFMT_NUM_CODECS;
+ else
+ *intval = id3->codectype;
+ }
+ return id3_get_codec(id3);
+
+ case WPS_TOKEN_FILE_FREQUENCY:
+ snprintf(buf, buf_size, "%ld", id3->frequency);
+ return buf;
+
+ case WPS_TOKEN_FILE_NAME:
+ if (get_dir(buf, buf_size, id3->path, 0)) {
+ /* Remove extension */
+ char* sep = strrchr(buf, '.');
+ if (NULL != sep) {
+ *sep = 0;
}
- else {
- if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
- ff_rewind_count = -wps_state.id3->elapsed;
+ return buf;
+ }
+ else {
+ return NULL;
+ }
+
+ case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
+ return get_dir(buf, buf_size, id3->path, 0);
+
+ case WPS_TOKEN_FILE_PATH:
+ return id3->path;
+
+ case WPS_TOKEN_FILE_SIZE:
+ snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
+ return buf;
+
+ case WPS_TOKEN_FILE_VBR:
+ return id3->vbr ? "(avg)" : NULL;
+
+ case WPS_TOKEN_FILE_DIRECTORY:
+ return get_dir(buf, buf_size, id3->path, data->tokens[i].value.i);
+
+ case WPS_TOKEN_BATTERY_PERCENT:
+ {
+ int l = battery_level();
+
+ if (intval)
+ {
+ limit = MAX(limit, 2);
+ if (l > -1) {
+ /* First enum is used for "unknown level". */
+ *intval = (limit - 1) * l / 100 + 2;
+ } else {
+ *intval = 1;
}
+ }
- FOR_NB_SCREENS(i)
- gui_wps_refresh(&gui_wps[i],
- (wps_state.wps_time_countup == false)?
- ff_rewind_count:-ff_rewind_count,
- WPS_REFRESH_PLAYER_PROGRESS |
- WPS_REFRESH_DYNAMIC);
+ if (l > -1) {
+ snprintf(buf, buf_size, "%d", l);
+ return buf;
+ } else {
+ return "?";
+ }
+ }
- break;
+ case WPS_TOKEN_BATTERY_VOLTS:
+ {
+ unsigned int v = battery_voltage();
+ snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
+ return buf;
+ }
- case ACTION_WPS_STOPSEEK:
- wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count;
- audio_ff_rewind(wps_state.id3->elapsed);
- ff_rewind_count = 0;
- wps_state.ff_rewind = false;
- status_set_ffmode(0);
-#if (CONFIG_CODEC != SWCODEC)
- if (!wps_state.paused)
- audio_resume();
+ case WPS_TOKEN_BATTERY_TIME:
+ {
+ int t = battery_time();
+ if (t >= 0)
+ snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
+ else
+ strncpy(buf, "?h ?m", buf_size);
+ return buf;
+ }
+
+#if CONFIG_CHARGING
+ case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
+ {
+ if(charger_input_state==CHARGER)
+ return "p";
+ else
+ return NULL;
+ }
+#endif
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ case WPS_TOKEN_BATTERY_CHARGING:
+ {
+ if (charge_state == CHARGING || charge_state == TOPOFF) {
+ return "c";
+ } else {
+ return NULL;
+ }
+ }
+#endif
+
+ case WPS_TOKEN_PLAYBACK_STATUS:
+ {
+ int status = audio_status();
+ int mode = 1;
+ if (status == AUDIO_STATUS_PLAY && \
+ !(status & AUDIO_STATUS_PAUSE))
+ mode = 2;
+ if (audio_status() & AUDIO_STATUS_PAUSE && \
+ (! status_get_ffmode()))
+ mode = 3;
+ if (status_get_ffmode() == STATUS_FASTFORWARD)
+ mode = 4;
+ if (status_get_ffmode() == STATUS_FASTBACKWARD)
+ mode = 5;
+
+ if (intval) {
+ *intval = mode;
+ }
+
+ snprintf(buf, buf_size, "%d", mode);
+ return buf;
+ }
+
+ case WPS_TOKEN_REPEAT_MODE:
+ if (intval)
+ *intval = global_settings.repeat_mode + 1;
+ snprintf(buf, buf_size, "%d", *intval);
+ return buf;
+
+#if CONFIG_RTC
+ case WPS_TOKEN_RTC:
+ tm = get_time();
+ return NULL;
+
+ case WPS_TOKEN_RTC_DAY_OF_MONTH:
+ /* d: day of month (01..31) */
+ if (tm->tm_mday > 31 || tm->tm_mday < 1) return NULL;
+ snprintf(buf, buf_size, "%02d", tm->tm_mday);
+ return buf;
+
+ case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
+ /* e: day of month, blank padded ( 1..31) */
+ if (tm->tm_mday > 31 || tm->tm_mday < 1) return NULL;
+ snprintf(buf, buf_size, "%2d", tm->tm_mday);
+ return buf;
+
+ case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
+ /* H: hour (00..23) */
+ if (tm->tm_hour > 23) return NULL;
+ snprintf(buf, buf_size, "%02d", tm->tm_hour);
+ return buf;
+
+ case WPS_TOKEN_RTC_HOUR_24:
+ /* k: hour ( 0..23) */
+ if (tm->tm_hour > 23) return NULL;
+ snprintf(buf, buf_size, "%2d", tm->tm_hour);
+ return buf;
+
+ case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
+ /* I: hour (01..12) */
+ if (tm->tm_hour > 23) return NULL;
+ snprintf(buf, buf_size, "%02d",
+ (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
+ return buf;
+
+ case WPS_TOKEN_RTC_HOUR_12:
+ /* l: hour ( 1..12) */
+ if (tm->tm_hour > 23) return NULL;
+ snprintf(buf, buf_size, "%2d",
+ (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
+ return buf;
+
+ case WPS_TOKEN_RTC_MONTH:
+ /* m: month (01..12) */
+ if (tm->tm_mon > 11 || tm->tm_mon < 0) return NULL;
+ snprintf(buf, buf_size, "%02d", tm->tm_mon + 1);
+ return buf;
+
+ case WPS_TOKEN_RTC_MINUTE:
+ /* M: minute (00..59) */
+ if (tm->tm_min > 59 || tm->tm_min < 0) return NULL;
+ snprintf(buf, buf_size, "%02d", tm->tm_min);
+ return buf;
+
+ case WPS_TOKEN_RTC_SECOND:
+ /* S: second (00..59) */
+ if (tm->tm_sec > 59 || tm->tm_sec < 0) return NULL;
+ snprintf(buf, buf_size, "%02d", tm->tm_sec);
+ return buf;
+
+ case WPS_TOKEN_RTC_YEAR_2_DIGITS:
+ /* y: last two digits of year (00..99) */
+ snprintf(buf, buf_size, "%02d", tm->tm_year % 100);
+ return buf;
+
+ case WPS_TOKEN_RTC_YEAR_4_DIGITS:
+ /* Y: year (1970...) */
+ if (tm->tm_year > 199 || tm->tm_year < 100) return NULL;
+ snprintf(buf, buf_size, "%04d", tm->tm_year + 1900);
+ return buf;
+
+ case WPS_TOKEN_RTC_AM_PM_UPPER:
+ /* p: upper case AM or PM indicator */
+ snprintf(buf, buf_size, (tm->tm_hour/12 == 0) ? "AM" : "PM");
+ return buf;
+
+ case WPS_TOKEN_RTC_AM_PM_LOWER:
+ /* P: lower case am or pm indicator */
+ snprintf(buf, buf_size, (tm->tm_hour/12 == 0) ? "am" : "pm");
+ return buf;
+
+ case WPS_TOKEN_RTC_WEEKDAY_NAME:
+ /* a: abbreviated weekday name (Sun..Sat) */
+ if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
+ snprintf(buf, buf_size, "%s",str(dayname[tm->tm_wday]));
+ return buf;
+
+ case WPS_TOKEN_RTC_MONTH_NAME:
+ /* b: abbreviated month name (Jan..Dec) */
+ if (tm->tm_mon > 11 || tm->tm_mon < 0) return NULL;
+ snprintf(buf, buf_size, "%s",str(monthname[tm->tm_mon]));
+ return buf;
+
+ case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
+ /* u: day of week (1..7); 1 is Monday */
+ if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
+ snprintf(buf, buf_size, "%1d", tm->tm_wday + 1);
+ return buf;
+
+ case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
+ /* w: day of week (0..6); 0 is Sunday */
+ if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
+ snprintf(buf, buf_size, "%1d", tm->tm_wday);
+ return buf;
#endif
+
#ifdef HAVE_LCD_CHARCELLS
- gui_wps_display();
+ case WPS_TOKEN_PROGRESSBAR:
+ {
+ char *end = utf8encode(data->wps_progress_pat[0], buf);
+ *end = '\0';
+ return buf;
+ }
+
+ case WPS_TOKEN_PLAYER_PROGRESSBAR:
+ if(is_new_player())
+ {
+ /* we need 11 characters (full line) for
+ progress-bar */
+ snprintf(buf, buf_size, " ");
+ }
+ else
+ {
+ /* Tell the user if we have an OldPlayer */
+ snprintf(buf, buf_size, " <Old LCD> ");
+ }
+ return buf;
#endif
- exit = true;
- break;
- default:
- if(default_event_handler(button) == SYS_USB_CONNECTED) {
- status_set_ffmode(0);
- usb = true;
- exit = true;
- }
- break;
+ case WPS_TOKEN_DATABASE_PLAYCOUNT:
+ if (intval) {
+ *intval = id3->playcount + 1;
+ }
+ snprintf(buf, buf_size, "%ld", id3->playcount);
+ return buf;
+
+ case WPS_TOKEN_DATABASE_RATING:
+ if (intval) {
+ *intval = id3->rating + 1;
+ }
+ snprintf(buf, buf_size, "%d", id3->rating);
+ return buf;
+
+#if (CONFIG_CODEC == SWCODEC)
+ case WPS_TOKEN_REPLAYGAIN:
+ {
+ int val;
+
+ if (global_settings.replaygain == 0)
+ val = 1; /* off */
+ else
+ {
+ int type =
+ get_replaygain_mode(id3->track_gain_string != NULL,
+ id3->album_gain_string != NULL);
+ if (type < 0)
+ val = 6; /* no tag */
+ else
+ val = type + 2;
+
+ if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
+ val += 2;
+ }
+
+ if (intval)
+ *intval = val;
+
+ switch (val)
+ {
+ case 1:
+ case 6:
+ return "+0.00 dB";
+ break;
+ case 2:
+ case 4:
+ strncpy(buf, id3->track_gain_string, buf_size);
+ break;
+ case 3:
+ case 5:
+ strncpy(buf, id3->album_gain_string, buf_size);
+ break;
+ }
+ return buf;
}
- if (!exit)
- button = get_action(CONTEXT_WPS,TIMEOUT_BLOCK);
+
+ case WPS_TOKEN_SOUND_PITCH:
+ snprintf(buf, buf_size, "%d.%d",
+ *intval / 10, *intval % 10);
+ return buf;
+
+#endif
+
+#ifdef HAS_BUTTON_HOLD
+ case WPS_TOKEN_MAIN_HOLD:
+ if (button_hold())
+ return "h";
+ else
+ return NULL;
+#endif
+#ifdef HAS_REMOTE_BUTTON_HOLD
+ case WPS_TOKEN_REMOTE_HOLD:
+ if (remote_button_hold())
+ return "r";
+ else
+ return NULL;
+#endif
+
+#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
+ case WPS_TOKEN_VLED_HDD:
+ if(led_read(HZ/2))
+ return "h";
+ else
+ return NULL;
+#endif
+
+ default:
+ return NULL;
}
- action_signalscreenchange();
- return usb;
}
-bool gui_wps_display(void)
+/* 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 i;
- if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY))
+ int type = data->tokens[index].type;
+
+ if (type != WPS_TOKEN_CONDITIONAL_START
+ && type != WPS_TOKEN_CONDITIONAL_OPTION)
{
- global_status.resume_index = -1;
-#ifdef HAVE_LCD_CHARCELLS
- gui_syncsplash(HZ, str(LANG_END_PLAYLIST_PLAYER));
-#else
- gui_syncstatusbar_draw(&statusbars, true);
- gui_syncsplash(HZ, str(LANG_END_PLAYLIST_RECORDER));
-#endif
- return true;
+ /* this function should only be used with "index" pointing to a
+ WPS_TOKEN_CONDITIONAL_START or a WPS_TOKEN_CONDITIONAL_OPTION */
+ return index + 1;
}
- else
+
+ int ret = index;
+ do
+ ret = data->tokens[ret].value.i;
+ while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END);
+
+ /* ret now is the index to the end token for the conditional. */
+ return ret;
+}
+
+/* Return the index of the appropriate case for the conditional
+ that starts at cond_index.
+*/
+static int evaluate_conditional(struct gui_wps *gwps, int cond_index)
+{
+ if (!gwps)
+ return 0;
+
+ struct wps_data *data = gwps->data;
+
+ int ret;
+ int num_options = data->tokens[cond_index].value.i;
+ char result[128], *value;
+ int cond_start = cond_index;
+
+ /* find the index of the conditional start token */
+ while (data->tokens[cond_start].type != WPS_TOKEN_CONDITIONAL_START
+ && cond_start < data->num_tokens)
+ cond_start++;
+
+ if (num_options > 2) /* enum */
{
- FOR_NB_SCREENS(i)
+ int intval = num_options;
+ /* get_tag needs to know the number of options in the enum */
+ get_tag(gwps, 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 > num_options || intval < 1)
+ intval = num_options;
+
+ int next = cond_start;
+ int i;
+ for (i = 1; i < intval; i++)
{
- gui_wps[i].display->clear_display();
- if (!gui_wps[i].data->wps_loaded) {
- if ( !gui_wps[i].data->format_buffer[0] ) {
- /* set the default wps for the main-screen */
- if(i == 0)
- {
+ next = data->tokens[next].value.i;
+ }
+ ret = next;
+ }
+ else /* %?xx<true|false> or %?<true> */
+ {
+ value = get_tag(gwps, cond_index + 1, result, sizeof(result), NULL);
+ ret = value ? cond_start : data->tokens[cond_start].value.i;
+ }
+
#ifdef HAVE_LCD_BITMAP
-#if LCD_DEPTH > 1
- unload_wps_backdrop();
-#endif
- wps_data_load(gui_wps[i].data,
- "%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
- wps_data_load(gui_wps[i].data,
- "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
- "%pc%?ps<*|/>%pt\n", false);
+ /* clear all pictures in the conditional */
+ int i;
+ for (i=0; i < MAX_IMAGES; i++)
+ {
+ if (data->img[i].cond_index == cond_index)
+ clear_image_pos(gwps, i);
+ }
#endif
- }
-#if NB_SCREENS == 2
- /* set the default wps for the remote-screen */
- else if(i == 1)
- {
- wps_data_load(gui_wps[i].data,
- "%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", false);
- }
+
+ return ret;
+}
+
+/* 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;
+ bool update = false;
+
+ /* alignment-related variables */
+ int cur_align;
+ char* cur_align_start;
+ cur_align_start = buf;
+ cur_align = WPS_ALIGN_LEFT;
+ align->left = 0;
+ align->center = 0;
+ align->right = 0;
+
+ /* start at the beginning of the current (sub)line */
+ int i = data->format_lines[line][subline];
+
+ while (data->tokens[i].type != WPS_TOKEN_EOL
+ && data->tokens[i].type != WPS_TOKEN_SUBLINE_SEPARATOR
+ && i < data->num_tokens)
+ {
+ switch(data->tokens[i].type)
+ {
+ case WPS_TOKEN_CONDITIONAL:
+ /* place ourselves in the right conditional case */
+ i = 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;
+ if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
+ img[n].display = true;
+ 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;
+
+ default:
+ {
+ /* get the value of the tag and copy it to the buffer */
+ char *value = get_tag(gwps, i, temp_buf,
+ sizeof(temp_buf), NULL);
+ if (value)
+ {
+ update = true;
+ while (*value && (buf < linebuf_end))
+ *buf++ = *value++;
+ }
+ break;
}
}
+ i++;
}
- yield();
- FOR_NB_SCREENS(i)
+
+ /* close the current alignment */
+ switch (cur_align)
{
- gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL);
+ 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 false;
+
+ return update;
}
-bool update(struct gui_wps *gwps)
+/* Calculate which subline should be displayed for each line */
+static bool get_curr_subline(struct wps_data *data, int line)
{
- bool track_changed = audio_has_changed_track();
- bool retcode = false;
+ int search, search_start;
+ bool reset_subline;
+ bool new_subline_refresh;
+ bool only_one_subline;
- gwps->state->nid3 = audio_next_track();
- if (track_changed)
+ reset_subline = (data->curr_subline[line] == SUBLINE_RESET);
+ new_subline_refresh = false;
+ only_one_subline = false;
+
+ /* if time to advance to next sub-line */
+ if (TIME_AFTER(current_tick, data->subline_expire_time[line] - 1) ||
+ reset_subline)
{
- gwps->display->stop_scroll();
- gwps->state->id3 = audio_current_track();
+ /* 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->curr_subline[line];
- if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
- && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
+ for (search = 0; search < WPS_MAX_SUBLINES; search++)
{
- /* the current cuesheet isn't the right one any more */
+ data->curr_subline[line]++;
- if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) {
- /* We have the new cuesheet in memory (temp_cue),
- let's make it the current one ! */
- memcpy(curr_cue, temp_cue, sizeof(struct cuesheet));
+ /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
+ if ((!data->format_lines[line][data->curr_subline[line]]) ||
+ (data->curr_subline[line] == WPS_MAX_SUBLINES))
+ {
+ if (data->curr_subline[line] == 1)
+ only_one_subline = true;
+ data->curr_subline[line] = 0;
}
- else {
- /* We need to parse the new cuesheet */
- char cuepath[MAX_PATH];
- strncpy(cuepath, gwps->state->id3->path, MAX_PATH);
- char *dot = strrchr(cuepath, '.');
- strcpy(dot, ".cue");
-
- if (parse_cuesheet(cuepath, curr_cue))
+ /* if back where we started after search or
+ only one subline is defined on the line */
+ if (((search > 0) && (data->curr_subline[line] == search_start)) ||
+ only_one_subline)
+ {
+ /* no other subline with a time > 0 exists */
+ data->subline_expire_time[line] = (reset_subline?
+ current_tick : data->subline_expire_time[line]) + 100 * HZ;
+ break;
+ }
+ else
+ {
+ /* only use this subline if subline time > 0 */
+ if (data->time_mult[line][data->curr_subline[line]] > 0)
{
- gwps->state->id3->cuesheet_type = 1;
- strcpy(curr_cue->audio_filename, gwps->state->id3->path);
+ new_subline_refresh = true;
+ data->subline_expire_time[line] = (reset_subline ?
+ current_tick : data->subline_expire_time[line]) +
+ BASE_SUBLINE_TIME * data->time_mult[line][data->curr_subline[line]];
+ break;
}
}
+ }
+ }
- cue_spoof_id3(curr_cue, gwps->state->id3);
+ 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, left_xpos;
+ int center_width, center_xpos;
+ int right_width, right_xpos;
+ int ypos;
+ int space_width;
+ int string_height;
+
+ /* 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);
+ }
+ else {
+ left_width = 0;
+ }
+ left_xpos = 0;
+
+ if (format_align->center != 0) {
+ display->getstringsize((unsigned char *)format_align->center,
+ &center_width, &string_height);
+ }
+ else {
+ center_width = 0;
+ }
+ center_xpos=(display->width - center_width) / 2;
+
+ if (format_align->right != 0) {
+ display->getstringsize((unsigned char *)format_align->right,
+ &right_width, &string_height);
+ }
+ else {
+ right_width = 0;
+ }
+ right_xpos = (display->width - right_width);
+
+ /* 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;
+ left_xpos = 0;
+ /* 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;
+ left_xpos = 0;
+ /* 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->width - 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->width - 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;
+ left_xpos = 0;
+ /* 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_xpos + 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;
+ left_xpos = 0;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+
+ ypos = (line * string_height) + display->getymargin();
+
+
+ if (scroll && left_width > display->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(0, ypos, display->width, 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);
+ }
+ }
+}
- if (gui_wps_display())
- retcode = true;
- else{
- gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
+/* Refresh the WPS according to refresh_mode. */
+bool gui_wps_refresh(struct gui_wps *gwps,
+ int ffwd_offset,
+ unsigned char refresh_mode)
+{
+ struct wps_data *data = gwps->data;
+ struct screen *display = gwps->display;
+ struct wps_state *state = gwps->state;
+
+ if(!gwps || !data || !state || !display)
+ return false;
+
+ int line, i;
+ unsigned char 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
+ gui_wps_statusbar_draw(gwps, true);
+
+ /* 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;
+
+ /* Set images to not to be displayed */
+ for (i = 0; i < MAX_IMAGES; i++)
+ {
+ data->img[i].display = false;
+ }
+#endif
+
+ /* reset to first subline if refresh all flag is set */
+ if (refresh_mode == WPS_REFRESH_ALL)
+ {
+ for (i = 0; i < data->num_lines; i++)
+ {
+ data->curr_subline[i] = SUBLINE_RESET;
}
+ }
- if (gwps->state->id3)
- memcpy(gwps->state->current_track_path, gwps->state->id3->path,
- sizeof(gwps->state->current_track_path));
+#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
- if (gwps->state->id3)
+ if (!state->id3)
{
- if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
- && (gwps->state->id3->elapsed < curr_cue->curr_track->offset
- || (curr_cue->curr_track_idx < curr_cue->track_count - 1
- && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset)))
+ display->stop_scroll();
+ return false;
+ }
+
+ state->ff_rewind_count = ffwd_offset;
+
+ for (line = 0; line < data->num_lines; line++)
+ {
+ memset(linebuf, 0, sizeof(linebuf));
+ update_line = false;
+
+ /* get current subline for the line */
+ new_subline_refresh = get_curr_subline(data, line);
+
+ flags = data->line_type[line][data->curr_subline[line]];
+
+ if (refresh_mode == WPS_REFRESH_ALL || flags & refresh_mode
+ || new_subline_refresh)
{
- /* We've changed tracks within the cuesheet :
- we need to update the ID3 info and refresh the WPS */
+ /* get_line tells us if we need to update the line */
+ update_line = get_line(gwps, line, data->curr_subline[line],
+ &align, linebuf, sizeof(linebuf));
+ }
- cue_find_current_track(curr_cue, gwps->state->id3->elapsed);
- cue_spoof_id3(curr_cue, gwps->state->id3);
+#ifdef HAVE_LCD_BITMAP
+ /* progressbar */
+ if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+ {
+ /* the progressbar should be alone on its line */
+ update_line = false;
+ draw_progressbar(gwps, line);
+ }
- gwps->display->stop_scroll();
- if (gui_wps_display())
- retcode = true;
+ /* peakmeter */
+ if (flags & refresh_mode & WPS_REFRESH_PEAK_METER)
+ {
+ /* the peakmeter should be alone on its line */
+ update_line = false;
+
+ int h = font_get(FONT_UI)->height;
+ int peak_meter_y = display->getymargin() + 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->height) {
+ /* found a line with a peak meter -> remember that we must
+ enable it later */
+ enable_pm = true;
+ peak_meter_screen(gwps->display, 0, peak_meter_y,
+ MIN(h, display->height - peak_meter_y));
+ }
+ }
+
+#else /* HAVE_LCD_CHARCELL */
+
+ /* progressbar */
+ if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+ {
+ if (data->full_line_progressbar)
+ draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
else
- gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
+ draw_player_progress(gwps);
+ }
+#endif
+
+ if (update_line)
+ {
+ /* calculate alignment and draw the strings */
+ write_line(display, &align, line, flags & WPS_REFRESH_SCROLL);
}
- else
- gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
}
- gui_wps_statusbar_draw(gwps, false);
+#ifdef HAVE_LCD_BITMAP
+ data->peak_meter_enabled = enable_pm;
+ wps_display_images(gwps);
+#endif
- return retcode;
-}
+ display->update();
+#if CONFIG_BACKLIGHT
+ if (global_settings.caption_backlight && state->id3)
+ {
+ /* turn on backlight n seconds before track ends, and turn it off n
+ seconds into the new track. n == backlight_timeout, or 5s */
+ int n = backlight_timeout_value[global_settings.backlight_timeout]
+ * 1000;
-void display_keylock_text(bool locked)
-{
- char* s;
- int i;
- FOR_NB_SCREENS(i)
- gui_wps[i].display->stop_scroll();
+ if ( n < 1000 )
+ n = 5000; /* use 5s if backlight is always on or off */
-#ifdef HAVE_LCD_CHARCELLS
- if(locked)
- s = str(LANG_KEYLOCK_ON_PLAYER);
- else
- s = str(LANG_KEYLOCK_OFF_PLAYER);
-#else
- if(locked)
- s = str(LANG_KEYLOCK_ON_RECORDER);
- else
- s = str(LANG_KEYLOCK_OFF_RECORDER);
+ if (((state->id3->elapsed < 1000) ||
+ ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
+ (state->paused == false))
+ backlight_on();
+ }
+#endif
+#ifdef HAVE_REMOTE_LCD
+ if (global_settings.remote_caption_backlight && state->id3)
+ {
+ /* turn on remote backlight n seconds before track ends, and turn it
+ off n seconds into the new track. n == remote_backlight_timeout,
+ or 5s */
+ int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
+ * 1000;
+
+ if ( n < 1000 )
+ n = 5000; /* use 5s if backlight is always on or off */
+
+ if (((state->id3->elapsed < 1000) ||
+ ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
+ (state->paused == false))
+ remote_backlight_on();
+ }
#endif
- gui_syncsplash(HZ, s);
-}
+ return true;
+}
diff --git a/apps/gui/gwps-common.h b/apps/gui/gwps-common.h
index 77bec83..1203113 100644
--- a/apps/gui/gwps-common.h
+++ b/apps/gui/gwps-common.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 Björn Stenberg
+ * Copyright (C) 2007 Nicolas Pennequin
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
@@ -24,16 +24,14 @@
#include "gwps.h"
void fade(bool fade_in);
-void gui_wps_format(struct wps_data *data);
-bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
- unsigned char refresh_mode);
bool gui_wps_display(void);
void setvol(void);
bool update_onvol_change(struct gui_wps * gwps);
bool update(struct gui_wps *gwps);
bool ffwd_rew(int button);
-bool wps_data_preload_tags(struct wps_data *data, char *buf,
- const char *bmpdir, size_t bmpdirlen);
void display_keylock_text(bool locked);
-#endif
+bool gui_wps_refresh(struct gui_wps *gwps,
+ int ffwd_offset,
+ unsigned char refresh_mode);
+#endif
diff --git a/apps/gui/gwps.c b/apps/gui/gwps.c
index d70863d..b43ff9d 100644
--- a/apps/gui/gwps.c
+++ b/apps/gui/gwps.c
@@ -61,8 +61,6 @@
#include "ata_idle_notify.h"
#include "root_menu.h"
-#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
-#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
/* currently only on wps_state is needed */
struct wps_state wps_state;
struct gui_wps gui_wps[NB_SCREENS];
@@ -81,7 +79,6 @@ static void gui_wps_set_disp(struct gui_wps *gui_wps, struct screen *display);
/* connects a wps with a statusbar*/
static void gui_wps_set_statusbar(struct gui_wps *gui_wps, struct gui_statusbar *statusbar);
-
#ifdef HAVE_LCD_BITMAP
static void gui_wps_set_margin(struct gui_wps *gwps)
{
@@ -671,144 +668,6 @@ long gui_wps_show(void)
/* needs checking if needed end*/
-/* wps_data*/
-#ifdef HAVE_LCD_BITMAP
-/* Clear the WPS image cache */
-static void wps_clear(struct wps_data *data )
-{
- int i;
- /* set images to unloaded and not displayed */
- for (i = 0; i < MAX_IMAGES; i++) {
- data->img[i].loaded = false;
- data->img[i].display = false;
- data->img[i].always_display = false;
- }
- data->wps_sb_tag = false;
- data->show_sb_on_wps = false;
- data->progressbar.have_bitmap_pb=false;
-}
-#else
-#define wps_clear(a)
-#endif
-
-/* initial setup of wps_data */
-void wps_data_init(struct wps_data *wps_data)
-{
-#ifdef HAVE_LCD_BITMAP
- wps_clear(wps_data);
-#else /* HAVE_LCD_CHARCELLS */
- {
- int i;
- for(i = 0; i < 8; i++)
- wps_data->wps_progress_pat[i] = 0;
- wps_data->full_line_progressbar = 0;
- }
-#endif
- wps_data->format_buffer[0] = '\0';
- wps_data->wps_loaded = false;
- wps_data->peak_meter_enabled = false;
-}
-
-static void wps_reset(struct wps_data *data)
-{
- data->wps_loaded = false;
- memset(&data->format_buffer, 0, sizeof data->format_buffer);
- wps_data_init(data);
-}
-
-/* to setup up the wps-data from a format-buffer (isfile = false)
- from a (wps-)file (isfile = true)*/
-bool wps_data_load(struct wps_data *wps_data,
- const char *buf,
- bool isfile)
-{
- int fd;
-
- if(!wps_data || !buf)
- return false;
-
- if(!isfile)
- {
- wps_clear(wps_data);
- strncpy(wps_data->format_buffer, buf, sizeof(wps_data->format_buffer));
- wps_data->format_buffer[sizeof(wps_data->format_buffer) - 1] = 0;
- gui_wps_format(wps_data);
- return true;
- }
- else
- {
- /*
- * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
- * wants to be a virtual file. Feel free to modify dirbrowse()
- * if you're feeling brave.
- */
- if (! strcmp(buf, WPS_DEFAULTCFG) )
- {
- wps_reset(wps_data);
- global_settings.wps_file[0] = 0;
- return false;
- }
-
-#ifdef HAVE_REMOTE_LCD
- if (! strcmp(buf, RWPS_DEFAULTCFG) )
- {
- wps_reset(wps_data);
- global_settings.rwps_file[0] = 0;
- return false;
- }
-#endif
-
- size_t bmpdirlen;
- char *bmpdir = strrchr(buf, '.');
- bmpdirlen = bmpdir - buf;
-
- fd = open(buf, O_RDONLY);
-
- if (fd >= 0)
- {
- unsigned int start = 0;
-
- wps_reset(wps_data);
-#ifdef HAVE_LCD_BITMAP
- wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
-
- wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
-#endif
- while( ( read_line(fd, &wps_data->format_buffer[start],
- sizeof(wps_data->format_buffer)-start) ) > 0 )
- {
- if(!wps_data_preload_tags(wps_data,
- &wps_data->format_buffer[start],
- buf, bmpdirlen))
- {
- start += strlen(&wps_data->format_buffer[start]);
-
- if (start < sizeof(wps_data->format_buffer) - 1)
- {
- wps_data->format_buffer[start++] = '\n';
- wps_data->format_buffer[start] = 0;
- }
- }
- }
-
- if (start > 0)
- {
- gui_wps_format(wps_data);
- }
-
- close(fd);
-
- wps_data->wps_loaded = true;
-
- return start > 0;
- }
- }
-
- return false;
-}
-
-/* wps_data end */
-
/* wps_state */
static void wps_state_init(void)
diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h
index 123bb8f..c18fc0c 100644
--- a/apps/gui/gwps.h
+++ b/apps/gui/gwps.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 Jerome Kuptz
+ * Copyright (C) 2007 Nicolas Pennequin
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
@@ -49,6 +49,9 @@ struct gui_img{
bool loaded; /* load state */
bool display; /* is to be displayed */
bool always_display; /* not using the preload/display mechanism */
+
+ /* the index of the conditional the image is in */
+ unsigned short cond_index;
};
struct prog_img{ /*progressbar image*/
@@ -64,23 +67,189 @@ struct align_pos {
};
#ifdef HAVE_LCD_BITMAP
+
#define MAX_IMAGES (26*2) /* a-z and A-Z */
#define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
+ (2*LCD_HEIGHT*LCD_WIDTH/8))
#define WPS_MAX_LINES (LCD_HEIGHT/5+1)
-#define FORMAT_BUFFER_SIZE 3072
+#define WPS_MAX_TOKENS 1024
+#define WPS_MAX_STRINGS 128
+#define STRING_BUFFER_SIZE 512
+#define WPS_MAX_COND_LEVEL 10
+
#else
+
#define WPS_MAX_LINES 2
-#define FORMAT_BUFFER_SIZE 400
+#define WPS_MAX_TOKENS 64
+#define WPS_MAX_STRINGS 32
+#define STRING_BUFFER_SIZE 64
+#define WPS_MAX_COND_LEVEL 5
+
#endif
+
#define WPS_MAX_SUBLINES 12
#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* (10ths of sec) */
#define BASE_SUBLINE_TIME 10 /* base time that multiplier is applied to
(1/HZ sec, or 100ths of sec) */
#define SUBLINE_RESET -1
+enum wps_token_type {
+ WPS_NO_TOKEN, /* for WPS tags we don't want to save as tokens */
+ WPS_TOKEN_UNKNOWN,
+
+ /* Markers */
+ WPS_TOKEN_CHARACTER,
+ WPS_TOKEN_STRING,
+ WPS_TOKEN_EOL,
+
+ /* Alignment */
+ WPS_TOKEN_ALIGN_LEFT,
+ WPS_TOKEN_ALIGN_CENTER,
+ WPS_TOKEN_ALIGN_RIGHT,
+
+ /* Scrolling */
+ WPS_TOKEN_SCROLL,
+
+ /* Alternating sublines */
+ WPS_TOKEN_SUBLINE_SEPARATOR,
+ WPS_TOKEN_SUBLINE_TIMEOUT,
+
+ /* Battery */
+ WPS_TOKEN_BATTERY_PERCENT,
+ WPS_TOKEN_BATTERY_VOLTS,
+ WPS_TOKEN_BATTERY_TIME,
+ WPS_TOKEN_BATTERY_CHARGER_CONNECTED,
+ WPS_TOKEN_BATTERY_CHARGING,
+ WPS_TOKEN_BATTERY_SLEEPTIME,
+
+#if (CONFIG_CODEC == SWCODEC)
+ /* Sound */
+ WPS_TOKEN_SOUND_PITCH,
+ WPS_TOKEN_REPLAYGAIN,
+#endif
+
+#if CONFIG_RTC
+ /* Time */
+ WPS_TOKEN_RTC,
+ WPS_TOKEN_RTC_DAY_OF_MONTH,
+ WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,
+ WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED,
+ WPS_TOKEN_RTC_HOUR_24,
+ WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED,
+ WPS_TOKEN_RTC_HOUR_12,
+ WPS_TOKEN_RTC_MONTH,
+ WPS_TOKEN_RTC_MINUTE,
+ WPS_TOKEN_RTC_SECOND,
+ WPS_TOKEN_RTC_YEAR_2_DIGITS,
+ WPS_TOKEN_RTC_YEAR_4_DIGITS,
+ WPS_TOKEN_RTC_AM_PM_UPPER,
+ WPS_TOKEN_RTC_AM_PM_LOWER,
+ WPS_TOKEN_RTC_WEEKDAY_NAME,
+ WPS_TOKEN_RTC_MONTH_NAME,
+ WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON,
+ WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN,
+#endif
+
+ /* Conditional */
+ WPS_TOKEN_CONDITIONAL,
+ WPS_TOKEN_CONDITIONAL_START,
+ WPS_TOKEN_CONDITIONAL_OPTION,
+ WPS_TOKEN_CONDITIONAL_END,
+
+ /* Database */
+ WPS_TOKEN_DATABASE_PLAYCOUNT,
+ WPS_TOKEN_DATABASE_RATING,
+
+ /* File */
+ WPS_TOKEN_FILE_BITRATE,
+ WPS_TOKEN_FILE_CODEC,
+ WPS_TOKEN_FILE_FREQUENCY,
+ WPS_TOKEN_FILE_NAME,
+ WPS_TOKEN_FILE_NAME_WITH_EXTENSION,
+ WPS_TOKEN_FILE_PATH,
+ WPS_TOKEN_FILE_SIZE,
+ WPS_TOKEN_FILE_VBR,
+ WPS_TOKEN_FILE_DIRECTORY,
+
+#ifdef HAVE_LCD_BITMAP
+ /* Image */
+ WPS_TOKEN_IMAGE_BACKDROP,
+ WPS_TOKEN_IMAGE_PROGRESS_BAR,
+ WPS_TOKEN_IMAGE_PRELOAD,
+ WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,
+ WPS_TOKEN_IMAGE_DISPLAY,
+#endif
+
+ /* Metadata */
+ WPS_TOKEN_METADATA_ARTIST,
+ WPS_TOKEN_METADATA_COMPOSER,
+ WPS_TOKEN_METADATA_ALBUM_ARTIST,
+ WPS_TOKEN_METADATA_ALBUM,
+ WPS_TOKEN_METADATA_GENRE,
+ WPS_TOKEN_METADATA_TRACK_NUMBER,
+ WPS_TOKEN_METADATA_TRACK_TITLE,
+ WPS_TOKEN_METADATA_VERSION,
+ WPS_TOKEN_METADATA_YEAR,
+ WPS_TOKEN_METADATA_COMMENT,
+
+ /* Mode */
+ WPS_TOKEN_REPEAT_MODE,
+ WPS_TOKEN_PLAYBACK_STATUS,
+
+#ifdef HAS_BUTTON_HOLD
+ WPS_TOKEN_MAIN_HOLD,
+#endif
+#ifdef HAS_REMOTE_BUTTON_HOLD
+ WPS_TOKEN_REMOTE_HOLD,
+#endif
+
+ /* Progressbar */
+ WPS_TOKEN_PROGRESSBAR,
+ WPS_TOKEN_PLAYER_PROGRESSBAR,
+
+#ifdef HAVE_LCD_BITMAP
+ /* Peakmeter */
+ WPS_TOKEN_PEAKMETER,
+#endif
+
+ /* Volume level */
+ WPS_TOKEN_VOLUME,
+
+ /* Current track */
+ WPS_TOKEN_TRACK_TIME_ELAPSED,
+ WPS_TOKEN_TRACK_TIME_REMAINING,
+ WPS_TOKEN_TRACK_LENGTH,
+
+ /* Playlist */
+ WPS_TOKEN_PLAYLIST_ENTRIES,
+ WPS_TOKEN_PLAYLIST_NAME,
+ WPS_TOKEN_PLAYLIST_POSITION,
+ WPS_TOKEN_PLAYLIST_SHUFFLE,
+
+#ifdef HAVE_LCD_BITMAP
+ /* Statusbar */
+ WPS_TOKEN_STATUSBAR_ENABLED,
+ WPS_TOKEN_STATUSBAR_DISABLED,
+#endif
+
+#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
+ /* Virtual LED */
+ WPS_TOKEN_VLED_HDD
+#endif
+};
+
+struct wps_token {
+ enum wps_token_type type;
+ bool next;
+ union {
+ char c;
+ unsigned short i;
+ } value;
+};
+
+
/* wps_data
- this struct old all necessary data which describes the
+ this struct holds all necessary data which describes the
viewable content of a wps */
struct wps_data
{
@@ -92,23 +261,32 @@ struct wps_data
int img_buf_free;
bool wps_sb_tag;
bool show_sb_on_wps;
-#endif
-#ifdef HAVE_LCD_CHARCELLS
+
+ short progress_top;
+ short progress_height;
+ short progress_start;
+ short progress_end;
+ bool peak_meter_enabled;
+#else /*HAVE_LCD_CHARCELLS */
unsigned short wps_progress_pat[8];
bool full_line_progressbar;
#endif
- char format_buffer[FORMAT_BUFFER_SIZE];
- char* format_lines[WPS_MAX_LINES][WPS_MAX_SUBLINES];
+ unsigned short format_lines[WPS_MAX_LINES][WPS_MAX_SUBLINES];
+ unsigned char num_lines;
unsigned char line_type[WPS_MAX_LINES][WPS_MAX_SUBLINES];
unsigned short time_mult[WPS_MAX_LINES][WPS_MAX_SUBLINES];
long subline_expire_time[WPS_MAX_LINES];
- int curr_subline[WPS_MAX_LINES];
- int progress_top;
- int progress_height;
- int progress_start;
- int progress_end;
+ short curr_subline[WPS_MAX_LINES];
+ unsigned char num_sublines[WPS_MAX_LINES];
+
+ struct wps_token tokens[WPS_MAX_TOKENS];
+ unsigned short num_tokens;
+
+ char string_buffer[STRING_BUFFER_SIZE];
+ char *strings[WPS_MAX_STRINGS];
+ unsigned char num_strings;
+
bool wps_loaded;
- bool peak_meter_enabled;
};
/* initial setup of wps_data */
diff --git a/apps/gui/wps_debug.c b/apps/gui/wps_debug.c
new file mode 100644
index 0000000..4532151
--- /dev/null
+++ b/apps/gui/wps_debug.c
@@ -0,0 +1,407 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifdef DEBUG
+
+#include <stdio.h>
+#include <string.h>
+#include "gwps.h"
+#include "debug.h"
+
+void dump_wps_tokens(struct wps_data *data)
+{
+ int i, j;
+ int indent = 0;
+ char buf[64];
+ bool next;
+
+ /* Dump parsed WPS */
+ for(i = 0; i < data->num_tokens && i < WPS_MAX_TOKENS; i++) {
+
+ next = data->tokens[i].next;
+
+ switch(data->tokens[i].type) {
+ case WPS_TOKEN_UNKNOWN:
+ snprintf(buf, sizeof(buf), "Unknown token");
+ break;
+ case WPS_TOKEN_CHARACTER:
+ snprintf(buf, sizeof(buf), "Character '%c'",
+ data->tokens[i].value.c);
+ break;
+
+ case WPS_TOKEN_STRING:
+ snprintf(buf, sizeof(buf), "String '%s'",
+ data->strings[data->tokens[i].value.i]);
+ break;
+
+ case WPS_TOKEN_EOL:
+ snprintf(buf, sizeof(buf), "%s", "EOL");
+ break;
+
+#ifdef HAVE_LCD_BITMAP
+ case WPS_TOKEN_ALIGN_LEFT:
+ snprintf(buf, sizeof(buf), "%s", "align left");
+ break;
+
+ case WPS_TOKEN_ALIGN_CENTER:
+ snprintf(buf, sizeof(buf), "%s", "align center");
+ break;
+
+ case WPS_TOKEN_ALIGN_RIGHT:
+ snprintf(buf, sizeof(buf), "%s", "align right");
+ break;
+#endif
+
+ case WPS_TOKEN_CONDITIONAL:
+ snprintf(buf, sizeof(buf), "%s, %d options", "conditional",
+ data->tokens[i].value.i);
+ break;
+
+ case WPS_TOKEN_CONDITIONAL_START:
+ snprintf(buf, sizeof(buf), "%s, next cond: %d",
+ "conditional start", data->tokens[i].value.i);
+ indent++;
+ break;
+
+ case WPS_TOKEN_CONDITIONAL_OPTION:
+ snprintf(buf, sizeof(buf), "%s, next cond: %d",
+ "conditional option", data->tokens[i].value.i);
+ break;
+
+ case WPS_TOKEN_CONDITIONAL_END:
+ snprintf(buf, sizeof(buf), "%s", "conditional end");
+ indent--;
+ break;
+
+#ifdef HAVE_LCD_BITMAP
+ case WPS_TOKEN_IMAGE_PRELOAD:
+ snprintf(buf, sizeof(buf), "%s", "preload image");
+ break;
+
+ case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
+ snprintf(buf, sizeof(buf), "%s %d", "display preloaded image",
+ data->tokens[i].value.i);
+ break;
+
+ case WPS_TOKEN_IMAGE_DISPLAY:
+ snprintf(buf, sizeof(buf), "%s", "display image");
+ break;
+#endif
+
+#ifdef HAS_BUTTON_HOLD
+ case WPS_TOKEN_MAIN_HOLD:
+ snprintf(buf, sizeof(buf), "%s", "mode hold");
+ break;
+#endif
+
+#ifdef HAS_REMOTE_BUTTON_HOLD
+ case WPS_TOKEN_REMOTE_HOLD:
+ snprintf(buf, sizeof(buf), "%s", "mode remote hold");
+ break;
+#endif
+
+ case WPS_TOKEN_REPEAT_MODE:
+ snprintf(buf, sizeof(buf), "%s", "mode repeat");
+ break;
+
+ case WPS_TOKEN_PLAYBACK_STATUS:
+ snprintf(buf, sizeof(buf), "%s", "mode playback");
+ break;
+
+#if CONFIG_RTC
+ case WPS_TOKEN_RTC_DAY_OF_MONTH:
+ case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
+ case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
+ case WPS_TOKEN_RTC_HOUR_24:
+ case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
+ case WPS_TOKEN_RTC_HOUR_12:
+ case WPS_TOKEN_RTC_MONTH:
+ case WPS_TOKEN_RTC_MINUTE:
+ case WPS_TOKEN_RTC_SECOND:
+ case WPS_TOKEN_RTC_YEAR_2_DIGITS:
+ case WPS_TOKEN_RTC_YEAR_4_DIGITS:
+ case WPS_TOKEN_RTC_AM_PM_UPPER:
+ case WPS_TOKEN_RTC_AM_PM_LOWER:
+ case WPS_TOKEN_RTC_WEEKDAY_NAME:
+ case WPS_TOKEN_RTC_MONTH_NAME:
+ case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
+ case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
+ case WPS_TOKEN_RTC:
+ snprintf(buf, sizeof(buf), "%s %c", "real-time clock tag:",
+ data->tokens[i].value.c);
+ break;
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+ case WPS_TOKEN_IMAGE_BACKDROP:
+ snprintf(buf, sizeof(buf), "%s", "backdrop image");
+ break;
+
+ case WPS_TOKEN_IMAGE_PROGRESS_BAR:
+ snprintf(buf, sizeof(buf), "%s", "progressbar bitmap");
+ break;
+
+
+ case WPS_TOKEN_STATUSBAR_ENABLED:
+ snprintf(buf, sizeof(buf), "%s", "statusbar enable");
+ break;
+
+ case WPS_TOKEN_STATUSBAR_DISABLED:
+ snprintf(buf, sizeof(buf), "%s", "statusbar disable");
+ break;
+
+ case WPS_TOKEN_PEAKMETER:
+ snprintf(buf, sizeof(buf), "%s", "peakmeter");
+ break;
+#endif
+
+ case WPS_TOKEN_PROGRESSBAR:
+ snprintf(buf, sizeof(buf), "%s", "progressbar");
+ break;
+
+#ifdef HAVE_LCD_CHARCELLS
+ case WPS_TOKEN_PLAYER_PROGRESSBAR:
+ snprintf(buf, sizeof(buf), "%s", "full line progressbar");
+ break;
+#endif
+
+ case WPS_TOKEN_TRACK_TIME_ELAPSED:
+ snprintf(buf, sizeof(buf), "%s", "time elapsed in track");
+ break;
+
+ case WPS_TOKEN_PLAYLIST_ENTRIES:
+ snprintf(buf, sizeof(buf), "%s", "number of entries in playlist");
+ break;
+
+ case WPS_TOKEN_PLAYLIST_NAME:
+ snprintf(buf, sizeof(buf), "%s", "playlist name");
+ break;
+
+ case WPS_TOKEN_PLAYLIST_POSITION:
+ snprintf(buf, sizeof(buf), "%s", "position in playlist");
+ break;
+
+ case WPS_TOKEN_TRACK_TIME_REMAINING:
+ snprintf(buf, sizeof(buf), "%s", "time remaining in track");
+ break;
+
+ case WPS_TOKEN_PLAYLIST_SHUFFLE:
+ snprintf(buf, sizeof(buf), "%s", "playlist shuffle mode");
+ break;
+
+ case WPS_TOKEN_TRACK_LENGTH:
+ snprintf(buf, sizeof(buf), "%s", "track length");
+ break;
+
+ case WPS_TOKEN_VOLUME:
+ snprintf(buf, sizeof(buf), "%s", "volume");
+ break;
+
+ case WPS_TOKEN_METADATA_ARTIST:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track artist");
+ break;
+
+ case WPS_TOKEN_METADATA_COMPOSER:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track composer");
+ break;
+
+ case WPS_TOKEN_METADATA_ALBUM:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track album");
+ break;
+
+ case WPS_TOKEN_METADATA_GENRE:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track genre");
+ break;
+
+ case WPS_TOKEN_METADATA_TRACK_NUMBER:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track number");
+ break;
+
+ case WPS_TOKEN_METADATA_TRACK_TITLE:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track title");
+ break;
+
+ case WPS_TOKEN_METADATA_VERSION:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track ID3 version");
+ break;
+
+ case WPS_TOKEN_METADATA_YEAR:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "track year");
+ break;
+
+ case WPS_TOKEN_BATTERY_PERCENT:
+ snprintf(buf, sizeof(buf), "%s", "battery percentage");
+ break;
+
+ case WPS_TOKEN_BATTERY_VOLTS:
+ snprintf(buf, sizeof(buf), "%s", "battery voltage");
+ break;
+
+ case WPS_TOKEN_BATTERY_TIME:
+ snprintf(buf, sizeof(buf), "%s", "battery time left");
+ break;
+
+ case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
+ snprintf(buf, sizeof(buf), "%s", "battery charger connected");
+ break;
+
+ case WPS_TOKEN_BATTERY_CHARGING:
+ snprintf(buf, sizeof(buf), "%s", "battery charging");
+ break;
+
+ case WPS_TOKEN_FILE_BITRATE:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file bitrate");
+ break;
+
+ case WPS_TOKEN_FILE_CODEC:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file codec");
+ break;
+
+ case WPS_TOKEN_FILE_FREQUENCY:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file audio frequency");
+ break;
+
+ case WPS_TOKEN_FILE_NAME:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file name");
+ break;
+
+ case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file name with extension");
+ break;
+
+ case WPS_TOKEN_FILE_PATH:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file path");
+ break;
+
+ case WPS_TOKEN_FILE_SIZE:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file size");
+ break;
+
+ case WPS_TOKEN_FILE_VBR:
+ snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+ "file is vbr");
+ break;
+
+ case WPS_TOKEN_FILE_DIRECTORY:
+ snprintf(buf, sizeof(buf), "%s%s: %d", next ? "next " : "",
+ "file directory, level",
+ data->tokens[i].value.i);
+ break;
+
+ case WPS_TOKEN_SCROLL:
+ snprintf(buf, sizeof(buf), "%s", "scrolling line");
+ break;
+
+ case WPS_TOKEN_SUBLINE_TIMEOUT:
+ snprintf(buf, sizeof(buf), "%s: %d", "subline timeout value",
+ data->tokens[i].value.i);
+ break;
+
+ case WPS_TOKEN_SUBLINE_SEPARATOR:
+ snprintf(buf, sizeof(buf), "%s", "subline separator");
+ break;
+
+ default:
+ snprintf(buf, sizeof(buf), "%s (code: %d)", "FIXME",
+ data->tokens[i].type);
+ break;
+ }
+
+ for(j = 0; j < indent; j++) {
+ DEBUGF("\t");
+ }
+
+ DEBUGF("[%03d] = %s\n", i, buf);
+ }
+ DEBUGF("\n");
+}
+
+void print_line_info(struct wps_data *data)
+{
+ int line, subline;
+
+ DEBUGF("line/subline start indexes :\n");
+ for (line = 0; line < data->num_lines; line++)
+ {
+ DEBUGF("%2d. ", line);
+ for (subline = 0; subline < data->num_sublines[line]; subline++)
+ {
+ DEBUGF("%3d ", data->format_lines[line][subline]);
+ }
+ DEBUGF("\n");
+ }
+
+ DEBUGF("\n");
+
+ DEBUGF("subline time multipliers :\n");
+ for (line = 0; line < data->num_lines; line++)
+ {
+ DEBUGF("%2d. ", line);
+ for (subline = 0; subline < data->num_sublines[line]; subline++)
+ {
+ DEBUGF("%3d ", data->time_mult[line][subline]);
+ }
+ DEBUGF("\n");
+ }
+
+}
+
+void print_wps_strings(struct wps_data *data)
+{
+ DEBUGF("strings :\n");
+ int i, len = 0;
+ for (i=0; i < data->num_strings; i++)
+ {
+ len += strlen(data->strings[i]);
+ DEBUGF("%2d: '%s'\n", i, data->strings[i]);
+ }
+ DEBUGF("total length : %d\n", len);
+ DEBUGF("\n");
+}
+
+#ifdef HAVE_LCD_BITMAP
+void print_img_cond_indexes(struct wps_data *data)
+{
+ DEBUGF("image conditional indexes :\n");
+ int i;
+ for (i=0; i < MAX_IMAGES; i++)
+ {
+ if (data->img[i].cond_index)
+ DEBUGF("%2d: %d\n", i, data->img[i].cond_index);
+ }
+ DEBUGF("\n");
+}
+#endif /*HAVE_LCD_BITMAP */
+
+#endif /* DEBUG */
diff --git a/apps/gui/wps_parser.c b/apps/gui/wps_parser.c
new file mode 100644
index 0000000..ef9d446
--- /dev/null
+++ b/apps/gui/wps_parser.c
@@ -0,0 +1,957 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "atoi.h"
+#include "gwps.h"
+#include "settings.h"
+#include "debug.h"
+#include "plugin.h"
+
+#ifdef HAVE_LCD_BITMAP
+#include "bmp.h"
+#if LCD_DEPTH > 1
+#include "backdrop.h"
+#endif
+#endif
+
+#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
+#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
+
+/* level of current conditional.
+ -1 means we're not in a conditional. */
+int level = -1;
+
+/* index of the last WPS_TOKEN_CONDITIONAL_OPTION
+ or WPS_TOKEN_CONDITIONAL_START in current level */
+int lastcond[WPS_MAX_COND_LEVEL];
+
+/* index of the WPS_TOKEN_CONDITIONAL in current level */
+int condindex[WPS_MAX_COND_LEVEL];
+
+/* number of condtional options in current level */
+int numoptions[WPS_MAX_COND_LEVEL];
+
+#ifdef HAVE_LCD_BITMAP
+/* pointers to the bitmap filenames in the WPS source */
+const char *bmp_names[MAX_IMAGES];
+const char *pb_bmp_name;
+#if LCD_DEPTH > 1
+const char *backdrop_bmp_name;
+#endif
+#endif
+
+#ifdef DEBUG
+/* debugging functions */
+extern void dump_wps_tokens(struct wps_data *data);
+extern void print_line_info(struct wps_data *data);
+extern void print_img_cond_indexes(struct wps_data *data);
+extern void print_wps_strings(struct wps_data *data);
+#endif
+
+typedef int (*wps_tag_parse_func)(const char *wps_token, struct wps_data *wps_data);
+
+struct wps_tag {
+ const char name[3];
+ enum wps_token_type type;
+ unsigned char refresh_type;
+ wps_tag_parse_func parse_func;
+};
+
+/* prototypes of all special parse functions : */
+
+static int parse_subline_timeout(const char *wps_token, struct wps_data *wps_data);
+static int parse_progressbar(const char *wps_token, struct wps_data *wps_data);
+static int parse_dir_level(const char *wps_token, struct wps_data *wps_data);
+#ifdef HAVE_LCD_BITMAP
+static int parse_image_special(const char *wps_token, struct wps_data *wps_data);
+static int parse_statusbar(const char *wps_token, struct wps_data *wps_data);
+static int parse_image_display(const char *wps_token, struct wps_data *wps_data);
+static int parse_image_load(const char *wps_token, struct wps_data *wps_data);
+#endif /*HAVE_LCD_BITMAP */
+#if CONFIG_RTC
+static int parse_rtc_format(const char *wps_token, struct wps_data *wps_data);
+
+/* RTC tokens array */
+static const struct wps_tag rtc_tags[] = {
+ { "d", WPS_TOKEN_RTC_DAY_OF_MONTH, 0, NULL },
+ { "e", WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, 0, NULL },
+ { "H", WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, 0, NULL },
+ { "k", WPS_TOKEN_RTC_HOUR_24, 0, NULL },
+ { "I", WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, 0, NULL },
+ { "l", WPS_TOKEN_RTC_HOUR_12, 0, NULL },
+ { "m", WPS_TOKEN_RTC_MONTH, 0, NULL },
+ { "M", WPS_TOKEN_RTC_MINUTE, 0, NULL },
+ { "S", WPS_TOKEN_RTC_SECOND, 0, NULL },
+ { "y", WPS_TOKEN_RTC_YEAR_2_DIGITS, 0, NULL },
+ { "Y", WPS_TOKEN_RTC_YEAR_4_DIGITS, 0, NULL },
+ { "p", WPS_TOKEN_RTC_AM_PM_UPPER, 0, NULL },
+ { "P", WPS_TOKEN_RTC_AM_PM_LOWER, 0, NULL },
+ { "a", WPS_TOKEN_RTC_WEEKDAY_NAME, 0, NULL },
+ { "b", WPS_TOKEN_RTC_MONTH_NAME, 0, NULL },
+ { "u", WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, 0, NULL },
+ { "w", WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, 0, NULL },
+ { "\0",WPS_TOKEN_CHARACTER, 0, NULL }
+ /* the array MUST end with a "\0" token */
+};
+#endif
+
+/* array of available tags - those with more characters have to go first
+ (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
+static const struct wps_tag all_tags[] = {
+
+ { "ac", WPS_TOKEN_ALIGN_CENTER, 0, NULL },
+ { "al", WPS_TOKEN_ALIGN_LEFT, 0, NULL },
+ { "ar", WPS_TOKEN_ALIGN_RIGHT, 0, NULL },
+
+ { "bl", WPS_TOKEN_BATTERY_PERCENT, WPS_REFRESH_DYNAMIC, NULL },
+ { "bv", WPS_TOKEN_BATTERY_VOLTS, WPS_REFRESH_DYNAMIC, NULL },
+ { "bt", WPS_TOKEN_BATTERY_TIME, WPS_REFRESH_DYNAMIC, NULL },
+ { "bs", WPS_TOKEN_BATTERY_SLEEPTIME, WPS_REFRESH_DYNAMIC, NULL },
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ { "bc", WPS_TOKEN_BATTERY_CHARGING, WPS_REFRESH_DYNAMIC, NULL },
+#endif
+#if CONFIG_CHARGING
+ { "bp", WPS_TOKEN_BATTERY_CHARGER_CONNECTED,WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+#if CONFIG_RTC
+ { "c", WPS_TOKEN_RTC, WPS_REFRESH_DYNAMIC, parse_rtc_format },
+#endif
+
+ /* current file */
+ { "fb", WPS_TOKEN_FILE_BITRATE, WPS_REFRESH_STATIC, NULL },
+ { "fc", WPS_TOKEN_FILE_CODEC, WPS_REFRESH_STATIC, NULL },
+ { "ff", WPS_TOKEN_FILE_FREQUENCY, WPS_REFRESH_STATIC, NULL },
+ { "fm", WPS_TOKEN_FILE_NAME_WITH_EXTENSION, WPS_REFRESH_STATIC, NULL },
+ { "fn", WPS_TOKEN_FILE_NAME, WPS_REFRESH_STATIC, NULL },
+ { "fp", WPS_TOKEN_FILE_PATH, WPS_REFRESH_STATIC, NULL },
+ { "fs", WPS_TOKEN_FILE_SIZE, WPS_REFRESH_STATIC, NULL },
+ { "fv", WPS_TOKEN_FILE_VBR, WPS_REFRESH_STATIC, NULL },
+ { "d", WPS_TOKEN_FILE_DIRECTORY, WPS_REFRESH_STATIC, parse_dir_level },
+
+ /* next file */
+ { "Fb", WPS_TOKEN_FILE_BITRATE, WPS_REFRESH_DYNAMIC, NULL },
+ { "Fc", WPS_TOKEN_FILE_CODEC, WPS_REFRESH_DYNAMIC, NULL },
+ { "Ff", WPS_TOKEN_FILE_FREQUENCY, WPS_REFRESH_DYNAMIC, NULL },
+ { "Fm", WPS_TOKEN_FILE_NAME_WITH_EXTENSION, WPS_REFRESH_DYNAMIC, NULL },
+ { "Fn", WPS_TOKEN_FILE_NAME, WPS_REFRESH_DYNAMIC, NULL },
+ { "Fp", WPS_TOKEN_FILE_PATH, WPS_REFRESH_DYNAMIC, NULL },
+ { "Fs", WPS_TOKEN_FILE_SIZE, WPS_REFRESH_DYNAMIC, NULL },
+ { "Fv", WPS_TOKEN_FILE_VBR, WPS_REFRESH_DYNAMIC, NULL },
+ { "D", WPS_TOKEN_FILE_DIRECTORY, WPS_REFRESH_DYNAMIC,parse_dir_level },
+
+ /* current metadata */
+ { "ia", WPS_TOKEN_METADATA_ARTIST, WPS_REFRESH_STATIC, NULL },
+ { "ic", WPS_TOKEN_METADATA_COMPOSER, WPS_REFRESH_STATIC, NULL },
+ { "id", WPS_TOKEN_METADATA_ALBUM, WPS_REFRESH_STATIC, NULL },
+ { "iA", WPS_TOKEN_METADATA_ALBUM_ARTIST, WPS_REFRESH_STATIC, NULL },
+ { "ig", WPS_TOKEN_METADATA_GENRE, WPS_REFRESH_STATIC, NULL },
+ { "in", WPS_TOKEN_METADATA_TRACK_NUMBER, WPS_REFRESH_STATIC, NULL },
+ { "it", WPS_TOKEN_METADATA_TRACK_TITLE, WPS_REFRESH_STATIC, NULL },
+ { "iv", WPS_TOKEN_METADATA_VERSION, WPS_REFRESH_STATIC, NULL },
+ { "iy", WPS_TOKEN_METADATA_YEAR, WPS_REFRESH_STATIC, NULL },
+ { "iC", WPS_TOKEN_METADATA_COMMENT, WPS_REFRESH_DYNAMIC, NULL },
+
+ /* next metadata */
+ { "Ia", WPS_TOKEN_METADATA_ARTIST, WPS_REFRESH_DYNAMIC, NULL },
+ { "Ic", WPS_TOKEN_METADATA_COMPOSER, WPS_REFRESH_DYNAMIC, NULL },
+ { "Id", WPS_TOKEN_METADATA_ALBUM, WPS_REFRESH_DYNAMIC, NULL },
+ { "IA", WPS_TOKEN_METADATA_ALBUM_ARTIST, WPS_REFRESH_STATIC, NULL },
+ { "Ig", WPS_TOKEN_METADATA_GENRE, WPS_REFRESH_DYNAMIC, NULL },
+ { "In", WPS_TOKEN_METADATA_TRACK_NUMBER, WPS_REFRESH_DYNAMIC, NULL },
+ { "It", WPS_TOKEN_METADATA_TRACK_TITLE, WPS_REFRESH_DYNAMIC, NULL },
+ { "Iv", WPS_TOKEN_METADATA_VERSION, WPS_REFRESH_DYNAMIC, NULL },
+ { "Iy", WPS_TOKEN_METADATA_YEAR, WPS_REFRESH_DYNAMIC, NULL },
+ { "IC", WPS_TOKEN_METADATA_COMMENT, WPS_REFRESH_DYNAMIC, NULL },
+
+#if (CONFIG_CODEC == SWCODEC)
+ { "Sp", WPS_TOKEN_SOUND_PITCH, WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
+ { "lh", WPS_TOKEN_VLED_HDD, WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+#ifdef HAS_BUTTON_HOLD
+ { "mh", WPS_TOKEN_MAIN_HOLD, WPS_REFRESH_DYNAMIC, NULL },
+#endif
+#ifdef HAS_REMOTE_BUTTON_HOLD
+ { "mr", WPS_TOKEN_REMOTE_HOLD, WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+ { "mm", WPS_TOKEN_REPEAT_MODE, WPS_REFRESH_DYNAMIC, NULL },
+ { "mp", WPS_TOKEN_PLAYBACK_STATUS, WPS_REFRESH_DYNAMIC, NULL },
+
+#ifdef HAVE_LCD_BITMAP
+ { "pm", WPS_TOKEN_PEAKMETER,
+ WPS_REFRESH_PEAK_METER, NULL },
+#else
+ { "pf", WPS_TOKEN_PLAYER_PROGRESSBAR,
+ WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS,
+ parse_progressbar },
+#endif
+ { "pb", WPS_TOKEN_PROGRESSBAR,
+ WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
+
+ { "pv", WPS_TOKEN_VOLUME, WPS_REFRESH_DYNAMIC, NULL },
+
+ { "pc", WPS_TOKEN_TRACK_TIME_ELAPSED, WPS_REFRESH_DYNAMIC, NULL },
+ { "pr", WPS_TOKEN_TRACK_TIME_REMAINING, WPS_REFRESH_DYNAMIC, NULL },
+ { "pt", WPS_TOKEN_TRACK_LENGTH, WPS_REFRESH_STATIC, NULL },
+
+ { "pp", WPS_TOKEN_PLAYLIST_POSITION, WPS_REFRESH_STATIC, NULL },
+ { "pe", WPS_TOKEN_PLAYLIST_ENTRIES, WPS_REFRESH_STATIC, NULL },
+ { "pn", WPS_TOKEN_PLAYLIST_NAME, WPS_REFRESH_STATIC, NULL },
+ { "ps", WPS_TOKEN_PLAYLIST_SHUFFLE, WPS_REFRESH_DYNAMIC, NULL },
+
+ { "rp", WPS_TOKEN_DATABASE_PLAYCOUNT, WPS_REFRESH_DYNAMIC, NULL },
+ { "rr", WPS_TOKEN_DATABASE_RATING, WPS_REFRESH_DYNAMIC, NULL },
+#if CONFIG_CODEC == SWCODEC
+ { "rg", WPS_TOKEN_REPLAYGAIN, WPS_REFRESH_STATIC, NULL },
+#endif
+
+ { "s", WPS_TOKEN_SCROLL, WPS_REFRESH_SCROLL, NULL },
+ { "t", WPS_TOKEN_SUBLINE_TIMEOUT, 0, parse_subline_timeout },
+
+#ifdef HAVE_LCD_BITMAP
+ { "we", WPS_TOKEN_STATUSBAR_ENABLED, 0, parse_statusbar },
+ { "wd", WPS_TOKEN_STATUSBAR_DISABLED, 0, parse_statusbar },
+
+ { "xl", WPS_NO_TOKEN, 0, parse_image_load },
+
+ { "xd", WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,
+ WPS_REFRESH_STATIC, parse_image_display },
+
+ { "x", WPS_TOKEN_IMAGE_DISPLAY, 0, parse_image_load },
+ { "P", WPS_TOKEN_IMAGE_PROGRESS_BAR, 0, parse_image_special },
+#if LCD_DEPTH > 1
+ { "X", WPS_TOKEN_IMAGE_BACKDROP, 0, parse_image_special },
+#endif
+#endif
+
+ { "\0", WPS_TOKEN_UNKNOWN, 0, NULL }
+ /* the array MUST end with a "\0" token */
+};
+
+
+static int skip_end_of_line(const char *wps_token)
+{
+ int skip = 0;
+ while(*(wps_token + skip) != '\n')
+ skip++;
+ return ++skip;
+}
+
+#if CONFIG_RTC
+static int parse_rtc_format(const char *wps_token, struct wps_data *wps_data)
+{
+ int skip = 0, i;
+
+ /* RTC tag format ends with a c or a newline */
+ while (wps_token && *wps_token != 'c' && *wps_token != '\n')
+ {
+ /* find what format char we have */
+ i = 0;
+ while (*(rtc_tags[i].name) && *wps_token != *(rtc_tags[i].name))
+ i++;
+
+ wps_data->num_tokens++;
+ wps_data->tokens[wps_data->num_tokens].type = rtc_tags[i].type;
+ wps_data->tokens[wps_data->num_tokens].value.c = *wps_token;
+ skip ++;
+ wps_token++;
+ }
+
+ /* eat the unwanted c at the end of the format */
+ if (*wps_token == 'c')
+ skip++;
+
+ return skip;
+}
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+
+static int parse_statusbar(const char *wps_token, struct wps_data *wps_data)
+{
+ wps_data->wps_sb_tag = true;
+
+ if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_STATUSBAR_ENABLED)
+ wps_data->show_sb_on_wps = true;
+ else
+ wps_data->show_sb_on_wps = false;
+
+ /* Skip the rest of the line */
+ return skip_end_of_line(wps_token);
+}
+
+static bool load_bitmap(struct wps_data *wps_data,
+ char* filename,
+ struct bitmap *bm)
+{
+ int ret = read_bmp_file(filename, bm,
+ wps_data->img_buf_free,
+ FORMAT_ANY|FORMAT_TRANSPARENT);
+
+ if (ret > 0)
+ {
+#if LCD_DEPTH == 16
+ if (ret % 2) ret++;
+ /* Always consume an even number of bytes */
+#endif
+ wps_data->img_buf_ptr += ret;
+ wps_data->img_buf_free -= ret;
+
+ return true;
+ }
+ else
+ return false;
+}
+
+static int get_image_id(int c)
+{
+ if(c >= 'a' && c <= 'z')
+ return c - 'a';
+ else if(c >= 'A' && c <= 'Z')
+ return c - 'A' + 26;
+ else
+ return -1;
+}
+
+static char *get_image_filename(const char *start, const char* bmpdir,
+ char *buf, int buf_size)
+{
+ const char *end = strchr(start, '|');
+
+ if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
+ {
+ buf = "\0";
+ return NULL;
+ }
+
+ int bmpdirlen = strlen(bmpdir);
+
+ strcpy(buf, bmpdir);
+ buf[bmpdirlen] = '/';
+ memcpy( &buf[bmpdirlen + 1], start, end - start);
+ buf[bmpdirlen + 1 + end - start] = 0;
+
+ return buf;
+}
+
+static int parse_image_display(const char *wps_token, struct wps_data *wps_data)
+{
+ int n = get_image_id(*wps_token);
+ wps_data->tokens[wps_data->num_tokens].value.i = n;
+
+ /* if the image is in a conditional, remember it */
+ if (level >= 0)
+ wps_data->img[n].cond_index = condindex[level];
+
+ return 1;
+}
+
+static int parse_image_load(const char *wps_token, struct wps_data *wps_data)
+{
+ int n;
+ const char *ptr = wps_token;
+ char *pos = NULL;
+
+ /* format: %x|n|filename.bmp|x|y|
+ or %xl|n|filename.bmp|x|y| */
+
+ ptr = strchr(ptr, '|') + 1;
+ pos = strchr(ptr, '|');
+ if (pos)
+ {
+ /* get the image ID */
+ n = get_image_id(*ptr);
+
+ /* check the image number and load state */
+ if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
+ {
+ /* Skip the rest of the line */
+ return skip_end_of_line(wps_token);
+ }
+
+ ptr = pos + 1;
+
+ /* get image name */
+ bmp_names[n] = ptr;
+
+ pos = strchr(ptr, '|');
+ ptr = pos + 1;
+
+ /* get x-position */
+ pos = strchr(ptr, '|');
+ if (pos)
+ wps_data->img[n].x = atoi(ptr);
+ else
+ {
+ /* weird syntax, bail out */
+ return skip_end_of_line(wps_token);
+ }
+
+ /* get y-position */
+ ptr = pos + 1;
+ pos = strchr(ptr, '|');
+ if (pos)
+ wps_data->img[n].y = atoi(ptr);
+ else
+ {
+ /* weird syntax, bail out */
+ return skip_end_of_line(wps_token);
+ }
+
+ if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_IMAGE_DISPLAY)
+ wps_data->img[n].always_display = true;
+ }
+
+ /* Skip the rest of the line */
+ return skip_end_of_line(wps_token);
+}
+
+static int parse_image_special(const char *wps_token, struct wps_data *wps_data)
+{
+ if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_IMAGE_PROGRESS_BAR)
+ {
+ /* format: %P|filename.bmp| */
+ pb_bmp_name = wps_token + 1;
+ }
+#if LCD_DEPTH > 1
+ else if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_IMAGE_BACKDROP)
+ {
+ /* format: %X|filename.bmp| */
+ backdrop_bmp_name = wps_token + 1;
+ }
+#endif
+
+ (void)wps_data; /* to avoid a warning */
+
+ /* Skip the rest of the line */
+ return skip_end_of_line(wps_token);
+}
+
+#endif /* HAVE_LCD_BITMAP */
+
+static int parse_dir_level(const char *wps_token, struct wps_data *wps_data)
+{
+ char val[] = { *wps_token, '\0' };
+ wps_data->tokens[wps_data->num_tokens].value.i = atoi(val);
+ return 1;
+}
+
+static int parse_subline_timeout(const char *wps_token, struct wps_data *wps_data)
+{
+ int skip = 0;
+ int val = 0;
+ bool have_point = false;
+ bool have_tenth = false;
+
+ while ( isdigit(*wps_token) || *wps_token == '.' )
+ {
+ if (*wps_token != '.')
+ {
+ val *= 10;
+ val += *wps_token - '0';
+ if (have_point)
+ {
+ have_tenth = true;
+ wps_token++;
+ skip++;
+ break;
+ }
+ }
+ else
+ have_point = true;
+
+ wps_token++;
+ skip++;
+ }
+
+ if (have_tenth == false)
+ val *= 10;
+
+ if (val > 0)
+ {
+ int line = wps_data->num_lines;
+ int subline = wps_data->num_sublines[line];
+ wps_data->time_mult[line][subline] = val;
+ }
+
+ wps_data->tokens[wps_data->num_tokens].value.i = val;
+ return skip;
+}
+
+static int parse_progressbar(const char *wps_token, struct wps_data *wps_data)
+{
+#ifdef HAVE_LCD_BITMAP
+
+ short *vals[] = {
+ &wps_data->progress_height,
+ &wps_data->progress_start,
+ &wps_data->progress_end,
+ &wps_data->progress_top };
+
+ /* default values : */
+ wps_data->progress_height = 6;
+ wps_data->progress_start = 0;
+ wps_data->progress_end = 0;
+ wps_data->progress_top = -1;
+
+ int i = 0;
+ char *newline = strchr(wps_token, '\n');
+ char *prev = strchr(wps_token, '|');
+ if (prev && prev < newline) {
+ char *next = strchr(prev+1, '|');
+ while (i < 4 && next && next < newline)
+ {
+ *(vals[i++]) = atoi(++prev);
+ prev = strchr(prev, '|');
+ next = strchr(++next, '|');
+ }
+
+ if (wps_data->progress_height < 3)
+ wps_data->progress_height = 3;
+ if (wps_data->progress_end < wps_data->progress_start + 3)
+ wps_data->progress_end = 0;
+ }
+
+ return newline - wps_token;
+
+#else
+
+ if (*(wps_token-1) == 'f')
+ wps_data->full_line_progressbar = true;
+ else
+ wps_data->full_line_progressbar = false;
+
+ return 0;
+
+#endif
+}
+
+/* Parse a generic token from the given string. Return the length read */
+static int parse_token(const char *wps_token, struct wps_data *wps_data)
+{
+ int skip = 0, taglen = 0;
+ int i = 0;
+ int line = wps_data->num_lines;
+ int subline = wps_data->num_sublines[line];
+
+ switch(*wps_token)
+ {
+
+ case '%':
+ case '<':
+ case '|':
+ case '>':
+ case ';':
+ /* escaped characters */
+ wps_data->tokens[wps_data->num_tokens].type = WPS_TOKEN_CHARACTER;
+ wps_data->tokens[wps_data->num_tokens].value.c = *wps_token;
+ wps_data->num_tokens++;
+ skip++;
+ break;
+
+ case '?':
+ /* conditional tag */
+ wps_data->tokens[wps_data->num_tokens].type = WPS_TOKEN_CONDITIONAL;
+ level++;
+ condindex[level] = wps_data->num_tokens;
+ numoptions[level] = 1;
+ wps_data->num_tokens++;
+ wps_token++;
+ skip++;
+ /* no "break" because a '?' is followed by a regular tag */
+
+ default:
+ /* find what tag we have */
+ while (all_tags[i].name &&
+ strncmp(wps_token, all_tags[i].name, strlen(all_tags[i].name)))
+ i++;
+
+ taglen = strlen(all_tags[i].name);
+ skip += taglen;
+ wps_data->tokens[wps_data->num_tokens].type = all_tags[i].type;
+
+ /* if the tag has a special parsing function, we call it */
+ if (all_tags[i].parse_func)
+ skip += all_tags[i].parse_func(wps_token + taglen, wps_data);
+
+ /* Some tags we don't want to save as tokens */
+ if (all_tags[i].type == WPS_NO_TOKEN)
+ break;
+
+ /* tags that start with 'F', 'I' or 'D' are for the next file */
+ if ( *(all_tags[i].name) == 'I' || *(all_tags[i].name) == 'F'
+ || *(all_tags[i].name) == 'D')
+ wps_data->tokens[wps_data->num_tokens].next = true;
+
+ wps_data->line_type[line][subline] |= all_tags[i].refresh_type;
+ wps_data->num_tokens++;
+ break;
+ }
+
+ return skip;
+}
+
+static bool wps_parse(struct wps_data *data, const char *wps_buffer)
+{
+ if (!data || !wps_buffer || !*wps_buffer)
+ return false;
+
+ int subline;
+ data->num_tokens = 0;
+ char *current_string = data->string_buffer;
+
+ while(wps_buffer && *wps_buffer && data->num_tokens < WPS_MAX_TOKENS
+ && data->num_lines < WPS_MAX_LINES)
+ {
+ switch(*wps_buffer++)
+ {
+
+ /* Regular tag */
+ case '%':
+ wps_buffer += parse_token(wps_buffer, data);
+ break;
+
+ /* Alternating sublines separator */
+ case ';':
+ if (data->num_sublines[data->num_lines]+1 < WPS_MAX_SUBLINES)
+ {
+ data->tokens[data->num_tokens++].type = WPS_TOKEN_SUBLINE_SEPARATOR;
+ subline = ++(data->num_sublines[data->num_lines]);
+ data->format_lines[data->num_lines][subline] = data->num_tokens;
+ }
+ else
+ wps_buffer += skip_end_of_line(wps_buffer);
+
+ break;
+
+ /* Conditional list start */
+ case '<':
+ data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
+ lastcond[level] = data->num_tokens++;
+ break;
+
+ /* Conditional list end */
+ case '>':
+ if (level < 0) /* not in a conditional, ignore the char */
+ break;
+
+condlistend: /* close a conditional. sometimes we want to close them even when
+ we don't have a closing token, e.g. at the end of a line. */
+
+ data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
+ if (lastcond[level])
+ data->tokens[lastcond[level]].value.i = data->num_tokens;
+
+ lastcond[level] = 0;
+ data->num_tokens++;
+ data->tokens[condindex[level]].value.i = numoptions[level];
+ level--;
+ break;
+
+ /* Conditional list option */
+ case '|':
+ if (level < 0) /* not in a conditional, ignore the char */
+ break;
+
+ data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
+ if (lastcond[level])
+ data->tokens[lastcond[level]].value.i = data->num_tokens;
+
+ lastcond[level] = data->num_tokens;
+ numoptions[level]++;
+ data->num_tokens++;
+ break;
+
+ /* Comment */
+ case '#':
+ wps_buffer += skip_end_of_line(wps_buffer);
+ break;
+
+ /* End of this line */
+ case '\n':
+ if (level >= 0)
+ {
+ /* We have unclosed conditionals, so we
+ close them before adding the EOL token */
+ wps_buffer--;
+ goto condlistend;
+ break;
+ }
+ data->tokens[data->num_tokens++].type = WPS_TOKEN_EOL;
+ (data->num_sublines[data->num_lines])++;
+ data->num_lines++;
+
+ if (data->num_lines < WPS_MAX_LINES)
+ {
+ data->format_lines[data->num_lines][0] = data->num_tokens;
+ }
+
+ break;
+
+ /* String */
+ default:
+ if (data->num_strings < WPS_MAX_STRINGS)
+ {
+ data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
+ data->strings[data->num_strings] = current_string;
+ data->tokens[data->num_tokens].value.i = data->num_strings++;
+ data->num_tokens++;
+
+ /* Copy the first byte */
+ *current_string++ = *(wps_buffer - 1);
+
+ /* continue until we hit something that ends the string */
+ while(wps_buffer &&
+ *wps_buffer != '%' && //*wps_buffer != '#' &&
+ *wps_buffer != '<' && *wps_buffer != '>' &&
+ *wps_buffer != '|' && *wps_buffer != '\n')
+ {
+ *current_string++ = *wps_buffer++;
+ }
+
+ /* null terminate the string */
+ *current_string++ = '\0';
+ }
+
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ /* debugging code */
+ if (false)
+ {
+ dump_wps_tokens(data);
+ print_line_info(data);
+ print_wps_strings(data);
+#ifdef HAVE_LCD_BITMAP
+ print_img_cond_indexes(data);
+#endif
+ }
+#endif
+
+ return true;
+}
+
+#ifdef HAVE_LCD_BITMAP
+/* Clear the WPS image cache */
+static void wps_images_clear(struct wps_data *data)
+{
+ int i;
+ /* set images to unloaded and not displayed */
+ for (i = 0; i < MAX_IMAGES; i++)
+ {
+ data->img[i].loaded = false;
+ data->img[i].display = false;
+ data->img[i].always_display = false;
+ }
+ data->progressbar.have_bitmap_pb = false;
+}
+#endif
+
+/* initial setup of wps_data */
+void wps_data_init(struct wps_data *wps_data)
+{
+#ifdef HAVE_LCD_BITMAP
+ wps_images_clear(wps_data);
+ wps_data->wps_sb_tag = false;
+ wps_data->show_sb_on_wps = false;
+ wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
+ wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
+ wps_data->peak_meter_enabled = false;
+#else /* HAVE_LCD_CHARCELLS */
+ int i;
+ for(i = 0; i < 8; i++)
+ {
+ wps_data->wps_progress_pat[i] = 0;
+ }
+ wps_data->full_line_progressbar = false;
+#endif
+ wps_data->wps_loaded = false;
+}
+
+static void wps_reset(struct wps_data *data)
+{
+ memset(data, 0, sizeof(*data));
+ data->wps_loaded = false;
+ wps_data_init(data);
+}
+
+#ifdef HAVE_LCD_BITMAP
+
+
+static void clear_bmp_names(void)
+{
+ int n;
+ for (n = 0; n < MAX_IMAGES; n++)
+ {
+ bmp_names[n] = NULL;
+ }
+ pb_bmp_name = NULL;
+#if LCD_DEPTH > 1
+ backdrop_bmp_name = NULL;
+#endif
+}
+
+static void load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
+{
+ char img_path[MAX_PATH];
+
+ int n;
+ for (n = 0; n < MAX_IMAGES; n++)
+ {
+ if (bmp_names[n])
+ {
+ get_image_filename(bmp_names[n], bmpdir,
+ img_path, sizeof(img_path));
+
+ /* load the image */
+ wps_data->img[n].bm.data = wps_data->img_buf_ptr;
+ if (load_bitmap(wps_data, img_path, &wps_data->img[n].bm))
+ {
+ wps_data->img[n].loaded = true;
+ }
+ }
+ }
+
+ if (pb_bmp_name)
+ {
+ get_image_filename(pb_bmp_name, bmpdir, img_path, sizeof(img_path));
+
+ /* load the image */
+ wps_data->progressbar.bm.data = wps_data->img_buf_ptr;
+ if (load_bitmap(wps_data, img_path, &wps_data->progressbar.bm)
+ && wps_data->progressbar.bm.width <= LCD_WIDTH)
+ {
+ wps_data->progressbar.have_bitmap_pb = true;
+ }
+ }
+
+#if LCD_DEPTH > 1
+ if (backdrop_bmp_name)
+ {
+ get_image_filename(backdrop_bmp_name, bmpdir,
+ img_path, sizeof(img_path));
+ load_wps_backdrop(img_path);
+ }
+#endif
+}
+
+#endif /* HAVE_LCD_BITMAP */
+
+/* to setup up the wps-data from a format-buffer (isfile = false)
+ from a (wps-)file (isfile = true)*/
+bool wps_data_load(struct wps_data *wps_data,
+ const char *buf,
+ bool isfile)
+{
+ if (!wps_data || !buf)
+ return false;
+
+ wps_reset(wps_data);
+
+ if (!isfile)
+ {
+ return wps_parse(wps_data, buf);
+ }
+ else
+ {
+ /*
+ * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
+ * wants to be a virtual file. Feel free to modify dirbrowse()
+ * if you're feeling brave.
+ */
+ if (! strcmp(buf, WPS_DEFAULTCFG) )
+ {
+ global_settings.wps_file[0] = 0;
+ return false;
+ }
+
+#ifdef HAVE_REMOTE_LCD
+ if (! strcmp(buf, RWPS_DEFAULTCFG) )
+ {
+ global_settings.rwps_file[0] = 0;
+ return false;
+ }
+#endif
+
+ int fd = open(buf, O_RDONLY);
+
+ if (fd < 0)
+ return false;
+
+ /* get buffer space from the plugin buffer */
+ unsigned int buffersize = 0;
+ char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
+
+ if (!wps_buffer)
+ return false;
+
+ /* copy the file's content to the buffer for parsing */
+ unsigned int start = 0;
+ while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
+ {
+ start += strlen(wps_buffer + start);
+ if (start < buffersize - 1)
+ {
+ wps_buffer[start++] = '\n';
+ wps_buffer[start] = 0;
+ }
+ }
+
+ close(fd);
+
+ if (start <= 0)
+ return false;
+
+#ifdef HAVE_LCD_BITMAP
+ clear_bmp_names();
+#endif
+
+ /* parse the WPS source */
+ if (!wps_parse(wps_data, wps_buffer))
+ return false;
+
+ wps_data->wps_loaded = true;
+
+#ifdef HAVE_LCD_BITMAP
+ /* get the bitmap dir */
+ char bmpdir[MAX_PATH];
+ size_t bmpdirlen;
+ char *dot = strrchr(buf, '.');
+ bmpdirlen = dot - buf;
+ strncpy(bmpdir, buf, dot - buf);
+ bmpdir[bmpdirlen] = 0;
+
+ /* load the bitmaps that were found by the parsing */
+ load_wps_bitmaps(wps_data, bmpdir);
+#endif
+ return true;
+ }
+}
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 5cc3748..336beec 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -7286,13 +7286,13 @@
</phrase>
<phrase>
id: LANG_END_PLAYLIST_PLAYER
- desc: when playlist has finished
+ desc: DEPRECATED
user:
<source>
- *: "End of List"
+ *: ""
</source>
<dest>
- *: "End of List"
+ *: deprecated
</dest>
<voice>
*: ""
@@ -7304,9 +7304,11 @@
user:
<source>
*: "End of Song List"
+ player: "End of List"
</source>
<dest>
*: "End of Song List"
+ player: "End of List"
</dest>
<voice>
*: ""