Multi screen support for playlist viewer, some fixes in other gui files


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7901 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/gui/icon.c b/apps/gui/icon.c
index db69b4b..0cdee11 100644
--- a/apps/gui/icon.c
+++ b/apps/gui/icon.c
@@ -26,24 +26,30 @@
 void screen_put_iconxy(struct screen * display, int x, int y, ICON icon)
 {
 #ifdef HAVE_LCD_BITMAP
-    if(icon==0)/* Don't display invalid icons */
-        return;
     int xpos, ypos;
     xpos = x*CURSOR_WIDTH;
     ypos = y*display->char_height + display->getymargin();
+
     if ( display->char_height > CURSOR_HEIGHT )/* center the cursor */
         ypos += (display->char_height - CURSOR_HEIGHT) / 2;
-    display->mono_bitmap(icon, xpos, ypos, CURSOR_WIDTH, CURSOR_HEIGHT);
+    if(icon==0)/* Don't display invalid icons */
+        screen_clear_area(display, xpos, ypos, CURSOR_WIDTH, CURSOR_HEIGHT);
+    else
+        display->mono_bitmap(icon, xpos, ypos, CURSOR_WIDTH, CURSOR_HEIGHT);
 #else
-    display->putc(x, y, icon);
+    if(icon==-1)
+        display->putc(x, y, ' ');
+    else
+        display->putc(x, y, icon);
 #endif
 }
 
-void screen_put_cursorxy(struct screen * display, int x, int y)
+void screen_put_cursorxy(struct screen * display, int x, int y, bool on)
 {
 #ifdef HAVE_LCD_BITMAP
-    screen_put_iconxy(display, x, y, bitmap_icons_6x8[Icon_Cursor]);
+    screen_put_iconxy(display, x, y, on?bitmap_icons_6x8[Icon_Cursor]:0);
 #else
-    screen_put_iconxy(display, x, y, CURSOR_CHAR);
+    screen_put_iconxy(display, x, y, on?CURSOR_CHAR:-1);
 #endif
+
 }
diff --git a/apps/gui/icon.h b/apps/gui/icon.h
index 1208820..a9ae058 100644
--- a/apps/gui/icon.h
+++ b/apps/gui/icon.h
@@ -31,16 +31,20 @@
 #define CURSOR_CHAR 0x92
 #define CURSOR_WIDTH 6
 #define CURSOR_HEIGHT 8
+
 /*
- * Draws a cursor at a given position
+ * Draws a cursor at a given position, if th
  * - screen : the screen where we put the cursor
  * - x, y : the position, in character, not in pixel !!
+ * - on : true if the cursor must be shown, false if it must be erased
  */
-extern void screen_put_cursorxy(struct screen * screen, int x, int y);
+extern void screen_put_cursorxy(struct screen * screen, int x, int y, bool on);
 
 /*
  * Put an icon on a screen at a given position
  * (the position is given in characters)
+ * If the given icon is null (HAVE_LCD_BITMAP) or -1 otherwise, the icon
+ * at the given position will be erased
  * - screen : the screen where we put our icon
  * - x, y : the position, in character, not in pixel !!
  * - icon : the icon to put
diff --git a/apps/gui/list.c b/apps/gui/list.c
index c0b6ce2..df398ea 100644
--- a/apps/gui/list.c
+++ b/apps/gui/list.c
@@ -40,10 +40,8 @@
 
 
 void gui_list_init(struct gui_list * gui_list,
-    void (*callback_get_item_icon)
-        (int selected_item, void * data, ICON * icon),
-    char * (*callback_get_item_name)
-        (int selected_item, void * data, char *buffer),
+    list_get_icon callback_get_item_icon,
+    list_get_name callback_get_item_name,
     void * data
     )
 {
@@ -75,11 +73,10 @@
     gui_list->cursor_flash_state=!gui_list->cursor_flash_state;
     int selected_line=gui_list->selected_item-gui_list->start_item;
 #ifdef HAVE_LCD_BITMAP
-    int cursor_xpos=global_settings.scrollbar?1:0;
-    int line_xpos=display->getxmargin();
     int line_ypos=display->getymargin()+display->char_height*selected_line;
     if (global_settings.invert_cursor)
     {
+        int line_xpos=display->getxmargin();
         display->set_drawmode(DRMODE_COMPLEMENT);
         display->fillrect(line_xpos, line_ypos, display->width,
                           display->char_height);
@@ -88,19 +85,14 @@
     }
     else
     {
-        if(gui_list->cursor_flash_state)
-            screen_clear_area(display, cursor_xpos*SCROLLBAR_WIDTH, line_ypos,
-                              CURSOR_WIDTH, CURSOR_HEIGHT);
-        else
-            screen_put_cursorxy(display, cursor_xpos, selected_line);
+        int cursor_xpos=(global_settings.scrollbar &&
+                         display->nb_lines < gui_list->nb_items)?1:0;
+        screen_put_cursorxy(display, cursor_xpos, selected_line, gui_list->cursor_flash_state);
     }
     display->update_rect(0, line_ypos,display->width,
                          display->char_height);
 #else
-    if(gui_list->cursor_flash_state)
-        display->putc(0, selected_line, ' ');
-    else
-        screen_put_cursorxy(display, 0, selected_line);
+    screen_put_cursorxy(display, 0, selected_line, gui_list->cursor_flash_state);
     gui_textarea_update(display);
 #endif
 }
@@ -199,7 +191,7 @@
 #endif
 
             if(draw_cursor)
-                screen_put_cursorxy(display, cursor_pos, i);
+                screen_put_cursorxy(display, cursor_pos, i, true);
         }
         else
         {/* normal item */
@@ -371,10 +363,8 @@
  */
 void gui_synclist_init(
     struct gui_synclist * lists,
-    void (*callback_get_item_icon)
-        (int selected_item, void * data, ICON * icon),
-    char * (*callback_get_item_name)
-        (int selected_item, void * data, char *buffer),
+    list_get_icon callback_get_item_icon,
+    list_get_name callback_get_item_name,
     void * data
     )
 {
@@ -472,7 +462,7 @@
         gui_list_flash(&(lists->gui_list[i]));
 }
 
-bool gui_synclist_do_button(struct gui_synclist * lists, unsigned button)
+unsigned gui_synclist_do_button(struct gui_synclist * lists, unsigned button)
 {
     gui_synclist_limit_scroll(lists, true);
     switch(button)
@@ -489,7 +479,7 @@
 #endif
             gui_synclist_select_previous(lists);
             gui_synclist_draw(lists);
-            return true;
+            return LIST_PREV;
 
         case LIST_NEXT:
 #ifdef LIST_RC_NEXT
@@ -504,7 +494,7 @@
 #endif
             gui_synclist_select_next(lists);
             gui_synclist_draw(lists);
-            return true;
+            return LIST_NEXT;
 /* for pgup / pgdown, we are obliged to have a different behaviour depending on the screen
  * for which the user pressed the key since for example, remote and main screen doesn't
  * have the same number of lines*/
@@ -514,7 +504,7 @@
         case LIST_PGUP | BUTTON_REPEAT:
             gui_synclist_select_previous_page(lists, SCREEN_MAIN);
             gui_synclist_draw(lists);
-            return true;
+            return LIST_NEXT;
 #endif
 
 #ifdef LIST_RC_PGUP
@@ -523,7 +513,7 @@
         case LIST_RC_PGUP | BUTTON_REPEAT:
             gui_synclist_select_previous_page(lists, SCREEN_REMOTE);
             gui_synclist_draw(lists);
-            return true;
+            return LIST_NEXT;
 #endif
 
 #ifdef LIST_PGDN
@@ -532,7 +522,7 @@
         case LIST_PGDN | BUTTON_REPEAT:
             gui_synclist_select_next_page(lists, SCREEN_MAIN);
             gui_synclist_draw(lists);
-            return true;
+            return LIST_PREV;
 #endif
 
 #ifdef LIST_RC_PGDN
@@ -541,8 +531,8 @@
         case LIST_RC_PGDN | BUTTON_REPEAT:
             gui_synclist_select_next_page(lists, SCREEN_REMOTE);
             gui_synclist_draw(lists);
-            return true;
+            return LIST_PREV;
 #endif
     }
-    return false;
+    return 0;
 }
diff --git a/apps/gui/list.h b/apps/gui/list.h
index e587942..42a8677 100644
--- a/apps/gui/list.h
+++ b/apps/gui/list.h
@@ -33,24 +33,33 @@
 #define LIST_PREV      BUTTON_UP
 #define LIST_PGUP      (BUTTON_ON | BUTTON_UP)
 #define LIST_PGDN      (BUTTON_ON | BUTTON_DOWN)
+
+#ifdef CONFIG_REMOTE_KEYPAD
 #define LIST_RC_NEXT   BUTTON_RC_FF
 #define LIST_RC_PREV   BUTTON_RC_REW
 #define LIST_RC_PGUP   BUTTON_RC_SOURCE
 #define LIST_RC_PGDN   BUTTON_RC_BITRATE
+#endif /* CONFIG_REMOTE_KEYPAD */
 
 #elif CONFIG_KEYPAD == RECORDER_PAD
 #define LIST_NEXT      BUTTON_DOWN
 #define LIST_PREV      BUTTON_UP
 #define LIST_PGUP      (BUTTON_ON | BUTTON_UP)
 #define LIST_PGDN      (BUTTON_ON | BUTTON_DOWN)
+
+#ifdef CONFIG_REMOTE_KEYPAD
 #define LIST_RC_NEXT   BUTTON_RC_RIGHT
 #define LIST_RC_PREV   BUTTON_RC_LEFT
+#endif /* CONFIG_REMOTE_KEYPAD */
 
 #elif CONFIG_KEYPAD == PLAYER_PAD
 #define LIST_NEXT      BUTTON_RIGHT
 #define LIST_PREV      BUTTON_LEFT
+
+#ifdef CONFIG_REMOTE_KEYPAD
 #define LIST_RC_NEXT   BUTTON_RC_RIGHT
 #define LIST_RC_PREV   BUTTON_RC_LEFT
+#endif /* CONFIG_REMOTE_KEYPAD */
 
 #elif CONFIG_KEYPAD == ONDIO_PAD
 #define LIST_NEXT      BUTTON_DOWN
@@ -73,8 +82,21 @@
  * tells it what to display.
  * There are two callback function :
  * one to get the text and one to get the icon
- * Callback interface :
- *
+ */
+
+/*
+ * Icon callback
+ *  - selected_item : an integer that tells the number of the item to display
+ *  - data : a void pointer to the data you gave to the list when
+ *           you initialized it
+ *  - icon : a pointer to the icon, the value inside it is used to display
+ *           the icon after the function returns.
+ * Note : we use the ICON type because the real type depends of the plateform
+ */
+typedef void list_get_icon(int selected_item,
+                           void * data,
+                           ICON * icon);
+/*
  * Text callback
  *  - selected_item : an integer that tells the number of the item to display
  *  - data : a void pointer to the data you gave to the list when
@@ -84,15 +106,11 @@
  *             the return value of the function in all cases to avoid filling
  *             a buffer when it's not necessary)
  * Returns a pointer to a string that contains the text to display
- *
- * Icon callback
- *  - selected_item : an integer that tells the number of the item to display
- *  - data : a void pointer to the data you gave to the list when
- *           you initialized it
- *  - icon : a pointer to the icon, the value inside it is used to display
- *           the icon after the function returns.
- * Note : we use the ICON type because the real type depends of the plateform
  */
+typedef char * list_get_name(int selected_item,
+                             void * data,
+                             char *buffer);
+
 struct gui_list
 {
     int nb_items;
@@ -100,13 +118,10 @@
     bool cursor_flash_state;
     int start_item; /* the item that is displayed at the top of the screen */
 
-    void (*callback_get_item_icon)
-        (int selected_item, void * data, ICON * icon);
-    char * (*callback_get_item_name)
-        (int selected_item, void * data, char *buffer);
+    list_get_icon *callback_get_item_icon;
+    list_get_name *callback_get_item_name;
 
     struct screen * display;
-    int line_scroll_limit;
     /* defines wether the list should stop when reaching the top/bottom
      * or should continue (by going to bottom/top) */
     bool limit_scroll;
@@ -123,10 +138,8 @@
  *    to a given item number
  */
 extern void gui_list_init(struct gui_list * gui_list,
-    void (*callback_get_item_icon)
-        (int selected_item, void * data, ICON * icon),
-    char * (*callback_get_item_name)
-        (int selected_item, void * data, char *buffer),
+    list_get_icon callback_get_item_icon,
+    list_get_name callback_get_item_name,
     void * data
     );
 
@@ -264,10 +277,8 @@
 
 extern void gui_synclist_init(
     struct gui_synclist * lists,
-    void (*callback_get_item_icon)
-        (int selected_item, void * data, ICON * icon),
-    char * (*callback_get_item_name)
-        (int selected_item, void * data, char *buffer),
+    list_get_icon callback_get_item_icon,
+    list_get_name callback_get_item_name,
     void * data
     );
 extern void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items);
@@ -295,10 +306,13 @@
 
 /*
  * Do the action implied by the given button,
- * returns true if something has been done, false otherwise
+ * returns the action taken if any, 0 else
  *  - lists : the synchronized lists
  *  - button : the keycode of a pressed button
+ * returned value :
+ *  - LIST_NEXT when moving forward (next item or pgup)
+ *  - LIST_PREV when moving backward (previous item or pgdown)
  */
-extern bool gui_synclist_do_button(struct gui_synclist * lists, unsigned button);
+extern unsigned gui_synclist_do_button(struct gui_synclist * lists, unsigned button);
 
 #endif /* _GUI_LIST_H_ */
diff --git a/apps/gui/scrollbar.c b/apps/gui/scrollbar.c
index 837b084..73c523f 100644
--- a/apps/gui/scrollbar.c
+++ b/apps/gui/scrollbar.c
@@ -17,12 +17,10 @@
  *
  ****************************************************************************/
 
-#include "config.h"
-#include "lcd.h"
-#ifdef HAVE_LCD_BITMAP
-#include "limits.h"
 #include "scrollbar.h"
-#include "screen_access.h"
+#ifdef HAVE_LCD_BITMAP
+#include "config.h"
+#include "limits.h"
 
 void gui_scrollbar_draw(struct screen * screen, int x, int y,
                         int width, int height, int items,
diff --git a/apps/gui/scrollbar.h b/apps/gui/scrollbar.h
index 3c562b4..d7d0be7 100644
--- a/apps/gui/scrollbar.h
+++ b/apps/gui/scrollbar.h
@@ -19,10 +19,9 @@
 
 #ifndef _GUI_SCROLLBAR_H_
 #define _GUI_SCROLLBAR_H_
-#include <lcd.h>
-#ifdef HAVE_LCD_BITMAP
+#include "screen_access.h"
 
-struct screen;
+#ifdef HAVE_LCD_BITMAP
 
 enum orientation {
     VERTICAL,
diff --git a/apps/player/icons.h b/apps/player/icons.h
index dbce795..89aa5f7 100644
--- a/apps/player/icons.h
+++ b/apps/player/icons.h
@@ -28,6 +28,8 @@
 #ifdef HAVE_LCD_CHARCELLS
 
 enum {
+    Icon_Queued = 'Q',
+    Icon_Moving = 'M',
     Icon_Unknown = 0x90,
     Icon_Bookmark = 0x16,
     Icon_Plugin,
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index 70c1cf8..58f2740 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -17,7 +17,10 @@
  * KIND, either express or implied.
  *
  ****************************************************************************/
-
+/*
+ * Kevin Ferrare 2005/10/16
+ * multi-screen support, rewrote a lot of code
+ */
 #include <string.h>
 #include <sprintf.h>
 #include "playlist.h"
@@ -35,76 +38,24 @@
 #include "misc.h"
 #include "action.h"
 
-#ifdef HAVE_LCD_BITMAP
-#include "widgets.h"
-#endif
-
 #include "lang.h"
 
 #include "playlist_viewer.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 + \
-                             (global_settings.playlist_viewer_icons ? \
-                                ICON_WIDTH : 0))
-    #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
+#include "icon.h"
+#include "list.h"
+#include "statusbar.h"
+#include "splash.h"
 
 /* Maximum number of tracks we can have loaded at one time */
-#define MAX_PLAYLIST_ENTRIES 200
+#define MAX_PLAYLIST_ENTRIES 40
+
+/* The number of items between the selected one and the end/start of
+ * the buffer under which the buffer must reload */
+#define MIN_BUFFER_MARGIN screens[0].nb_lines
 
 /* Default playlist name for saving */
 #define DEFAULT_VIEWER_PLAYLIST_NAME "/viewer.m3u"
 
-/* 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 {
-    struct playlist_info* playlist; /* playlist being viewed                */
-    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             */
-    int 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 {
@@ -115,22 +66,60 @@
     bool skipped;               /* Is track marked as bad?                  */
 };
 
-static struct playlist_viewer_info  viewer;
-static struct playlist_entry        tracks[MAX_PLAYLIST_ENTRIES];
+enum direction
+{
+    FORWARD,
+    BACKWARD
+};
+
+struct playlist_buffer
+{
+    char *name_buffer;        /* Buffer used to store track names */
+    int buffer_size;          /* Size of name buffer */
+
+    int first_index;          /* Real index of first track loaded inside
+                                 the buffer */
+
+    enum direction direction; /* Direction of the buffer (if the buffer
+                                 was loaded BACKWARD, the last track in
+                                 the buffer has a real index < to the
+                                 real index of the the first track)*/
+
+    struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES];
+    int num_loaded;           /* Number of track entries loaded in buffer */
+};
+
+/* Global playlist viewer settings */
+struct playlist_viewer {
+    struct playlist_info* playlist; /* playlist being viewed                */
+    int num_tracks;             /* Number of tracks in playlist             */
+    int current_playing_track;  /* Index of current playing track           */
+    int selected_track;         /* The selected track, relative (first is 0)*/
+    int move_track;             /* Playlist index of track to move or -1    */
+    struct playlist_buffer buffer;
+};
+
+static struct playlist_viewer  viewer;
 
 /* Used when viewing playlists on disk */
 static struct playlist_info         temp_playlist;
 
-static bool initialize(char* filename, bool reload);
-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);
+void playlist_buffer_init(struct playlist_buffer * pb, char * names_buffer,
+                          int names_buffer_size);
+void playlist_buffer_load_entries(struct playlist_buffer * pb, int index,
+                                  enum direction direction);
+int playlist_entry_load(struct playlist_entry *entry, int index,
+                        char* name_buffer, int remaining_size);
+
+struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer * pb,
+                                                  int index);
+
+static bool playlist_viewer_init(struct playlist_viewer * viewer,
+                                 char* filename, bool reload);
+
 static void format_name(char* dest, const char* src);
 static void format_line(const struct playlist_entry* track, char* str, int len);
-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);
 static bool viewer_menu(void);
@@ -139,8 +128,148 @@
 static bool track_display(void);
 static bool save_playlist(void);
 
+void playlist_buffer_init(struct playlist_buffer * pb, char * names_buffer,
+                          int names_buffer_size)
+{
+    pb->name_buffer=names_buffer;
+    pb->buffer_size=names_buffer_size;
+    pb->first_index=0;
+    pb->num_loaded=0;
+}
+/*
+ * Loads the entries following 'index' in the playlist buffer
+ */
+void playlist_buffer_load_entries(struct playlist_buffer * pb, int index,
+                                  enum direction direction)
+{
+    int num_entries = viewer.num_tracks;
+    char* p = pb->name_buffer;
+    int remaining = pb->buffer_size;
+    int i;
+
+    pb->first_index = index;
+    if (num_entries > MAX_PLAYLIST_ENTRIES)
+        num_entries = MAX_PLAYLIST_ENTRIES;
+
+    for(i=0; i<num_entries; i++)
+    {
+        int len = playlist_entry_load(&(pb->tracks[i]), index, p, remaining);
+        if (len < 0)
+        {
+            /* Out of name buffer space */
+            num_entries = i;
+            break;
+        }
+
+        p += len;
+        remaining -= len;
+
+        if(direction==FORWARD)
+            index++;
+        else
+            index--;
+        index+=viewer.num_tracks;
+        index%=viewer.num_tracks;
+    }
+    pb->direction=direction;
+    pb->num_loaded = i;
+}
+
+void playlist_buffer_load_entries_screen(struct playlist_buffer * pb,
+                                         enum direction direction)
+{
+    if(direction==FORWARD)
+    {
+        int min_start=viewer.selected_track-2*screens[0].nb_lines;
+        if(min_start<0)
+            min_start=0;
+        playlist_buffer_load_entries(pb, min_start, FORWARD);
+    }
+     else
+    {
+        int max_start=viewer.selected_track+2*screens[0].nb_lines;
+        max_start%=viewer.num_tracks;
+        playlist_buffer_load_entries(pb, max_start, BACKWARD);
+    }
+}
+
+int playlist_entry_load(struct playlist_entry *entry, int index,
+                        char* name_buffer, int remaining_size)
+{
+    struct playlist_track_info info;
+    int len;
+
+    /* 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.playlist)) %
+        viewer.num_tracks;
+    if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
+        return -1;
+
+    len = strlen(info.filename) + 1;
+
+    if (len <= remaining_size)
+    {
+        strcpy(name_buffer, info.filename);
+
+        entry->name = name_buffer;
+        entry->index = info.index;
+        entry->display_index = info.display_index;
+        entry->queued = info.attr & PLAYLIST_ATTR_QUEUED;
+        entry->skipped = info.attr & PLAYLIST_ATTR_SKIPPED;
+        return len;
+    }
+    return -1;
+}
+
+int playlist_buffer_get_index(struct playlist_buffer * pb, int index )
+{
+    int buffer_index;
+    if(pb->direction==FORWARD)
+    {
+        if(index>=pb->first_index)
+            buffer_index=index-pb->first_index;
+        else /* rotation : track0 in buffer + requested track */
+            buffer_index=(viewer.num_tracks-pb->first_index)+(index);
+    }
+    else
+    {
+        if(index<=pb->first_index)
+            buffer_index=pb->first_index-index;
+        else /* rotation : track0 in buffer + dist from the last track
+                to the requested track (num_tracks-requested track) */
+            buffer_index=(pb->first_index)+(viewer.num_tracks-index);
+    }
+    return(buffer_index);
+}
+
+#define distance(a, b) \
+    a>b? (a) - (b) : (b) - (a)
+bool playlist_buffer_needs_reload(struct playlist_buffer * pb, int track_index)
+{
+    if(pb->num_loaded==viewer.num_tracks)
+        return(false);
+    int selected_index=playlist_buffer_get_index(pb, track_index);
+    int first_buffer_index=playlist_buffer_get_index(pb, pb->first_index);
+    int distance_beginning=distance(selected_index, first_buffer_index);
+    if(distance_beginning<MIN_BUFFER_MARGIN)
+        return(true);
+
+    if(pb->num_loaded - distance_beginning < MIN_BUFFER_MARGIN)
+       return(true);
+    return(false);
+}
+
+struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer * pb,
+                                                  int index)
+{
+    int buffer_index=playlist_buffer_get_index(pb, index);
+    return(&(pb->tracks[buffer_index]));
+}
+
 /* Initialize the playlist viewer. */
-static bool initialize(char* filename, bool reload)
+static bool playlist_viewer_init(struct playlist_viewer * viewer,
+                                 char* filename, bool reload)
 {
     char* buffer;
     int buffer_size;
@@ -155,15 +284,15 @@
         return false;
 
     if (!filename)
-        viewer.playlist = NULL;
+        viewer->playlist = NULL;
     else
     {
         /* Viewing playlist on disk */
         char *dir, *file, *temp_ptr;
         char *index_buffer = NULL;
         int  index_buffer_size = 0;
-        
-        viewer.playlist = &temp_playlist;
+
+        viewer->playlist = &temp_playlist;
 
         /* Separate directory from filename */
         temp_ptr = strrchr(filename+1,'/');
@@ -187,7 +316,7 @@
             index_buffer = buffer;
         }
 
-        playlist_create_ex(viewer.playlist, dir, file, index_buffer,
+        playlist_create_ex(viewer->playlist, dir, file, index_buffer,
             index_buffer_size, buffer+index_buffer_size,
             buffer_size-index_buffer_size);
 
@@ -197,201 +326,18 @@
         buffer += index_buffer_size;
         buffer_size -= index_buffer_size;
     }
+    playlist_buffer_init(&viewer->buffer, buffer, buffer_size );
 
-    viewer.name_buffer = buffer;
-    viewer.buffer_size = buffer_size;
-
-#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.move_track = -1;
+    viewer->move_track = -1;
 
     if (!reload)
-    {
-        viewer.cursor_pos = 0;
-
-        if (!viewer.playlist)
-            /* Start displaying at current playing track */
-            viewer.first_display_index = playlist_get_display_index() - 1;
-        else
-            viewer.first_display_index = 0;
-
-        update_first_index();
-    }
+        viewer->selected_track = 0;
 
     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-1;
-
-    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;
-
-    /* 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.playlist)) %
-        viewer.num_tracks;
-    if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
-        return -1;
-    
-    len = strlen(info.filename) + 1;
-    
-    if (len <= size)
-    {
-        strcpy(p, info.filename);
-        
-        tracks[pos].name = p;
-        tracks[pos].index = info.index;
-        tracks[pos].display_index = info.display_index;
-        tracks[pos].queued = info.attr & PLAYLIST_ATTR_QUEUED;
-        tracks[pos].skipped = info.attr & PLAYLIST_ATTR_SKIPPED;
-        
-        result = len;
-    }
-    else
-        result = -1;
-
-    return result;
-}
-
 /* Format trackname for display purposes */
 static void format_name(char* dest, const char* src)
 {
@@ -403,10 +349,10 @@
             /* Only display the mp3 filename */
             char* p = strrchr(src, '/');
             int len;
-            
+
             strcpy(dest, p+1);
             len = strlen(dest);
-            
+
             /* Remove the extension */
             if (!strcasecmp(&dest[len-4], ".mp3") ||
                 !strcasecmp(&dest[len-4], ".mp2") ||
@@ -441,205 +387,6 @@
 
 }
 
-/* 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++)
-    {
-        if (global_settings.playlist_viewer_icons)
-        {
-            /* 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_mono_bitmap(bitmap_icons_6x8[Icon_Audio],
-                    CURSOR_X * 6 + CURSOR_WIDTH,
-                    MARGIN_Y+(i*viewer.line_height) + offset, 6, 8);
-#else
-                lcd_putc(LINE_X-1, i, Icon_Audio);
-#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];
-    
-    format_line(&tracks[INDEX(line)], str, sizeof(str));
-
-    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_ex(viewer.playlist);
-
-    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)
 {
@@ -647,32 +394,18 @@
         playlist_get_resume_info(&viewer.current_playing_track);
     else
         viewer.current_playing_track = -1;
-        
-    if (force || playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
+    int nb_tracks=playlist_amount_ex(viewer.playlist);
+    force=force || nb_tracks != viewer.num_tracks;
+    if (force)
     {
-        int index;
-
         /* Reload tracks */
-        viewer.num_tracks = playlist_amount_ex(viewer.playlist);
+        viewer.num_tracks = nb_tracks;
         if (viewer.num_tracks < 0)
             return false;
-        
-        index = viewer.first_display_index;
-
-        load_playlist_entries(index);
-
-        if (viewer.num_loaded <= 0)
+        playlist_buffer_load_entries_screen(&viewer.buffer, FORWARD);
+        if (viewer.buffer.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;
 }
 
@@ -683,7 +416,9 @@
 {
     struct menu_item items[3]; /* increase this if you add entries! */
     int m, i=0, result, ret = 0;
-    bool current = (tracks[index].index == viewer.current_playing_track);
+    struct playlist_entry * current_track=
+        playlist_buffer_get_track(&viewer.buffer, index);
+    bool current = (current_track->index == viewer.current_playing_track);
 
     items[i].desc = ID2P(LANG_REMOVE);
     i++;
@@ -707,13 +442,14 @@
         {
             case 0:
                 /* delete track */
-                playlist_delete(viewer.playlist, tracks[index].index);
-
+                playlist_delete(viewer.playlist, current_track->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 ||
+                    current_track=
+                        playlist_buffer_get_track(&viewer.buffer, index);
+                    if (current_track->display_index != viewer.num_tracks ||
                         global_settings.repeat_mode == REPEAT_ALL)
                     {
                         talk_buffer_steal(); /* will use the mp3 buffer */
@@ -721,30 +457,26 @@
                         viewer.current_playing_track = -1;
                     }
                 }
-
                 ret = 1;
                 break;
             case 1:
                 /* move track */
-                viewer.move_track = tracks[index].index;
+                viewer.move_track = current_track->index;
                 ret = 0;
                 break;
             case 2:
             {
-                onplay(tracks[index].name, TREE_ATTR_MPA, CONTEXT_TREE);
+                onplay(current_track->name, TREE_ATTR_MPA, CONTEXT_TREE);
 
                 if (!viewer.playlist)
                     ret = 1;
                 else
                     ret = 0;
-
                 break;
             }
         }
     }
-
     menu_exit(m);
-
     return ret;
 }
 
@@ -792,7 +524,7 @@
         { STR(LANG_DISPLAY_TRACK_NAME_ONLY) },
         { STR(LANG_DISPLAY_FULL_PATH) }
     };
-    
+
     return set_option(str(LANG_TRACK_DISPLAY), 
         &global_settings.playlist_viewer_track_display, INT, names, 2, NULL);
 }
@@ -807,7 +539,6 @@
     if (!kbd_input(filename, sizeof(filename)))
     {
         playlist_save(viewer.playlist, filename);
-        
         /* reload in case playlist was saved to cwd */
         reload_directory();
     }
@@ -821,63 +552,89 @@
     return playlist_viewer_ex(NULL);
 }
 
+
+
+
+char * playlist_callback_name(int selected_item, void * data, char *buffer)
+{
+    struct playlist_viewer * local_viewer = (struct playlist_viewer *)data;
+    struct playlist_entry *track=
+        playlist_buffer_get_track(&(local_viewer->buffer), selected_item);
+    format_line(track, buffer, MAX_PATH);
+    return(buffer);
+}
+
+
+void playlist_callback_icons(int selected_item, void * data, ICON * icon)
+{
+    struct playlist_viewer * local_viewer=(struct playlist_viewer *)data;
+    struct playlist_entry *track=
+        playlist_buffer_get_track(&(local_viewer->buffer), selected_item);
+    if (track->index == local_viewer->current_playing_track)
+    {
+        /* Current playing track */
+#ifdef HAVE_LCD_BITMAP
+        *icon=bitmap_icons_6x8[Icon_Audio];
+#else
+        *icon=Icon_Audio;
+#endif
+    }
+    else if (track->index == local_viewer->move_track)
+    {
+        /* Track we are moving */
+#ifdef HAVE_LCD_BITMAP
+        *icon=bitmap_icons_6x8[Icon_Moving];
+#else
+        *icon=Icon_Moving;
+#endif
+    }
+    else if (track->queued)
+    {
+        /* Queued track */
+#ifdef HAVE_LCD_BITMAP
+        *icon=bitmap_icons_6x8[Icon_Queued];
+#else
+        *icon=Icon_Queued;
+#endif
+    }
+    else
+        *icon=0;
+}
+
+
+
 /* Main viewer function.  Filename identifies playlist to be viewed.  If NULL,
    view current playlist. */
 bool playlist_viewer_ex(char* filename)
 {
     bool ret = false;       /* return value */
     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, lastbutton = BUTTON_NONE;
-
-    if (!initialize(filename, false))
+    struct gui_synclist playlist_lists;
+    if (!playlist_viewer_init(&viewer, filename, false))
         goto exit;
 
-    old_cursor_pos = viewer.cursor_pos;
-
+    gui_synclist_init(&playlist_lists, playlist_callback_icons,
+                      playlist_callback_name, &viewer);
+    gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks);
+    gui_synclist_select_item(&playlist_lists, viewer.selected_track);
+    gui_synclist_draw(&playlist_lists);
     while (!exit)
     {
         int track;
-
         if (!viewer.playlist && !(audio_status() & AUDIO_STATUS_PLAY))
         {
             /* Play has stopped */
 #ifdef HAVE_LCD_CHARCELLS
-            splash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
+            gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
 #else
-            splash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
+            gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
 #endif
             goto exit;
         }
 
-        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_set_drawmode(DRMODE_COMPLEMENT);
-                lcd_fillrect(
-                    MARGIN_X, MARGIN_Y+(viewer.cursor_pos*viewer.line_height),
-                    LCD_WIDTH, viewer.line_height);
-                lcd_set_drawmode(DRMODE_SOLID);
-                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
-        }
+        if (viewer.move_track != -1)
+            gui_synclist_flash(&playlist_lists);
 
         if (!viewer.playlist)
             playlist_get_resume_info(&track);
@@ -888,19 +645,27 @@
             playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
         {
             /* Playlist has changed (new track started?) */
-            update_first_index();
             if (!update_playlist(false))
                 goto exit;
-            else
-                update = true;
-
+            gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks);
             /* Abort move on playlist change */
             viewer.move_track = -1;
         }
 
         /* Timeout so we can determine if play status has changed */
         button = button_get_w_tmo(HZ/2);
-
+        int list_action;
+        if( (list_action=gui_synclist_do_button(&playlist_lists, button))!=0 )
+        {
+            viewer.selected_track=gui_synclist_get_sel_pos(&playlist_lists);
+            if(playlist_buffer_needs_reload(&viewer.buffer, viewer.selected_track))
+                playlist_buffer_load_entries_screen(&viewer.buffer,
+                    list_action==LIST_NEXT?
+                        FORWARD
+                        :
+                        BACKWARD
+                    );
+        }
         switch (button)
         {
             case TREE_EXIT:
@@ -910,48 +675,20 @@
                 exit = true;
                 break;
 
-            case TREE_PREV:
-            case TREE_PREV | BUTTON_REPEAT:
-                scroll_display(-1);
-                update = true;
-                break;
-
-            case TREE_NEXT:
-            case TREE_NEXT | BUTTON_REPEAT:
-                scroll_display(1);
-                update = true;
-                break;
-
-#ifdef TREE_PGUP
-            case TREE_PGUP:
-            case TREE_PGUP | BUTTON_REPEAT:
-                /* Pageup */
-                scroll_display(-viewer.num_display_lines);
-                update = true;
-                break;
-
-            case TREE_PGDN:
-            case TREE_PGDN | BUTTON_REPEAT:
-                /* Pagedown */
-                scroll_display(viewer.num_display_lines);
-                update = true;
-                break;
-#endif
-
             case TREE_RUN:
 #ifdef TREE_RUN_PRE
                 if (lastbutton != TREE_RUN_PRE)
                     break;
 #endif
+                struct playlist_entry * current_track=playlist_buffer_get_track(&viewer.buffer, viewer.selected_track);
                 if (viewer.move_track >= 0)
                 {
                     /* Move track */
                     int ret;
 
-                    ret = playlist_move(viewer.playlist, viewer.move_track,
-                        tracks[INDEX(viewer.cursor_pos)].index);
+                    ret = playlist_move(viewer.playlist, viewer.move_track, current_track->index);
                     if (ret < 0)
-                        splash(HZ, true, str(LANG_MOVE_FAILED));
+                        gui_syncsplash(HZ, true, str(LANG_MOVE_FAILED));
 
                     update_playlist(true);
                     viewer.move_track = -1;
@@ -959,7 +696,7 @@
                 else if (!viewer.playlist)
                 {
                     /* play new track */
-                    playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
+                    playlist_start(current_track->index, 0);
                     update_playlist(false);
                 }
                 else
@@ -968,15 +705,14 @@
                     if (playlist_set_current(viewer.playlist) < 0)
                         goto exit;
 
-                    playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
+                    playlist_start(current_track->index, 0);
 
                     /* Our playlist is now the current list */
-                    if (!initialize(NULL, true))
+                    if (!playlist_viewer_init(&viewer, NULL, true))
                         goto exit;
                 }
+                gui_synclist_draw(&playlist_lists);
 
-                display_playlist();
-                update = true;
                 break;
 
             case TREE_CONTEXT:
@@ -987,7 +723,7 @@
                 /* ON+PLAY menu */
                 int ret;
 
-                ret = onplay_menu(INDEX(viewer.cursor_pos));
+                ret = onplay_menu(viewer.selected_track);
 
                 if (ret < 0)
                 {
@@ -997,15 +733,12 @@
                 else if (ret > 0)
                 {
                     /* Playlist changed */
-                    update_first_index();
-                    update_playlist(false);
+                    gui_synclist_del_item(&playlist_lists);
+                    update_playlist(true);
                     if (viewer.num_tracks <= 0)
                         exit = true;
                 }
-                else
-                    display_playlist();
-
-                update = true;
+                gui_synclist_draw(&playlist_lists);
                 break;
             }
 
@@ -1015,13 +748,11 @@
                     ret = true;
                     goto exit;
                 }
-               
-                display_playlist();
-                update = true;
+                gui_synclist_draw(&playlist_lists);
                 break;
 
             case BUTTON_NONE:
-                status_draw(false);
+                gui_syncstatusbar_draw(&statusbars, false);
                 break;
 
             default:
@@ -1032,36 +763,6 @@
                 }
                 break;
         }
-
-        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;
-        }
         lastbutton = button;
     }
 
diff --git a/apps/recorder/icons.c b/apps/recorder/icons.c
index 148235f..66ac3cb 100644
--- a/apps/recorder/icons.c
+++ b/apps/recorder/icons.c
@@ -47,6 +47,8 @@
     { 0x4e, 0x51, 0x51, 0x40, 0x55, 0x55 }, /* Config file */
     { 0x0a, 0x0a, 0x5f, 0x4e, 0x24, 0x18 }, /* Plugin file */
     { 0xff, 0x81, 0xaf, 0xaa, 0x8c, 0xf8 }, /* Bookmark file */
+    { 0x77, 0x55, 0x55, 0x55, 0x55, 0x77 }, /* Queued Item */
+    { 0x3e, 0x41, 0x3e, 0x1c, 0x1c, 0x08 }, /* Moving Item */
 };
 
 const unsigned char bitmap_icons_7x8[][7] =
diff --git a/apps/recorder/icons.h b/apps/recorder/icons.h
index c8241b4..5223473 100644
--- a/apps/recorder/icons.h
+++ b/apps/recorder/icons.h
@@ -44,6 +44,8 @@
     Icon_Config,
     Icon_Plugin,
     Icon_Bookmark,
+    Icon_Queued,
+    Icon_Moving,
     LastIcon
 };
 
diff --git a/apps/screen_access.c b/apps/screen_access.c
index 1d2535fb..dfaba4d 100644
--- a/apps/screen_access.c
+++ b/apps/screen_access.c
@@ -150,13 +150,6 @@
     gui_textarea_update_nblines(screen);
 }
 
-void screen_access_init(void)
-{
-    int i;
-    FOR_NB_SCREENS(i)
-        screen_init(&screens[i], i);
-}
-
 #ifdef HAVE_LCD_BITMAP
 void screen_clear_area(struct screen * display, int xstart, int ystart,
                        int width, int height)
@@ -166,3 +159,10 @@
     display->set_drawmode(DRMODE_SOLID);
 }
 #endif
+
+void screen_access_init(void)
+{
+    int i;
+    FOR_NB_SCREENS(i)
+        screen_init(&screens[i], i);
+}