summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHardeep Sidhu <dyp@pobox.com>2003-12-10 00:11:25 +0000
committerHardeep Sidhu <dyp@pobox.com>2003-12-10 00:11:25 +0000
commit00acdfa6ef624e1d13b461210ddd71dd589d192e (patch)
treef31b42d33d417bef4dc040e412a9f8fea61a0ac9
parentc882d45ebba3861b973339564d927412cb04c5b2 (diff)
downloadrockbox-00acdfa6ef624e1d13b461210ddd71dd589d192e.zip
rockbox-00acdfa6ef624e1d13b461210ddd71dd589d192e.tar.gz
rockbox-00acdfa6ef624e1d13b461210ddd71dd589d192e.tar.bz2
rockbox-00acdfa6ef624e1d13b461210ddd71dd589d192e.tar.xz
Added viewer for currently playing playlist. Accessed from Menu->Playlist Options->View Current Playlist.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4124 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/lang/english.lang17
-rw-r--r--apps/playlist.c171
-rw-r--r--apps/playlist.h18
-rw-r--r--apps/playlist_menu.c8
-rw-r--r--apps/playlist_viewer.c848
-rw-r--r--apps/playlist_viewer.h26
-rw-r--r--apps/wps.c29
-rw-r--r--firmware/drivers/lcd-recorder.c23
-rw-r--r--firmware/export/lcd.h1
-rw-r--r--uisimulator/win32/Makefile5
-rw-r--r--uisimulator/win32/rockbox.dsp4
-rw-r--r--uisimulator/x11/Makefile5
12 files changed, 1111 insertions, 44 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 0a1cfae..ce2b300 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -1660,7 +1660,7 @@ new:
id: LANG_SAVE_DYNAMIC_PLAYLIST
desc: in playlist menu.
-eng: "Save Dynamic Playlist"
+eng: "Save Current Playlist"
new:
id: LANG_PLAYLIST_MENU
@@ -1827,3 +1827,18 @@ id: LANG_FM_BUTTONBAR_RECORD
desc: in main menu
eng: "Record"
new:
+
+id: LANG_VIEW_DYNAMIC_PLAYLIST
+desc: in playlist menu.
+eng: "View Current Playlist"
+new:
+
+id: LANG_MOVE
+desc: The verb/action Move
+eng: "Move"
+new:
+
+id: LANG_MOVE_FAILED
+desc: Error message displayed in playlist viewer
+eng: "Move failed"
+new:
diff --git a/apps/playlist.c b/apps/playlist.c
index f834a3d..1ededb2 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -146,6 +146,7 @@ static int format_track_path(char *dest, char *src, int buf_length, int max,
static void display_playlist_count(int count, char *fmt);
static void display_buffer_full(void);
static int flush_pending_control(void);
+static int rotate_index(int index);
/*
* remove any files and indices associated with the playlist
@@ -679,11 +680,7 @@ static int get_next_index(int steps)
{
case REPEAT_OFF:
{
- /* Rotate indices such that first_index is considered index 0 to
- simplify next calculation */
- current_index -= playlist.first_index;
- if (current_index < 0)
- current_index += playlist.amount;
+ current_index = rotate_index(current_index);
next_index = current_index+steps;
if ((next_index < 0) || (next_index >= playlist.amount))
@@ -993,6 +990,18 @@ static int flush_pending_control(void)
}
/*
+ * Rotate indices such that first_index is index 0
+ */
+static int rotate_index(int index)
+{
+ index -= playlist.first_index;
+ if (index < 0)
+ index += playlist.amount;
+
+ return index;
+}
+
+/*
* Initialize playlist entries at startup
*/
void playlist_init(void)
@@ -1604,25 +1613,108 @@ int playlist_insert_playlist(char *filename, int position, bool queue)
return result;
}
-/* delete track at specified index */
+/*
+ * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
+ * we want to delete the current playing track.
+ */
int playlist_delete(int index)
{
- int result;
-
+ int result = 0;
+
if (playlist.control_fd < 0)
{
splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
return -1;
}
- result = remove_track_from_playlist(index, true);
+ if (index == PLAYLIST_DELETE_CURRENT)
+ index = playlist.index;
+ result = remove_track_from_playlist(index, true);
+
if (result != -1)
mpeg_flush_and_reload_tracks();
return result;
}
+/*
+ * Move track at index to new_index. Tracks between the two are shifted
+ * appropriately. Returns 0 on success and -1 on failure.
+ */
+int playlist_move(int index, int new_index)
+{
+ int result;
+ int seek;
+ bool control_file;
+ bool queue;
+ bool current = false;
+ int r = rotate_index(new_index);
+ char filename[MAX_PATH];
+
+ if (index == new_index)
+ return -1;
+
+ if (index == playlist.index)
+ /* Moving the current track */
+ current = true;
+
+ control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ queue = playlist.indices[index] & PLAYLIST_QUEUE_MASK;
+ seek = playlist.indices[index] & PLAYLIST_SEEK_MASK;
+
+ if (get_filename(seek, control_file, filename, sizeof(filename)) < 0)
+ return -1;
+
+ /* Delete track from original position */
+ result = remove_track_from_playlist(index, true);
+
+ if (result != -1)
+ {
+ /* We want to insert the track at the position that was specified by
+ new_index. This may be different then new_index because of the
+ shifting that occurred after the delete */
+ if (r == 0)
+ /* First index */
+ new_index = PLAYLIST_PREPEND;
+ else if (r == playlist.amount)
+ /* Append */
+ new_index = PLAYLIST_INSERT_LAST;
+ else
+ /* Calculate index of desired position */
+ new_index = (r+playlist.first_index)%playlist.amount;
+
+ result = add_track_to_playlist(filename, new_index, queue, -1);
+
+ if (result != -1)
+ {
+ if (current)
+ {
+ /* Moved the current track */
+ switch (new_index)
+ {
+ case PLAYLIST_PREPEND:
+ playlist.index = playlist.first_index;
+ break;
+ case PLAYLIST_INSERT_LAST:
+ playlist.index = playlist.first_index - 1;
+ if (playlist.index < 0)
+ playlist.index += playlist.amount;
+ break;
+ default:
+ playlist.index = new_index;
+ break;
+ }
+ }
+
+ fsync(playlist.control_fd);
+ mpeg_flush_and_reload_tracks();
+ }
+ }
+
+ return result;
+}
+
/* shuffle newly created playlist using random seed. */
int playlist_shuffle(int random_seed, int start_index)
{
@@ -1763,21 +1855,13 @@ int playlist_next(int steps)
index = get_next_index(steps);
playlist.index = index;
- if (playlist.last_insert_pos >= 0)
+ if (playlist.last_insert_pos >= 0 && steps > 0)
{
/* check to see if we've gone beyond the last inserted track */
- int rot_index = index;
- int rot_last_pos = playlist.last_insert_pos;
+ int cur = rotate_index(index);
+ int last_pos = rotate_index(playlist.last_insert_pos);
- rot_index -= playlist.first_index;
- if (rot_index < 0)
- rot_index += playlist.amount;
-
- rot_last_pos -= playlist.first_index;
- if (rot_last_pos < 0)
- rot_last_pos += playlist.amount;
-
- if (rot_index > rot_last_pos)
+ if (cur > last_pos)
{
/* reset last inserted track */
playlist.last_insert_pos = -1;
@@ -1826,16 +1910,18 @@ int playlist_get_resume_info(short *resume_index)
index into the playlist */
int playlist_get_display_index(void)
{
- int index = playlist.index;
-
/* first_index should always be index 0 for display purposes */
- index -= playlist.first_index;
- if (index < 0)
- index += playlist.amount;
+ int index = rotate_index(playlist.index);
return (index+1);
}
+/* returns index of first track in playlist */
+int playlist_get_first_index(void)
+{
+ return playlist.first_index;
+}
+
/* returns number of tracks in playlist (includes queued/inserted tracks) */
int playlist_amount(void)
{
@@ -1860,6 +1946,39 @@ char *playlist_name(char *buf, int buf_size)
return buf;
}
+/* Fills info structure with information about track at specified index.
+ Returns 0 on success and -1 on failure */
+int playlist_get_track_info(int index, struct playlist_track_info* info)
+{
+ int seek;
+ bool control_file;
+
+ if (index < 0 || index >= playlist.amount)
+ return -1;
+
+ control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ seek = playlist.indices[index] & PLAYLIST_SEEK_MASK;
+
+ if (get_filename(seek, control_file, info->filename,
+ sizeof(info->filename)) < 0)
+ return -1;
+
+ info->attr = 0;
+
+ if (control_file)
+ {
+ if (playlist.indices[index] & PLAYLIST_QUEUE_MASK)
+ info->attr |= PLAYLIST_ATTR_QUEUED;
+ else
+ info->attr |= PLAYLIST_ATTR_INSERTED;
+ }
+
+ info->index = index;
+ info->display_index = rotate_index(index) + 1;
+
+ return 0;
+}
+
/* save the current dynamic playlist to specified file */
int playlist_save(char *filename)
{
diff --git a/apps/playlist.h b/apps/playlist.h
index 45ecba5..82d67bf 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -47,6 +47,17 @@ struct playlist_info
struct mutex control_mutex; /* mutex for control file access */
};
+#define PLAYLIST_ATTR_QUEUED 0x01
+#define PLAYLIST_ATTR_INSERTED 0x02
+
+struct playlist_track_info
+{
+ char filename[MAX_PATH]; /* path name of mp3 file */
+ int attr; /* playlist attributes for track */
+ int index; /* index of track in playlist */
+ int display_index; /* index of track for display */
+};
+
void playlist_init(void);
int playlist_create(char *dir, char *file);
int playlist_resume(void);
@@ -56,6 +67,7 @@ int playlist_insert_directory(char *dirname, int position, bool queue,
bool recurse);
int playlist_insert_playlist(char *filename, int position, bool queue);
int playlist_delete(int index);
+int playlist_move(int index, int new_index);
int playlist_shuffle(int random_seed, int start_index);
int playlist_randomise(unsigned int seed, bool start_current);
int playlist_sort(bool start_current);
@@ -65,8 +77,10 @@ char *playlist_peek(int steps);
int playlist_next(int steps);
int playlist_get_resume_info(short *resume_index);
int playlist_get_display_index(void);
+int playlist_get_first_index(void);
int playlist_amount(void);
char *playlist_name(char *buf, int buf_size);
+int playlist_get_track_info(int index, struct playlist_track_info* info);
int playlist_save(char *filename);
enum {
@@ -76,4 +90,8 @@ enum {
PLAYLIST_INSERT_FIRST = -4
};
+enum {
+ PLAYLIST_DELETE_CURRENT = -1
+};
+
#endif /* __PLAYLIST_H__ */
diff --git a/apps/playlist_menu.c b/apps/playlist_menu.c
index 4223e3c..f4e4867 100644
--- a/apps/playlist_menu.c
+++ b/apps/playlist_menu.c
@@ -25,6 +25,7 @@
#include "playlist.h"
#include "tree.h"
#include "settings.h"
+#include "playlist_viewer.h"
#include "lang.h"
@@ -64,9 +65,10 @@ bool playlist_menu(void)
bool result;
struct menu_items items[] = {
- { str(LANG_CREATE_PLAYLIST), create_playlist },
- { str(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
- { str(LANG_RECURSE_DIRECTORY), recurse_directory },
+ { str(LANG_CREATE_PLAYLIST), create_playlist },
+ { str(LANG_VIEW_DYNAMIC_PLAYLIST), playlist_viewer },
+ { str(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
+ { str(LANG_RECURSE_DIRECTORY), recurse_directory },
};
m = menu_init( items, sizeof items / sizeof(struct menu_items) );
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
new file mode 100644
index 0000000..555c9a7
--- /dev/null
+++ b/apps/playlist_viewer.c
@@ -0,0 +1,848 @@
+/***************************************************************************
+ *
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2003 Hardeep Sidhu
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <sprintf.h>
+#include "playlist.h"
+#include "mpeg.h"
+#include "screens.h"
+#include "status.h"
+#include "settings.h"
+#include "icons.h"
+#include "menu.h"
+#include "plugin.h"
+
+#ifdef HAVE_LCD_BITMAP
+#include "widgets.h"
+#endif
+
+#include "lang.h"
+
+/* Defines for LCD display purposes. Taken from tree.c */
+#ifdef HAVE_LCD_BITMAP
+ #define CURSOR_X (global_settings.scrollbar && \
+ viewer.num_tracks>viewer.num_display_lines?1:0)
+ #define CURSOR_Y 0
+ #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
+
+ #define ICON_WIDTH ((viewer.char_width > 6) ? viewer.char_width : 6)
+
+ #define MARGIN_X ((global_settings.scrollbar && \
+ viewer.num_tracks > viewer.num_display_lines ? \
+ SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + ICON_WIDTH)
+ #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
+
+ #define LINE_X 0
+ #define LINE_Y (global_settings.statusbar ? 1 : 0)
+
+ #define SCROLLBAR_X 0
+ #define SCROLLBAR_Y lcd_getymargin()
+ #define SCROLLBAR_WIDTH 6
+#else
+ #define MARGIN_X 0
+ #define MARGIN_Y 0
+ #define LINE_X 2
+ #define LINE_Y 0
+ #define CURSOR_X 0
+ #define CURSOR_Y 0
+#endif
+
+/* Maximum number of tracks we can have loaded at one time */
+#define MAX_PLAYLIST_ENTRIES 200
+
+/* Index of track on display line _pos */
+#define INDEX(_pos) (viewer.first_display_index - viewer.first_index + (_pos))
+
+/* Global playlist viewer settings */
+struct playlist_viewer_info {
+ char *name_buffer; /* Buffer used to store track names */
+ int buffer_size; /* Size of name buffer */
+
+ int num_display_lines; /* Number of lines on lcd */
+ int line_height; /* Height (in pixels) of display line */
+ int char_width; /* Width (in pixels) of a character */
+
+ int num_tracks; /* Number of tracks in playlist */
+ short current_playing_track;/* Index of current playing track */
+
+ int num_loaded; /* Number of track entries loaded in viewer */
+ int first_index; /* Index of first loaded track */
+ int last_index; /* Index of last loaded track */
+ int first_display_index; /* Index of first track on display */
+ int last_display_index; /* Index of last track on display */
+ int cursor_pos; /* Line number of cursor */
+
+ int move_track; /* Playlist index of track to move or -1 */
+};
+
+/* Information about a specific track */
+struct playlist_entry {
+ char *name; /* Formatted track name */
+ int index; /* Playlist index */
+ int display_index; /* Display index */
+ bool queued; /* Is track queued? */
+};
+
+static struct playlist_viewer_info viewer;
+static struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES];
+
+#ifdef HAVE_LCD_BITMAP
+extern unsigned char bitmap_icons_6x8[LastIcon][6];
+#endif
+
+static bool initialize(void);
+static void load_playlist_entries(int start_index);
+static void load_playlist_entries_r(int end_index);
+static int load_entry(int index, int pos, char* p, int size);
+static void format_name(char* dest, char* src);
+static void display_playlist(void);
+static void update_display_line(int line, bool scroll);
+static void scroll_display(int lines);
+static void update_first_index(void);
+static bool update_playlist(bool force);
+static int onplay_menu(int index);
+
+/* Initialize the playlist viewer */
+static bool initialize(void)
+{
+ if (!(mpeg_status() & MPEG_STATUS_PLAY))
+ /* Nothing is playing, exit */
+ return false;
+
+ viewer.name_buffer = plugin_get_buffer(&viewer.buffer_size);
+ if (!viewer.name_buffer)
+ return false;
+
+#ifdef HAVE_LCD_BITMAP
+ {
+ char icon_chars[] = "MQ"; /* characters used as icons */
+ unsigned int i;
+
+ viewer.char_width = 0;
+ viewer.line_height = 0;
+
+ /* Use icon characters to calculate largest possible width/height so
+ that we set proper margins */
+ for (i=0; i<sizeof(icon_chars); i++)
+ {
+ char str[2];
+ int w, h;
+
+ snprintf(str, sizeof(str), "%c", icon_chars[i]);
+ lcd_getstringsize(str, &w, &h);
+
+ if (w > viewer.char_width)
+ viewer.char_width = w;
+
+ if (h > viewer.line_height)
+ {
+ viewer.line_height = h;
+ viewer.num_display_lines = (LCD_HEIGHT - MARGIN_Y)/h;
+ }
+ }
+ }
+#else
+ viewer.num_display_lines = 2;
+ viewer.char_width = 1;
+ viewer.line_height = 1;
+#endif
+
+ viewer.cursor_pos = 0;
+ viewer.move_track = -1;
+
+ /* Start displaying at current playing track */
+ viewer.first_display_index = playlist_get_display_index() - 1;
+ update_first_index();
+
+ if (!update_playlist(true))
+ return false;
+
+ return true;
+}
+
+/* Load tracks starting at start_index */
+static void load_playlist_entries(int start_index)
+{
+ int num_entries = viewer.num_tracks - start_index;
+ char* p = viewer.name_buffer;
+ int remaining = viewer.buffer_size;
+ int i;
+
+ viewer.first_index = start_index;
+
+ if (num_entries > MAX_PLAYLIST_ENTRIES)
+ num_entries = MAX_PLAYLIST_ENTRIES;
+
+ for(i=0; i<num_entries; i++, start_index++)
+ {
+ int len = load_entry(start_index, i, p, remaining);
+ if (len < 0)
+ {
+ /* Out of name buffer space */
+ num_entries = i;
+ break;
+ }
+
+ p += len;
+ remaining -= len;
+ }
+
+ viewer.num_loaded = num_entries;
+ viewer.last_index = viewer.first_index + (viewer.num_loaded - 1);
+}
+
+/* Load tracks in reverse, ending at end_index */
+static void load_playlist_entries_r(int end_index)
+{
+ int num_entries = end_index;
+ char* p = viewer.name_buffer;
+ int remaining = viewer.buffer_size;
+ int i;
+
+ viewer.last_index = end_index;
+
+ if (num_entries > MAX_PLAYLIST_ENTRIES)
+ num_entries = MAX_PLAYLIST_ENTRIES;
+
+ for(i=num_entries; i>=0; i--, end_index--)
+ {
+ int len = load_entry(end_index, i, p, remaining);
+ if (len < 0)
+ {
+ int j;
+
+ /* Out of name buffer space */
+ num_entries -= i;
+
+ /* Shift loaded tracks up such that first track is index 0 */
+ for (j=0; j<num_entries; j++, i++)
+ {
+ tracks[j].name = tracks[i].name;
+ tracks[j].index = tracks[i].index;
+ tracks[j].display_index = tracks[i].display_index;
+ tracks[j].queued = tracks[i].queued;
+ }
+
+ break;
+ }
+
+ p += len;
+ remaining -= len;
+ }
+
+ viewer.first_index = viewer.last_index - num_entries;
+
+ num_entries++;
+ if (!viewer.first_index &&
+ num_entries < viewer.num_tracks &&
+ num_entries < MAX_PLAYLIST_ENTRIES)
+ {
+ /* Lets see if we can load more data at the end of the list */
+ int max = viewer.num_tracks;
+ if (max > MAX_PLAYLIST_ENTRIES)
+ max = MAX_PLAYLIST_ENTRIES;
+
+ for (i = num_entries; i<max; i++)
+ {
+ int len = load_entry(num_entries, num_entries, p, remaining);
+ if (len < 0)
+ /* Out of name buffer space */
+ break;
+
+ p += len;
+ remaining -= len;
+
+ num_entries++;
+ viewer.last_index++;
+ }
+ }
+
+ viewer.num_loaded = num_entries;
+}
+
+/* Load track at playlist index. pos is the position in the tracks array and
+ p is a pointer to the name buffer (max size), Returns -1 if buffer is
+ full. */
+static int load_entry(int index, int pos, char* p, int size)
+{
+ struct playlist_track_info info;
+ int len;
+ int result = 0;
+ char name[MAX_PATH];
+
+ /* Playlist viewer orders songs based on display index. We need to
+ convert to real playlist index to access track */
+ index = (index + playlist_get_first_index()) % viewer.num_tracks;
+ if (playlist_get_track_info(index, &info) < 0)
+ return -1;
+
+ format_name(name, info.filename);
+
+ len = strlen(name) + 1;
+
+ if (len <= size)
+ {
+ strcpy(p, name);
+
+ tracks[pos].name = p;
+ tracks[pos].index = info.index;
+ tracks[pos].display_index = info.display_index;
+ tracks[pos].queued = info.attr & PLAYLIST_ATTR_QUEUED;
+
+ result = len;
+ }
+ else
+ result = -1;
+
+ return result;
+}
+
+/* Format trackname for display purposes */
+static void format_name(char* dest, char* src)
+{
+ char* p = strrchr(src, '/');
+ int len;
+
+ /* Only display the mp3 filename */
+ strcpy(dest, p+1);
+ len = strlen(dest);
+
+ /* Remove the extension */
+ if (!strcasecmp(&dest[len-4], ".mp3") ||
+ !strcasecmp(&dest[len-4], ".mp2") ||
+ !strcasecmp(&dest[len-4], ".mpa"))
+ dest[len-4] = '\0';
+}
+
+/* Display tracks on screen */
+static void display_playlist(void)
+{
+ int i;
+ int num_display_tracks =
+ viewer.last_display_index - viewer.first_display_index;
+
+ lcd_clear_display();
+
+#ifdef HAVE_LCD_BITMAP
+ lcd_setmargins(MARGIN_X, MARGIN_Y);
+ lcd_setfont(FONT_UI);
+#endif
+
+ for (i=0; i<=num_display_tracks; i++)
+ {
+ /* Icons */
+ if (tracks[INDEX(i)].index == viewer.current_playing_track)
+ {
+ /* Current playing track */
+#ifdef HAVE_LCD_BITMAP
+ int offset=0;
+ if ( viewer.line_height > 8 )
+ offset = (viewer.line_height - 8) / 2;
+ lcd_bitmap(bitmap_icons_6x8[File],
+ CURSOR_X * 6 + CURSOR_WIDTH,
+ MARGIN_Y+(i*viewer.line_height) + offset,
+ 6, 8, true);
+#else
+ lcd_putc(LINE_X-1, i, File);
+#endif
+ }
+ else if (tracks[INDEX(i)].index == viewer.move_track)
+ {
+ /* Track we are moving */
+#ifdef HAVE_LCD_BITMAP
+ lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
+ MARGIN_Y+(i*viewer.line_height), "M");
+#else
+ lcd_putc(LINE_X-1, i, 'M');
+#endif
+ }
+ else if (tracks[INDEX(i)].queued)
+ {
+ /* Queued track */
+#ifdef HAVE_LCD_BITMAP
+ lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
+ MARGIN_Y+(i*viewer.line_height), "Q");
+#else
+ lcd_putc(LINE_X-1, i, 'Q');
+#endif
+ }
+
+ update_display_line(i, false);
+ }
+
+#ifdef HAVE_LCD_BITMAP
+ if (global_settings.scrollbar &&
+ (viewer.num_tracks > viewer.num_display_lines))
+ scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
+ LCD_HEIGHT - SCROLLBAR_Y, viewer.num_tracks-1,
+ viewer.first_display_index, viewer.last_display_index,
+ VERTICAL);
+#endif
+
+ put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
+ status_draw(true);
+}
+
+/* Scroll cursor or display by num lines */
+static void scroll_display(int lines)
+{
+ int new_index = viewer.first_display_index + viewer.cursor_pos + lines;
+ bool pagescroll = false;
+ bool wrap = false;
+
+ put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
+
+ if (lines > 1 || lines < -1)
+ pagescroll = true;
+
+ if (new_index < 0)
+ {
+ /* Wrap around if not pageup */
+ if (pagescroll)
+ new_index = 0;
+ else
+ {
+ new_index += viewer.num_tracks;
+ viewer.cursor_pos = viewer.num_display_lines-1;
+ wrap = true;
+ }
+ }
+ else if (new_index >= viewer.num_tracks)
+ {
+ /* Wrap around if not pagedown */
+ if (pagescroll)
+ new_index = viewer.num_tracks - 1;
+ else
+ {
+ new_index -= viewer.num_tracks;
+ viewer.cursor_pos = 0;
+ wrap = true;
+ }
+ }
+
+ if (new_index >= viewer.first_display_index &&
+ new_index <= viewer.last_display_index)
+ {
+ /* Just update the cursor */
+ viewer.cursor_pos = new_index - viewer.first_display_index;
+ }
+ else
+ {
+ /* New track is outside of display */
+ if (wrap)
+ viewer.first_display_index = new_index;
+ else
+ viewer.first_display_index = viewer.first_display_index + lines;
+
+ if (viewer.first_display_index < 0)
+ viewer.first_display_index = 0;
+
+ viewer.last_display_index =
+ viewer.first_display_index + (viewer.num_display_lines - 1);
+ if (viewer.last_display_index >= viewer.num_tracks)
+ {
+ /* display as many tracks as possible on screen */
+ if (viewer.first_display_index > 0)
+ {
+ viewer.first_display_index -=
+ (viewer.last_display_index - viewer.num_tracks + 1);
+ if (viewer.first_display_index < 0)
+ viewer.first_display_index = 0;
+ }
+
+ viewer.last_display_index = viewer.num_tracks - 1;
+ }
+
+ if (viewer.cursor_pos >
+ (viewer.last_display_index - viewer.first_display_index))
+ viewer.cursor_pos =
+ viewer.last_display_index - viewer.first_display_index;
+
+ /* Load more data if needed */
+ if (viewer.first_display_index < viewer.first_index)
+ load_playlist_entries_r(viewer.last_display_index);
+ else if (viewer.last_display_index > viewer.last_index)
+ load_playlist_entries(viewer.first_display_index);
+
+ display_playlist();
+ }
+
+ put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
+}
+
+/* Update lcd line. Scroll line if requested */
+static void update_display_line(int line, bool scroll)
+{
+ char str[MAX_PATH + 16];
+
+ snprintf(str, sizeof(str), "%d. %s",
+ tracks[INDEX(line)].display_index,
+ tracks[INDEX(line)].name);
+
+ if (scroll)
+ {
+#ifdef HAVE_LCD_BITMAP
+ if (global_settings.invert_cursor)
+ lcd_puts_scroll_style(LINE_X, line, str, STYLE_INVERT);
+ else
+#endif
+ lcd_puts_scroll(LINE_X, line, str);
+ }
+ else
+ lcd_puts(LINE_X, line, str);
+}
+
+/* Update first index, if necessary, to put as much as possible on the
+ screen */
+static void update_first_index(void)
+{
+ /* viewer.num_tracks may be invalid at this point */
+ int num_tracks = playlist_amount();
+
+ if ((num_tracks - viewer.first_display_index) < viewer.num_display_lines)
+ {
+ /* Try to display as much as possible */
+ int old_index = viewer.first_display_index;
+
+ viewer.first_display_index = num_tracks - viewer.num_display_lines;
+ if (viewer.first_display_index < 0)
+ viewer.first_display_index = 0;
+
+ /* Cursor should still point at current track */
+ viewer.cursor_pos += old_index - viewer.first_display_index;
+ }
+}
+
+/* Update playlist in case something has changed or forced */
+static bool update_playlist(bool force)
+{
+ playlist_get_resume_info(&viewer.current_playing_track);
+
+ if (force || playlist_amount() != viewer.num_tracks)
+ {
+ int index;
+
+ /* Reload tracks */
+ viewer.num_tracks = playlist_amount();
+ if (viewer.num_tracks < 0)
+ return false;
+
+ index = viewer.first_display_index;
+
+ load_playlist_entries(index);
+
+ if (viewer.num_loaded <= 0)
+ return false;
+
+ viewer.first_display_index = viewer.first_index;
+ viewer.last_display_index =
+ viewer.first_index + viewer.num_display_lines - 1;
+ if (viewer.last_display_index >= viewer.num_tracks)
+ viewer.last_display_index = viewer.num_tracks - 1;
+ }
+
+ display_playlist();
+
+ return true;
+}
+
+/* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
+ Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
+ changed. */
+static int onplay_menu(int index)
+{
+ struct menu_items menu[2]; /* increase this if you add entries! */
+ int m, i=0, result, ret = 0;
+ bool current = (tracks[index].index == viewer.current_playing_track);
+
+ menu[i].desc = str(LANG_DELETE);
+ i++;
+
+ menu[i].desc = str(LANG_MOVE);
+ i++;
+
+ m = menu_init(menu, i);
+ result = menu_show(m);
+ if (result == MENU_ATTACHED_USB)
+ ret = -1;
+ else if (result >= 0)
+ {
+ /* Abort current move */
+ viewer.move_track = -1;
+
+ switch (result)
+ {
+ case 0:
+ /* delete track */
+ if (current)
+ mpeg_stop();
+
+ playlist_delete(tracks[index].index);
+
+ if (current)
+ {
+ /* Start playing new track except if it's the last track
+ in the playlist and repeat mode is disabled */
+ if (tracks[index].display_index != viewer.num_tracks ||
+ global_settings.repeat_mode == REPEAT_ALL)
+ {
+ mpeg_play(0);
+ viewer.current_playing_track = -1;
+ }
+ }
+
+ ret = 1;
+ break;
+ case 1:
+ /* move track */
+ viewer.move_track = tracks[index].index;
+ ret = 0;
+ break;
+ }
+ }
+
+ menu_exit(m);
+
+ return ret;
+}
+
+/* Main viewer function */
+bool playlist_viewer(void)
+{
+ bool exit=false; /* exit viewer */
+ bool update=true; /* update display */
+ bool cursor_on=true; /* used for flashing cursor */
+ int old_cursor_pos; /* last cursor position */
+ int button;
+
+ if (!initialize())
+ return false;
+
+ old_cursor_pos = viewer.cursor_pos;
+
+ while (!exit)
+ {
+ short track;
+
+ /* Timeout so we can determine if play status has changed */
+ button = button_get_w_tmo(HZ/2);
+
+ if (!(mpeg_status() & MPEG_STATUS_PLAY))
+ {
+ /* Play has stopped */
+#ifdef HAVE_LCD_CHARCELLS
+ splash(HZ, 0, true, str(LANG_END_PLAYLIST_PLAYER));
+#else
+ splash(HZ, 0, true, str(LANG_END_PLAYLIST_RECORDER));
+#endif
+ status_set_playmode(STATUS_STOP);
+ return false;;
+ }
+
+ if (viewer.move_track != -1 || !cursor_on)
+ {
+ /* Flash cursor to identify that we are moving a track */
+ cursor_on = !cursor_on;
+#ifdef HAVE_LCD_BITMAP
+ if (global_settings.invert_cursor)
+ {
+ lcd_invertrect(
+ MARGIN_X, MARGIN_Y+(viewer.cursor_pos*viewer.line_height),
+ LCD_WIDTH, viewer.line_height);
+ lcd_invertscroll(LINE_X, viewer.cursor_pos);
+ }
+ else
+ put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos,
+ cursor_on);
+
+ lcd_update_rect(
+ 0, MARGIN_Y + (viewer.cursor_pos * viewer.line_height),
+ LCD_WIDTH, viewer.line_height);
+#else
+ put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, cursor_on);
+ lcd_update();
+#endif
+ }
+
+ playlist_get_resume_info(&track);
+
+ if (track != viewer.current_playing_track ||
+ playlist_amount() != viewer.num_tracks)
+ {
+ /* Playlist has changed (new track started?) */
+ update_first_index();
+ if (!update_playlist(false))
+ exit = true;
+ else
+ update = true;
+
+ /* Abort move on playlist change */
+ viewer.move_track = -1;
+ }
+
+ switch (button)
+ {
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_OFF:
+ case BUTTON_LEFT:
+#else
+ case BUTTON_STOP:
+#endif
+ exit = true;
+ break;
+
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_UP:
+ case BUTTON_UP | BUTTON_REPEAT:
+#else
+ case BUTTON_LEFT:
+ case BUTTON_LEFT | BUTTON_REPEAT:
+#endif
+ scroll_display(-1);
+ update = true;
+ break;
+
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_DOWN:
+ case BUTTON_DOWN | BUTTON_REPEAT:
+#else
+ case BUTTON_RIGHT:
+ case BUTTON_RIGHT | BUTTON_REPEAT:
+#endif
+ scroll_display(1);
+ update = true;
+ break;
+
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_ON | BUTTON_UP:
+ case BUTTON_ON | BUTTON_UP | BUTTON_REPEAT:
+#else
+ case BUTTON_ON | BUTTON_LEFT:
+ case BUTTON_ON | BUTTON_LEFT | BUTTON_REPEAT:
+#endif
+ /* Pageup */
+ scroll_display(-viewer.num_display_lines);
+ update = true;
+ break;
+
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_ON | BUTTON_DOWN:
+ case BUTTON_ON | BUTTON_DOWN | BUTTON_REPEAT:
+#else
+ case BUTTON_ON | BUTTON_RIGHT:
+ case BUTTON_ON | BUTTON_RIGHT | BUTTON_REPEAT:
+#endif
+ /* Pagedown */
+ scroll_display(viewer.num_display_lines);
+ update = true;
+ break;
+
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_RIGHT:
+#endif
+ case BUTTON_PLAY:
+ if (viewer.move_track >= 0)
+ {
+ /* Move track */
+ int ret;
+
+ ret = playlist_move(viewer.move_track,
+ tracks[INDEX(viewer.cursor_pos)].index);
+ if (ret < 0)
+ splash(HZ, 0, true, str(LANG_MOVE_FAILED));
+
+ update_playlist(true);
+ viewer.move_track = -1;
+ }
+ else
+ {
+ /* Stop current track and play new track */
+ mpeg_stop();
+ playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
+ status_set_playmode(STATUS_PLAY);
+ update_playlist(false);
+ }
+
+ display_playlist();
+ update = true;
+ break;
+
+ case BUTTON_ON | BUTTON_PLAY:
+ {
+ /* ON+PLAY menu */
+ int ret;
+
+ ret = onplay_menu(INDEX(viewer.cursor_pos));
+
+ if (ret < 0)
+ /* USB attached */
+ return true;
+ else if (ret > 0)
+ {
+ /* Playlist changed */
+ update_first_index();
+ update_playlist(true);
+ if (viewer.num_tracks <= 0)
+ exit = true;
+ }
+ else
+ display_playlist();
+
+ update = true;
+ break;
+ }
+ case SYS_USB_CONNECTED:
+ usb_screen();
+ return true;
+ }
+
+ if (update && !exit)
+ {
+ lcd_stop_scroll();
+
+ if (viewer.cursor_pos >
+ (viewer.last_display_index - viewer.first_display_index))
+ {
+ /* Cursor position is invalid */
+ put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
+ viewer.cursor_pos =
+ viewer.last_display_index - viewer.first_display_index;
+ put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
+ }
+
+ if (viewer.cursor_pos != old_cursor_pos &&
+ old_cursor_pos <=
+ (viewer.last_display_index - viewer.first_display_index))
+ /* Stop scrolling previous line */
+ update_display_line(old_cursor_pos, false);
+
+ /* Scroll line at new cursor position */
+ update_display_line(viewer.cursor_pos, true);
+
+ lcd_update();
+
+ old_cursor_pos = viewer.cursor_pos;
+ cursor_on = true;
+ update = false;
+ }
+ }
+
+ return false;
+}
diff --git a/apps/playlist_viewer.h b/apps/playlist_viewer.h
new file mode 100644
index 0000000..ecc5197
--- /dev/null
+++ b/apps/playlist_viewer.h
@@ -0,0 +1,26 @@
+/***************************************************************************
+ *
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2003 Hardeep Sidhu
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _PLAYLIST_VIEWER_H_
+#define _PLAYLIST_VIEWER_H_
+
+bool playlist_viewer(void);
+
+#endif
diff --git a/apps/wps.c b/apps/wps.c
index 7044369..a39f2ba 100644
--- a/apps/wps.c
+++ b/apps/wps.c
@@ -578,8 +578,6 @@ static bool menu(void)
status_set_param(false);
#endif
- wps_display(id3);
- wps_refresh(id3, 0, WPS_REFRESH_ALL);
return false;
}
@@ -627,6 +625,7 @@ int wps_show(void)
bool ignore_keyup = true;
bool restore = false;
bool exit = false;
+ bool update_track = false;
id3 = NULL;
current_track_path[0] = '\0';
@@ -902,6 +901,7 @@ int wps_show(void)
if (menu())
return SYS_USB_CONNECTED;
+ update_track = true;
restore = true;
break;
@@ -939,18 +939,24 @@ int wps_show(void)
return SYS_USB_CONNECTED;
case BUTTON_NONE: /* Timeout */
- if (update())
- {
- /* set dir browser to current playing song */
- if (global_settings.browse_current &&
- current_track_path[0] != '\0')
- set_current_file(current_track_path);
-
- return 0;
- }
+ update_track = true;
break;
}
+ if (update_track)
+ {
+ if (update())
+ {
+ /* set dir browser to current playing song */
+ if (global_settings.browse_current &&
+ current_track_path[0] != '\0')
+ set_current_file(current_track_path);
+
+ return 0;
+ }
+ update_track = false;
+ }
+
if (exit) {
#ifdef HAVE_LCD_CHARCELLS
status_set_record(false);
@@ -975,7 +981,6 @@ int wps_show(void)
return 0;
}
-
if ( button )
ata_spin();
diff --git a/firmware/drivers/lcd-recorder.c b/firmware/drivers/lcd-recorder.c
index 597c7d2..a409c9f 100644
--- a/firmware/drivers/lcd-recorder.c
+++ b/firmware/drivers/lcd-recorder.c
@@ -543,6 +543,29 @@ void lcd_invertrect (int x, int y, int nx, int ny)
INVERT_PIXEL((x + i), (y + j));
}
+/* Reverse the invert setting of the scrolling line (if any) at given char
+ position. Setting will go into affect next time line scrolls. */
+void lcd_invertscroll(int x, int y)
+{
+ struct scrollinfo* s;
+ int index;
+
+ for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
+ /* is this a scrolling line? */
+ if ( !(scrolling_lines&(1<<index)) )
+ continue;
+
+ s = &scroll[index];
+
+ if (s->startx == x && s->starty == y)
+ {
+ /* Found the line */
+ s->invert = !s->invert;
+ break;
+ }
+ }
+}
+
void lcd_drawline( int x1, int y1, int x2, int y2 )
{
int numpixels;
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h
index 3f3ea97..f4fa337 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -126,6 +126,7 @@ extern void lcd_clearrect (int x, int y, int nx, int ny);
extern void lcd_fillrect (int x, int y, int nx, int ny);
extern void lcd_drawrect (int x, int y, int nx, int ny);
extern void lcd_invertrect (int x, int y, int nx, int ny);
+extern void lcd_invertscroll(int x, int y);
extern void lcd_drawline( int x1, int y1, int x2, int y2 );
extern void lcd_clearline( int x1, int y1, int x2, int y2 );
extern void lcd_drawpixel(int x, int y);
diff --git a/uisimulator/win32/Makefile b/uisimulator/win32/Makefile
index cc5ac73..f7b562c 100644
--- a/uisimulator/win32/Makefile
+++ b/uisimulator/win32/Makefile
@@ -101,7 +101,7 @@ FIRMSRCS = $(LCDSRSC) id3.c mp3data.c usb.c mpeg.c powermgmt.c power.c \
APPS = main.c tree.c menu.c credits.c main_menu.c icons.c language.c \
playlist.c wps.c wps-display.c settings.c status.c \
screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\
- misc.c plugin.c
+ misc.c plugin.c playlist_viewer.c
MENUS = settings_menu.c sound_menu.c playlist_menu.c
@@ -200,6 +200,9 @@ $(OBJDIR)/onplay.o: $(APPDIR)/onplay.c
$(OBJDIR)/playlist.o: $(APPDIR)/playlist.c
$(CC) $(APPCFLAGS) -c $< -o $@
+$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c
+ $(CC) $(APPCFLAGS) -c $< -o $@
+
$(OBJDIR)/plugin.o: $(APPDIR)/plugin.c plugin-win32.h
$(CC) $(APPCFLAGS) -c $< -o $@
diff --git a/uisimulator/win32/rockbox.dsp b/uisimulator/win32/rockbox.dsp
index dd1ada6..ebfb98f 100644
--- a/uisimulator/win32/rockbox.dsp
+++ b/uisimulator/win32/rockbox.dsp
@@ -365,6 +365,10 @@ SOURCE=..\..\apps\playlist_menu.c
# End Source File
# Begin Source File
+SOURCE=..\..\apps\playlist_viewer.c
+# End Source File
+# Begin Source File
+
SOURCE=..\..\apps\plugin.c
# End Source File
# Begin Source File
diff --git a/uisimulator/x11/Makefile b/uisimulator/x11/Makefile
index 94f4581..c4b4779 100644
--- a/uisimulator/x11/Makefile
+++ b/uisimulator/x11/Makefile
@@ -101,7 +101,7 @@ FIRMSRCS = $(LCDSRSC) id3.c debug.c usb.c mpeg.c power.c\
APPS = main.c tree.c menu.c credits.c main_menu.c language.c\
playlist.c wps.c wps-display.c settings.c status.c icons.c\
screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\
- misc.c plugin.c
+ misc.c plugin.c playlist_viewer.c
MENUS = settings_menu.c sound_menu.c playlist_menu.c
@@ -197,6 +197,9 @@ $(OBJDIR)/onplay.o: $(APPDIR)/onplay.c
$(OBJDIR)/playlist.o: $(APPDIR)/playlist.c
$(CC) $(APPCFLAGS) -c $< -o $@
+$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c
+ $(CC) $(APPCFLAGS) -c $< -o $@
+
$(OBJDIR)/build.lang: $(APPDIR)/lang/$(LANGUAGE).lang
perl $(TOOLSDIR)/uplang $(APPDIR)/lang/english.lang $< > $@