Commit viewports-in-WPS patch (FS#8385).  This adds the %V tag - see the CustomWPS page for details (shortly...).  There is still some work to do - decide how to handle font references, decide how to handle conditionals.  Plus checkwps is broken - I'll fix that in a separate commit.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16733 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/filetree.c b/apps/filetree.c
index edd421c..34bed71 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -478,7 +478,7 @@
 #if LCD_DEPTH > 1
                 unload_wps_backdrop();
 #endif
-                wps_data_load(gui_wps[0].data, buf, true);
+                wps_data_load(gui_wps[0].data, &screens[0], buf, true);
                 set_file(buf, (char *)global_settings.wps_file,
                          MAX_FILENAME);
                 break;
@@ -490,7 +490,7 @@
 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
                 unload_remote_wps_backdrop();
 #endif
-                wps_data_load(gui_wps[1].data, buf, true);
+                wps_data_load(gui_wps[1].data, &screens[1], buf, true);
                 set_file(buf, (char *)global_settings.rwps_file,
                          MAX_FILENAME);
                 break;
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
index cfaabaf..ae5492a 100644
--- a/apps/gui/gwps-common.c
+++ b/apps/gui/gwps-common.c
@@ -295,6 +295,19 @@
     {
         FOR_NB_SCREENS(i)
         {
+            /* Update the values in the first (default) viewport - in case the user
+               has modified the statusbar or colour settings */
+#ifdef HAVE_LCD_BITMAP
+            gui_wps[i].data->viewports[0].vp.ymargin = gui_wps[i].display->getymargin();
+#if LCD_DEPTH > 1
+            if (gui_wps[i].display->depth > 1)
+            {
+                gui_wps[i].data->viewports[0].vp.fg_pattern = gui_wps[i].display->get_foreground();
+                gui_wps[i].data->viewports[0].vp.bg_pattern = gui_wps[i].display->get_background();
+            }
+#endif
+#endif
+
             gui_wps[i].display->clear_display();
             if (!gui_wps[i].data->wps_loaded) {
                 if ( !gui_wps[i].data->num_tokens ) {
@@ -306,6 +319,7 @@
                         unload_wps_backdrop();
 #endif
                         wps_data_load(gui_wps[i].data,
+                                      gui_wps[i].display,
                                       "%s%?it<%?in<%in. |>%it|%fn>\n"
                                       "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
                                       "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
@@ -316,6 +330,7 @@
                                       "%pm\n", false);
 #else
                         wps_data_load(gui_wps[i].data,
+                                      gui_wps[i].display,
                                       "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
                                       "%pc%?ps<*|/>%pt\n", false);
 #endif
@@ -328,6 +343,7 @@
                          unload_remote_wps_backdrop();
 #endif
                          wps_data_load(gui_wps[i].data,
+                                       gui_wps[i].display,
                                        "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
                                        "%s%?it<%?in<%in. |>%it|%fn>\n"
                                        "%al%pc/%pt%ar[%pp:%pe]\n"
@@ -448,7 +464,7 @@
     struct wps_data *data = gwps->data;
     struct screen *display = gwps->display;
     struct wps_state *state = gwps->state;
-    int h = font_get(FONT_UI)->height;
+    int h = font_get(display->getfont())->height;
 
     int sb_y;
     if (data->progress_top < 0)
@@ -459,7 +475,7 @@
         sb_y = data->progress_top;
 
     if (!data->progress_end)
-        data->progress_end=display->width;
+        data->progress_end=display->getwidth();
 
     if (gwps->data->progressbar.have_bitmap_pb)
         gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
@@ -529,7 +545,7 @@
 #endif
 }
 
-static void wps_display_images(struct gui_wps *gwps)
+static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
 {
     if(!gwps || !gwps->data || !gwps->display)
         return;
@@ -541,7 +557,8 @@
     for (n = 0; n < MAX_IMAGES; n++)
     {
         if (data->img[n].loaded &&
-            (data->img[n].display || data->img[n].always_display))
+            (data->img[n].display || 
+             (data->img[n].always_display && data->img[n].vp == vp)))
         {
             wps_draw_image(gwps, n);
         }
@@ -1449,7 +1466,7 @@
    The return value indicates whether the line needs to be updated.
 */
 static bool get_line(struct gui_wps *gwps,
-                     int line, int subline,
+                     int v, int line, int subline,
                      struct align_pos *align,
                      char *linebuf,
                      int linebuf_size)
@@ -1477,8 +1494,8 @@
 #endif
 
     /* Process all tokens of the desired subline */
-    last_token_idx = wps_last_token_index(data, line, subline);
-    for (i = wps_first_token_index(data, line, subline);
+    last_token_idx = wps_last_token_index(data, v, line, subline);
+    for (i = wps_first_token_index(data, v, line, subline);
          i <= last_token_idx; i++)
     {
         switch(data->tokens[i].type)
@@ -1577,16 +1594,16 @@
     return update;
 }
 
-static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
+static void get_subline_timeout(struct gui_wps *gwps, int v, int line, int subline)
 {
     struct wps_data *data = gwps->data;
     int i;
-    int subline_idx = wps_subline_index(data, line, subline);
-    int last_token_idx = wps_last_token_index(data, line, subline);
+    int subline_idx = wps_subline_index(data, v, line, subline);
+    int last_token_idx = wps_last_token_index(data, v, line, subline);
 
     data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
 
-    for (i = wps_first_token_index(data, line, subline);
+    for (i = wps_first_token_index(data, v, line, subline);
          i <= last_token_idx; i++)
     {
         switch(data->tokens[i].type)
@@ -1614,7 +1631,7 @@
 
 /* Calculates which subline should be displayed for the specified line
    Returns true iff the subline must be refreshed */
-static bool update_curr_subline(struct gui_wps *gwps, int line)
+static bool update_curr_subline(struct gui_wps *gwps, int v, int line)
 {
     struct wps_data *data = gwps->data;
 
@@ -1623,13 +1640,13 @@
     bool new_subline_refresh;
     bool only_one_subline;
 
-    num_sublines = data->lines[line].num_sublines;
-    reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
+    num_sublines = data->viewports[v].lines[line].num_sublines;
+    reset_subline = (data->viewports[v].lines[line].curr_subline == SUBLINE_RESET);
     new_subline_refresh = false;
     only_one_subline = false;
 
     /* if time to advance to next sub-line  */
-    if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) ||
+    if (TIME_AFTER(current_tick, data->viewports[v].lines[line].subline_expire_time - 1) ||
         reset_subline)
     {
         /* search all sublines until the next subline with time > 0
@@ -1637,46 +1654,46 @@
         if (reset_subline)
             search_start = 0;
         else
-            search_start = data->lines[line].curr_subline;
+            search_start = data->viewports[v].lines[line].curr_subline;
 
         for (search = 0; search < num_sublines; search++)
         {
-            data->lines[line].curr_subline++;
+            data->viewports[v].lines[line].curr_subline++;
 
             /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
-            if (data->lines[line].curr_subline == num_sublines)
+            if (data->viewports[v].lines[line].curr_subline == num_sublines)
             {
-                if (data->lines[line].curr_subline == 1)
+                if (data->viewports[v].lines[line].curr_subline == 1)
                     only_one_subline = true;
-                data->lines[line].curr_subline = 0;
+                data->viewports[v].lines[line].curr_subline = 0;
             }
 
             /* if back where we started after search or
                 only one subline is defined on the line */
             if (((search > 0) &&
-                 (data->lines[line].curr_subline == search_start)) ||
+                 (data->viewports[v].lines[line].curr_subline == search_start)) ||
                 only_one_subline)
             {
                 /* no other subline with a time > 0 exists */
-                data->lines[line].subline_expire_time = (reset_subline ?
+                data->viewports[v].lines[line].subline_expire_time = (reset_subline ?
                     current_tick :
-                    data->lines[line].subline_expire_time) + 100 * HZ;
+                    data->viewports[v].lines[line].subline_expire_time) + 100 * HZ;
                 break;
             }
             else
             {
                 /* get initial time multiplier for this subline */
-                get_subline_timeout(gwps, line, data->lines[line].curr_subline);
+                get_subline_timeout(gwps, v, line, data->viewports[v].lines[line].curr_subline);
 
-                int subline_idx = wps_subline_index(data, line,
-                                               data->lines[line].curr_subline);
+                int subline_idx = wps_subline_index(data, v, line,
+                                               data->viewports[v].lines[line].curr_subline);
 
                 /* only use this subline if subline time > 0 */
                 if (data->sublines[subline_idx].time_mult > 0)
                 {
                     new_subline_refresh = true;
-                    data->lines[line].subline_expire_time = (reset_subline ?
-                        current_tick : data->lines[line].subline_expire_time) +
+                    data->viewports[v].lines[line].subline_expire_time = (reset_subline ?
+                        current_tick : data->viewports[v].lines[line].subline_expire_time) +
                         BASE_SUBLINE_TIME*data->sublines[subline_idx].time_mult;
                     break;
                 }
@@ -1724,10 +1741,10 @@
     }
 
     left_xpos = display->getxmargin();
-    right_xpos = (display->width - right_width);
-    center_xpos = (display->width + left_xpos - center_width) / 2;
+    right_xpos = (display->getwidth() - right_width);
+    center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
 
-    scroll_width = display->width - left_xpos;
+    scroll_width = display->getwidth() - left_xpos;
 
     /* Checks for overlapping strings.
         If needed the overlapping strings will be merged, separated by a
@@ -1767,7 +1784,7 @@
         format_align->right = format_align->center;
         /* calculate the new width and position of the merged string */
         right_width = center_width + space_width + right_width;
-        right_xpos = (display->width - right_width);
+        right_xpos = (display->getwidth() - right_width);
         /* there is no centered string anymore */
         center_width = 0;
     }
@@ -1778,7 +1795,7 @@
         format_align->right = format_align->center;
         /* calculate the new width and position of the string */
         right_width = center_width;
-        right_xpos = (display->width - right_width);
+        right_xpos = (display->getwidth() - right_width);
         /* there is no centered string anymore */
         center_width = 0;
     }
@@ -1823,7 +1840,7 @@
 #ifdef HAVE_LCD_BITMAP
         /* clear the line first */
         display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
-        display->fillrect(left_xpos, ypos, display->width, string_height);
+        display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
         display->set_drawmode(DRMODE_SOLID);
 #endif
 
@@ -1862,7 +1879,7 @@
     if(!gwps || !data || !state || !display)
         return false;
 
-    int line, i, subline_idx;
+    int v, line, i, subline_idx;
     unsigned char flags;
     char linebuf[MAX_PATH];
 
@@ -1885,19 +1902,19 @@
     */
     bool enable_pm = false;
 
-    /* Set images to not to be displayed */
-    for (i = 0; i < MAX_IMAGES; i++)
-    {
-        data->img[i].display = false;
-    }
 #endif
 
     /* reset to first subline if refresh all flag is set */
     if (refresh_mode == WPS_REFRESH_ALL)
     {
-        for (i = 0; i < data->num_lines; i++)
+        display->clear_display();
+
+        for (v = 0; v < data->num_viewports; v++)
         {
-            data->lines[i].curr_subline = SUBLINE_RESET;
+            for (i = 0; i < data->viewports[v].num_lines; i++)
+            {
+                data->viewports[v].lines[i].curr_subline = SUBLINE_RESET;
+            }
         }
     }
 
@@ -1917,88 +1934,114 @@
 
     state->ff_rewind_count = ffwd_offset;
 
-    for (line = 0; line < data->num_lines; line++)
+    for (v = 0; v < data->num_viewports; v++)
     {
-        memset(linebuf, 0, sizeof(linebuf));
-        update_line = false;
+        display->set_viewport(&data->viewports[v].vp);
 
-        /* get current subline for the line */
-        new_subline_refresh = update_curr_subline(gwps, line);
-
-        subline_idx = wps_subline_index(data, line,
-                                        data->lines[line].curr_subline);
-        flags = data->sublines[subline_idx].line_type;
-
-        if (refresh_mode == WPS_REFRESH_ALL || (flags & refresh_mode)
-            || new_subline_refresh)
+        if (refresh_mode == WPS_REFRESH_ALL)
         {
-            /* get_line tells us if we need to update the line */
-            update_line = get_line(gwps, line, data->lines[line].curr_subline,
-                                   &align, linebuf, sizeof(linebuf));
+            display->clear_viewport();
         }
 
 #ifdef HAVE_LCD_BITMAP
-        /* progressbar */
-        if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+        /* Set images to not to be displayed */
+        for (i = 0; i < MAX_IMAGES; i++)
         {
-            /* the progressbar should be alone on its line */
-            update_line = false;
-            draw_progressbar(gwps, line);
-        }
-
-        /* peakmeter */
-        if (flags & refresh_mode & WPS_REFRESH_PEAK_METER)
-        {
-            /* the peakmeter should be alone on its line */
-            update_line = false;
-
-            int h = font_get(FONT_UI)->height;
-            int peak_meter_y = display->getymargin() + line * h;
-
-            /* The user might decide to have the peak meter in the last
-                line so that it is only displayed if no status bar is
-                visible. If so we neither want do draw nor enable the
-                peak meter. */
-            if (peak_meter_y + h <= display->height) {
-                /* found a line with a peak meter -> remember that we must
-                    enable it later */
-                enable_pm = true;
-                peak_meter_screen(gwps->display, 0, peak_meter_y,
-                                  MIN(h, display->height - peak_meter_y));
-            }
-        }
-
-#else /* HAVE_LCD_CHARCELL */
-
-        /* progressbar */
-        if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
-        {
-            if (data->full_line_progressbar)
-                draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
-            else
-                draw_player_progress(gwps);
+            data->img[i].display = false;
         }
 #endif
 
-        if (update_line)
+        for (line = 0; line < data->viewports[v].num_lines; line++)
         {
-            if (flags & WPS_REFRESH_SCROLL)
+            memset(linebuf, 0, sizeof(linebuf));
+            update_line = false;
+
+            /* get current subline for the line */
+            new_subline_refresh = update_curr_subline(gwps, v, line);
+
+            subline_idx = wps_subline_index(data, v, line,
+                                            data->viewports[v].lines[line].curr_subline);
+            flags = data->sublines[subline_idx].line_type;
+
+            if (refresh_mode == WPS_REFRESH_ALL || (flags & refresh_mode)
+                || new_subline_refresh)
             {
-                /* if the line is a scrolling one we don't want to update
-                   too often, so that it has the time to scroll */
-                if ((refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
-                    write_line(display, &align, line, true);
+                /* get_line tells us if we need to update the line */
+                update_line = get_line(gwps, v, line, data->viewports[v].lines[line].curr_subline,
+                                       &align, linebuf, sizeof(linebuf));
             }
-            else
-                write_line(display, &align, line, false);
+
+#ifdef HAVE_LCD_BITMAP
+            /* progressbar */
+            if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+            {
+                /* the progressbar should be alone on its line */
+                update_line = false;
+                draw_progressbar(gwps, line);
+            }
+
+            /* peakmeter */
+            if (flags & refresh_mode & WPS_REFRESH_PEAK_METER)
+            {
+                /* the peakmeter should be alone on its line */
+                update_line = false;
+
+                int h = font_get(display->getfont())->height;
+                int peak_meter_y = display->getymargin() + line * h;
+
+                /* The user might decide to have the peak meter in the last
+                    line so that it is only displayed if no status bar is
+                    visible. If so we neither want do draw nor enable the
+                    peak meter. */
+                if (peak_meter_y + h <= display->getheight()) {
+                    /* found a line with a peak meter -> remember that we must
+                        enable it later */
+                    enable_pm = true;
+                    peak_meter_screen(gwps->display, 0, peak_meter_y,
+                                      MIN(h, display->getheight() - peak_meter_y));
+                }
+            }
+
+#else /* HAVE_LCD_CHARCELL */
+
+            /* progressbar */
+            if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+            {
+                if (data->full_line_progressbar)
+                    draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
+                else
+                    draw_player_progress(gwps);
+            }
+#endif
+
+            if (update_line)
+            {
+                if (flags & WPS_REFRESH_SCROLL)
+                {
+                    /* if the line is a scrolling one we don't want to update
+                       too often, so that it has the time to scroll */
+                    if ((refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
+                        write_line(display, &align, line, true);
+                }
+                else
+                    write_line(display, &align, line, false);
+            }
+
         }
+
+#ifdef HAVE_LCD_BITMAP
+        /* Now display any images in this viewport */
+        wps_display_images(gwps, &data->viewports[v].vp);
+#endif
     }
 
 #ifdef HAVE_LCD_BITMAP
     data->peak_meter_enabled = enable_pm;
-    wps_display_images(gwps);
 #endif
 
+    /* Restore the default viewport */
+    display->set_viewport(NULL);
+
     display->update();
 
 #ifdef HAVE_BACKLIGHT
diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h
index 70f4c56..d31471c 100644
--- a/apps/gui/gwps.h
+++ b/apps/gui/gwps.h
@@ -61,6 +61,7 @@
 #ifdef HAVE_LCD_BITMAP
 struct gui_img{
     struct bitmap bm;
+    struct viewport* vp;    /* The viewport to display this image in */
     int x;                  /* x-pos */
     int y;                  /* y-pos */
     bool loaded;            /* load state */
@@ -86,6 +87,7 @@
 #define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
                    + (2*LCD_HEIGHT*LCD_WIDTH/8))
 
+#define WPS_MAX_VIEWPORTS   16
 #define WPS_MAX_LINES       (LCD_HEIGHT/5+1)
 #define WPS_MAX_SUBLINES    (WPS_MAX_LINES*3)
 #define WPS_MAX_TOKENS      1024
@@ -95,6 +97,7 @@
 
 #else
 
+#define WPS_MAX_VIEWPORTS   2
 #define WPS_MAX_LINES       2
 #define WPS_MAX_SUBLINES    12
 #define WPS_MAX_TOKENS      64
@@ -315,6 +318,14 @@
     long subline_expire_time;
 };
 
+struct wps_viewport {
+    struct viewport vp;   /* The LCD viewport struct */
+
+    /* Number of lines in this viewport. During WPS parsing, this is
+       the index of the line being parsed. */
+    int num_lines;
+    struct wps_line lines[WPS_MAX_LINES];
+};
 
 /* wps_data
    this struct holds all necessary data which describes the
@@ -360,10 +371,9 @@
     bool remote_wps;
 #endif
 
-    /* Number of lines in the WPS. During WPS parsing, this is
-       the index of the line being parsed. */
-    int num_lines;
-    struct wps_line lines[WPS_MAX_LINES];
+    /* Number of viewports in the WPS */
+    int num_viewports;
+    struct wps_viewport viewports[WPS_MAX_VIEWPORTS];
 
     /* Total number of sublines in the WPS. During WPS parsing, this is
        the index of the subline where the parsed tokens are added to. */
@@ -388,26 +398,30 @@
 /* to setup up the wps-data from a format-buffer (isfile = false)
    from a (wps-)file (isfile = true)*/
 bool wps_data_load(struct wps_data *wps_data,
+                   struct screen *display,
                    const char *buf,
                    bool isfile);
 
 /* Returns the index of the subline in the subline array
+   v - 0-based viewport number
    line - 0-based line number
    subline - 0-based subline number within the line
  */
-int wps_subline_index(struct wps_data *wps_data, int line, int subline);
+int wps_subline_index(struct wps_data *wps_data, int v, int line, int subline);
 
 /* Returns the index of the first subline's token in the token array
+   v - 0-based viewport number
    line - 0-based line number
    subline - 0-based subline number within the line
  */
-int wps_first_token_index(struct wps_data *data, int line, int subline);
+int wps_first_token_index(struct wps_data *data, int v, int line, int subline);
 
 /* Returns the index of the last subline's token in the token array.
+   v - 0-based viewport number
    line - 0-based line number
    subline - 0-based subline number within the line
  */
-int wps_last_token_index(struct wps_data *data, int line, int subline);
+int wps_last_token_index(struct wps_data *data, int v, int line, int subline);
 
 /* wps_data end */
 
diff --git a/apps/gui/wps_debug.c b/apps/gui/wps_debug.c
index 0f69c76..0c13fd2 100644
--- a/apps/gui/wps_debug.c
+++ b/apps/gui/wps_debug.c
@@ -493,40 +493,51 @@
 
 static void print_line_info(struct wps_data *data)
 {
-    int i, j;
+    int i, j, v;
     struct wps_line *line;
     struct wps_subline *subline;
 
     if (wps_verbose_level > 0)
     {
-        DEBUGF("Number of lines   : %d\n", data->num_lines);
-        DEBUGF("Number of sublines: %d\n", data->num_sublines);
-        DEBUGF("Number of tokens  : %d\n", data->num_tokens);
+        DEBUGF("Number of viewports : %d\n", data->num_viewports);
+        for (v = 0; v < data->num_viewports; v++)
+        {
+            DEBUGF("vp %d: Number of lines: %d\n", v, data->viewports[v].num_lines);
+        }
+        DEBUGF("Number of sublines  : %d\n", data->num_sublines);
+        DEBUGF("Number of tokens    : %d\n", data->num_tokens);
         DEBUGF("\n");
     }
 
     if (wps_verbose_level > 1)
     {
-        for (i = 0, line = data->lines; i < data->num_lines; i++,line++)
+        for (v = 0; v < data->num_viewports; v++)
         {
-            DEBUGF("Line %2d (num_sublines=%d, first_subline=%d)\n",
-                   i, line->num_sublines, line->first_subline_idx);
-
-            for (j = 0, subline = data->sublines + line->first_subline_idx;
-                 j < line->num_sublines; j++, subline++)
+            DEBUGF("Viewport %d - +%d+%d (%dx%d)\n",v,data->viewports[v].vp.x, 
+                                                      data->viewports[v].vp.y,
+                                                      data->viewports[v].vp.width,
+                                                      data->viewports[v].vp.height);
+            for (i = 0, line = data->viewports[v].lines; i < data->viewports[v].num_lines; i++,line++)
             {
-                DEBUGF("    Subline %d: first_token=%3d, last_token=%3d",
-                       j, subline->first_token_idx,
-                       wps_last_token_index(data, i, j));
+                DEBUGF("Line %2d (num_sublines=%d, first_subline=%d)\n",
+                       i, line->num_sublines, line->first_subline_idx);
 
-                if (subline->line_type & WPS_REFRESH_SCROLL)
-                    DEBUGF(", scrolled");
-                else if (subline->line_type & WPS_REFRESH_PLAYER_PROGRESS)
-                    DEBUGF(", progressbar");
-                else if (subline->line_type & WPS_REFRESH_PEAK_METER)
-                    DEBUGF(", peakmeter");
+                for (j = 0, subline = data->sublines + line->first_subline_idx;
+                     j < line->num_sublines; j++, subline++)
+                {
+                    DEBUGF("    Subline %d: first_token=%3d, last_token=%3d",
+                           j, subline->first_token_idx,
+                           wps_last_token_index(data, v, i, j));
 
-                DEBUGF("\n");
+                    if (subline->line_type & WPS_REFRESH_SCROLL)
+                        DEBUGF(", scrolled");
+                    else if (subline->line_type & WPS_REFRESH_PLAYER_PROGRESS)
+                        DEBUGF(", progressbar");
+                    else if (subline->line_type & WPS_REFRESH_PEAK_METER)
+                        DEBUGF(", peakmeter");
+
+                    DEBUGF("\n");
+                }
             }
         }
 
diff --git a/apps/gui/wps_parser.c b/apps/gui/wps_parser.c
index 307fa2a..c641f2c 100644
--- a/apps/gui/wps_parser.c
+++ b/apps/gui/wps_parser.c
@@ -69,13 +69,13 @@
 #ifdef HAVE_LCD_BITMAP
 
 #if LCD_DEPTH > 1
-#define MAX_BITMAPS MAX_IMAGES+2 /* WPS images + pbar bitmap + backdrop */
+#define MAX_BITMAPS (MAX_IMAGES+2) /* WPS images + pbar bitmap + backdrop */
 #else
-#define MAX_BITMAPS MAX_IMAGES+1 /* WPS images + pbar bitmap */
+#define MAX_BITMAPS (MAX_IMAGES+1) /* WPS images + pbar bitmap */
 #endif
 
 #define PROGRESSBAR_BMP MAX_IMAGES
-#define BACKDROP_BMP    MAX_IMAGES+1
+#define BACKDROP_BMP    (MAX_IMAGES+1)
 
 /* pointers to the bitmap filenames in the WPS source */
 static const char *bmp_names[MAX_BITMAPS];
@@ -118,6 +118,8 @@
         struct wps_token *token, struct wps_data *wps_data);
 
 #ifdef HAVE_LCD_BITMAP
+static int parse_viewport(const char *wps_bufptr,
+        struct wps_token *token, struct wps_data *wps_data);
 static int parse_leftmargin(const char *wps_bufptr,
         struct wps_token *token, struct wps_data *wps_data);
 static int parse_image_special(const char *wps_bufptr,
@@ -131,7 +133,6 @@
 static int parse_image_load(const char *wps_bufptr,
         struct wps_token *token, struct wps_data *wps_data);
 #endif /*HAVE_LCD_BITMAP */
-
 #ifdef HAVE_ALBUMART
 static int parse_albumart_load(const char *wps_bufptr,
         struct wps_token *token, struct wps_data *wps_data);
@@ -311,6 +312,9 @@
     { WPS_TOKEN_ALBUMART_DISPLAY,         "C",   WPS_REFRESH_STATIC,
                                                 parse_albumart_conditional },
 #endif
+
+    { WPS_NO_TOKEN,                       "V",   0,    parse_viewport      },
+
 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
     { WPS_TOKEN_IMAGE_BACKDROP,           "X",   0,    parse_image_special },
 #endif
@@ -334,9 +338,11 @@
 /* Starts a new subline in the current line during parsing */
 static void wps_start_new_subline(struct wps_data *data)
 {
+    struct wps_viewport* vp = &data->viewports[data->num_viewports];
+
     data->num_sublines++;
     data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
-    data->lines[data->num_lines].num_sublines++;
+    vp->lines[vp->num_lines].num_sublines++;
 }
 
 #ifdef HAVE_LCD_BITMAP
@@ -482,6 +488,9 @@
     wps_data->img[n].x = x;
     wps_data->img[n].y = y;
 
+    /* save current viewport */
+    wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
+
     if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
         wps_data->img[n].always_display = true;
 
@@ -489,6 +498,110 @@
     return skip_end_of_line(wps_bufptr);
 }
 
+static int parse_viewport(const char *wps_bufptr,
+                          struct wps_token *token,
+                          struct wps_data *wps_data)
+{
+    const char *ptr = wps_bufptr;
+    struct viewport* vp;
+    int depth;
+
+    (void)token; /* Kill warnings */
+
+    if (*wps_bufptr != '|')
+        return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7  */
+
+    ptr = wps_bufptr + 1;
+    /* format: %V|x|y|width|height|fg_pattern|bg_pattern| */
+
+    if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
+        return WPS_ERROR_INVALID_PARAM;
+
+    wps_data->num_viewports++;
+    vp = &wps_data->viewports[wps_data->num_viewports].vp;
+
+    /* Set the defaults for fields not user-specified */
+    vp->drawmode = DRMODE_SOLID;
+    vp->xmargin  = 0;
+    vp->ymargin  = 0;
+
+    /* Work out the depth of this display */
+#ifdef HAVE_REMOTE_LCD
+    depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
+#else
+    depth = LCD_DEPTH;
+#endif
+
+#ifdef HAVE_LCD_COLOR
+    if (depth == 16)
+    {
+        parse_list("dddddcc", '|', ptr, &vp->x, &vp->y, &vp->width,
+                   &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern);
+    }
+    else 
+#endif
+#if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
+    if (depth == 2) {
+        parse_list("dddddgg", '|', ptr, &vp->x, &vp->y, &vp->width,
+                   &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern);
+    }
+    else 
+#endif
+#if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
+    if (depth == 1)
+    {
+        parse_list("ddddd", '|', ptr, &vp->x, &vp->y, &vp->width, &vp->height, 
+                                      &vp->font);
+    }
+      else
+#endif
+    {}
+
+    /* Default to using the user font if the font was an invalid number */
+    if ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI))
+        vp->font = FONT_UI;
+
+    /* Validate the viewport dimensions - we know that the numbers are
+       non-negative integers */
+#ifdef HAVE_REMOTE_LCD
+    if (wps_data->remote_wps)
+    {
+        if ((vp->x >= LCD_REMOTE_WIDTH) || 
+            ((vp->x + vp->width) >= LCD_REMOTE_WIDTH) ||
+            (vp->y >= LCD_REMOTE_HEIGHT) || 
+            ((vp->y + vp->height) >= LCD_REMOTE_HEIGHT))
+        {
+            return WPS_ERROR_INVALID_PARAM;
+        }
+    }
+    else
+#else
+    {
+        if ((vp->x >= LCD_WIDTH) || 
+            (vp->y >= LCD_HEIGHT) || 
+            ((vp->y + vp->height) >= LCD_HEIGHT))
+        {
+            return WPS_ERROR_INVALID_PARAM;
+        }
+    }
+#endif
+  
+    wps_data->viewports[wps_data->num_viewports].num_lines = 0;
+  
+    if (wps_data->num_sublines < WPS_MAX_SUBLINES)
+    {
+        wps_data->viewports[wps_data->num_viewports].lines[0].first_subline_idx =
+            wps_data->num_sublines;
+
+        wps_data->sublines[wps_data->num_sublines].first_token_idx =
+            wps_data->num_tokens;
+    }
+
+    /* Skip the rest of the line */
+    return skip_end_of_line(wps_bufptr);
+  }
+
+
 static int parse_image_special(const char *wps_bufptr,
                                struct wps_token *token,
                                struct wps_data *wps_data)
@@ -958,7 +1071,8 @@
     level = -1;
 
     while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
-          && data->num_lines < WPS_MAX_LINES)
+          && data->num_viewports < WPS_MAX_VIEWPORTS 
+          && data->viewports[data->num_viewports].num_lines < WPS_MAX_LINES)
     {
         switch(*wps_bufptr++)
         {
@@ -1066,12 +1180,12 @@
 
                 line++;
                 wps_start_new_subline(data);
-                data->num_lines++; /* Start a new line */
+                data->viewports[data->num_viewports].num_lines++; /* Start a new line */
 
-                if ((data->num_lines < WPS_MAX_LINES) &&
+                if ((data->viewports[data->num_viewports].num_lines < WPS_MAX_LINES) &&
                     (data->num_sublines < WPS_MAX_SUBLINES))
                 {
-                    data->lines[data->num_lines].first_subline_idx =
+                    data->viewports[data->num_viewports].lines[data->viewports[data->num_viewports].num_lines].first_subline_idx =
                         data->num_sublines;
 
                     data->sublines[data->num_sublines].first_token_idx =
@@ -1148,6 +1262,9 @@
     if (!fail && level >= 0) /* there are unclosed conditionals */
         fail = PARSE_FAIL_UNCLOSED_COND;
 
+    /* We have finished with the last viewport, so increment count */
+    data->num_viewports++;
+
 #ifdef DEBUG
     print_debug_info(data, fail, line);
 #endif
@@ -1212,16 +1329,6 @@
 
 #ifdef HAVE_LCD_BITMAP
 
-
-static void clear_bmp_names(void)
-{
-    int n;
-    for (n = 0; n < MAX_BITMAPS; n++)
-    {
-        bmp_names[n] = NULL;
-    }
-}
-
 static void load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
 {
     char img_path[MAX_PATH];
@@ -1291,6 +1398,7 @@
 /* to setup up the wps-data from a format-buffer (isfile = false)
    from a (wps-)file (isfile = true)*/
 bool wps_data_load(struct wps_data *wps_data,
+                   struct screen *display,
                    const char *buf,
                    bool isfile)
 {
@@ -1299,6 +1407,24 @@
 
     wps_reset(wps_data);
 
+    /* Initialise the first (default) viewport */
+    wps_data->viewports[0].vp.x          = 0;
+    wps_data->viewports[0].vp.y          = 0;
+    wps_data->viewports[0].vp.width      = display->width;
+    wps_data->viewports[0].vp.height     = display->height;
+#ifdef HAVE_LCD_BITMAP
+    wps_data->viewports[0].vp.font       = FONT_UI;
+    wps_data->viewports[0].vp.drawmode   = DRMODE_SOLID;
+#endif
+    wps_data->viewports[0].vp.xmargin    = display->getxmargin();
+    wps_data->viewports[0].vp.ymargin    = display->getymargin();
+#if LCD_DEPTH > 1
+    if (display->depth > 1)
+    {
+        wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
+        wps_data->viewports[0].vp.bg_pattern = display->get_background();
+    }
+#endif
     if (!isfile)
     {
         return wps_parse(wps_data, buf);
@@ -1357,7 +1483,8 @@
             return false;
 
 #ifdef HAVE_LCD_BITMAP
-        clear_bmp_names();
+        /* Set all filename pointers to NULL */
+        memset(bmp_names, sizeof(bmp_names), 0);
 #endif
 
         /* Skip leading UTF-8 BOM, if present. */
@@ -1385,20 +1512,20 @@
     }
 }
 
-int wps_subline_index(struct wps_data *data, int line, int subline)
+int wps_subline_index(struct wps_data *data, int v, int line, int subline)
 {
-    return data->lines[line].first_subline_idx + subline;
+    return data->viewports[v].lines[line].first_subline_idx + subline;
 }
 
-int wps_first_token_index(struct wps_data *data, int line, int subline)
+int wps_first_token_index(struct wps_data *data, int v, int line, int subline)
 {
-    int first_subline_idx = data->lines[line].first_subline_idx;
+    int first_subline_idx = data->viewports[v].lines[line].first_subline_idx;
     return data->sublines[first_subline_idx + subline].first_token_idx;
 }
 
-int wps_last_token_index(struct wps_data *data, int line, int subline)
+int wps_last_token_index(struct wps_data *data, int v, int line, int subline)
 {
-    int first_subline_idx = data->lines[line].first_subline_idx;
+    int first_subline_idx = data->viewports[v].lines[line].first_subline_idx;
     int idx = first_subline_idx + subline;
     if (idx < data->num_sublines - 1)
     {
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c
index 541101c..fb2e465 100644
--- a/apps/recorder/peakmeter.c
+++ b/apps/recorder/peakmeter.c
@@ -915,14 +915,14 @@
 void peak_meter_screen(struct screen *display, int x, int y, int height)
 {
     peak_meter_draw(display, &scales[display->screen_type], x, y,
-                        display->width - x, height);
+                        display->getwidth() - x, height);
 }           
 /**
  * Draws a peak meter in the specified size at the specified position.
  * @param int x - The x coordinate. 
- *                Make sure that 0 <= x and x + width < display->width
+ *                Make sure that 0 <= x and x + width < display->getwidth()
  * @param int y - The y coordinate. 
- *                Make sure that 0 <= y and y + height < display->height
+ *                Make sure that 0 <= y and y + height < display->getheight()
  * @param int width - The width of the peak meter. Note that for display
  *                    of clips a 3 pixel wide area is used ->
  *                    width > 3
@@ -1111,7 +1111,7 @@
         start_trigx = x+peak_meter_scale_value(trig_strt_threshold,meterwidth);
         display->vline(start_trigx, ycenter - 2, ycenter);
         start_trigx ++;
-        if (start_trigx < display->width ) display->drawpixel(start_trigx, ycenter - 1);
+        if (start_trigx < display->getwidth() ) display->drawpixel(start_trigx, ycenter - 1);
 
         stop_trigx = x + peak_meter_scale_value(trig_stp_threshold,meterwidth);
         display->vline(stop_trigx, ycenter - 2, ycenter);
diff --git a/apps/settings.c b/apps/settings.c
index 6fb5e3e..449a38b 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -806,7 +806,7 @@
             global_settings.wps_file[0] != 0xff ) {
             snprintf(buf, sizeof buf, WPS_DIR "/%s.wps",
                     global_settings.wps_file);
-            wps_data_load(gui_wps[0].data, buf, true);
+            wps_data_load(gui_wps[0].data, &screens[0], buf, true);
         }
         else
         {
@@ -835,7 +835,7 @@
         if ( global_settings.rwps_file[0]) {
             snprintf(buf, sizeof buf, WPS_DIR "/%s.rwps",
                     global_settings.rwps_file);
-            wps_data_load(gui_wps[1].data, buf, true);
+            wps_data_load(gui_wps[1].data, &screens[1], buf, true);
         }
         else
         {