From 0eaa11bc51dac9b6f81c542bc64ddb642d7574d8 Mon Sep 17 00:00:00 2001 From: jdgordon Date: Wed, 13 Jan 2010 06:02:38 +0000 Subject: [PATCH] FS#10898 - Add a playlist viewer to the WPS. http://www.rockbox.org/wiki/CustomWPS#Playlist_viewer for an exaplanation how to use it. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24220 a1c6a512-1295-4272-9138-f99709370657 --- apps/gui/skin_engine/skin_display.c | 95 ++++++++++++++++++++++++++++ apps/gui/skin_engine/skin_parser.c | 118 +++++++++++++++++++++++++++++++++++ apps/gui/skin_engine/skin_tokens.c | 2 +- apps/gui/skin_engine/skin_tokens.h | 2 + apps/gui/skin_engine/wps_internals.h | 20 ++++++ apps/playback.c | 17 +++++ 6 files changed, 253 insertions(+), 1 deletion(-) diff --git a/apps/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c index b8cc75e10..e781d4337 100644 --- a/apps/gui/skin_engine/skin_display.c +++ b/apps/gui/skin_engine/skin_display.c @@ -35,6 +35,8 @@ #include "statusbar.h" #include "scrollbar.h" #include "screen_access.h" +#include "playlist.h" +#include "playback.h" #ifdef HAVE_LCD_BITMAP #include "peakmeter.h" @@ -163,6 +165,94 @@ static void draw_progressbar(struct gui_wps *gwps, cue_draw_markers(display, state->id3->cuesheet, length, pb->x, pb->x + pb->width, y+1, pb->height-2); } +bool audio_peek_track(struct mp3entry* id3, int offset); +static void draw_playlist_viewer_list(struct gui_wps *gwps, + struct playlistviewer *viewer) +{ + int lines = viewport_get_nb_lines(viewer->vp); + int line_height = font_get(viewer->vp->font)->height; + int cur_playlist_pos = playlist_get_display_index(); + int start_item = MAX(0, cur_playlist_pos + viewer->start_offset); + int i; + + struct mp3entry *pid3, id3; + char buf[MAX_PATH*2], tempbuf[MAX_PATH]; + + + gwps->display->set_viewport(viewer->vp); + for(i=start_item; (i-start_item)cur_playlist_pos) && audio_peek_track(&id3, i-cur_playlist_pos)) + { + pid3 = &id3; + } + else + pid3 = NULL; + + int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO; + int token = 0, cur_string = 0; + char *filename = playlist_peek(i-cur_playlist_pos); + buf[0] = '\0'; + while (token < viewer->lines[line].count) + { + switch (viewer->lines[line].tokens[token]) + { + case WPS_TOKEN_STRING: + case WPS_TOKEN_CHARACTER: + strcat(buf, viewer->lines[line].strings[cur_string++]); + break; + case WPS_TOKEN_PLAYLIST_POSITION: + snprintf(tempbuf, sizeof(tempbuf), "%d", i); + strcat(buf, tempbuf); + break; + case WPS_TOKEN_FILE_NAME: + get_dir(tempbuf, sizeof(tempbuf), filename, 0); + strcat(buf, tempbuf); + break; + case WPS_TOKEN_FILE_PATH: + strcat(buf, filename); + break; + case WPS_TOKEN_METADATA_ARTIST: + if (pid3) + strcat(buf, pid3->artist ? pid3->artist : ""); + break; + case WPS_TOKEN_METADATA_TRACK_TITLE: + if (pid3) + strcat(buf, pid3->title ? pid3->title : ""); + break; + case WPS_TOKEN_TRACK_LENGTH: + if (pid3) + { + format_time(tempbuf, sizeof(tempbuf), pid3->length); + strcat(buf, tempbuf); + } + break; + + default: + break; + } + token++; + } + + if (viewer->lines[line].scroll) + { + gwps->display->puts_scroll(0, (i-start_item), buf ); + } + else + { + gwps->display->putsxy(0, (i-start_item)*line_height, buf ); + } + } +} + /* clears the area where the image was shown */ static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img) @@ -595,6 +685,11 @@ static bool get_line(struct gui_wps *gwps, } } break; +#ifdef HAVE_LCD_BITMAP + case WPS_VIEWPORT_CUSTOMLIST: + draw_playlist_viewer_list(gwps, data->tokens[i].value.data); + break; +#endif default: { /* get the value of the tag and copy it to the buffer */ diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c index c7bca24d1..7efd16909 100644 --- a/apps/gui/skin_engine/skin_parser.c +++ b/apps/gui/skin_engine/skin_parser.c @@ -160,6 +160,8 @@ int parse_languagedirection(const char *wps_bufptr, #ifdef HAVE_LCD_BITMAP static int parse_viewport_display(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); +static int parse_playlistview(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data); static int parse_viewport(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_statusbar_enable(const char *wps_bufptr, @@ -373,6 +375,9 @@ static const struct wps_tag all_tags[] = { { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC, parse_viewport_display }, +#ifdef HAVE_LCD_BITMAP + { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview }, +#endif { WPS_NO_TOKEN, "V", 0, parse_viewport }, #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) @@ -714,6 +719,119 @@ static int parse_viewport_display(const char *wps_bufptr, return 1; } +#ifdef HAVE_LCD_BITMAP +int parse_playlistview_text(struct playlistviewer *viewer, + enum info_line_type line, char* text) +{ + int cur_string = 0; + const struct wps_tag *tag; + int taglen = 0; + const char *start = text; + if (*text != '|') + return -1; + text++; + viewer->lines[line].count = 0; + viewer->lines[line].scroll = false; + while (*text != '|') + { + if (*text == '%') /* it is a token of some type */ + { + text++; + taglen = 0; + switch(*text) + { + case '%': + case '<': + case '|': + case '>': + case ';': + case '#': + /* escaped characters */ + viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER; + viewer->lines[line].strings[cur_string][0] = *text; + viewer->lines[line].strings[cur_string++][0] = '\0'; + break; + default: + for (tag = all_tags; + strncmp(text, tag->name, strlen(tag->name)) != 0; + tag++) ; + /* %s isnt stored as a tag so manually check for it */ + if (tag->type == WPS_NO_TOKEN) + { + if (!strncmp(tag->name, "s", 1)) + { + viewer->lines[line].scroll = true; + taglen = 1; + } + } + else if (tag->type == WPS_TOKEN_UNKNOWN) + { + int i = 0; + /* just copy the string */ + viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING; + while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%') + { + viewer->lines[line].strings[cur_string][i] = text[i]; + i++; + } + viewer->lines[line].strings[cur_string][i] = '\0'; + cur_string++; + taglen = i; + } + else + { + taglen = strlen(tag->name); + viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type; + } + text += taglen; + } + } + else + { + /* regular string */ + int i = 0; + /* just copy the string */ + viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING; + while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%') + { + viewer->lines[line].strings[cur_string][i] = text[i]; + i++; + } + viewer->lines[line].strings[cur_string][i] = '\0'; + cur_string++; + text += i; + } + } + return text - start; +} + + +static int parse_playlistview(const char *wps_bufptr, + struct wps_token *token, struct wps_data *wps_data) +{ + (void)wps_data; + /* %Vp|||info line text|no info text| */ + struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer)); + char *ptr = strchr(wps_bufptr, '|'); + int length; + if (!viewer || !ptr) + return WPS_ERROR_INVALID_PARAM; + viewer->vp = &curr_vp->vp; + viewer->show_icons = true; + viewer->start_offset = atoi(ptr+1); + token->value.data = (void*)viewer; + ptr = strchr(ptr+1, '|'); + length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr); + if (length < 0) + return WPS_ERROR_INVALID_PARAM; + length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length); + if (length < 0) + return WPS_ERROR_INVALID_PARAM; + + return skip_end_of_line(wps_bufptr); +} +#endif + static int parse_viewport(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c index 794d7f445..9b5436132 100644 --- a/apps/gui/skin_engine/skin_tokens.c +++ b/apps/gui/skin_engine/skin_tokens.c @@ -81,7 +81,7 @@ static char* get_codectype(const struct mp3entry* id3) * * Returns buf if the desired level was found, NULL otherwise. */ -static char* get_dir(char* buf, int buf_size, const char* path, int level) +char* get_dir(char* buf, int buf_size, const char* path, int level) { const char* sep; const char* last_sep; diff --git a/apps/gui/skin_engine/skin_tokens.h b/apps/gui/skin_engine/skin_tokens.h index 25acfdace..5778f9580 100644 --- a/apps/gui/skin_engine/skin_tokens.h +++ b/apps/gui/skin_engine/skin_tokens.h @@ -54,6 +54,7 @@ enum wps_token_type { /* Viewport display */ WPS_VIEWPORT_ENABLE, + WPS_VIEWPORT_CUSTOMLIST, /* Battery */ TOKEN_MARKER_BATTERY, @@ -237,6 +238,7 @@ struct skin_token_list { struct skin_token_list *next; }; +char* get_dir(char* buf, int buf_size, const char* path, int level); #endif diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h index bd0c1c01c..362f3947e 100644 --- a/apps/gui/skin_engine/wps_internals.h +++ b/apps/gui/skin_engine/wps_internals.h @@ -225,6 +225,26 @@ struct touchregion { }; #endif +#define MAX_PLAYLISTLINE_TOKENS 16 +#define MAX_PLAYLISTLINE_STRINGS 8 +#define MAX_PLAYLISTLINE_STRLEN 8 +enum info_line_type { + TRACK_HAS_INFO = 0, + TRACK_HAS_NO_INFO +}; +struct playlistviewer { + struct viewport *vp; + bool show_icons; + int start_offset; + struct { + enum wps_token_type tokens[MAX_PLAYLISTLINE_TOKENS]; + char strings[MAX_PLAYLISTLINE_STRINGS][MAX_PLAYLISTLINE_STRLEN]; + int count; + bool scroll; + } lines[2]; +}; + + #ifdef HAVE_ALBUMART struct skin_albumart { diff --git a/apps/playback.c b/apps/playback.c index ff20172d8..fca21ae1f 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -629,6 +629,23 @@ struct mp3entry* audio_next_track(void) return NULL; } +bool audio_peek_track(struct mp3entry* id3, int offset) +{ + int next_idx; + int new_offset = ci.new_track + wps_offset + offset; + + if (!audio_have_tracks()) + return false; + next_idx = (track_ridx + new_offset) & MAX_TRACK_MASK; + + if (tracks[next_idx].id3_hid >= 0) + { + bufread(tracks[next_idx].id3_hid, sizeof(struct mp3entry), id3); + return true; + } + return false; +} + #ifdef HAVE_ALBUMART int playback_current_aa_hid(int slot) { -- 2.11.4.GIT