Index: apps/gui/gwps-common.c
===================================================================
--- apps/gui/gwps-common.c.orig
+++ apps/gui/gwps-common.c
@@ -69,6 +69,44 @@ static char cur_displayed_albumart_path[
 static int cur_displayed_albumart_width = -1;
 static int cur_displayed_albumart_height = -1;
 
+#ifdef ROCKBOX_HAS_LOGF
+# include "logf.h"
+# define LOGF logf
+#else
+# define LOGF(...)
+#endif
+
+/* We allow any number of enums for conditionals that are based on internal
+    values, for example battery or volume levels.  This allows WPS designers
+    to use however many bitmaps they want for eg. volume segments, instead
+    of being restricted to hardcoded values.
+    We do this by counting and remembering the largest number of enums
+    found in those conditionals and working off that. */
+static bool        count_enums = false; /* counts conditional enums when enabled */
+static int         enum_count;          /* counter */
+static int         *enum_count_var;     /* points to the variable to set */
+#ifdef ROCKBOX_HAS_LOGF
+static const char* enum_type;           /* tag type being counted */
+# define COUNT_ENUMS(var, type_string) do { count_enums    = true; \
+                                            enum_count_var = &var; \
+                                            enum_type      = type_string; } \
+                                            while(0)
+#else
+# define COUNT_ENUMS(var, type_string) do { count_enums    = true; \
+                                            enum_count_var = &var; } while(0)
+#endif
+/* conditional tags that are enum counted: */
+static int  volume_enums ;
+static int  battery_enums;
+/* NOTE: if you add enum counts here, you must also set them to 2
++          in gui_wps_format(). */
+ 
+
+static int hexvalue(char x)
+{
+    return isdigit(x) ? (x) - '0' : (tolower(x) - 'a' + 10);
+}
+
 /* Skip leading UTF-8 BOM, if present. */
 static char* skip_utf8_bom(char* buf)
 {
@@ -539,7 +577,6 @@ static char* get_tag(struct wps_data* wp
                      int *intval)
 {
     struct mp3entry *id3 = cid3; /* default to current song */
-    int limit = *intval;
 #ifndef HAVE_LCD_CHARCELLS
     (void)wps_data;
 #endif
@@ -827,15 +864,26 @@ static char* get_tag(struct wps_data* wp
                         return NULL;
                     break;
 
-                case 'v': /* volume */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    snprintf(buf, buf_size, "%d", global_settings.volume);
-                    *intval = limit * (global_settings.volume 
-                                    - sound_min(SOUND_VOLUME))
-                                 / (sound_max(SOUND_VOLUME)
-                                    - sound_min(SOUND_VOLUME)) + 1;
-                    return buf;
-
+                 case 'v': /* volume */
+                      *flags |= WPS_REFRESH_DYNAMIC;
+                      snprintf(buf, buf_size, "%d", global_settings.volume);
+                     /* shift our input volume level to start at zero: */
+                     const int min_vol = sound_min(SOUND_VOLUME);
+                     const int max_vol = sound_max(SOUND_VOLUME);
+                     int       volume  = global_settings.volume - min_vol;
+                     int       range   = (max_vol - min_vol) + 1;
+                     /* regardless of the number of volume enums, ensure
+                         that the first (all off) and last (all on) enums
+                         are only hit on exactly the first and last input
+                         volume levels: */
+                      if(volume == 0)
+                         *intval = 1;
+                     else if (volume == (range-1))
+                         *intval = volume_enums;
+                     else
+                         *intval = 2 + (volume * (volume_enums-2) / range);
+                     //debug: snprintf(buf, buf_size, "%d/%d", volume, range);
+                     return buf;
             }
             break;
 
@@ -904,17 +952,59 @@ static char* get_tag(struct wps_data* wp
             {
                 case 'l': /* battery level */
                 {
-                    int l = battery_level();
-                    limit = MAX(limit, 2);
-                    if (l > -1)
-                    {
-                        snprintf(buf, buf_size, "%d", l);
-                        /* First enum is used for "unknown level". */
-                        *intval = (limit - 1) * l / 100 + 1 + 1;
-                    }
+                    int percent;
+                    percent = battery_level();
+                    if (percent > -1)
+                    {
+                        int segments = battery_enums - 1;
+                        snprintf(buf, buf_size, "%d", percent);
+#ifdef HAVE_CHARGING
+                        if(charger_input_state == CHARGER)
+                        {
+                             /* when charging, we use the following scheme
+                                 (assumes a 4 segment display, ie. 5 enums
+                                  were counted (4+off)):
+                                     
+                                  batt. percent:   |enum| segments lit
+                                  -----------------|----|-------------
+                                   0-24%           | 1  | 0 (1 flashing)
+                                  25-49%           | 2  | 1 (2 flashing)
+                                  50-74%           | 3  | 2 (3 flashing)
+                                  75-99%           | 4  | 3 (4 flashing)
+                                  100%             | 5  | 4              */
+                             *intval = 1 + (percent * segments / 100);
+                         }
+                         else
+#endif                  
+                         {
+                             /* when draining, we use the following scheme
+                                 (assumes a 4 segment display, ie. 5 enums
+                                  were counted (4+off)):
+                                     
+                                  batt. percent:      |enum| segments lit
+                                  --------------------|----|-------------
+                                  0-danger_level      |  1 | 0
+                                  [danger level+1]-24%|  2 | 1
+                                  25-49%              |  3 | 2
+                                  50-74%              |  4 | 3
+                                  75-100%             |  5 | 4          */
+                             bool danger_level = battery_time() <= 15;
+                             if(danger_level) /* all segments off */
+                                 *intval = 1; 
+                             else{            /* at least one segment is on */
+                                 /* we want a segment to extinguish as soon as
+                                     a boundary is reached, eg. in a 4 segment
+                                     display, the first segment should vanish at
+                                     75%, not 74%: */
+                                 if(percent > 0)
+                                     percent--;
+                                 *intval = 2 + (percent * segments / 100);
+                             }
+                         }      
+                    }             
                     else
                     {
-                        *intval = 1;
+                        *intval = 1 + battery_enums;
                         return "?";
                     }
                     return buf;
@@ -1315,6 +1405,9 @@ static const char* skip_conditional(stru
                     if (enums)
                         (*enums)++;
                     last_alternative = fmt;
+                    if(count_enums) {
+                        enum_count++;
+                    }
                     if(num) {
                         count--;
                         if(count == 0)
@@ -1327,6 +1420,18 @@ static const char* skip_conditional(stru
             case '>':
                 if (0 == --level)
                 {
+                    if(count_enums) {
+                        enum_count++;
+                        count_enums = false;
+                        /* remember the largest count found (this allows
+                            designers to use the same tag with differing
+                            counts where only the first (few) enum(s) are
+                            needed for eg. a 2nd instance */ 
+                        if(enum_count > *enum_count_var)
+                        {
+                            *enum_count_var = enum_count;
+                        }
+                    }
                     /* We're just skipping to the end */
                     if(num == 0)
                         return fmt;
@@ -1469,7 +1574,31 @@ static void format_display(struct gui_wp
                 }
                 gwps->display->setmargins( x1, x2, gwps->display->getymargin() );
             } break;
+            case 'C':
+                /* format is %C[fb]RRGGBB, hexidecimal values */
+                {
+                    int r = 0, g = 0, b = 0;
 
+                    r = hexvalue(fmt[2]) * 16 + hexvalue(fmt[3]);
+                    g = hexvalue(fmt[4]) * 16 + hexvalue(fmt[5]);
+                    b = hexvalue(fmt[6]) * 16 + hexvalue(fmt[7]);
+
+#if LCD_DEPTH > 1
+                    switch (fmt[1])
+                    {
+						case 'f': 
+							lcd_set_foreground(LCD_RGBPACK(r, g, b));
+							break;
+						case 'b':
+							lcd_set_background(LCD_RGBPACK(r, g, b));
+							break;
+						default:
+							break;
+                    }
+#endif
+                }
+                fmt += 8;
+                break;
             case 'a':
                 ++fmt;
                 /* remember where the current aligned text started */
@@ -1549,6 +1678,18 @@ static void format_display(struct gui_wp
                                 sizeof(temp_buf),&tag_length,
                                 subline_time_mult, flags, &intval);
 
+                /* is this the volume tag? */
+                if(value)
+                {
+                    if(fmt[0] == 'p' && fmt[1] == 'v')
+                        COUNT_ENUMS(volume_enums, "volume levels");
+                    else if(fmt[0] == 'b' && fmt[1] == 'l')
+                        COUNT_ENUMS(battery_enums, "battery levels");
+                    /* are we counting eums? */
+                    if(count_enums)
+                        enum_count = 0;
+                }
+
                 while (*fmt && ('<' != *fmt))
                     fmt++;
 
@@ -1665,6 +1806,12 @@ void gui_wps_format(struct wps_data *dat
     if(!data)
         return;
 
+    /* (re)set all enum count values to safe defaults */
+    volume_enums  =
+    battery_enums = 2; /* a minimum of on/off, any less and a buggy WPS
+                           might cause a divide-by-zero in other parts
+                           of the code */
+
     for (line=0; line<WPS_MAX_LINES; line++)
     {
         for (subline=0; subline<WPS_MAX_SUBLINES; subline++)
Index: firmware/drivers/lcd-16bit.c
===================================================================
--- firmware/drivers/lcd-16bit.c.orig
+++ firmware/drivers/lcd-16bit.c
@@ -872,6 +872,11 @@ void lcd_puts_scroll_style_offset(int x,
 
     s->start_tick = current_tick + scroll_delay;
     s->invert = false;
+#if LCD_DEPTH > 1
+    s->fgcolor = lcd_get_foreground();
+    s->bgcolor = lcd_get_background();
+#endif
+
     if (style & STYLE_INVERT) {
         s->invert = true;
         lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
@@ -929,7 +934,8 @@ static void scroll_thread(void)
     int index;
     int xpos, ypos;
     int lastmode;
-
+    int fgcolor_save, bgcolor_save;
+    
     /* initialize scroll struct array */
     scrolling_lines = 0;
 
@@ -974,6 +980,14 @@ static void scroll_thread(void)
                     s->offset %= s->width;
             }
 
+#if LCD_DEPTH > 1
+            fgcolor_save = lcd_get_foreground();
+            bgcolor_save = lcd_get_background();
+
+            lcd_set_foreground(s->fgcolor);
+            lcd_set_background(s->bgcolor);
+#endif
+
             lastmode = drawmode;
             drawmode = s->invert ? 
                        (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
@@ -984,6 +998,11 @@ static void scroll_thread(void)
             }
             drawmode = lastmode;
             lcd_update_rect(xpos, ypos, s->right_margin - xpos, pf->height);
+            
+#if LCD_DEPTH > 1
+            lcd_set_foreground(fgcolor_save);
+            lcd_set_background(bgcolor_save);
+#endif
         }
 
         sleep(scroll_ticks);
Index: firmware/export/lcd.h
===================================================================
--- firmware/export/lcd.h.orig
+++ firmware/export/lcd.h
@@ -379,6 +379,8 @@ struct scrollinfo {
     long start_tick;
     int left_margin;
     int right_margin;
+    int fgcolor;
+    int bgcolor;
 };
 #else /* !HAVE_LCD_BITMAP */
 
@@ -394,6 +396,8 @@ struct scrollinfo {
     int direction; /* +1 for right or -1 for left*/
     int jump_scroll;
     int jump_scroll_steps;
+    int fgcolor;
+    int bgcolor;
 };
 #endif
 
