diff options
Diffstat (limited to 'apps/gui/gwps-common.c')
-rw-r--r-- | apps/gui/gwps-common.c | 3644 |
1 files changed, 1474 insertions, 2170 deletions
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c index bce213ab4d..2bd2e4c617 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, - ¢er_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, + ¢er_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; +} |