Index: apps/recorder/icons.h
===================================================================
--- apps/recorder/icons.h	(revision 13161)
+++ apps/recorder/icons.h	(working copy)
@@ -86,6 +86,7 @@
     Icon_file_view_menu,
     Icon_EQ,
     Icon_Rockbox,
+    Icon_Dictionary,
     Icon6x8Last,
 };
 
Index: apps/recorder/icons.c
===================================================================
--- apps/recorder/icons.c	(revision 13161)
+++ apps/recorder/icons.c	(working copy)
@@ -80,6 +80,7 @@
     { 0x1f, 0x11, 0x7d, 0x46, 0x44, 0x78 }, /* File View Menu */
     { 0x06, 0x7f, 0x06, 0x18, 0x7f, 0x18 }, /* EQ menu */
     { 0x20, 0x70, 0x70, 0x3f, 0x0a, 0x0a }, /* "rockbox" musical note */
+    { 0x01, 0x55, 0x55, 0x55, 0x54 }, /* Dictionary */
 };
 
 const unsigned char bitmap_icons_7x8[][7] =
Index: apps/lang/english.lang
===================================================================
--- apps/lang/english.lang	(revision 13161)
+++ apps/lang/english.lang	(working copy)
@@ -10741,3 +10741,17 @@
     *: "Context Menu"
   </voice>
 </phrase>
+<phrase>
+  id: LANG_DICTIONARIES
+  desc: in the main menu
+  user:
+  <source>
+    *: "Dictionaries"
+  </source>
+  <dest>
+    *: "Dictionaries"
+  </dest>
+  <voice>
+    *: "Dictionaries"
+  </voice>
+</phrase>
Index: apps/plugins/viewers.config
===================================================================
--- apps/plugins/viewers.config	(revision 13161)
+++ apps/plugins/viewers.config	(working copy)
@@ -30,3 +30,5 @@
 tzx,viewers/zxbox,66 52 4A 66 52 4A
 z80,viewers/zxbox,66 52 4A 66 52 4A
 zzz,viewers/properties,00 00 00 00 00 00
+idx,viewers/dict,01 55 55 55 55 54
+lidx,viewers/dict,01 55 55 55 55 54
Index: apps/plugins/test_styles.c
===================================================================
--- apps/plugins/test_styles.c	(revision 0)
+++ apps/plugins/test_styles.c	(revision 0)
@@ -0,0 +1,66 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: helloworld.c 8349 2006-01-15 18:20:18Z amiconn $
+ *
+ * Copyright (C) 2007 Timo Horstsch�er
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+
+PLUGIN_HEADER
+
+static struct plugin_api* rb;
+
+struct style {
+    const char *name;
+    int style;
+};
+
+struct style test_styles[] = {
+    { "normal",                     STYLE_DEFAULT },
+    { "bold",                       STYLE_BOLD },
+    { "underline",                  STYLE_UNDERLINE },
+    { "bold + underline",           STYLE_BOLD|STYLE_UNDERLINE },
+    { "shadow",                     STYLE_SHADOW },
+    { "bold + shadow",              STYLE_BOLD|STYLE_SHADOW },
+    { "bold + underline + inverse + shadow", STYLE_BOLD|STYLE_UNDERLINE|STYLE_INVERT|STYLE_SHADOW },
+    { "bold + inverse",             STYLE_BOLD|STYLE_INVERT },
+    { "\33[1mm\33[3mi\33[7mx\33[27me\33[4md\33[22m!", STYLE_INTERPRET },
+};
+
+#define NUM_STYLES ((int) (sizeof test_styles / sizeof (struct style)) )
+
+enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
+{
+    int i;
+
+    (void)parameter;
+    rb = api;
+
+    for (i = 0; i < NUM_STYLES; i++) {
+        rb->lcd_puts_style(0, i, test_styles[i].name, test_styles[i].style);
+#ifdef HAVE_REMOTE_LCD
+        rb->lcd_remote_puts_style(0, i, test_styles[i].name, test_styles[i].style);
+#endif
+    }
+    rb->lcd_update();
+#ifdef HAVE_REMOTE_LCD
+    rb->lcd_remote_update();
+#endif
+
+    while (!rb->button_get(true))
+        ;
+
+    return PLUGIN_OK;
+}
Index: apps/plugins/dict.c
===================================================================
--- apps/plugins/dict.c	(revision 13161)
+++ apps/plugins/dict.c	(working copy)
@@ -7,7 +7,7 @@
  *                     \/            \/     \/    \/            \/
  * $Id$
  *
- * Copyright (C) 2005 Tomas Salfischberger
+ * Copyright (C) 2005 Tomas Salfischberger, 2006 Timo Horstschäfer
  *
  * All files in this archive are subject to the GNU General Public License.
  * See the file COPYING in the source tree root for full license agreement.
@@ -18,299 +18,883 @@
  ****************************************************************************/
 
 #include "plugin.h"
+#include "pluginlib_actions.h"
+#include "configfile.h"
+#include "playback_control.h"
+#include "viewer.h"
 
 PLUGIN_HEADER
 
-/* save the plugin api pointer. */
 static struct plugin_api* rb;
-/* screen info */
-static int display_columns, display_lines;
 
-/* Some lenghts */
-#define WORDLEN 32 /* has to be the same in rdf2binary.c */
+#define CONFIG_VERSION  2
+#define CACHE_VERSION   1
 
-/* Struct packing */
-#ifdef __GNUC__
-#define STRUCT_PACKED __attribute__((packed))
-#else
-#define STRUCT_PACKED
-#pragma pack (push, 2)
-#endif
+//! Global dict variables
+static struct dict_s {
+    char path[255]; /**< Path of current dictionary */
+    char name[255]; /**< Basename of current dictionary */
 
-/* The word struct :) */
-struct stWord
+    int DataLen;
+
+    int fCache;
+    int fIndex;
+    int fSyn;
+    int fDict;
+    //! Current description file (there may be more than one)
+    int fDict_n; 
+} Dict;
+
+#define DESC_BUFFER_ADD 512
+
+#define CONFIG_FILENAME "dict.cfg"
+#define CONFIG_ITEMS ( sizeof(Conf_data) / sizeof (struct configdata) )
+struct conf_s {
+    int scroll; /**< Number of lines to scroll */
+    int max_list; /**< Max number of articles listed */
+} Conf;
+
+struct configdata Conf_data[] = {
+    { TYPE_INT, VIEWER_SCROLL_PAGE, 4, &Conf.scroll, "scroll", NULL, NULL },
+    { TYPE_INT, 1, 1000, &Conf.scroll, "max_list", NULL, NULL },
+};
+
+#define SCROLL_DEFAULT      VIEWER_SCROLL_PAGE
+#define MAX_LIST_DEFAULT    100
+
+//! Malloc variables
+static struct malloc_s {
+    char *buf;
+    int bufsize;
+    int bufpos;
+} Malloc;
+
+struct cache_h {
+    char magic[9];
+    uint16_t version;
+};
+static struct cache_h Cache_h = {
+    "DICT_OFT",
+    CACHE_VERSION
+};
+
+//! Length of word_str in stardict's DICT format
+#define WORDLEN 256
+
+//! Rockbox filesize limit
+#define MAX_FILESIZE 2147483647 
+
+enum dict_action
 {
-    char word[WORDLEN];
-    long offset;
-} STRUCT_PACKED;
+    DICT_USB_CONNECTED = -4,
+    DICT_NOT_FOUND = -2,
+    DICT_QUIT = -3,
+    DICT_ERROR = -1,
+    DICT_OK,
+};
 
-/* A funtion to get width and height etc (from viewer.c) */
-void init_screen(void)
+#define VIEWER_NEW ( VIEWER_LAST + 1 )
+
+/** \brief Data structure in .idx files
+ *
+ * Structure of .idx files:
+ * \code
+ *      char[]      name;   // variable length, zero-terminated, UTF-8
+ *      uint32_t    offset; // beginning of the article in the .dict file, Big Endian
+ *      uint32_t    size;   // article size, Big Endian
+ * \endcode
+ */
+struct WordData_s
 {
-#ifdef HAVE_LCD_BITMAP
-    int w,h;
+    uint32_t offset;
+    uint32_t size;
+};
 
-    rb->lcd_getstringsize("o", &w, &h);
-    display_lines = LCD_HEIGHT / h;
-    display_columns = LCD_WIDTH / w;
+struct WordData
+{
+    uint64_t offset;
+    uint32_t size;
+};
+
+//! Internal structure for the result
+struct DictEntry
+{
+    char name[WORDLEN];
+    long index;
+    struct WordData data;
+};
+
+#ifndef betoh64
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+static inline uint64_t swap64(uint64_t value)
+{
+    uint64_t hi = swap32(value >> 32);
+    uint64_t lo = swap32(value & 0xffffffff);
+    return (lo << 32) | hi;
+}
+#define betoh64(x) swap64(x)
 #else
+#define betoh64(x) (x)
+#endif
 
-    display_lines = 2;
-    display_columns = 11;
 #endif
+
+
+//! work around gcc wanting memcpy
+void *memcpy(void *destination, const void *source, size_t num)
+{
+    return rb->memcpy(destination, source, num);
 }
 
-/* global vars for pl_malloc() */
-void *bufptr;
-int bufleft;
+//! Display an error message
+void dict_error(const char *msg)
+{
+    DEBUGF("%s\n", msg);
+    rb->splash(HZ, msg);
+}
 
-/* simple function to "allocate" memory in pluginbuffer. */
-void *pl_malloc(int size)
+//! Initialize the plugin buffer
+void dict_buf_init(void)
 {
-    void *ptr;
-    ptr = bufptr;
+    Malloc.buf = rb->plugin_get_buffer(&Malloc.bufsize);
+    Malloc.bufpos = 0;
+}
 
-    if (bufleft < size)
+/** \brief Stack-like malloc
+ *
+ * This malloc implementation only keeps track of how much memory is allocated
+ */
+void *dict_malloc(int size)
+{
+    if (Malloc.bufpos+size <= Malloc.bufsize)
     {
+        Malloc.bufpos += size;
+        return Malloc.buf + Malloc.bufpos - size;
+    } else
         return NULL;
-    }
+}
+
+/** \brief Stack-like free
+ *
+ * @param size Nnumber of bytes to free, 0 will free the whole buffer
+ */
+void dict_free(int size)
+{
+    if (size == 0 || size > Malloc.bufpos)
+        Malloc.bufpos = 0;
     else
+        Malloc.bufpos -= size;
+}
+
+//! Extract the path and name of the given filename
+void dict_get_name(const char *name)
+{
+    char *slash, *dot;
+
+    slash = rb->strrchr(name, '/');
+    dot = rb->strrchr(slash+1, '.');
+    
+    /* get path */
+    rb->strncpy(Dict.path, name, slash-name);
+
+    /* get filename without extension */
+    rb->strncpy(Dict.name, slash+1, dot-slash-1);
+
+    /* get index type */
+    if (rb->strcmp(dot, ".lidx") == 0)
+        Dict.DataLen = sizeof(struct WordData);
+    else
+        Dict.DataLen = sizeof(struct WordData_s);
+}
+
+//! Generates the cache file to the current dictionary
+enum dict_action dict_create_cache(void)
+{
+    uint32_t offset;
+    int size;
+    char c;
+    enum dict_action ret = DICT_OK;
+
+    int button;
+    const struct button_mapping *plugin_contexts[] = {
+        generic_actions,
+    };
+
+    rb->splash(0, "Creating offset table... may take some minutes");
+
+    rb->lseek(Dict.fCache, 0, SEEK_SET);
+
+    /* write header */
+    rb->write(Dict.fCache, &Cache_h, sizeof(struct cache_h));
+
+    offset = 0;
+    while (ret == DICT_OK)
     {
-        bufptr += size;
-        return ptr;
+        /* get name length */
+        size = 0;
+        while (true)
+        {
+            if (rb->read(Dict.fIndex, &c, 1) < 1)
+                break;
+            size++;
+            if (c == '\0')
+                break;
+        }
+        if (size < 1)
+            break;
+
+        /* write current position to file */
+        rb->write(Dict.fCache, &offset, sizeof(offset));
+
+        rb->lseek(Dict.fIndex, Dict.DataLen, SEEK_CUR);
+        offset += Dict.DataLen + size;
+
+        /* don't take over all control */
+        button = pluginlib_getaction(rb, TIMEOUT_NOBLOCK, plugin_contexts, 1);
+        switch (button)
+        {
+            case PLA_QUIT:
+                ret = DICT_ERROR;
+                break;
+            default:
+                if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+                    ret = DICT_USB_CONNECTED;
+                break;
+        }
+        rb->yield();
     }
+
+    return ret;
 }
 
-/* init function for pl_malloc() */
-void pl_malloc_init(void)
+//! Closes all file descriptors
+void dict_close(void)
 {
-    bufptr = rb->plugin_get_buffer(&bufleft);
+    rb->close(Dict.fCache);
+    rb->close(Dict.fIndex);
+    rb->close(Dict.fDict);
+
+    configfile_save(CONFIG_FILENAME, Conf_data,
+                    CONFIG_ITEMS, CONFIG_VERSION);
 }
 
-/* for endian problems */
-#ifdef ROCKBOX_BIG_ENDIAN
-#define reverse(x) x
-#else
-long reverse (long N) {
-    unsigned char B[4];
-    B[0] = (N & 0x000000FF) >> 0;
-    B[1] = (N & 0x0000FF00) >> 8;
-    B[2] = (N & 0x00FF0000) >> 16;
-    B[3] = (N & 0xFF000000) >> 24;
-    return ((B[0] << 24) | (B[1] << 16) | (B[2] << 8) | (B[3] << 0));
+enum dict_action dict_open(void)
+{
+    char fn[FAT_FILENAME_BYTES];
+
+    /* index file */
+    if (Dict.DataLen == sizeof(struct WordData))
+        rb->snprintf(fn, sizeof(fn), "%s/%s.lidx", Dict.path, Dict.name);
+    else
+        rb->snprintf(fn, sizeof(fn), "%s/%s.idx", Dict.path, Dict.name);
+    Dict.fIndex = rb->open(fn, O_RDONLY);
+    if (Dict.fIndex < 0)
+    {
+        dict_error("Failed to open index file");
+        dict_close();
+        return DICT_ERROR;
+    }
+
+    /* cache file */
+    rb->snprintf(fn, sizeof(fn), "%s/%s.oft", ROCKBOX_DIR, Dict.name);
+    Dict.fCache = rb->open(fn, O_RDONLY);
+    if (Dict.fCache >= 0)
+    {
+        /* check the cache file */
+        struct cache_h header;
+
+        rb->read(Dict.fCache, &header, sizeof(struct cache_h));
+        if (!rb->memcmp(&header, &Cache_h, sizeof(struct cache_h)))
+            return PLUGIN_OK;
+
+        dict_error("Cache file outdated");
+
+        rb->close(Dict.fCache);
+        rb->remove(fn);
+    }
+
+    /* incorrect cache file, create a new one */
+    Dict.fCache = rb->creat(fn);
+    if (Dict.fCache >= 0)
+    {
+        if (dict_create_cache() == DICT_OK)
+        {
+            /* close it to reopen it read-only */
+            rb->close(Dict.fCache);
+            Dict.fCache = rb->open(fn, O_RDONLY);
+            return DICT_OK;
+        }
+        else
+        {
+            rb->close(Dict.fCache);
+            rb->remove(fn);
+        }
+    }
+
+    dict_error("Failed to open cache file");
+    return DICT_ERROR;
 }
-#endif
 
-/* Button definitions */
-#if CONFIG_KEYPAD == PLAYER_PAD
-#define LP_QUIT BUTTON_STOP
-#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
-      (CONFIG_KEYPAD == IPOD_3G_PAD)
-#define LP_QUIT BUTTON_MENU
-#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
-#define LP_QUIT BUTTON_PLAY
-#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
-#define LP_QUIT BUTTON_POWER
-#elif CONFIG_KEYPAD == GIGABEAT_PAD
-#define LP_QUIT BUTTON_A
-#elif CONFIG_KEYPAD == SANSA_E200_PAD
-#define LP_QUIT BUTTON_POWER
-#elif CONFIG_KEYPAD == IRIVER_H10_PAD
-#define LP_QUIT BUTTON_POWER
-#else
-#define LP_QUIT BUTTON_OFF
-#endif
+/** \brief Initializes everything related to dict.
+ *
+ * @param filename Filename of the .idx file for the dictionary
+ */
+enum dict_action dict_init(const char *file)
+{
+    /* malloc */
+    dict_buf_init();
 
-/* data files */
-#define DICT_INDEX ROCKBOX_DIR "/dict.index"
-#define DICT_DESC ROCKBOX_DIR "/dict.desc"
+    /* variables */
+    Conf.scroll     = SCROLL_DEFAULT;
+    Conf.max_list   = MAX_LIST_DEFAULT;
+    Dict.fDict = Dict.fDict_n = -1;
 
-/* the main plugin function */
-enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
+    /* configuration data */
+    configfile_init(rb);
+    configfile_load(CONFIG_FILENAME, Conf_data,
+                    CONFIG_ITEMS, CONFIG_VERSION);
+
+    /* dict name */
+    dict_get_name(file);
+
+    /* files */
+    return dict_open();
+}
+
+/** \brief Returns offset for an entry in the idx file
+ *
+ * This function allows random access to the idx file
+ */
+uint32_t dict_cache_offset(long index)
 {
-    char searchword[WORDLEN]; /* word to search for */
-    char *description; /* pointer to description buffer */
-    char *output; /* pointer to output buffer */
-    char *ptr, *space;
-    struct stWord word; /* the struct to read into */
-    int fIndex, fData; /* files */
-    int filesize, high, low, probe;
-    int lines, len, outputted, next;
+    uint32_t offset;
 
-    /* plugin stuff */
-    (void)parameter;
-    rb = api;
+    /* read offset */
+    rb->lseek(Dict.fCache, sizeof(uint32_t)*index + sizeof(struct cache_h),
+              SEEK_SET);
+    rb->read(Dict.fCache, &offset, sizeof(uint32_t));
 
-    /* get screen info */
-    init_screen();
+    return offset;
+}
 
-    /* get pl_malloc() buffer ready. */
-    pl_malloc_init();
+//! Compare function, that represents the order in the .idx file
+int dict_index_strcmp(const char *str1, const char *str2)
+{
+    int a;
 
-    /* init description buffer (size is because we don't have scrolling)*/
-    description = (char *)pl_malloc(display_columns * display_lines);
-    if (description == NULL)
+    a = rb->strcasecmp(str1, str2);
+    if (!a)
     {
-        DEBUGF("Err: failed to allocate description buffer.");
-        return PLUGIN_ERROR;
+        return rb->strcmp(str1, str2);
     }
+    
+    return a;
+}
 
-    /* init output buffer */
-    output = (char *)pl_malloc(display_columns);
-    if (output == NULL)
+/** \brief Reads up to n bytes from current fIndex position until a zero occurs
+ *
+ * \return Number of bytes read.
+ */
+int dict_index_gets(char *buf, int n)
+{
+    int i;
+    for (i=0; i<n; i++)
     {
-        DEBUGF("Err: failed to allocate output buffer.");
-        return PLUGIN_ERROR;
+        if (!rb->read(Dict.fIndex, buf, 1))
+            break;
+        if (! *buf++)
+        {
+            i++;
+            break;
+        }
     }
+    return i;
+}
 
-    /* "clear" input buffer */
-    searchword[0] = '\0';
+/** \brief Reads up to n bytes from an entry in the idx file until a zero occurs
+ *
+ * @param index The index in idx t read from.
+ * \return Number of bytes read.
+ */
+int dict_index_gets_from(long index, char *buf, int n)
+{
+    rb->lseek(Dict.fIndex, dict_cache_offset(index), SEEK_SET);
+    return dict_index_gets(buf, n);
+}
 
-    rb->kbd_input(searchword, sizeof(searchword)); /* get the word to search */
+/** \brief Extracts all data about an entry in idx
+ *
+ * \return length of the article name
+ */
+int dict_index_entry(long index, struct DictEntry *out)
+{
+    int size;
+    void *buf;
 
-    fIndex = rb->open(DICT_INDEX, O_RDONLY); /* index file */
-    if (fIndex < 0)
+    buf = dict_malloc(Dict.DataLen);
+    out->index = index;
+
+    size = dict_index_gets_from(index, out->name, WORDLEN);
+
+    rb->read(Dict.fIndex, buf, Dict.DataLen);
+
+    /* convert to host endianess */
+    if (Dict.DataLen == sizeof(struct WordData))
     {
-        DEBUGF("Err: Failed to open index file.\n");
-        rb->splash(HZ*2, "Failed to open index.");
-        return PLUGIN_ERROR;
+        out->data.offset = betoh64( ((struct WordData *)buf)->offset );
+        out->data.size   = betoh32( ((struct WordData *)buf)->size );
     }
+    else
+    {
+        out->data.offset = betoh32( ((struct WordData_s *)buf)->offset );
+        out->data.size   = betoh32( ((struct WordData_s *)buf)->size );
+    }
 
-    filesize = rb->filesize(fIndex); /* get filesize */
+    dict_free(Dict.DataLen);
 
-    DEBUGF("Filesize: %d bytes = %d words \n", filesize,
-           (filesize / (int)sizeof(struct stWord)));
+    return size;
+}
 
-    /* for the searching algorithm */
-    high = filesize / sizeof( struct stWord );
+/** \brief Binary search function
+ *
+ * @return If nothing was found, the last probe is returned negative.
+ */
+long dict_index_binary_search(const char *search,
+                              int(*cmpf)(const char *, const char *))
+{
+    char word[WORDLEN];
+    int32_t high, low, probe;
+    int cmp;
+
+    high = (rb->filesize(Dict.fCache) - sizeof (struct cache_h)) /
+           sizeof( uint32_t );
     low = -1;
+    probe = 0;
 
     while (high - low > 1)
     {
         probe = (high + low) / 2;
 
-        /* Jump to word pointed by probe, and read it. */
-        rb->lseek(fIndex, sizeof(struct stWord) * probe, SEEK_SET);
-        rb->read(fIndex, &word, sizeof(struct stWord));
+        dict_index_gets_from(probe, word, WORDLEN);
 
         /* jump according to the found word. */
-        if (rb->strcasecmp(searchword, word.word) < 0)
-        {
+        cmp = cmpf(search, word);
+        if (cmp < 0)
             high = probe;
-        }
+        else if (cmp > 0)
+            low = probe;
         else
+            return probe;
+    }
+
+    return -probe;
+}
+
+/** \brief Shows a selection menu for all articles starting with the given word
+ *
+ * The selections screen is only displayed, if there's more than one result.
+ */
+long dict_index_find_startingWith(const char *search)
+{
+    long index;
+    char buf[WORDLEN];
+    int len, new_len;
+    int n;
+    int buffer_size;
+    struct menu_item *items;
+    int menu;
+    int sel;
+    char *new_word;
+    int statusbar_setting;
+
+    len = rb->strlen(search);
+
+    index = dict_index_binary_search(search, rb->strcasecmp);
+    if (index < 0)
+        index = -index;
+
+    /* go to the first entry that starts with the word */
+    for (;;index--)
+    {
+        dict_index_gets_from(index, buf, WORDLEN);
+        if (rb->strncasecmp(search, buf, len) != 0)
         {
-            low = probe;
+            index++;
+            break;
         }
+        else if (index == 0)
+            break;
     }
 
-    /* read in the word */
-    rb->lseek(fIndex, sizeof(struct stWord) * low, SEEK_SET);
-    rb->read(fIndex, &word, sizeof(struct stWord));
+    buffer_size = Conf.max_list * sizeof(struct menu_item);
+    items = (struct menu_item *)dict_malloc(buffer_size);
+    rb->memset(items, 0, buffer_size);
 
-    /* Check if we found something */
-    if (low == -1 || rb->strcasecmp(searchword, word.word) != 0)
+    rb->lseek(Dict.fIndex, dict_cache_offset(index), SEEK_SET);
+
+    /* find all words that start with the word */
+    for (sel=n=0; n<Conf.max_list; n++)
     {
-        DEBUGF("Not found.\n");
-        rb->splash(HZ*2, "Not found.");
-        rb->close(fIndex);
-        return PLUGIN_OK;
+        new_len = dict_index_gets(buf, WORDLEN);
+
+        /* find entries starting with... */
+        if (!new_len || rb->strncasecmp(search, buf, len) != 0)
+            break;
+
+        /* allocate memory and add to menu_items list */
+        new_word = (char *)dict_malloc(new_len);
+        if (new_word == NULL)
+            break;
+        buffer_size += new_len;
+
+        rb->strncpy(new_word, buf, new_len);
+
+        items[n].desc = new_word;
+        items[n].function = NULL;
+
+        /* ignore the entry data */
+        rb->lseek(Dict.fIndex, Dict.DataLen, SEEK_CUR);
     }
 
-    DEBUGF("Found %s at offset %ld\n", word.word, reverse(word.offset));
+    /* show the selection list */
+    if (n > 1)
+    {
+        /* disable status bar */
+        statusbar_setting = rb->global_settings->statusbar;
+        rb->global_settings->statusbar = false;
 
-    /* now open the description file */
-    fData = rb->open(DICT_DESC, O_RDONLY);
-    if (fData < 0)
-    {
-        DEBUGF("Err: Failed to open description file.\n");
-        rb->splash(HZ*2, "Failed to open descriptions.");
-        rb->close(fIndex);
-        return PLUGIN_ERROR;
+        menu = rb->menu_init(items, n, NULL, NULL, NULL, NULL);
+        sel = rb->menu_show(menu);
+        rb->menu_exit(menu);
+
+        /* restore status bar */
+        rb->global_settings->statusbar = statusbar_setting;
+
+        rb->lcd_setmargins(0, 0);
+#ifdef HAVE_REMOTE_LCD
+        rb->lcd_remote_setmargins(0, 0);
+#endif
     }
 
-    /* seek to the right offset */
-    rb->lseek(fData, (off_t)reverse(word.offset), SEEK_SET);
+    dict_free(buffer_size);
 
-    /* Read in the description */
-    rb->read_line(fData, description, display_columns * display_lines);
+    if (n == 0)
+        return DICT_NOT_FOUND;
 
-    /* And print it to debug. */
-    DEBUGF("Description: %s\n", description);
+    switch (sel)
+    {
+        case MENU_ATTACHED_USB:
+            return DICT_USB_CONNECTED;
+        case MENU_SELECTED_EXIT:
+            return DICT_QUIT;
+        default:
+            return index + sel;
+    }
+}
 
-    /* get pointer to first char */
-    ptr = description;
+//! Wrapper to open the correct .desc file depending on the description offset
+enum dict_action dict_desc_open(int n)
+{
+    char fn[FAT_FILENAME_BYTES];
 
-    lines = 0;
-    outputted = 0;
-    len = rb->strlen(description);
+    if (n == Dict.fDict_n)
+        return DICT_OK;
 
-    /* clear screen */
-    rb->lcd_clear_display();
+    if (Dict.fDict > 0)
+        rb->close(Dict.fIndex);
 
-    /* for large screens display the searched word. */
-    if(display_lines > 4)
+    if (n < 1)
+        rb->snprintf(fn, sizeof(fn), "%s/%s.dict", Dict.path, Dict.name);
+    else
+        rb->snprintf(fn, sizeof(fn), "%s/%s.dict.%d", Dict.path, Dict.name, n);
+
+    Dict.fDict = rb->open(fn, O_RDONLY);
+    if (Dict.fDict < 0)
     {
-        rb->lcd_puts(0, lines, searchword);
-        lines++;
+        dict_error("Failed to open description file");
+        /* show filename */
+        dict_error(fn);
+        return DICT_ERROR;
     }
 
-    /* TODO: Scroll, or just stop when there are to much lines. */
-    while (1)
+    Dict.fDict_n = n;
+
+    return DICT_OK;
+}
+
+/** \brief Reads the article description
+ *
+ * @param article The article to be read.
+ */
+char *dict_desc_read(struct WordData *artl, char *buf)
+{
+    uint64_t offset = artl->offset;
+    int len, n = offset/MAX_FILESIZE;
+
+    if (dict_desc_open(n) != DICT_OK)
+        return NULL;
+
+    offset %= MAX_FILESIZE;
+
+    rb->lseek(Dict.fDict, offset, SEEK_SET);
+    len = rb->read(Dict.fDict, buf, artl->size);
+
+    /* check if article is splitted between two files */
+    if (offset > MAX_FILESIZE - artl->size)
     {
-        /* copy one lcd line */
-        rb->strncpy(output, ptr, display_columns);
-        output[display_columns] = '\0';
+        rb->close(Dict.fDict);
+        if (dict_desc_open(++n) != DICT_OK)
+            return NULL;
 
-        /* typecast to kill a warning... */
-        if((int)rb->strlen(ptr) < display_columns)
+        rb->read(Dict.fDict, buf+len, artl->size-len);
+    }
+    buf[artl->size] = '\0';
+
+    /* And print it to debug. */
+    DEBUGF("Description: %s\n", buf);
+
+    return buf;
+}
+
+void dict_menu(void)
+{
+    int m;
+    bool quit = false;
+    static struct menu_item items[] = {
+        { "Scrolling", NULL },
+        { "Max listed articles", NULL },
+        { "Playback control", NULL },
+    };
+
+    static const struct opt_items scroll[] = {
+        { "Full page", -1 },
+        { "1 line", -1 },
+        { "2 lines", -1 },
+        { "3 lines", -1 },
+        { "4 lines", -1 },
+        { "5 lines", -1 },
+    };
+
+    m = rb->menu_init(items, sizeof(items) / sizeof(struct menu_item),
+                      NULL, NULL, NULL, NULL);
+
+    rb->button_clear_queue();
+    rb->sleep(HZ/5);
+    while (!quit) {
+        switch (rb->menu_show(m))
         {
-            rb->lcd_puts(0, lines, output);
-            lines++;
-            break;
+            case 0:
+                quit = rb->set_option("Scrolling", &Conf.scroll, INT, scroll, 6, NULL);
+                viewer_set_scroll(Conf.scroll);
+                break;
+            case 1:
+                quit = rb->set_int("Max listed articles", "", UNIT_INT,
+                                   &Conf.max_list, NULL, 100, 100, 1000, NULL);
+                break;
+            case 2:
+                quit = playback_control(rb);
+                break;
+            default:
+                quit = true;
+                break;
         }
+    }
 
+    rb->menu_exit(m);
 
-        /* get the last spacechar */
-        space = rb->strrchr(output, ' ');
+    rb->lcd_setmargins(0, 0);
+#ifdef HAVE_REMOTE_LCD
+    rb->lcd_remote_setmargins(0, 0);
+#endif
 
-        if (space != NULL)
+}
+
+//! Parses #redirect from MediaWiki
+int parse_mw_redirect(struct DictEntry *a, const char *desc)
+{
+    char *p;
+    int len;
+    long index;
+    static const char redirect[] = "#redirect";
+
+    if ( rb->strncasecmp(desc, redirect, sizeof(redirect)-1) ) 
+        return 0;
+
+    /* find destination article */
+    p = rb->strchr(desc+sizeof(redirect)-1, '[') + 2;
+    len = rb->strchr(p, ']') - p;
+
+    rb->strncpy(a->name, p, len);
+    a->name[len] = '\0';
+
+    /* replace '_' with ' ' */
+    for (p=a->name; *p; p++)
+    {
+        if (*p == '_')
+            *p = ' ';
+    }
+
+
+    index = dict_index_binary_search(a->name, dict_index_strcmp);
+    if (index < 0)
+    {
+        dict_error("Illegal redirect");
+        dict_error(a->name);
+        return 0;
+    }
+    dict_index_entry(index, a);
+
+    return 1;
+}
+    
+void dict_viewer_callback(int button)
+{
+    switch (button)
+    {
+	case PLA_START:
+	    viewer_exit(VIEWER_NEW);
+	    break;
+	case PLA_MENU:
+	    dict_menu();
+            viewer_redraw();
+	    break;
+    }
+}
+
+//! Dict main loop
+enum dict_action dict_main(void)
+{
+    struct DictEntry result;
+    char search[WORDLEN], old[WORDLEN];
+    char *desc;
+    int desc_size = 0, title_size;
+    bool new_search = false;
+
+    search[0] = '\0';
+
+    viewer_init(rb);
+    viewer_set_scroll(Conf.scroll);
+    viewer_set_callback(dict_viewer_callback);
+
+    while (true)
+    {
+        /* keep the disk running */
+        rb->ata_spindown(256);
+
+        rb->strcpy(old, search);
+        rb->kbd_input(search, WORDLEN);
+        /* exit if the search string is empty or the user didn't change it */
+        if (!rb->strlen(search) ||
+            (!new_search && !rb->strncmp(old, search, sizeof(old)) ))
         {
-            *space = '\0';
-            next = (space - (char*)output) + 1;
+            return DICT_OK;
         }
-        else
+        new_search = false;
+
+        result.index = dict_index_find_startingWith(search);
+        switch (result.index)
         {
-            next = display_columns;
+            case DICT_NOT_FOUND:
+                dict_error("No articles found");
+                continue;
+            case DICT_QUIT:
+                return DICT_OK;
+            case DICT_USB_CONNECTED:
+                return DICT_USB_CONNECTED;
         }
 
-        /* put the line on screen */
-        rb->lcd_puts(0, lines, output);
+        /* get result data */
+        dict_index_entry(result.index, &result); 
 
-        /* get output count */
-        outputted += rb->strlen(output);
+        do {
+            if (desc_size > 0)
+                dict_free(desc_size);
 
-        if (outputted < len)
+            /* increase the buffer to put a title in front of the article */
+            desc_size = result.data.size + DESC_BUFFER_ADD;
+
+            /* set desc pointer to beginning of the description */
+            desc = (char *)dict_malloc(desc_size) + DESC_BUFFER_ADD;
+            if (!dict_desc_read(&result.data, desc)) 
+                return DICT_ERROR;
+
+        } while (parse_mw_redirect(&result, desc));
+
+        /* stop the disk */
+        rb->ata_spindown(0);
+
+        DEBUGF("\
+result:\n\
+    name:   %s\n\
+    index:  %ld\n\
+    offset: %llu (%llx)\n\
+    size:   %lu\n",
+                result.name, result.index, result.data.offset,
+                result.data.offset, result.data.size);
+
+
+        /* make title */
+        title_size = rb->strlen(result.name) + 20;
+        if (title_size > DESC_BUFFER_ADD)
+            title_size = DESC_BUFFER_ADD;
+        desc -= title_size;
+        rb->snprintf(desc, title_size, "[3m[1m%s[22m[23m\n", result.name);
+        desc[title_size-1] = '\n';
+
+	viewer_set_text(desc);
+
+        while (!new_search)
         {
-            /* set pointer to the next part */
-            ptr += next;
-            lines++;
+            switch(viewer_run())
+            {
+                case VIEWER_NEW:
+                    new_search = true;
+                    continue;
+                case VIEWER_ATTACHED_USB:
+                    return DICT_USB_CONNECTED;
+                default:
+                    return DICT_OK;
+            }
         }
-        else
-        {
+
+        dict_free(desc_size);
+    }
+}
+
+enum plugin_status plugin_start(struct plugin_api* api, void* file)
+{
+    enum plugin_status ret;
+
+    rb = api;
+
+    switch (dict_init(file))
+    {
+        case DICT_ERROR:
+            return PLUGIN_ERROR;
+        case DICT_USB_CONNECTED:
+            return PLUGIN_USB_CONNECTED;
+        default:
             break;
-        }
     }
-    rb->lcd_update();
 
-    /* wait for keypress */
-    while(rb->button_get(true) != LP_QUIT)
+    switch (dict_main())
     {
-        /* do nothing */
-        /* maybe define some keys for navigation here someday. */
+        case DICT_OK:
+            ret = PLUGIN_OK;
+            break;
+        case DICT_USB_CONNECTED:
+            ret = PLUGIN_USB_CONNECTED;
+            break;
+        default:
+            ret = PLUGIN_ERROR;
+            break;
     }
 
-    rb->close(fIndex);
-    rb->close(fData);
-    return PLUGIN_OK;
+    /* reset disk setting */
+    rb->ata_spindown(rb->global_settings->disk_spindown);
+
+    dict_close();
+
+    return ret;
 }
Index: apps/plugins/lib/pluginlib_actions.h
===================================================================
--- apps/plugins/lib/pluginlib_actions.h	(revision 13161)
+++ apps/plugins/lib/pluginlib_actions.h	(working copy)
@@ -47,9 +47,27 @@
 
 static const struct button_mapping generic_directions[] = 
 {
-#if    (CONFIG_KEYPAD == IRIVER_H100_PAD)   \
-    || (CONFIG_KEYPAD == IRIVER_H300_PAD)   \
-    || (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)     \
+#if    (CONFIG_KEYPAD == IRIVER_H100_PAD)
+    { PLA_UP,                BUTTON_UP,                  BUTTON_NONE},
+    { PLA_DOWN,              BUTTON_DOWN,                BUTTON_NONE},
+    { PLA_LEFT,              BUTTON_LEFT,                BUTTON_NONE},
+    { PLA_RIGHT,             BUTTON_RIGHT,               BUTTON_NONE},
+    { PLA_UP_REPEAT,         BUTTON_UP|BUTTON_REPEAT,    BUTTON_NONE},
+    { PLA_DOWN_REPEAT,       BUTTON_DOWN|BUTTON_REPEAT,  BUTTON_NONE},
+    { PLA_LEFT_REPEAT,       BUTTON_LEFT|BUTTON_REPEAT,  BUTTON_NONE},
+    { PLA_RIGHT_REPEAT,      BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+
+    /* remote */
+    { PLA_UP,                BUTTON_RC_SOURCE,                BUTTON_NONE},
+    { PLA_DOWN,              BUTTON_RC_BITRATE,               BUTTON_NONE},
+    { PLA_LEFT,              BUTTON_RC_REW,                   BUTTON_NONE},
+    { PLA_RIGHT,             BUTTON_RC_FF,                    BUTTON_NONE},
+    { PLA_UP_REPEAT,         BUTTON_RC_SOURCE|BUTTON_REPEAT,  BUTTON_NONE},
+    { PLA_DOWN_REPEAT,       BUTTON_RC_BITRATE|BUTTON_REPEAT, BUTTON_NONE},
+    { PLA_LEFT_REPEAT,       BUTTON_RC_REW|BUTTON_REPEAT,     BUTTON_NONE},
+    { PLA_RIGHT_REPEAT,      BUTTON_RC_FF|BUTTON_REPEAT,      BUTTON_NONE},
+#elif  (CONFIG_KEYPAD == IRIVER_H300_PAD)   \
+    || (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)   \
     || (CONFIG_KEYPAD == GIGABEAT_PAD)      \
     || (CONFIG_KEYPAD == RECORDER_PAD)      \
     || (CONFIG_KEYPAD == ARCHOS_AV300_PAD)  \
@@ -176,13 +194,25 @@
 /* these were taken from the bubbles plugin, so may need tweaking */
 static const struct button_mapping generic_actions[] = 
 {
-#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#if (CONFIG_KEYPAD == IRIVER_H100_PAD)
     {PLA_QUIT,          BUTTON_OFF,     BUTTON_NONE},
-    {PLA_QUIT,          BUTTON_RC_STOP,  BUTTON_NONE},
     {PLA_START,         BUTTON_ON,      BUTTON_NONE},
     {PLA_MENU,          BUTTON_MODE,    BUTTON_NONE},
     {PLA_FIRE,          BUTTON_SELECT,  BUTTON_NONE},
     {PLA_FIRE_REPEAT,   BUTTON_SELECT|BUTTON_REPEAT,  BUTTON_NONE},
+
+    /* remote */
+    {PLA_QUIT,          BUTTON_RC_STOP,                  BUTTON_NONE},
+    {PLA_START,         BUTTON_RC_ON,                    BUTTON_NONE },
+    {PLA_START,         BUTTON_RC_MENU|BUTTON_REL,       BUTTON_RC_MENU },
+    {PLA_MENU,          BUTTON_RC_MODE|BUTTON_REL,       BUTTON_RC_MODE },
+    {PLA_FIRE,          BUTTON_RC_MODE|BUTTON_REPEAT,    BUTTON_RC_MODE },
+#elif (CONFIG_KEYPAD == IRIVER_H300_PAD)
+    {PLA_QUIT,          BUTTON_OFF,     BUTTON_NONE},
+    {PLA_START,         BUTTON_ON,      BUTTON_NONE},
+    {PLA_MENU,          BUTTON_MODE,    BUTTON_NONE},
+    {PLA_FIRE,          BUTTON_SELECT,  BUTTON_NONE},
+    {PLA_FIRE_REPEAT,   BUTTON_SELECT|BUTTON_REPEAT,  BUTTON_NONE},
 #elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD)
     {PLA_QUIT,          BUTTON_MENU|BUTTON_SELECT,      BUTTON_NONE},
     {PLA_START,         BUTTON_PLAY|BUTTON_REL,         BUTTON_PLAY},
Index: apps/plugins/lib/viewer.c
===================================================================
--- apps/plugins/lib/viewer.c	(revision 0)
+++ apps/plugins/lib/viewer.c	(revision 0)
@@ -0,0 +1,368 @@
+/***************************************************************************
+*             __________               __   ___.
+*   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+*   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+*   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+*   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+*                     \/            \/     \/    \/            \/
+* $Id: viewer.c 12008 2007-01-14 13:48:09Z dave $
+*
+* Copyright (C) 2007 Timo Horstschäfer
+*
+*
+* All files in this archive are subject to the GNU General Public License.
+* See the file COPYING in the source tree root for full license agreement.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+
+#include "plugin.h"
+#include "pluginlib_actions.h"
+#include "viewer.h"
+
+#define VIEWER_LINE_BUF 255
+#define VIEWER_LAST_PAGE (v.last_line - v.rows)
+
+#define VIEWER_SCROLLBAR_WIDTH 4
+
+#define VIEWER_SEEK_BACK 100
+
+static struct plugin_api *rb;
+
+/* Viewer configuration */
+struct _viewer_conf {
+    /* reinitialized on every viewer_set_text() call */
+    const char          *text;
+    int                 last_line, size;
+    const char          *pos;
+    int                 line;
+
+    /* text independant, only initialized by viewer_init() */
+    int                 cols, rows;
+#ifdef HAVE_REMOTE_LCD
+    int                 remote_cols, remote_rows;
+#endif
+
+    /* user-controllable configuration */
+    void                (*callback)(int button);
+    int                 scroll;
+
+    bool                quit;
+    int                 retval;
+};
+
+struct _viewer_conf v;
+
+/** \brief Returns a pointer to the last character of a line.
+ *
+ * @param s Pointer to the actual line.
+ */
+static char *viewer_endl(const char *s, int cols)
+{
+    const char *space = NULL;
+    int l;
+
+    while (true)
+    {
+        if ((l = rb->ansi_escape(s, NULL)))
+        {
+            s += l;
+            continue;
+        }
+        if (*s == '\n' || *s == '\0')
+            return (char *)s;
+        if (*s == ' ')
+            space = s;
+        if (!cols--)
+            break;
+        s += rb->utf8seek(s, 1);
+    }
+
+    return (char *) ( (space==NULL) ? s : space );
+}
+
+//! Returns a pointer to the beginning of the given line
+static char *viewer_line(const char *s, int line)
+{
+    while (line-- > 0)
+    {
+        s = viewer_endl(s, v.cols);
+        if (!*s)
+            return NULL;
+        s += rb->utf8seek(s, 1);
+    }
+
+    return (char *) s;
+}
+
+/** \brief Draws text on the display.
+ *
+ * Handles newline characters and line wrapping.
+ */
+static void viewer_draw(const char *s, int style_begin)
+{
+    const char *p, *next;
+    int line, len;
+    char buf[VIEWER_LINE_BUF];
+    int style;
+
+    rb->lcd_clear_display();
+
+#ifdef HAVE_LCD_BITMAP
+    if (v.last_line > v.rows)
+    { 
+        rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN], 0, 0,
+                                VIEWER_SCROLLBAR_WIDTH-1, LCD_HEIGHT,
+                                v.last_line+v.rows-1, v.line, v.line+v.rows,
+                                VERTICAL);
+        rb->lcd_setmargins(VIEWER_SCROLLBAR_WIDTH, 0);
+    }
+#endif
+
+    /* draw main display */
+    style = style_begin; 
+    for (p=s, line=0; line<v.rows; line++)
+    {
+        next = viewer_endl(p, v.cols);
+
+        len = next-p;
+        rb->strncpy(buf, p, len);
+        buf[len] = '\0';
+
+        rb->lcd_puts_style(0, line, buf, style|STYLE_INTERPRET);
+        style = rb->lcd_get_last_style();
+
+        if (!*next)
+            break;
+        p = next + rb->utf8seek(p, 1);
+    }
+
+    rb->lcd_update();
+
+#ifdef HAVE_REMOTE_LCD
+    rb->lcd_remote_clear_display();
+
+    /* draw remote */
+    style = style_begin; 
+    for (p=s, line=0; line<v.remote_rows; line++)
+    {
+        next = viewer_endl(p, v.remote_cols);
+
+        len = next-p;
+        rb->strncpy(buf, p, len);
+        buf[len] = '\0';
+
+        rb->lcd_remote_puts_style(0, line, buf, style|STYLE_INTERPRET);
+        style = rb->lcd_remote_get_last_style();
+
+        if (!*next)
+            break;
+        p = next + rb->utf8seek(p, 1);
+    }
+
+    rb->lcd_remote_update();
+#endif
+}
+
+inline int viewer(struct plugin_api *api, const char *text)
+{
+    viewer_init(api);
+    viewer_set_text(text);
+
+    return viewer_run();
+}
+
+void viewer_init(struct plugin_api *api)
+{
+    if (!api)
+        return;
+
+    rb = api;
+
+    v.callback = NULL;
+
+#ifdef HAVE_LCD_BITMAP
+    rb->lcd_getstringsize("o", &v.cols, &v.rows);
+
+#ifdef HAVE_REMOTE_LCD
+    v.remote_cols = LCD_REMOTE_WIDTH / v.cols;
+    v.remote_rows = LCD_REMOTE_HEIGHT / v.rows;
+#endif
+
+    v.cols = (LCD_WIDTH-VIEWER_SCROLLBAR_WIDTH) / v.cols;
+    v.rows = LCD_HEIGHT / v.rows;
+#else
+    v.cols = 11;
+    v.rows = 2;
+#endif
+}
+
+int viewer_run(void)
+{
+    int button;
+    const struct button_mapping *viewer_contexts[] = {
+        generic_directions,
+        generic_actions,
+    };
+
+    v.quit = false;
+    viewer_redraw();
+
+    while (!v.quit)
+    {
+        rb->yield();
+
+        button = pluginlib_getaction(rb, HZ, viewer_contexts, 2);
+        switch (button)
+        {
+            case PLA_UP: case PLA_UP_REPEAT:
+                viewer_up();
+                break;
+            case PLA_DOWN: case PLA_DOWN_REPEAT:
+                viewer_down();
+                break;
+            case PLA_QUIT:
+                viewer_exit(VIEWER_EXIT);
+                break;
+            default:
+                if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+                    viewer_exit(VIEWER_ATTACHED_USB);
+
+                if (v.callback)
+                    v.callback(button);
+
+                break;
+        }
+   }
+
+    return v.retval;
+}
+
+void viewer_set_text(const char *text)
+{
+    const char *p;
+
+    v.text = v.pos = p = text;
+    v.last_line = v.line = 0;
+
+    /* get the last line */
+    while (true)
+    {
+        v.last_line++;
+        p = viewer_endl(p, v.cols);
+        if (*p)
+            p += rb->utf8seek(p, 1);
+        else
+            break;
+    }
+    v.size = p - v.text;
+}
+
+void viewer_set_scroll(int lines)
+{
+    v.scroll = (lines <= 0) ? v.rows : lines;
+}
+
+void viewer_set_callback(void (*callback)(int button))
+{
+    v.callback = callback;
+}
+
+void viewer_set_line(int line)
+{
+    v.line = (line < 0 || line > v.last_line) ? VIEWER_LAST_PAGE : line;
+    v.pos = viewer_line(v.text, v.line);
+
+    viewer_redraw();
+}
+
+void viewer_set_pos(int pos)
+{
+    const char *p;
+
+    if (pos < 0 || pos >= v.size)
+    {
+        viewer_set_line(VIEWER_BOTTOM);
+        return;
+    }
+
+    v.pos = p = v.text;
+    v.line = 0;
+    while (true)
+    {
+        p = viewer_endl(p, v.cols);
+        p += rb->utf8seek(p, 1);
+        if (p-v.text < pos)
+        {
+            v.pos = p;
+            v.line++;
+        } else
+            break;
+    }
+
+    viewer_redraw();
+}
+
+int viewer_get_line(void)
+{
+    return v.line;
+}
+
+int viewer_get_pos(void)
+{
+    return v.pos - v.text;
+}
+
+void viewer_up(void)
+{
+    v.line = (v.line < v.scroll) ? 0 : v.line - v.scroll;
+    v.pos = viewer_line(v.text, v.line);
+
+    viewer_redraw();
+}
+
+void viewer_down(void)
+{
+    int n;
+
+#ifdef HAVE_REMOTE_LCD
+    if (v.last_line < v.remote_rows)
+#else
+    if (v.last_line < v.rows)
+#endif
+        return;
+
+    n = v.scroll;
+
+    if (v.line + n >= v.last_line)
+        n = v.last_line - v.line - 1;
+
+    v.pos = viewer_line(v.pos, n);
+    v.line += n;
+    
+    viewer_redraw();
+}
+
+void viewer_redraw(void)
+{
+    int style = STYLE_DEFAULT, len;
+    const char *p;
+
+    /* search for escape sequences, that are before the drawn text */
+    p = (v.pos-v.text > VIEWER_SEEK_BACK) ? v.pos-VIEWER_SEEK_BACK : v.text;
+    while (p < v.pos)
+    {
+        len = rb->ansi_escape(p, &style);
+        p += (len) ? len : 1;
+    }
+    
+    viewer_draw(v.pos, style);
+}
+
+void viewer_exit(int retval)
+{
+    v.retval = retval;
+    v.quit = true;
+}
Index: apps/plugins/lib/SOURCES
===================================================================
--- apps/plugins/lib/SOURCES	(revision 13161)
+++ apps/plugins/lib/SOURCES	(working copy)
@@ -28,3 +28,4 @@
 #endif
 #endif
 pluginlib_actions.c
+viewer.c
Index: apps/plugins/lib/viewer.h
===================================================================
--- apps/plugins/lib/viewer.h	(revision 0)
+++ apps/plugins/lib/viewer.h	(revision 0)
@@ -0,0 +1,66 @@
+/***************************************************************************
+*             __________               __   ___.
+*   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+*   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+*   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+*   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+*                     \/            \/     \/    \/            \/
+* $Id: viewer.h 12008 2007-01-14 13:48:09Z dave $
+*
+* Copyright (C) 2007 Timo Horstschäfer
+*
+*
+* All files in this archive are subject to the GNU General Public License.
+* See the file COPYING in the source tree root for full license agreement.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+
+#include "plugin.h"
+
+/** \brief Shows a text viewer.
+ *
+ * How to view some text:
+ *  viewer(api, textptr);       // Text must be zero-terminated.
+ *
+ * For full control, call each function seperately:
+ *  viewer_init(rb);            // Must be done once in a plugin.
+ *  viewer_set_text(textptr);
+ *  viewer_run();               // Display the text.
+ *
+ * viewer() is just a short form for all three calls.
+ *
+ * @param text Zero-terminated string.
+ */
+int viewer(struct plugin_api *api, const char *text);
+#define VIEWER_ATTACHED_USB -2
+#define VIEWER_EXIT         0
+#define VIEWER_LAST         VIEWER_EXIT
+/**< Use values greater than VIEWER_LAST for custom return values. */
+
+void viewer_init(struct plugin_api *api);
+int viewer_run(void);
+
+void viewer_set_text(const char *text);
+
+void viewer_set_scroll(int lines);
+#define VIEWER_SCROLL_PAGE 0
+
+/** Set a callback.
+ * Have a look at viewer_run() in viewer.c to see how it is used. */
+void viewer_set_callback(void (*callback)(int button));
+
+void viewer_set_line(int line);
+void viewer_set_pos(int pos);
+#define VIEWER_BOTTOM -1
+
+int viewer_get_line(void);
+int viewer_get_pos(void);
+
+/** Additional functions to control the viewer in the callback function. */
+void viewer_up(void);
+void viewer_down(void);
+void viewer_redraw(void);
+void viewer_exit(int retval);
Index: apps/plugins/SOURCES
===================================================================
--- apps/plugins/SOURCES	(revision 13161)
+++ apps/plugins/SOURCES	(working copy)
@@ -36,6 +36,7 @@
 #ifdef HAVE_LCD_BITMAP             /* Not for the Player */
 mazezam.c
 text_editor.c
+test_styles.c
 wavview.c
 
 /* Plugins needing the grayscale lib on low-depth LCDs */
Index: apps/gui/list.c
===================================================================
--- apps/gui/list.c	(revision 13161)
+++ apps/gui/list.c	(working copy)
@@ -305,9 +305,9 @@
         item_offset = gui_list_get_item_offset(gui_list, gui_list->title_width,
                                                text_pos);
         if (item_offset > gui_list->title_width - (display->width - text_pos))
-            display->puts_offset(0, 0, gui_list->title, item_offset);
+            display->puts_style_offset(0, 0, gui_list->title, STYLE_BOLD, item_offset);
         else
-            display->puts_scroll_offset(0, 0, gui_list->title, item_offset);
+            display->puts_scroll_style_offset(0, 0, gui_list->title, STYLE_BOLD, item_offset);
 #else
         display->puts_scroll(text_pos, 0, gui_list->title);
 #endif
Index: apps/settings.h
===================================================================
--- apps/settings.h	(revision 13161)
+++ apps/settings.h	(working copy)
@@ -62,6 +62,7 @@
 #define EQS_DIR     ROCKBOX_DIR "/eqs"
 #define CODECS_DIR  ROCKBOX_DIR"/codecs"
 #define FMPRESET_PATH ROCKBOX_DIR "/fmpresets"
+#define DICTS_DIR  "/dicts"
 
 #define VIEWERS_CONFIG      ROCKBOX_DIR "/viewers.config"
 #define CONFIGFILE          ROCKBOX_DIR "/config.cfg"
@@ -157,7 +158,8 @@
  *       must be added after NUM_FILTER_MODES. */
 enum { SHOW_ALL, SHOW_SUPPORTED, SHOW_MUSIC, SHOW_PLAYLIST, SHOW_ID3DB,
        NUM_FILTER_MODES,
-       SHOW_WPS, SHOW_RWPS, SHOW_FMR, SHOW_CFG, SHOW_LNG, SHOW_MOD, SHOW_FONT, SHOW_PLUGINS};
+       SHOW_WPS, SHOW_RWPS, SHOW_FMR, SHOW_CFG, SHOW_LNG,
+       SHOW_MOD, SHOW_FONT, SHOW_DICTS, SHOW_PLUGINS};
 
 /* recursive dir insert options */
 enum { RECURSE_OFF, RECURSE_ON, RECURSE_ASK };
Index: apps/filetree.c
===================================================================
--- apps/filetree.c	(revision 13161)
+++ apps/filetree.c	(working copy)
@@ -266,7 +266,8 @@
             ((*c->dirfilter == SHOW_MUSIC &&
              (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MPA) &&
              (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) ||
-            (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)))) ||
+            ((*c->dirfilter == SHOW_SUPPORTED || *c->dirfilter == SHOW_DICTS)
+             && !filetype_supported(dptr->attr)))) ||
             (*c->dirfilter == SHOW_WPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_WPS) ||
 #ifdef HAVE_REMOTE_LCD
             (*c->dirfilter == SHOW_RWPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_RWPS) ||
@@ -569,6 +570,7 @@
         else {
             if (*c->dirfilter > NUM_FILTER_MODES &&
                 *c->dirfilter != SHOW_FONT &&
+                *c->dirfilter != SHOW_DICTS &&
                 *c->dirfilter != SHOW_PLUGINS)
             {
                 exit_func = true;
Index: apps/player/icons.h
===================================================================
--- apps/player/icons.h	(revision 13161)
+++ apps/player/icons.h	(working copy)
@@ -41,6 +41,7 @@
     Icon_Playlist,
     Icon_Text,
     Icon_Config,
+    Icon_Dictionary,
 };
 
 /* put icons from the 6x8 enum here if the player
Index: apps/plugin.c
===================================================================
--- apps/plugin.c	(revision 13161)
+++ apps/plugin.c	(working copy)
@@ -72,7 +72,9 @@
     lcd_getstringsize,
     lcd_putsxy,
     lcd_puts,
+    lcd_puts_style,
     lcd_puts_scroll,
+    lcd_puts_scroll_style,
     lcd_stop_scroll,
 #ifdef HAVE_LCD_CHARCELLS
     lcd_define_pattern,
@@ -111,8 +113,6 @@
     bidi_l2v,
     font_get_bits,
     font_load,
-    lcd_puts_style,
-    lcd_puts_scroll_style,
     &lcd_framebuffer[0][0],
     lcd_blit,
     lcd_update_rect,
@@ -129,6 +129,7 @@
     /* remote lcd */
     lcd_remote_set_contrast,
     lcd_remote_clear_display,
+    lcd_remote_setmargins,
     lcd_remote_puts,
     lcd_remote_puts_scroll,
     lcd_remote_stop_scroll,
@@ -492,6 +493,12 @@
     spinlock_lock,
     spinlock_unlock,
 #endif
+
+    ansi_escape,
+    lcd_get_last_style,
+#ifdef HAVE_REMOTE_LCD
+    lcd_remote_get_last_style,
+#endif
 };
 
 int plugin_load(const char* plugin, void* parameter)
Index: apps/plugin.h
===================================================================
--- apps/plugin.h	(revision 13161)
+++ apps/plugin.h	(working copy)
@@ -75,6 +75,7 @@
 #include "rbunicode.h"
 #include "list.h"
 #include "tree.h"
+#include "ansi.h"
 
 #ifdef HAVE_REMOTE_LCD
 #include "lcd-remote.h"
@@ -140,7 +141,10 @@
     int  (*lcd_getstringsize)(const unsigned char *str, int *w, int *h);
     void (*lcd_putsxy)(int x, int y, const unsigned char *string);
     void (*lcd_puts)(int x, int y, const unsigned char *string);
+    void (*lcd_puts_style)(int x, int y, const unsigned char *str, int style);
     void (*lcd_puts_scroll)(int x, int y, const unsigned char* string);
+    void (*lcd_puts_scroll_style)(int x, int y, const unsigned char* string,
+                                  int style);
     void (*lcd_stop_scroll)(void);
 #ifdef HAVE_LCD_CHARCELLS
     void (*lcd_define_pattern)(unsigned long ucs, const char *pattern);
@@ -186,9 +190,6 @@
     unsigned short *(*bidi_l2v)( const unsigned char *str, int orientation );
     const unsigned char *(*font_get_bits)( struct font *pf, unsigned short char_code );
     struct font* (*font_load)(const char *path);
-    void (*lcd_puts_style)(int x, int y, const unsigned char *str, int style);
-    void (*lcd_puts_scroll_style)(int x, int y, const unsigned char* string,
-                                  int style);
     fb_data* lcd_framebuffer;
     void (*lcd_blit) (const fb_data* data, int x, int by, int width,
                       int bheight, int stride);
@@ -211,6 +212,7 @@
     /* remote lcd */
     void (*lcd_remote_set_contrast)(int x);
     void (*lcd_remote_clear_display)(void);
+    void (*lcd_remote_setmargins)(int xmargin, int ymargin);
     void (*lcd_remote_puts)(int x, int y, const unsigned char *string);
     void (*lcd_remote_lcd_puts_scroll)(int x, int y, const unsigned char* string);
     void (*lcd_remote_lcd_stop_scroll)(void);
@@ -606,6 +608,12 @@
     void (*spinlock_lock)(struct mutex *m);
     void (*spinlock_unlock)(struct mutex *m);
 #endif
+
+    int (*ansi_escape)(const char *s, int *style);
+    int (*lcd_get_last_style)(void);
+#ifdef HAVE_REMOTE_LCD
+    int (*lcd_remote_get_last_style)(void);
+#endif
 };
 
 /* plugin header */
Index: apps/root_menu.c
===================================================================
--- apps/root_menu.c	(revision 13161)
+++ apps/root_menu.c	(working copy)
@@ -172,6 +172,10 @@
             tc->selected_item = last_db_selection;
         break;
 #endif
+        case GO_TO_BROWSEDICTS:
+            filter = SHOW_DICTS;
+            snprintf(folder, MAX_PATH, "%s/", DICTS_DIR);
+        break;
         case GO_TO_BROWSEPLUGINS:
             filter = SHOW_PLUGINS;
             snprintf(folder, MAX_PATH, "%s/", PLUGIN_DIR);
@@ -275,6 +279,7 @@
 #endif
     
     [GO_TO_RECENTBMARKS] =  { load_bmarks, NULL, &bookmark_settings_menu }, 
+    [GO_TO_BROWSEDICTS] = { browser, (void*)GO_TO_BROWSEDICTS, NULL }, 
     [GO_TO_BROWSEPLUGINS] = { browser, (void*)GO_TO_BROWSEPLUGINS, NULL }, 
     
 };
@@ -288,6 +293,8 @@
 MENUITEM_RETURNVALUE(db_browser, ID2P(LANG_TAGCACHE), GO_TO_DBBROWSER, 
                         NULL, Icon_Audio);
 #endif
+MENUITEM_RETURNVALUE(dicts_browser, ID2P(LANG_DICTIONARIES), GO_TO_BROWSEDICTS, 
+                        NULL, Icon_Dictionary);
 MENUITEM_RETURNVALUE(rocks_browser, ID2P(LANG_PLUGINS), GO_TO_BROWSEPLUGINS, 
                         NULL, Icon_Plugin);
 char *get_wps_item_name(int selected_item, void * data, char *buffer)
@@ -339,7 +346,7 @@
 #if CONFIG_TUNER
             &fm,
 #endif
-            &playlist_options, &rocks_browser,  &info_menu
+            &playlist_options, &dicts_browser, &rocks_browser,  &info_menu
 
 #ifdef HAVE_LCD_CHARCELLS
             ,&do_shutdown_item
Index: apps/root_menu.h
===================================================================
--- apps/root_menu.h	(revision 13161)
+++ apps/root_menu.h	(working copy)
@@ -46,6 +46,7 @@
     /* Do Not add any items above here unless you want it to be able to 
        be the "start screen" after a boot up. The setting in settings_list.c
        will need editing if this is the case. */
+    GO_TO_BROWSEDICTS,
     GO_TO_BROWSEPLUGINS,
 };
 
Index: firmware/export/ansi.h
===================================================================
--- firmware/export/ansi.h	(revision 0)
+++ firmware/export/ansi.h	(revision 0)
@@ -0,0 +1,23 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: lcd.h 12419 2007-02-20 19:31:34Z amiconn $
+ *
+ * Copyright (C) 2007 Timo Horstschäfer
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "lcd.h"
+
+int ansi_escape(const char *s, int *style);
+int ansi_escape_ucs(const unsigned short *s, int *style);
Index: firmware/export/lcd.h
===================================================================
--- firmware/export/lcd.h	(revision 13161)
+++ firmware/export/lcd.h	(working copy)
@@ -24,8 +24,14 @@
 #include "cpu.h"
 #include "config.h"
 
-#define STYLE_DEFAULT 0
-#define STYLE_INVERT  1
+#define STYLE_DEFAULT   STYLE_NORMAL
+#define STYLE_NORMAL    0x0
+#define STYLE_BOLD      0x1
+#define STYLE_SHADOW    0x2
+#define STYLE_UNDERLINE 0x4
+#define STYLE_INVERT    0x8
+/* allow to control the style using ANSI escape sequences */
+#define STYLE_INTERPRET 0x10
 
 #ifdef SIMULATOR
 #ifndef MAX_PATH
@@ -69,8 +75,8 @@
 extern void lcd_puts(int x, int y, const unsigned char *string);
 extern void lcd_puts_style(int x, int y, const unsigned char *string, int style);
 extern void lcd_puts_offset(int x, int y, const unsigned char *str, int offset);
-extern void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
-                                  int offset);
+extern void lcd_puts_style_offset(int x, int y, const unsigned char *str,
+                                  int style, int offset);
 extern void lcd_putc(int x, int y, unsigned long ucs);
 extern void lcd_stop_scroll(void);
 extern void lcd_bidir_scroll(int threshold);
@@ -79,7 +85,14 @@
 extern void lcd_puts_scroll(int x, int y, const unsigned char* string);
 extern void lcd_puts_scroll_style(int x, int y, const unsigned char* string,
                                   int style);
+extern void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
+                                  int offset);
+extern void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
+                                  int style, int offset);
+extern void lcd_icon(int icon, bool enable);
 
+extern int  lcd_get_last_style(void);
+
 #if defined(HAVE_LCD_COLOR) && !defined(SIMULATOR)
 extern void lcd_yuv_blit(unsigned char * const src[3],
                          int src_x, int src_y, int stride,
@@ -307,11 +320,6 @@
 extern int  lcd_get_drawmode(void);
 extern void lcd_setfont(int font);
 
-extern void lcd_puts_style_offset(int x, int y, const unsigned char *str,
-                                  int style, int offset);
-extern void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
-                                  int style, int offset);
-
 /* low level drawing function pointer arrays */
 #if LCD_DEPTH >= 8
 extern lcd_fastpixelfunc_type* const *lcd_fastpixelfuncs;
@@ -333,6 +341,11 @@
                             int stride, int x, int y, int width, int height);
 extern void lcd_bitmap(const fb_data *src, int x, int y, int width,
                        int height);
+extern void lcd_putsxy(int x, int y, const unsigned char *str);
+extern void lcd_putsxy_style(int x, int y, const unsigned char *str, int style);
+extern void lcd_putsxy_offset(int x, int y, int ofs, const unsigned char *str);
+extern void lcd_putsxy_style_offset(int x, int y, int ofs,
+                                    const unsigned char *str, int style);
 
 extern void lcd_invertscroll(int x, int y);
 extern void lcd_scroll_step(int pixels);
Index: firmware/export/lcd-remote.h
===================================================================
--- firmware/export/lcd-remote.h	(revision 13161)
+++ firmware/export/lcd-remote.h	(working copy)
@@ -43,8 +43,14 @@
 extern struct event_queue remote_scroll_queue;
 #endif
 
-#define STYLE_DEFAULT 0
-#define STYLE_INVERT  1
+#define STYLE_DEFAULT   STYLE_NORMAL
+#define STYLE_NORMAL    0x0
+#define STYLE_BOLD      0x1
+#define STYLE_SHADOW    0x2
+#define STYLE_UNDERLINE 0x4
+#define STYLE_INVERT    0x8
+/* allow to control the style using ANSI escape sequences */
+#define STYLE_INTERPRET 0x10
 
 #if LCD_REMOTE_DEPTH <= 8
 #if (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) \
@@ -147,6 +153,7 @@
 extern int  lcd_remote_getymargin(void);
 extern void lcd_remote_setfont(int font);
 extern int  lcd_remote_getstringsize(const unsigned char *str, int *w, int *h);
+extern int  lcd_remote_get_last_style(void);
 
 /* low level drawing function pointer arrays */
 #if LCD_REMOTE_DEPTH > 1
@@ -169,6 +176,12 @@
 extern void lcd_remote_bitmap(const fb_remote_data *src, int x, int y,
                               int width, int height);
 extern void lcd_remote_putsxy(int x, int y, const unsigned char *str);
+extern void lcd_remote_putsxy_style(int x, int y, const unsigned char *str,
+                                    int style);
+extern void lcd_remote_putsxy_offset(int x, int y, int ofs,
+                                     const unsigned char *str);
+extern void lcd_remote_putsxy_style_offset(int x, int y, int ofs,
+                                           const unsigned char *str, int style);
 
 extern void lcd_remote_invertscroll(int x, int y);
 extern void lcd_remote_bidir_scroll(int threshold);
Index: firmware/SOURCES
===================================================================
--- firmware/SOURCES	(revision 13161)
+++ firmware/SOURCES	(working copy)
@@ -1,3 +1,4 @@
+ansi.c
 ata_idle_notify.c
 backlight.c
 buffer.c
Index: firmware/ansi.c
===================================================================
--- firmware/ansi.c	(revision 0)
+++ firmware/ansi.c	(revision 0)
@@ -0,0 +1,105 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: buffer.c 10958 2006-09-16 16:18:11Z miipekk $
+ *
+ * Copyright (C) 2007 Timo Horstschäfer
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "ansi.h"
+#include "stdlib.h"
+#include "debug.h"
+
+/**
+ * Handles ANSI escape sequences for SGR.
+ * The sequence begins with 'ESC [' and ends with 'm'.
+ * 
+ * Syntax:
+ *  \033[##m
+ *      where ## is the value.
+ *
+ * Currently supports:
+ *  0   all attributes off
+ *  1   bold:       on
+ *  3   shadow:     on (instead of italic)
+ *  4   underline:  on
+ *  7   invert:     on
+ *  22  bold:       off
+ *  23  shadow      off
+ *  24  underline:  off
+ *  27  invert:     off
+ *
+ *  Example:
+ *      printf("\33[1mAppears bold\33[22m\nAppears normal\n");
+ */
+int ansi_sgr(int code, int style)
+{
+    switch (code) {
+        case 0:
+            style = STYLE_NORMAL;
+            break;
+        case 1:
+            style |= STYLE_BOLD;
+            break;
+        case 3:
+            style |= STYLE_SHADOW;
+            break;
+        case 4:
+            style |= STYLE_UNDERLINE;
+            break;
+        case 7:
+            style |= STYLE_INVERT;
+            break;
+        case 22:
+            style &= ~STYLE_BOLD;
+            break;
+        case 23:
+            style &= ~STYLE_SHADOW;
+            break;
+        case 24:
+            style &= ~STYLE_UNDERLINE;
+            break;
+        case 27:
+            style &= ~STYLE_INVERT;
+            break;
+    }
+
+    return style;
+}
+
+#define TEMPLATE_ANSI_ESCAPE(NAME, T)           \
+int NAME(const T *s, int *style)                \
+{                                               \
+    int code = 0;                               \
+    const T *p;                                 \
+                                                \
+    /* check CSI */                             \
+    if (*s != '\33' || *(s+1) != '[')           \
+        return 0;                               \
+                                                \
+    /* get value */                             \
+    for (p = s+2; *p > '0' && *p < '9'; p++)    \
+    {                                           \
+        if (style)                              \
+            code = code*10 + *p - '0';          \
+    }                                           \
+                                                \
+    if (style && *p == 'm')                     \
+        *style = ansi_sgr(code, *style);        \
+                                                \
+    return p - s + 1;                           \
+}
+
+TEMPLATE_ANSI_ESCAPE(ansi_escape, char)
+TEMPLATE_ANSI_ESCAPE(ansi_escape_ucs, unsigned short)
Index: firmware/drivers/lcd-1bit-vert.c
===================================================================
--- firmware/drivers/lcd-1bit-vert.c	(revision 13161)
+++ firmware/drivers/lcd-1bit-vert.c	(working copy)
@@ -29,6 +29,7 @@
 #include "font.h"
 #include "rbunicode.h"
 #include "bidi.h"
+#include "ansi.h"
 
 /*** definitions ***/
 
@@ -39,6 +40,7 @@
 unsigned char lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH];
 
 static int drawmode = DRMODE_SOLID;
+static int last_style = STYLE_DEFAULT;
 static int xmargin = 0;
 static int ymargin = 0;
 static int curfont = FONT_SYSFIXED;
@@ -109,6 +111,11 @@
     return font_getstringsize(str, w, h, curfont);
 }
 
+int lcd_get_last_style(void)
+{
+    return last_style;
+}
+
 /*** low-level drawing functions ***/
 
 static void setpixel(int x, int y)
@@ -599,22 +606,43 @@
     lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
 }
 
-/* put a string at a given pixel position, skipping first ofs pixel columns */
-static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
+void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
 {
-    unsigned short ch;
+    lcd_putsxy_style_offset(x, y, ofs, str, STYLE_DEFAULT);
+}
+
+/* put a string at a given pixel position, style, and pixel position,
+ * skipping first offset pixel columns */
+void lcd_putsxy_style_offset(int x, int y, int ofs, const unsigned char *str,
+                             int style)
+{
     unsigned short *ucs;
     struct font* pf = font_get(curfont);
+    int *sp;
+    int lastmode = drawmode;
+    int l;
 
+    if(!str || !str[0])
+        return;
+
+    sp = (style & STYLE_INTERPRET) ? &style : NULL;
     ucs = bidi_l2v(str, 1);
 
-    while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
+    for (; *ucs != 0; ucs++)
     {
         int width;
         const unsigned char *bits;
 
+        l = ansi_escape_ucs(ucs, sp);
+        if (x > LCD_WIDTH)
+            break;
+        if (l) {
+            ucs += l - 1;
+            continue;
+        }
+
         /* get proportional width and glyph bits */
-        width = font_get_width(pf,ch);
+        width = font_get_width(pf, *ucs);
 
         if (ofs > width)
         {
@@ -622,15 +650,31 @@
             continue;
         }
 
-        bits = font_get_bits(pf, ch);
+        bits = font_get_bits(pf, *ucs);
 
+        /* clear background */
+        drawmode ^= (style & STYLE_INVERT) ? 0 : DRMODE_INVERSEVID;
+        lcd_fillrect(x, y, width, pf->height);
+
+        /* compose styles */
+        drawmode = (style & STYLE_INVERT) ?
+                    (DRMODE_BG|DRMODE_INVERSEVID) : DRMODE_FG;
         lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
                              pf->height);
-        
+        if (style & STYLE_BOLD) {
+            lcd_mono_bitmap_part(bits, ofs, 0, width, x+1, y, width - ofs,
+                                 pf->height);
+        }
+        if (style & STYLE_UNDERLINE)
+            lcd_drawline(x, y+pf->height-1, x+width, y+pf->height-1);
+
+        drawmode = lastmode;
         x += width - ofs;
         ofs = 0;
     }
+    last_style = style;
 }
+
 /* put a string at a given pixel position */
 void lcd_putsxy(int x, int y, const unsigned char *str)
 {
@@ -672,10 +716,9 @@
     lcd_getstringsize(str, &w, &h);
     xpos = xmargin + x*w / utf8length(str);
     ypos = ymargin + y*h;
-    drawmode = (style & STYLE_INVERT) ?
-               (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-    lcd_putsxyofs(xpos, ypos, offset, str);
-    drawmode ^= DRMODE_INVERSEVID;
+    lcd_putsxy_style_offset(xpos, ypos, offset, str, style);
+    if (~style & STYLE_INVERT)
+        drawmode ^= DRMODE_INVERSEVID;
     xrect = xpos + MAX(w - offset, 0);
     lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
     drawmode = lastmode;
@@ -804,7 +847,6 @@
     struct scrollinfo* s;
     int index;
     int xpos, ypos;
-    int lastmode;
 
     /* initialize scroll struct array */
     scrolling_lines = 0;
@@ -850,11 +892,8 @@
                     s->offset %= s->width;
             }
 
-            lastmode = drawmode;
-            drawmode = s->invert ?
-                       (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-            lcd_putsxyofs(xpos, ypos, s->offset, s->line);
-            drawmode = lastmode;
+            lcd_putsxy_style_offset(xpos, ypos, s->offset, s->line,
+                                           (s->invert) ? STYLE_INVERT : STYLE_DEFAULT);
             lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
         }
 
Index: firmware/drivers/lcd-charcell.c
===================================================================
--- firmware/drivers/lcd-charcell.c	(revision 13161)
+++ firmware/drivers/lcd-charcell.c	(working copy)
@@ -29,6 +29,7 @@
 #include "system.h"
 #include "lcd-charcell.h"
 #include "rbunicode.h"
+#include "ansi.h"
 
 /** definitions **/
 
@@ -118,6 +119,11 @@
     return width;
 }
 
+int lcd_get_last_style(void)
+{
+	return STYLE_DEFAULT;
+}
+
 /** low-level functions **/
 
 static int find_xchar(unsigned long ucs)
@@ -356,9 +362,15 @@
 {
     unsigned short ucs;
     const unsigned char *utf8 = str;
+    int l;
 
     while (*utf8 && x < LCD_WIDTH)
     {
+	l = ansi_escape(utf8, NULL);
+	if (l > 0) {
+		utf8 += l;
+		continue;
+	}
         utf8 = utf8decode(utf8, &ucs);
         
         if (ofs > 0)
@@ -388,6 +400,12 @@
     lcd_puts_offset(x, y, str, 0);
 }
 
+void lcd_puts_style(int x, int y, const unsigned char *string, int style)
+{
+	(void)style;
+	lcd_puts(x, y, string);
+}
+
 /* Put a string at a given char position,  skipping first offset chars */
 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
 {
@@ -405,6 +423,13 @@
         lcd_putxchar(x++, y, xspace);
 }
 
+void lcd_puts_style_offset(int x, int y, const unsigned char *string,
+                           int style, int offset)
+{
+	(void)style;
+	lcd_puts_offset(x, y, string, offset);
+}
+
 /** scrolling **/
 
 void lcd_stop_scroll(void)
@@ -442,6 +467,13 @@
     lcd_puts_scroll_offset(x, y, string, 0);
 }
 
+void lcd_puts_scroll_style(int x, int y, const unsigned char *string,
+			   int style)
+{
+	(void)style;
+	lcd_puts_scroll(x, y, string);
+}
+
 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
                             int offset)
 {
@@ -496,6 +528,13 @@
         scrolling_lines &= ~(1<<y);
 }
 
+void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
+                                  int style, int offset)
+{
+	(void)style;
+	lcd_puts_scroll_offset(x, y, string, offset);
+}
+
 static void scroll_thread(void)
 {
     struct scrollinfo* s;
Index: firmware/drivers/lcd-2bit-vert.c
===================================================================
--- firmware/drivers/lcd-2bit-vert.c	(revision 13161)
+++ firmware/drivers/lcd-2bit-vert.c	(working copy)
@@ -30,6 +30,7 @@
 #include "font.h"
 #include "rbunicode.h"
 #include "bidi.h"
+#include "ansi.h"
 
 /*** definitions ***/
 
@@ -54,6 +55,7 @@
 static unsigned fg_pattern IDATA_ATTR = 0xFF; /* initially black */
 static unsigned bg_pattern IDATA_ATTR = 0x00; /* initially white */
 static int drawmode = DRMODE_SOLID;
+static int last_style = STYLE_NORMAL;
 static int xmargin = 0;
 static int ymargin = 0;
 static int curfont = FONT_SYSFIXED;
@@ -151,6 +153,11 @@
     return font_getstringsize(str, w, h, curfont);
 }
 
+int lcd_get_last_style(void)
+{
+    return last_style;
+}
+
 /*** low-level drawing functions ***/
 
 static void setpixel(int x, int y)
@@ -932,22 +939,53 @@
     lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
 }
 
-/* put a string at a given pixel position, skipping first ofs pixel columns */
-static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
+/*** pixel oriented text output ***/
+
+/* put a string at a given pixel position */
+void lcd_putsxy(int x, int y, const unsigned char *str)
 {
-    unsigned short ch;
+    lcd_putsxy_style_offset(x, y, 0, str, STYLE_DEFAULT);
+}
+
+void lcd_putsxy_style(int x, int y, const unsigned char *str, int style)
+{
+    lcd_putsxy_style_offset(x, y, 0, str, style);
+}
+
+void lcd_putsxy_offset(int x, int y, int ofs, const unsigned char *str)
+{
+    lcd_putsxy_style_offset(x, y, ofs, str, STYLE_DEFAULT);
+}
+
+/* put a string at a given pixel position, style, and pixel position,
+ * skipping first offset pixel columns */
+void lcd_putsxy_style_offset(int x, int y, int ofs, const unsigned char *str,
+                             int style)
+{
     unsigned short *ucs;
     struct font* pf = font_get(curfont);
+    int *sp;
+    int lastmode = drawmode, lastcolor;
+    int l;
 
+    sp = (style & STYLE_INTERPRET) ? &style : NULL;
     ucs = bidi_l2v(str, 1);
 
-    while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
+    for (; *ucs != 0; ucs++)
     {
         int width;
         const unsigned char *bits;
 
+        l = ansi_escape_ucs(ucs, sp);
+        if (x > LCD_WIDTH)
+            break;
+        if (l) {
+            ucs += l - 1;
+            continue;
+        }
+
         /* get proportional width and glyph bits */
-        width = font_get_width(pf,ch);
+        width = font_get_width(pf, *ucs);
 
         if (ofs > width)
         {
@@ -955,20 +993,50 @@
             continue;
         }
 
-        bits = font_get_bits(pf, ch);
+        bits = font_get_bits(pf, *ucs);
 
+        /* clear background */
+        drawmode ^= (style & STYLE_INVERT) ? 0 : DRMODE_INVERSEVID;
+        lcd_fillrect(x, y, width, pf->height);
+
+        /* compose styles */
+        drawmode = (style & STYLE_INVERT) ?
+                    (DRMODE_BG|DRMODE_INVERSEVID) : DRMODE_FG;
+        if (style & STYLE_SHADOW) {
+            if (style & STYLE_INVERT) {
+                lastcolor = lcd_get_background();
+                lcd_set_background(LCD_LIGHTGRAY);
+            } else {
+                lastcolor = lcd_get_foreground();
+                lcd_set_foreground(LCD_LIGHTGRAY);
+            }
+
+            lcd_mono_bitmap_part(bits, ofs, 0, width, x+1, y+1, width - ofs,
+                                 pf->height);
+            if (style & STYLE_BOLD)
+                lcd_mono_bitmap_part(bits, ofs, 0, width, x+2, y+1, width - ofs,
+                                     pf->height);
+
+            if (style & STYLE_INVERT)
+                lcd_set_background(lastcolor);
+            else
+                lcd_set_foreground(lastcolor);
+        }
         lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
                              pf->height);
-        
+        if (style & STYLE_BOLD) {
+            lcd_mono_bitmap_part(bits, ofs, 0, width, x+1, y, width - ofs,
+                                 pf->height);
+        }
+        if (style & STYLE_UNDERLINE)
+            lcd_drawline(x, y+pf->height-1, x+width, y+pf->height-1);
+
+        drawmode = lastmode;
         x += width - ofs;
         ofs = 0;
     }
-}
 
-/* put a string at a given pixel position */
-void lcd_putsxy(int x, int y, const unsigned char *str)
-{
-    lcd_putsxyofs(x, y, 0, str);
+    last_style = style;
 }
 
 /*** line oriented text output ***/
@@ -1000,16 +1068,18 @@
     /* make sure scrolling is turned off on the line we are updating */
     scrolling_lines &= ~(1 << y);
 
+    /* set style in advance if string is empty */
+    last_style = style;
+
     if(!str || !str[0])
         return;
 
     lcd_getstringsize(str, &w, &h);
     xpos = xmargin + x*w / utf8length((char *)str);
     ypos = ymargin + y*h;
-    drawmode = (style & STYLE_INVERT) ?
-               (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-    lcd_putsxyofs(xpos, ypos, offset, str);
-    drawmode ^= DRMODE_INVERSEVID;
+    lcd_putsxy_style_offset(xpos, ypos, offset, str, style);
+    if (~style & STYLE_INVERT)
+        drawmode ^= DRMODE_INVERSEVID;
     xrect = xpos + MAX(w - offset, 0);
     lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
     drawmode = lastmode;
@@ -1137,7 +1207,6 @@
     struct scrollinfo* s;
     int index;
     int xpos, ypos;
-    int lastmode;
 
     /* initialize scroll struct array */
     scrolling_lines = 0;
@@ -1183,11 +1252,8 @@
                     s->offset %= s->width;
             }
 
-            lastmode = drawmode;
-            drawmode = s->invert ?
-                       (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-            lcd_putsxyofs(xpos, ypos, s->offset, s->line);
-            drawmode = lastmode;
+            lcd_putsxy_style_offset(xpos, ypos, s->offset, s->line,
+                        (s->invert) ? STYLE_INVERT : STYLE_DEFAULT);
             lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
         }
 
Index: firmware/drivers/lcd-2bit-horz.c
===================================================================
--- firmware/drivers/lcd-2bit-horz.c	(revision 13161)
+++ firmware/drivers/lcd-2bit-horz.c	(working copy)
@@ -33,6 +33,7 @@
 #include "font.h"
 #include "rbunicode.h"
 #include "bidi.h"
+#include "ansi.h"
 
 #define SCROLLABLE_LINES (((LCD_HEIGHT+4)/5 < 32) ? (LCD_HEIGHT+4)/5 : 32)
 
@@ -50,6 +51,7 @@
 static unsigned fg_pattern IDATA_ATTR = 0xFF; /* initially black */
 static unsigned bg_pattern IDATA_ATTR = 0x00; /* initially white */
 static int drawmode = DRMODE_SOLID;
+static int last_style = STYLE_NORMAL;
 static int xmargin = 0;
 static int ymargin = 0;
 static int curfont = FONT_SYSFIXED;
@@ -148,6 +150,11 @@
     return font_getstringsize(str, w, h, curfont);
 }
 
+int lcd_get_last_style(void)
+{
+    return last_style;
+}
+
 /*** low-level drawing functions ***/
 
 static void setpixel(int x, int y)
@@ -796,22 +803,56 @@
     lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
 }
 
-/* put a string at a given pixel position, skipping first ofs pixel columns */
-static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
+/*** pixel oriented text output ***/
+
+/* put a string at a given pixel position */
+void lcd_putsxy(int x, int y, const unsigned char *str)
 {
-    unsigned short ch;
+    lcd_putsxy_style_offset(x, y, 0, str, STYLE_DEFAULT);
+}
+
+void lcd_putsxy_style(int x, int y, const unsigned char *str, int style)
+{
+    lcd_putsxy_style_offset(x, y, 0, str, style);
+}
+
+void lcd_putsxy_offset(int x, int y, int ofs, const unsigned char *str)
+{
+    lcd_putsxy_style_offset(x, y, ofs, str, STYLE_DEFAULT);
+}
+
+/* put a string at a given pixel position, style, and pixel position,
+ * skipping first offset pixel columns */
+void lcd_putsxy_style_offset(int x, int y, int ofs, const unsigned char *str,
+                          int style)
+{
     unsigned short *ucs;
     struct font* pf = font_get(curfont);
+    int *sp;
+    int lastmode = drawmode, lastcolor;
+    int l;
 
+    if(!str || !str[0])
+        return;
+
+    sp = (style & STYLE_INTERPRET) ? &style : NULL;
     ucs = bidi_l2v(str, 1);
 
-    while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
+    for (; *ucs != 0; ucs++)
     {
         int width;
         const unsigned char *bits;
 
+        l = ansi_escape_ucs(ucs, sp);
+        if (x > LCD_WIDTH)
+            break;
+        if (l) {
+            ucs += l - 1;
+            continue;
+        }
+
         /* get proportional width and glyph bits */
-        width = font_get_width(pf,ch);
+        width = font_get_width(pf, *ucs);
 
         if (ofs > width)
         {
@@ -819,22 +860,51 @@
             continue;
         }
 
-        bits = font_get_bits(pf, ch);
+        bits = font_get_bits(pf, *ucs);
 
+        /* clear background */
+        drawmode ^= (style & STYLE_INVERT) ? 0 : DRMODE_INVERSEVID;
+        lcd_fillrect(x, y, width, pf->height);
+
+        /* compose styles */
+        drawmode = (style & STYLE_INVERT) ?
+                    (DRMODE_BG|DRMODE_INVERSEVID) : DRMODE_FG;
+        if (style & STYLE_SHADOW) {
+            if (style & STYLE_INVERT) {
+                lastcolor = lcd_get_background();
+                lcd_set_background(LCD_LIGHTGRAY);
+            } else {
+                lastcolor = lcd_get_foreground();
+                lcd_set_foreground(LCD_LIGHTGRAY);
+            }
+
+            lcd_mono_bitmap_part(bits, ofs, 0, width, x+1, y+1, width - ofs,
+                                 pf->height);
+            if (style & STYLE_BOLD)
+                lcd_mono_bitmap_part(bits, ofs, 0, width, x+2, y+1, width - ofs,
+                                     pf->height);
+
+            if (style & STYLE_INVERT)
+                lcd_set_background(lastcolor);
+            else
+                lcd_set_foreground(lastcolor);
+        }
         lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
                              pf->height);
-        
+        if (style & STYLE_BOLD) {
+            lcd_mono_bitmap_part(bits, ofs, 0, width, x+1, y, width - ofs,
+                                 pf->height);
+        }
+        if (style & STYLE_UNDERLINE)
+            lcd_drawline(x, y+pf->height-1, x+width, y+pf->height-1);
+
+        drawmode = lastmode;
         x += width - ofs;
         ofs = 0;
     }
+    last_style = style;
 }
 
-/* put a string at a given pixel position */
-void lcd_putsxy(int x, int y, const unsigned char *str)
-{
-    lcd_putsxyofs(x, y, 0, str);
-}
-
 /*** line oriented text output ***/
 
 /* put a string at a given char position */
@@ -870,10 +940,9 @@
     lcd_getstringsize(str, &w, &h);
     xpos = xmargin + x*w / utf8length((char *)str);
     ypos = ymargin + y*h;
-    drawmode = (style & STYLE_INVERT) ?
-               (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-    lcd_putsxyofs(xpos, ypos, offset, str);
-    drawmode ^= DRMODE_INVERSEVID;
+    lcd_putsxy_style_offset(xpos, ypos, offset, str, style);
+    if (~style & STYLE_INVERT)
+        drawmode ^= DRMODE_INVERSEVID;
     xrect = xpos + MAX(w - offset, 0);
     lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
     drawmode = lastmode;
@@ -1001,7 +1070,6 @@
     struct scrollinfo* s;
     int index;
     int xpos, ypos;
-    int lastmode;
 
     /* initialize scroll struct array */
     scrolling_lines = 0;
@@ -1047,11 +1115,8 @@
                     s->offset %= s->width;
             }
 
-            lastmode = drawmode;
-            drawmode = s->invert ?
-                       (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-            lcd_putsxyofs(xpos, ypos, s->offset, s->line);
-            drawmode = lastmode;
+            lcd_putsxy_style_offset(xpos, ypos, s->offset, s->line, (s->invert) ?
+                                                          STYLE_INVERT : STYLE_DEFAULT);
             lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
         }
 
Index: firmware/drivers/lcd-remote-2bit-vi.c
===================================================================
--- firmware/drivers/lcd-remote-2bit-vi.c	(revision 13161)
+++ firmware/drivers/lcd-remote-2bit-vi.c	(working copy)
@@ -34,6 +34,7 @@
 #include "rbunicode.h"
 #include "bidi.h"
 #include "lcd-remote-target.h"
+#include "ansi.h"
 
 #define SCROLLABLE_LINES (((LCD_REMOTE_HEIGHT+4)/5 < 32) ? (LCD_REMOTE_HEIGHT+4)/5 : 32)
 
@@ -50,6 +51,7 @@
 static unsigned fg_pattern IDATA_ATTR = 0xFFFF; /* initially black */
 static unsigned bg_pattern IDATA_ATTR = 0x0000; /* initially white */
 static int drawmode = DRMODE_SOLID;
+static int last_style = STYLE_DEFAULT;
 static int xmargin = 0;
 static int ymargin = 0;
 static int curfont = FONT_SYSFIXED;
@@ -154,6 +156,11 @@
     return font_getstringsize(str, w, h, curfont);
 }
 
+int lcd_remote_get_last_style(void)
+{
+    return last_style;
+}
+
 /*** low-level drawing functions ***/
 
 static void setpixel(int x, int y)
@@ -934,22 +941,56 @@
     lcd_remote_bitmap_part(src, 0, 0, width, x, y, width, height);
 }
 
-/* put a string at a given pixel position, skipping first ofs pixel columns */
-static void lcd_remote_putsxyofs(int x, int y, int ofs, const unsigned char *str)
+/*** pixel oriented text output ***/
+
+/* put a string at a given pixel position */
+void lcd_remote_putsxy(int x, int y, const unsigned char *str)
 {
-    unsigned short ch;
+    lcd_remote_putsxy_style_offset(x, y, 0, str, STYLE_DEFAULT);
+}
+
+void lcd_remote_putsxy_style(int x, int y, const unsigned char *str, int style)
+{
+    lcd_remote_putsxy_style_offset(x, y, 0, str, style);
+}
+
+void lcd_remote_putsxy_offset(int x, int y, int ofs, const unsigned char *str)
+{
+    lcd_remote_putsxy_style_offset(x, y, ofs, str, STYLE_DEFAULT);
+}
+
+/* put a string at a given pixel position, style, and pixel position,
+ * skipping first offset pixel columns */
+void lcd_remote_putsxy_style_offset(int x, int y, int ofs, const unsigned char *str,
+                          int style)
+{
     unsigned short *ucs;
     struct font* pf = font_get(curfont);
+    int *sp;
+    int lastmode = drawmode;
+    int l;
 
+    if(!str || !str[0])
+        return;
+
+    sp = (style & STYLE_INTERPRET) ? &style : NULL;
     ucs = bidi_l2v(str, 1);
 
-    while ((ch = *ucs++) != 0 && x < LCD_REMOTE_WIDTH)
+    for (; *ucs != 0; ucs++)
     {
         int width;
         const unsigned char *bits;
 
+        l = ansi_escape_ucs(ucs, sp);
+        if (x > LCD_WIDTH)
+            break;
+        if (l) {
+            ucs += l - 1;
+            continue;
+        }
+
         /* get proportional width and glyph bits */
-        width = font_get_width(pf, ch);
+        width = font_get_width(pf, *ucs);
 
         if (ofs > width)
         {
@@ -957,22 +998,31 @@
             continue;
         }
 
-        bits = font_get_bits(pf, ch);
+        bits = font_get_bits(pf, *ucs);
 
+        /* clear background */
+        drawmode ^= (style & STYLE_INVERT) ? 0 : DRMODE_INVERSEVID;
+        lcd_remote_fillrect(x, y, width, pf->height);
+
+        /* compose styles */
+        drawmode = (style & STYLE_INVERT) ?
+                    (DRMODE_BG|DRMODE_INVERSEVID) : DRMODE_FG;
         lcd_remote_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
-                                    pf->height);
+                             pf->height);
+        if (style & STYLE_BOLD) {
+            lcd_remote_mono_bitmap_part(bits, ofs, 0, width, x+1, y, width - ofs,
+                                 pf->height);
+        }
+        if (style & STYLE_UNDERLINE)
+            lcd_remote_drawline(x, y+pf->height-1, x+width, y+pf->height-1);
 
+        drawmode = lastmode;
         x += width - ofs;
         ofs = 0;
     }
+    last_style = style;
 }
 
-/* put a string at a given pixel position */
-void lcd_remote_putsxy(int x, int y, const unsigned char *str)
-{
-    lcd_remote_putsxyofs(x, y, 0, str);
-}
-
 /*** line oriented text output ***/
 
 /* put a string at a given char position */
@@ -1008,10 +1058,9 @@
     lcd_remote_getstringsize(str, &w, &h);
     xpos = xmargin + x*w / utf8length((char *)str);
     ypos = ymargin + y*h;
-    drawmode = (style & STYLE_INVERT) ?
-               (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-    lcd_remote_putsxyofs(xpos, ypos, offset, str);
-    drawmode ^= DRMODE_INVERSEVID;
+    lcd_remote_putsxy_style_offset(xpos, ypos, offset, str, style);
+    if (~style & STYLE_INVERT)
+        drawmode ^= DRMODE_INVERSEVID;
     xrect = xpos + MAX(w - offset, 0);
     lcd_remote_fillrect(xrect, ypos, LCD_REMOTE_WIDTH - xrect, h);
     drawmode = lastmode;
@@ -1139,7 +1188,6 @@
     struct scrollinfo* s;
     int index;
     int xpos, ypos;
-    int lastmode;
     long delay = 0;
     long next_tick = current_tick;
 #ifndef SIMULATOR
@@ -1216,11 +1264,8 @@
                     s->offset %= s->width;
             }
 
-            lastmode = drawmode;
-            drawmode = s->invert ?
-                       (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-            lcd_remote_putsxyofs(xpos, ypos, s->offset, s->line);
-            drawmode = lastmode;
+            lcd_remote_putsxy_style_offset(xpos, ypos, s->offset, s->line,
+                                           (s->invert) ? STYLE_INVERT : STYLE_DEFAULT);
             lcd_remote_update_rect(xpos, ypos, LCD_REMOTE_WIDTH - xpos, pf->height);
         }
 
Index: firmware/drivers/lcd-16bit.c
===================================================================
--- firmware/drivers/lcd-16bit.c	(revision 13161)
+++ firmware/drivers/lcd-16bit.c	(working copy)
@@ -33,6 +33,7 @@
 #include "font.h"
 #include "rbunicode.h"
 #include "bidi.h"
+#include "ansi.h"
 #if defined(TOSHIBA_GIGABEAT_F)
 #define NON_GB_STATIC
 #else
@@ -57,6 +58,7 @@
 NON_GB_STATIC unsigned fg_pattern IDATA_ATTR = LCD_DEFAULT_FG;
 NON_GB_STATIC unsigned bg_pattern IDATA_ATTR = LCD_DEFAULT_BG;
 static int drawmode = DRMODE_SOLID;
+static int last_style = STYLE_NORMAL;
 static int xmargin = 0;
 static int ymargin = 0;
 static int curfont = FONT_SYSFIXED;
@@ -162,6 +164,11 @@
     return font_getstringsize(str, w, h, curfont);
 }
 
+int lcd_get_last_style(void)
+{
+    return last_style;
+}
+
 /*** low-level drawing functions ***/
 
 #define LCDADDR(x, y) (&lcd_framebuffer[(y)][(x)])
@@ -756,22 +763,56 @@
     lcd_bitmap_transparent_part(src, 0, 0, width, x, y, width, height);
 }
 
-/* put a string at a given pixel position, skipping first ofs pixel columns */
-static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
+/*** pixel oriented text output ***/
+
+/* put a string at a given pixel position */
+void lcd_putsxy(int x, int y, const unsigned char *str)
 {
-    unsigned short ch;
+    lcd_putsxy_style_offset(x, y, 0, str, STYLE_DEFAULT);
+}
+
+void lcd_putsxy_style(int x, int y, const unsigned char *str, int style)
+{
+    lcd_putsxy_style_offset(x, y, 0, str, style);
+}
+
+void lcd_putsxy_offset(int x, int y, int ofs, const unsigned char *str)
+{
+    lcd_putsxy_style_offset(x, y, ofs, str, STYLE_DEFAULT);
+}
+
+/* put a string at a given pixel position, style, and pixel position,
+ * skipping first offset pixel columns */
+void lcd_putsxy_style_offset(int x, int y, int ofs, const unsigned char *str,
+                          int style)
+{
     unsigned short *ucs;
     struct font* pf = font_get(curfont);
+    int *sp;
+    int lastmode = drawmode, lastcolor;
+    int l;
 
+    if(!str || !str[0])
+        return;
+
+    sp = (style & STYLE_INTERPRET) ? &style : NULL;
     ucs = bidi_l2v(str, 1);
 
-    while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
+    for (; *ucs != 0; ucs++)
     {
         int width;
         const unsigned char *bits;
 
+        l = ansi_escape_ucs(ucs, sp);
+        if (x > LCD_WIDTH)
+            break;
+        if (l) {
+            ucs += l - 1;
+            continue;
+        }
+
         /* get proportional width and glyph bits */
-        width = font_get_width(pf,ch);
+        width = font_get_width(pf, *ucs);
 
         if (ofs > width)
         {
@@ -779,21 +820,51 @@
             continue;
         }
 
-        bits = font_get_bits(pf, ch);
+        bits = font_get_bits(pf, *ucs);
 
-        lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
+        /* clear background */
+        drawmode ^= (style & STYLE_INVERT) ? 0 : DRMODE_INVERSEVID;
+        lcd_fillrect(x, y, width, pf->height);
 
+        /* compose styles */
+        drawmode = (style & STYLE_INVERT) ?
+                    (DRMODE_BG|DRMODE_INVERSEVID) : DRMODE_FG;
+        if (style & STYLE_SHADOW) {
+            if (style & STYLE_INVERT) {
+                lastcolor = lcd_get_background();
+                lcd_set_background(LCD_LIGHTGRAY);
+            } else {
+                lastcolor = lcd_get_foreground();
+                lcd_set_foreground(LCD_LIGHTGRAY);
+            }
+
+            lcd_mono_bitmap_part(bits, ofs, 0, width, x+1, y+1, width - ofs,
+                                 pf->height);
+            if (style & STYLE_BOLD)
+                lcd_mono_bitmap_part(bits, ofs, 0, width, x+2, y+1, width - ofs,
+                                     pf->height);
+
+            if (style & STYLE_INVERT)
+                lcd_set_background(lastcolor);
+            else
+                lcd_set_foreground(lastcolor);
+        }
+        lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
+                             pf->height);
+        if (style & STYLE_BOLD) {
+            lcd_mono_bitmap_part(bits, ofs, 0, width, x+1, y, width - ofs,
+                                 pf->height);
+        }
+        if (style & STYLE_UNDERLINE)
+            lcd_drawline(x, y+pf->height-1, x+width, y+pf->height-1);
+
+        drawmode = lastmode;
         x += width - ofs;
         ofs = 0;
     }
+    last_style = style;
 }
 
-/* put a string at a given pixel position */
-void lcd_putsxy(int x, int y, const unsigned char *str)
-{
-    lcd_putsxyofs(x, y, 0, str);
-}
-
 /*** line oriented text output ***/
 
 /* put a string at a given char position */
@@ -829,10 +900,9 @@
     lcd_getstringsize(str, &w, &h);
     xpos = xmargin + x*w / utf8length(str);
     ypos = ymargin + y*h;
-    drawmode = (style & STYLE_INVERT) ?
-               (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-    lcd_putsxyofs(xpos, ypos, offset, str);
-    drawmode ^= DRMODE_INVERSEVID;
+    lcd_putsxy_style_offset(xpos, ypos, offset, str, style);
+    if (~style & STYLE_INVERT)
+        drawmode ^= DRMODE_INVERSEVID;
     xrect = xpos + MAX(w - offset, 0);
     lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
     drawmode = lastmode;
@@ -960,7 +1030,6 @@
     struct scrollinfo* s;
     int index;
     int xpos, ypos;
-    int lastmode;
 
     /* initialize scroll struct array */
     scrolling_lines = 0;
@@ -1010,11 +1079,8 @@
                     s->offset %= s->width;
             }
 
-            lastmode = drawmode;
-            drawmode = s->invert ?
-                       (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-            lcd_putsxyofs(xpos, ypos, s->offset, s->line);
-            drawmode = lastmode;
+            lcd_putsxy_style_offset(xpos, ypos, s->offset, s->line,
+                                    s->invert ? STYLE_INVERT : STYLE_DEFAULT);
             lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
         }
 
Index: firmware/drivers/lcd-remote-1bit-v.c
===================================================================
--- firmware/drivers/lcd-remote-1bit-v.c	(revision 13161)
+++ firmware/drivers/lcd-remote-1bit-v.c	(working copy)
@@ -31,6 +31,7 @@
 #include "font.h"
 #include "rbunicode.h"
 #include "bidi.h"
+#include "ansi.h"
 
 #define SCROLLABLE_LINES (((LCD_REMOTE_HEIGHT+4)/5 < 32) ? (LCD_REMOTE_HEIGHT+4)/5 : 32)
 
@@ -40,6 +41,7 @@
                                       IBSS_ATTR;
 
 static int drawmode = DRMODE_SOLID;
+static int last_style = STYLE_DEFAULT;
 static int xmargin = 0;
 static int ymargin = 0;
 static int curfont = FONT_SYSFIXED;
@@ -105,6 +107,11 @@
     return font_getstringsize(str, w, h, curfont);
 }
 
+int lcd_remote_get_last_style(void)
+{
+    return last_style;
+}
+
 /*** low-level drawing functions ***/
 
 static void setpixel(int x, int y)
@@ -598,22 +605,56 @@
     lcd_remote_bitmap_part(src, 0, 0, width, x, y, width, height);
 }
 
-/* put a string at a given pixel position, skipping first ofs pixel columns */
-static void lcd_remote_putsxyofs(int x, int y, int ofs, const unsigned char *str)
+/*** pixel oriented text output ***/
+
+/* put a string at a given pixel position */
+void lcd_remote_putsxy(int x, int y, const unsigned char *str)
 {
-    unsigned short ch;
+    lcd_remote_putsxy_style_offset(x, y, 0, str, STYLE_DEFAULT);
+}
+
+void lcd_remote_putsxy_style(int x, int y, const unsigned char *str, int style)
+{
+    lcd_remote_putsxy_style_offset(x, y, 0, str, style);
+}
+
+void lcd_remote_putsxy_offset(int x, int y, int ofs, const unsigned char *str)
+{
+    lcd_remote_putsxy_style_offset(x, y, ofs, str, STYLE_DEFAULT);
+}
+
+/* put a string at a given pixel position, style, and pixel position,
+ * skipping first offset pixel columns */
+void lcd_remote_putsxy_style_offset(int x, int y, int ofs, const unsigned char *str,
+                          int style)
+{
     unsigned short *ucs;
     struct font* pf = font_get(curfont);
+    int *sp;
+    int lastmode = drawmode;
+    int l;
 
+    if(!str || !str[0])
+        return;
+
+    sp = (style & STYLE_INTERPRET) ? &style : NULL;
     ucs = bidi_l2v(str, 1);
 
-    while ((ch = *ucs++) != 0 && x < LCD_REMOTE_WIDTH)
+    for (; *ucs != 0; ucs++)
     {
         int width;
         const unsigned char *bits;
 
+        l = ansi_escape_ucs(ucs, sp);
+        if (x > LCD_WIDTH)
+            break;
+        if (l) {
+            ucs += l - 1;
+            continue;
+        }
+
         /* get proportional width and glyph bits */
-        width = font_get_width(pf, ch);
+        width = font_get_width(pf, *ucs);
 
         if (ofs > width)
         {
@@ -621,22 +662,31 @@
             continue;
         }
 
-        bits = font_get_bits(pf, ch);
+        bits = font_get_bits(pf, *ucs);
 
-        lcd_remote_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
-                               pf->height);
+        /* clear background */
+        drawmode ^= (style & STYLE_INVERT) ? 0 : DRMODE_INVERSEVID;
+        lcd_remote_fillrect(x, y, width, pf->height);
 
+        /* compose styles */
+        drawmode = (style & STYLE_INVERT) ?
+                    (DRMODE_BG|DRMODE_INVERSEVID) : DRMODE_FG;
+        lcd_remote_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
+                             pf->height);
+        if (style & STYLE_BOLD) {
+            lcd_remote_mono_bitmap_part(bits, ofs, 0, width, x+1, y, width - ofs,
+                                 pf->height);
+        }
+        if (style & STYLE_UNDERLINE)
+            lcd_remote_drawline(x, y+pf->height-1, x+width, y+pf->height-1);
+
+        drawmode = lastmode;
         x += width - ofs;
         ofs = 0;
     }
+    last_style = style;
 }
 
-/* put a string at a given pixel position */
-void lcd_remote_putsxy(int x, int y, const unsigned char *str)
-{
-    lcd_remote_putsxyofs(x, y, 0, str);
-}
-
 /*** line oriented text output ***/
 
 /* put a string at a given char position */
@@ -672,10 +722,9 @@
     lcd_remote_getstringsize(str, &w, &h);
     xpos = xmargin + x*w / utf8length((char *)str);
     ypos = ymargin + y*h;
-    drawmode = (style & STYLE_INVERT) ?
-               (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-    lcd_remote_putsxyofs(xpos, ypos, offset, str);
-    drawmode ^= DRMODE_INVERSEVID;
+    lcd_remote_putsxy_style_offset(xpos, ypos, offset, str, style);
+    if (~style & STYLE_INVERT)
+        drawmode ^= DRMODE_INVERSEVID;
     xrect = xpos + MAX(w - offset, 0);
     lcd_remote_fillrect(xrect, ypos, LCD_REMOTE_WIDTH - xrect, h);
     drawmode = lastmode;
@@ -805,7 +854,6 @@
     long delay = 0;
     int index;
     int xpos, ypos;
-    int lastmode;
 #ifndef SIMULATOR
     struct event ev;
 #endif
@@ -879,11 +927,8 @@
                     s->offset %= s->width;
             }
 
-            lastmode = drawmode;
-            drawmode = s->invert ?
-                       (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
-            lcd_remote_putsxyofs(xpos, ypos, s->offset, s->line);
-            drawmode = lastmode;
+            lcd_remote_putsxy_style_offset(xpos, ypos, s->offset, s->line,
+                                           (s->invert) ? STYLE_INVERT : STYLE_DEFAULT);
             lcd_remote_update_rect(xpos, ypos, LCD_REMOTE_WIDTH - xpos, pf->height);
         }
 
