/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 Björn Stenberg
 *
 * 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 <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "backlight.h"
#include "button.h"
#include "lcd.h"
#include "lang.h"
#include "icons.h"
#include "font.h"
#include "audio.h"
#include "mp3_playback.h"
#include "usb.h"
#include "settings.h"
#include "status.h"
#include "playlist.h"
#include "sprintf.h"
#include "kernel.h"
#include "power.h"
#include "system.h"
#include "powermgmt.h"
#include "adc.h"
#include "action.h"
#include "talk.h"
#include "misc.h"
#include "id3.h"
#include "screens.h"
#include "debug.h"
#include "led.h"
#include "sound.h"
#include "wps-display.h"
#ifdef HAVE_MMC
#include "ata_mmc.h"
#endif

#ifdef HAVE_LCD_BITMAP
#define BMPHEIGHT_usb_logo 32
#define BMPWIDTH_usb_logo 100
static const unsigned char usb_logo[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08,
    0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x81, 0x81, 0x81, 0x81,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0xf1, 0x4f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0,
    0x00, 0x00, 0xe0, 0x1c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x81, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0,
    0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x1c,
    0x0c, 0x0e, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f,
    0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xc0, 0xc0, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x00, 0xe0, 0x1f, 0x00, 0xf8, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x02, 0x02, 0x02, 0x82, 0x7e, 0x00, 0xc0, 0x3e, 0x01,
    0x70, 0x4f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x07, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f,
    0x0f, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x0f,
    0x1f, 0x3f, 0x7b, 0xf3, 0xe3, 0xc3, 0x83, 0x83, 0x83, 0x83, 0xe3, 0xe3, 0xe3,
    0xe3, 0xe3, 0xe3, 0x03, 0x03, 0x03, 0x3f, 0x1f, 0x1f, 0x0f, 0x0f, 0x07, 0x02,
    0xc0, 0x3e, 0x01, 0xe0, 0x9f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0xf0, 0x0f, 0x80, 0x78, 0x07, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x10, 0x20, 0x40, 0x40, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x87, 0x87, 0x87,
    0x87, 0x87, 0x87, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf0,
    0x0f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x07, 0x00, 0x00, 0x00, 0x00,
};
#endif

void usb_display_info(void)
{
    lcd_clear_display();

#ifdef HAVE_LCD_BITMAP
    /* Center bitmap on screen */
    lcd_mono_bitmap(usb_logo, LCD_WIDTH/2-BMPWIDTH_usb_logo/2,
                    LCD_HEIGHT/2-BMPHEIGHT_usb_logo/2, BMPWIDTH_usb_logo,
                    BMPHEIGHT_usb_logo);
    status_draw(true);
    lcd_update();
#else
    lcd_double_height(false);
    lcd_puts(0, 0, "[USB Mode]");
    status_set_param(false);
    status_set_audio(false);
    status_set_usb(true);
    status_draw(false);
#endif
}

void usb_screen(void)
{
#ifdef USB_NONE
    /* nothing here! */
#else
#ifndef SIMULATOR
    backlight_on();
    usb_acknowledge(SYS_USB_CONNECTED_ACK);
    usb_display_info();
    while(usb_wait_for_disconnect_w_tmo(&button_queue, HZ)) {
        if(usb_inserted()) {

#ifdef HAVE_MMC /* USB-MMC bridge can report activity */

            led(mmc_usb_active(HZ));

#endif

            status_draw(false);
        }
    }
#ifdef HAVE_LCD_CHARCELLS
    status_set_usb(false);
#endif

    backlight_on();
#endif
#endif /* USB_NONE */
}

#ifdef HAVE_MMC
int mmc_remove_request(void)
{
    struct event ev;

    lcd_clear_display();
    splash(1, true, str(LANG_REMOVE_MMC));
    if (global_settings.talk_menu)
        talk_id(LANG_REMOVE_MMC, false);

    while (1)
    {
        queue_wait_w_tmo(&button_queue, &ev, HZ/2);
        switch (ev.id)
        {
            case SYS_MMC_EXTRACTED:
                return SYS_MMC_EXTRACTED;

            case SYS_USB_DISCONNECTED:
                return SYS_USB_DISCONNECTED;
        }
    }
}
#endif


/* some simulator dummies */
#ifdef SIMULATOR
#define BATTERY_SCALE_FACTOR 7000
unsigned short adc_read(int channel)
{
   (void)channel;
   return 100;
}
#endif

#if defined(HAVE_CHARGING) && !defined(HAVE_POWEROFF_WHILE_CHARGING)

#ifdef HAVE_LCD_BITMAP
void charging_display_info(bool animate)
{
    unsigned char charging_logo[36];
    const int pox_x = (LCD_WIDTH - sizeof(charging_logo)) / 2;
    const int pox_y = 32;
    static unsigned phase = 3;
    unsigned i;
    char buf[32];
    (void)buf;

#ifdef NEED_ATA_POWER_BATT_MEASURE
    if (ide_powered()) /* FM and V2 can only measure when ATA power is on */
#endif
    {
        int battery_voltage;
        int batt_int, batt_frac;

        battery_voltage = (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) / 10000;
        batt_int = battery_voltage / 100;
        batt_frac = battery_voltage % 100;

        snprintf(buf, 32, "  Batt: %d.%02dV %d%%  ", batt_int, batt_frac,
                 battery_level());
        lcd_puts(0, 7, buf);
    }

#ifdef HAVE_CHARGE_CTRL

    snprintf(buf, 32, "Charge mode:");
    lcd_puts(0, 2, buf);

    if (charge_state == 1)
        snprintf(buf, 32, str(LANG_BATTERY_CHARGE));
    else if (charge_state == 2)
        snprintf(buf, 32, str(LANG_BATTERY_TOPOFF_CHARGE));
    else if (charge_state == 3)
        snprintf(buf, 32, str(LANG_BATTERY_TRICKLE_CHARGE));
    else
        snprintf(buf, 32, "not charging");

    lcd_puts(0, 3, buf);
    if (!charger_enabled)
        animate = false;
#endif /* HAVE_CHARGE_CTRL */


    /* middle part */
    memset(charging_logo+3, 0x00, 32);
    charging_logo[0] = 0x3C;
    charging_logo[1] = 0x24;
    charging_logo[2] = charging_logo[35] = 0xFF;

    if (!animate)
    {   /* draw the outline */
        /* middle part */
        lcd_mono_bitmap(charging_logo, pox_x, pox_y + 8, sizeof(charging_logo), 8);
        lcd_set_drawmode(DRMODE_FG);
        /* upper line */
        charging_logo[0] = charging_logo[1] = 0x00;
        memset(charging_logo+2, 0x80, 34);
        lcd_mono_bitmap(charging_logo, pox_x, pox_y, sizeof(charging_logo), 8);
        /* lower line */
        memset(charging_logo+2, 0x01, 34);
        lcd_mono_bitmap(charging_logo, pox_x, pox_y + 16, sizeof(charging_logo), 8);
        lcd_set_drawmode(DRMODE_SOLID);
    }
    else
    {   /* animate the middle part */
        for (i = 3; i<MIN(sizeof(charging_logo)-1, phase); i++)
        {
            if ((i-phase) % 8 == 0)
            {   /* draw a "bubble" here */
                unsigned bitpos;
                bitpos = (phase + i/8) % 15; /* "bounce" effect */
                if (bitpos > 7)
                    bitpos = 14 - bitpos;
                charging_logo[i] = 0x01 << bitpos;
            }
        }
        lcd_mono_bitmap(charging_logo, pox_x, pox_y + 8, sizeof(charging_logo), 8);
        phase++;
    }
    lcd_update();
}
#else /* not HAVE_LCD_BITMAP */

static unsigned char logo_chars[5];
static const unsigned char logo_pattern[] = {
    0x07, 0x04, 0x1c, 0x14, 0x1c, 0x04, 0x07, /* char 1 */
    0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, /* char 2 */
    0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, /* char 3 */
    0x1f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f, /* char 4 */
};

static void logo_lock_patterns(bool on)
{
    int i;

    if (on)
    {
        for (i = 0; i < 4; i++)
            logo_chars[i] = lcd_get_locked_pattern();
        logo_chars[4] = '\0';
    }
    else
    {
        for (i = 0; i < 4; i++)
            lcd_unlock_pattern(logo_chars[i]);
    }
}

void charging_display_info(bool animate)
{
    int battery_voltage;
    unsigned i, ypos;
    static unsigned phase = 3;
    char buf[28];

    battery_voltage = (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR)
                      / 10000;
    snprintf(buf, sizeof(buf), "%s %d.%02dV", logo_chars,
             battery_voltage / 100, battery_voltage % 100);
    lcd_puts(0, 1, buf);

    memcpy(buf, logo_pattern, 28); /* copy logo patterns */

    if (!animate) /* build the screen */
    {
        lcd_double_height(false);
        lcd_puts(0, 0, "[Charging]");
    }
    else          /* animate the logo */
    {
        for (i = 3; i < MIN(19, phase); i++)
        {
            if ((i - phase) % 5 == 0)
            {    /* draw a "bubble" here */
                ypos = (phase + i/5) % 9; /* "bounce" effect */
                if (ypos > 4)
                    ypos = 8 - ypos;
                buf[5 - ypos + 7 * (i/5)] |= 0x10 >> (i%5);
            }
        }
        phase++;
    }

    for (i = 0; i < 4; i++)
        lcd_define_pattern(logo_chars[i], buf + 7 * i);
}
#endif /* (not) HAVE_LCD_BITMAP */

/* blocks while charging, returns on event:
   1 if charger cable was removed
   2 if Off/Stop key was pressed
   3 if On key was pressed
   4 if USB was connected */
int charging_screen(void)
{
    unsigned int button;
    int rc = 0;

    ide_power_enable(false); /* power down the disk, else would be spinning */

    lcd_clear_display();
    backlight_set_timeout(global_settings.backlight_timeout);
#ifdef HAVE_REMOTE_LCD
    remote_backlight_set_timeout(global_settings.remote_backlight_timeout);
#endif
    backlight_set_on_when_charging(global_settings.backlight_on_when_charging);
    status_draw(true);

#ifdef HAVE_LCD_CHARCELLS
    logo_lock_patterns(true);
#endif
    charging_display_info(false);

    do
    {
        status_draw(false);
        charging_display_info(true);
        button = button_get_w_tmo(HZ/3);
        if (button == BUTTON_ON)
            rc = 2;
        else if (usb_detect())
            rc = 3;
        else if (!charger_inserted())
            rc = 1;
    } while (!rc);

#ifdef HAVE_LCD_CHARCELLS
    logo_lock_patterns(false);
#endif
    return rc;
}
#endif /* HAVE_CHARGING && !HAVE_POWEROFF_WHILE_CHARGING */


#if CONFIG_KEYPAD == RECORDER_PAD
/* returns:
   0 if no key was pressed
   1 if a key was pressed (or if ON was held down long enough to repeat)
   2 if USB was connected */
int pitch_screen(void)
{
    int button;
    int pitch = sound_get_pitch();
    bool exit = false;
    bool used = false;

    while (!exit) {

        if ( used ) {
            char* ptr;
            char buf[32];
            int w, h;

            lcd_clear_display();
            lcd_setfont(FONT_SYSFIXED);

            ptr = str(LANG_PITCH_UP);
            lcd_getstringsize(ptr,&w,&h);
            lcd_putsxy((LCD_WIDTH-w)/2, 0, ptr);
            lcd_mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
                            LCD_WIDTH/2 - 3, h*2, 7, 8);

            snprintf(buf, sizeof buf, "%d.%d%%", pitch / 10, pitch % 10 );
            lcd_getstringsize(buf,&w,&h);
            lcd_putsxy((LCD_WIDTH-w)/2, h, buf);

            ptr = str(LANG_PITCH_DOWN);
            lcd_getstringsize(ptr,&w,&h);
            lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h, ptr);
            lcd_mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
                            LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8);

            ptr = str(LANG_PAUSE);
            lcd_getstringsize(ptr,&w,&h);
            lcd_putsxy((LCD_WIDTH-(w/2))/2, LCD_HEIGHT/2 - h/2, ptr);
            lcd_mono_bitmap(bitmap_icons_7x8[Icon_Pause],
                            (LCD_WIDTH-(w/2))/2-10, LCD_HEIGHT/2 - h/2, 7, 8);

            lcd_update();
        }

        /* use lastbutton, so the main loop can decide whether to
           exit to browser or not */
        button = button_get(true);
        switch (button) {
            case BUTTON_UP:
            case BUTTON_ON | BUTTON_UP:
            case BUTTON_ON | BUTTON_UP | BUTTON_REPEAT:
                used = true;
                if ( pitch < 2000 )
                    pitch++;
                sound_set_pitch(pitch);
                break;

            case BUTTON_DOWN:
            case BUTTON_ON | BUTTON_DOWN:
            case BUTTON_ON | BUTTON_DOWN | BUTTON_REPEAT:
                used = true;
                if ( pitch > 500 )
                    pitch--;
                sound_set_pitch(pitch);
                break;

            case BUTTON_ON | BUTTON_PLAY:
                audio_pause();
                used = true;
                break;

            case BUTTON_PLAY | BUTTON_REL:
                audio_resume();
                used = true;
                break;

            case BUTTON_ON | BUTTON_PLAY | BUTTON_REL:
                audio_resume();
                exit = true;
                break;

            case BUTTON_ON | BUTTON_RIGHT:
            case BUTTON_LEFT | BUTTON_REL:
                if ( pitch < 2000 ) {
                    pitch += 20;
                    sound_set_pitch(pitch);
                }
                break;

            case BUTTON_ON | BUTTON_LEFT:
            case BUTTON_RIGHT | BUTTON_REL:
                if ( pitch > 500 ) {
                    pitch -= 20;
                    sound_set_pitch(pitch);
                }
                break;

#ifdef SIMULATOR
            case BUTTON_ON:
#else
            case BUTTON_ON | BUTTON_REL:
            case BUTTON_ON | BUTTON_UP | BUTTON_REL:
            case BUTTON_ON | BUTTON_DOWN | BUTTON_REL:
#endif
                exit = true;
                break;

            case BUTTON_ON | BUTTON_REPEAT:
                used = true;
                break;

            default:
                if(default_event_handler(button) == SYS_USB_CONNECTED)
                    return 2;
                break;
        }
    }

    lcd_setfont(FONT_UI);

    if ( used )
        return 1;
    else
        return 0;
}
#endif

#if (CONFIG_KEYPAD == RECORDER_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD) ||\
    (CONFIG_KEYPAD == IRIVER_H300_PAD)
bool quick_screen(int context, int button)
{
    bool exit = false;
    bool used = false;
    int w, h, key;
    char buf[32];
    int oldrepeat = global_settings.repeat_mode;

    /* just to stop compiler warning */
    context = context;
    lcd_setfont(FONT_SYSFIXED);

    lcd_getstringsize("A",&w,&h);

    while (!exit) {
        char* ptr=NULL;

        lcd_clear_display();

        switch(button)
        {
#if CONFIG_KEYPAD == RECORDER_PAD
            case SCREENS_QUICK:
#endif
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
            case SCREENS_QUICK | BUTTON_REPEAT:
#endif
                /* Shuffle mode */
                lcd_putsxy(0, LCD_HEIGHT/2 - h*2, str(LANG_SHUFFLE));
                lcd_putsxy(0, LCD_HEIGHT/2 - h, str(LANG_F2_MODE));
                lcd_putsxy(0, LCD_HEIGHT/2,
                           global_settings.playlist_shuffle ?
                           str(LANG_ON) : str(LANG_OFF));

                /* Directory Filter */
                switch ( global_settings.dirfilter ) {
                    case SHOW_ALL:
                        ptr = str(LANG_FILTER_ALL);
                        break;

                    case SHOW_SUPPORTED:
                        ptr = str(LANG_FILTER_SUPPORTED);
                        break;

                    case SHOW_MUSIC:
                        ptr = str(LANG_FILTER_MUSIC);
                        break;

                    case SHOW_PLAYLIST:
                        ptr = str(LANG_FILTER_PLAYLIST);
                        break;

                    case SHOW_ID3DB:
                        ptr = str(LANG_FILTER_ID3DB);
                        break;
                }

                snprintf(buf, sizeof buf, "%s:", str(LANG_FILTER));
                lcd_getstringsize(buf,&w,&h);
                lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, buf);
                lcd_getstringsize(ptr,&w,&h);
                lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h, ptr);

                /* Repeat Mode */
                switch ( global_settings.repeat_mode ) {
                    case REPEAT_OFF:
                        ptr = str(LANG_OFF);
                        break;

                    case REPEAT_ALL:
                        ptr = str(LANG_REPEAT_ALL);
                        break;

                    case REPEAT_ONE:
                        ptr = str(LANG_REPEAT_ONE);
                        break;

                    case REPEAT_SHUFFLE:
                        ptr = str(LANG_SHUFFLE);
                        break;
                }

                lcd_getstringsize(str(LANG_REPEAT),&w,&h);
                lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h*2, str(LANG_REPEAT));
                lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h, str(LANG_F2_MODE));
                lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2, ptr);
                break;
#ifdef BUTTON_F3
            case BUTTON_F3:
                /* Scrollbar */
                lcd_putsxy(0, LCD_HEIGHT/2 - h*2, str(LANG_F3_SCROLL));
                lcd_putsxy(0, LCD_HEIGHT/2 - h, str(LANG_F3_BAR));
                lcd_putsxy(0, LCD_HEIGHT/2,
                           global_settings.scrollbar ? str(LANG_ON) : str(LANG_OFF));

                /* Status bar */
                ptr = str(LANG_F3_STATUS);
                lcd_getstringsize(ptr,&w,&h);
                lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h*2, ptr);
                lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h, str(LANG_F3_BAR));
                lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2,
                           global_settings.statusbar ? str(LANG_ON) : str(LANG_OFF));

                /* Flip */
                ptr = str(LANG_FLIP_DISPLAY);
                lcd_getstringsize(ptr,&w,&h);
                lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, str(LANG_FLIP_DISPLAY));
                ptr = global_settings.flip_display ?
                           str(LANG_SET_BOOL_YES) : str(LANG_SET_BOOL_NO);
                lcd_getstringsize(ptr,&w,&h);
                lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h, ptr);
                break;
#endif
        }

        lcd_mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
                        LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8);
        lcd_mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
                        LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8);
        lcd_mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
                        LCD_WIDTH/2 + 8, LCD_HEIGHT/2 - 4, 7, 8);

        lcd_update();
        key = button_get(true);

        /*
         *  This is a temporary kludge so that the F2 & F3 menus operate in exactly
         *  the same manner up until the full F2/F3 configurable menus are complete
         */

        if( key == BUTTON_LEFT || key == BUTTON_RIGHT || key == BUTTON_DOWN || key == ( BUTTON_LEFT | BUTTON_REPEAT ) || key == ( BUTTON_RIGHT  | BUTTON_REPEAT ) || key == ( BUTTON_DOWN  | BUTTON_REPEAT ) )
            key = button | key;

        switch (key) {
            case SCREENS_QUICK | BUTTON_LEFT:
            case SCREENS_QUICK | BUTTON_LEFT | BUTTON_REPEAT:
                global_settings.playlist_shuffle =
                    !global_settings.playlist_shuffle;

                if(audio_status() & AUDIO_STATUS_PLAY)
                {
                    if (global_settings.playlist_shuffle)
                        playlist_randomise(NULL, current_tick, true);
                    else
                        playlist_sort(NULL, true);
                }
                used = true;
                break;

            case SCREENS_QUICK | BUTTON_DOWN:
            case SCREENS_QUICK | BUTTON_DOWN | BUTTON_REPEAT:
                global_settings.dirfilter++;
                if ( global_settings.dirfilter >= NUM_FILTER_MODES )
                    global_settings.dirfilter = 0;
                used = true;
                break;

            case SCREENS_QUICK | BUTTON_RIGHT:
            case SCREENS_QUICK | BUTTON_RIGHT | BUTTON_REPEAT:
                global_settings.repeat_mode++;
                if ( global_settings.repeat_mode >= NUM_REPEAT_MODES )
                    global_settings.repeat_mode = 0;
                used = true;
                break;

#ifdef BUTTON_F3
            case BUTTON_F3 | BUTTON_LEFT:
            case BUTTON_F3 | BUTTON_LEFT | BUTTON_REPEAT:
                global_settings.scrollbar = !global_settings.scrollbar;
                used = true;
                break;

            case BUTTON_F3 | BUTTON_RIGHT:
            case BUTTON_F3 | BUTTON_RIGHT | BUTTON_REPEAT:
                global_settings.statusbar = !global_settings.statusbar;
                used = true;
                break;

            case BUTTON_F3 | BUTTON_DOWN:
            case BUTTON_F3 | BUTTON_DOWN | BUTTON_REPEAT:
            case BUTTON_F3 | BUTTON_UP:
            case BUTTON_F3 | BUTTON_UP | BUTTON_REPEAT:
                global_settings.flip_display = !global_settings.flip_display;
                button_set_flip(global_settings.flip_display);
                lcd_set_flip(global_settings.flip_display);
                used = true;
                break;

            case BUTTON_F3 | BUTTON_REL:
#endif
            case SCREENS_QUICK | BUTTON_REL:

                if( used )
                    exit = true;

                used = true;

                break;

            case BUTTON_OFF | BUTTON_REL:
                lcd_setfont(FONT_UI);
                return false;

            default:
                if(default_event_handler(key) == SYS_USB_CONNECTED)
                    return true;
                break;
        }
    }

    settings_save();

    switch( button )
    {
#if CONFIG_KEYPAD == RECORDER_PAD
          case SCREENS_QUICK:
#endif
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
          case SCREENS_QUICK | BUTTON_REPEAT:
#endif

            if ( oldrepeat != global_settings.repeat_mode &&
                (audio_status() & AUDIO_STATUS_PLAY) )
                audio_flush_and_reload_tracks();

            break;
#ifdef BUTTON_F3
        case BUTTON_F3:

            if (global_settings.statusbar)
                lcd_setmargins(0, STATUSBAR_HEIGHT);
            else
                lcd_setmargins(0, 0);

            break;
#endif
    }

    lcd_setfont(FONT_UI);

    return false;
}
#endif

#ifdef HAVE_LCD_BITMAP
#define SPACE 3 /* pixels between words */
#define MAXLETTERS 128 /* 16*8 */
#define MAXLINES 10
#else
#define SPACE 1 /* one letter space */
#undef LCD_WIDTH
#define LCD_WIDTH 11
#undef LCD_HEIGHT
#define LCD_HEIGHT 2
#define MAXLETTERS 22 /* 11 * 2 */
#define MAXLINES 2
#endif

void splash(int ticks,       /* how long the splash is displayed */
            bool center,     /* FALSE means left-justified, TRUE means
                                horizontal and vertical center */
            const char *fmt, /* what to say *printf style */
            ...)
{
    char *next;
    char *store=NULL;
    int x=0;
    int y=0;
    int w, h;
    unsigned char splash_buf[MAXLETTERS];
    va_list ap;
    unsigned char widths[MAXLINES];
    int line=0;
    bool first=true;
#ifdef HAVE_LCD_BITMAP
    int maxw=0;
#endif

#ifdef HAVE_LCD_CHARCELLS
    lcd_double_height (false);
#endif
    va_start( ap, fmt );
    vsnprintf( splash_buf, sizeof(splash_buf), fmt, ap );

    if(center) {

        /* first a pass to measure sizes */
        next = strtok_r(splash_buf, " ", &store);
        while (next) {
#ifdef HAVE_LCD_BITMAP
            lcd_getstringsize(next, &w, &h);
#else
            w = strlen(next);
            h = 1; /* store height in characters */
#endif
            if(!first) {
                if(x+w> LCD_WIDTH) {
                    /* Too wide, wrap */
                    y+=h;
                    line++;
                    if((y > (LCD_HEIGHT-h)) || (line > MAXLINES))
                        /* STOP */
                        break;
                    x=0;
                    first=true;
                }
            }
            else
                first = false;

            /* think of it as if the text was written here at position x,y
               being w pixels/chars wide and h high */

            x += w+SPACE;
            widths[line]=x-SPACE; /* don't count the trailing space */
#ifdef HAVE_LCD_BITMAP
            /* store the widest line */
            if(widths[line]>maxw)
                maxw = widths[line];
#endif
            next = strtok_r(NULL, " ", &store);
        }
#ifdef HAVE_LCD_BITMAP
        /* Start displaying the message at position y. The reason for the
           added h here is that it isn't added until the end of lines in the
           loop above and we always break the loop in the middle of a line. */
        y = (LCD_HEIGHT - (y+h) )/2;
#else
        y = 0; /* vertical center on 2 lines would be silly */
#endif
        first=true;

        /* Now recreate the string again since the strtok_r() above has ruined
           the one we already have! Here's room for improvements! */
        vsnprintf( splash_buf, sizeof(splash_buf), fmt, ap );
    }
    va_end( ap );

    if(center)
    {
        x = (LCD_WIDTH-widths[0])/2;
        if(x < 0)
            x = 0;
    }

#ifdef HAVE_LCD_BITMAP
    /* If we center the display, then just clear the box we need and put
       a nice little frame and put the text in there! */
    if(center && (y > 2)) {
        int xx = (LCD_WIDTH-maxw)/2 - 2;
        /* The new graphics routines handle clipping, so no need to check */
#if LCD_DEPTH > 1
#ifdef HAVE_LCD_COLOR
        lcd_set_background((struct rgb){LCD_MAX_RED-1, LCD_MAX_GREEN-1, LCD_MAX_BLUE-1});
#else
        lcd_set_background(LCD_MAX_LEVEL-1);
#endif
#endif
        lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
        lcd_fillrect(xx, y-2, maxw+4, LCD_HEIGHT-y*2+4);
        lcd_set_drawmode(DRMODE_SOLID);
        lcd_drawrect(xx, y-2, maxw+4, LCD_HEIGHT-y*2+4);
    }
    else
#endif
        lcd_clear_display();
    line=0;
    next = strtok_r(splash_buf, " ", &store);
    while (next) {
#ifdef HAVE_LCD_BITMAP
        lcd_getstringsize(next, &w, &h);
#else
        w = strlen(next);
        h = 1;
#endif
        if(!first) {
            if(x+w> LCD_WIDTH) {
                /* too wide */
                y+=h;
                line++; /* goto next line */
                first=true;
                if(y > (LCD_HEIGHT-h))
                    /* STOP */
                    break;
                if(center) {
                    x = (LCD_WIDTH-widths[line])/2;
                    if(x < 0)
                       x = 0;
                }
                else
                    x=0;
            }
        }
        else
            first=false;
#ifdef HAVE_LCD_BITMAP
        lcd_putsxy(x, y, next);
#else
        lcd_puts(x, y, next);
#endif
        x += w+SPACE; /*  pixels space! */
        next = strtok_r(NULL, " ", &store);
    }
#if defined(HAVE_LCD_BITMAP) && (LCD_DEPTH > 1)
    lcd_set_background(LCD_WHITE);
#endif
    lcd_update();

    if(ticks)
        /* unbreakable! */
        sleep(ticks);
}

#if defined(HAVE_CHARGING) || defined(SIMULATOR)
void charging_splash(void)
{
    splash(2*HZ, true, str(LANG_BATTERY_CHARGE));
    button_clear_queue();
}
#endif


#if defined(HAVE_LCD_BITMAP) && defined (HAVE_RTC)

/* little helper function for voice output */
static void say_time(int cursorpos, const struct tm *tm)
{
    static const int unit[] = { UNIT_HOUR, UNIT_MIN, UNIT_SEC, 0, 0, 0 };
    int value = 0;

    if (!global_settings.talk_menu)
        return;

    switch(cursorpos)
    {
    case 0:
        value = tm->tm_hour;
        break;
    case 1:
        value = tm->tm_min;
        break;
    case 2:
        value = tm->tm_sec;
        break;
    case 3:
        value = tm->tm_year + 1900;
        break;
    case 5:
        value = tm->tm_mday;
        break;
    }

    if (cursorpos == 4) /* month */
        talk_id(LANG_MONTH_JANUARY + tm->tm_mon, false);
    else
        talk_value(value, unit[cursorpos], false);
}


#define INDEX_X 0
#define INDEX_Y 1
#define INDEX_WIDTH 2
bool set_time_screen(const char* string, struct tm *tm)
{
    bool done = false;
    int button;
    int min = 0, steps = 0;
    int cursorpos = 0;
    int lastcursorpos = !cursorpos;
    unsigned char buffer[19];
    int realyear;
    int julianday;
    int i;
    unsigned char reffub[5];
    unsigned int width, height;
    unsigned int separator_width, weekday_width;
    unsigned int line_height, prev_line_height;
    int lastmode = lcd_get_drawmode();

    static const int dayname[] = {
        LANG_WEEKDAY_SUNDAY,
        LANG_WEEKDAY_MONDAY,
        LANG_WEEKDAY_TUESDAY,
        LANG_WEEKDAY_WEDNESDAY,
        LANG_WEEKDAY_THURSDAY,
        LANG_WEEKDAY_FRIDAY,
        LANG_WEEKDAY_SATURDAY
    };
    static const int monthname[] = {
        LANG_MONTH_JANUARY,
        LANG_MONTH_FEBRUARY,
        LANG_MONTH_MARCH,
        LANG_MONTH_APRIL,
        LANG_MONTH_MAY,
        LANG_MONTH_JUNE,
        LANG_MONTH_JULY,
        LANG_MONTH_AUGUST,
        LANG_MONTH_SEPTEMBER,
        LANG_MONTH_OCTOBER,
        LANG_MONTH_NOVEMBER,
        LANG_MONTH_DECEMBER
    };
    char cursor[][3] = {{ 0,  8, 12}, {18,  8, 12}, {36,  8, 12},
                        {24, 16, 24}, {54, 16, 18}, {78, 16, 12}};
    char daysinmonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    int monthname_len = 0, dayname_len = 0;

    int *valptr = NULL;

#ifdef HAVE_LCD_BITMAP
    if(global_settings.statusbar)
        lcd_setmargins(0, STATUSBAR_HEIGHT);
    else
        lcd_setmargins(0, 0);
#endif
    lcd_clear_display();
    lcd_puts_scroll(0, 0, string);

    while ( !done ) {
        /* calculate the number of days in febuary */
        realyear = tm->tm_year + 1900;
        if((realyear % 4 == 0 && !(realyear % 100 == 0)) || realyear % 400 == 0)
            daysinmonth[1] = 29;
        else
            daysinmonth[1] = 28;

        /* fix day if month or year changed */
        if (tm->tm_mday > daysinmonth[tm->tm_mon])
            tm->tm_mday = daysinmonth[tm->tm_mon];

        /* calculate day of week */
        julianday = 0;
        for(i = 0; i < tm->tm_mon; i++) {
           julianday += daysinmonth[i];
        }
        julianday += tm->tm_mday;
        tm->tm_wday = (realyear + julianday + (realyear - 1) / 4 -
                       (realyear - 1) / 100 + (realyear - 1) / 400 + 7 - 1) % 7;

        snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d ",
                 tm->tm_hour, tm->tm_min, tm->tm_sec);
        lcd_puts(0, 1, buffer);

        /* recalculate the positions and offsets */
        lcd_getstringsize(string, &width, &prev_line_height);
        lcd_getstringsize(buffer, &width, &line_height);
        lcd_getstringsize(":", &separator_width, &height);

        /* hour */
        strncpy(reffub, buffer, 2);
        reffub[2] = '\0';
        lcd_getstringsize(reffub, &width, &height);
        cursor[0][INDEX_X] = 0;
        cursor[0][INDEX_Y] = prev_line_height;
        cursor[0][INDEX_WIDTH] = width;

        /* minute */
        strncpy(reffub, buffer + 3, 2);
        reffub[2] = '\0';
        lcd_getstringsize(reffub, &width, &height);
        cursor[1][INDEX_X] = cursor[0][INDEX_WIDTH] + separator_width;
        cursor[1][INDEX_Y] = prev_line_height;
        cursor[1][INDEX_WIDTH] = width;

        /* second */
        strncpy(reffub, buffer + 6, 2);
        reffub[2] = '\0';
        lcd_getstringsize(reffub, &width, &height);
        cursor[2][INDEX_X] = cursor[0][INDEX_WIDTH] + separator_width +
                             cursor[1][INDEX_WIDTH] + separator_width;
        cursor[2][INDEX_Y] = prev_line_height;
        cursor[2][INDEX_WIDTH] = width;

        lcd_getstringsize(buffer, &width, &prev_line_height);

        snprintf(buffer, sizeof(buffer), "%s %04d %s %02d ",
                 str(dayname[tm->tm_wday]), tm->tm_year+1900,
                 str(monthname[tm->tm_mon]), tm->tm_mday);
        lcd_puts(0, 2, buffer);

        /* recalculate the positions and offsets */
        lcd_getstringsize(buffer, &width, &line_height);

        /* store these 2 to prevent _repeated_ strlen calls */
        monthname_len = strlen(str(monthname[tm->tm_mon]));
        dayname_len = strlen(str(dayname[tm->tm_wday]));

        /* weekday */
        strncpy(reffub, buffer, dayname_len);
        reffub[dayname_len] = '\0';
        lcd_getstringsize(reffub, &weekday_width, &height);
        lcd_getstringsize(" ", &separator_width, &height);

        /* year */
        strncpy(reffub, buffer + dayname_len + 1, 4);
        reffub[4] = '\0';
        lcd_getstringsize(reffub, &width, &height);
        cursor[3][INDEX_X] = weekday_width + separator_width;
        cursor[3][INDEX_Y] = cursor[0][INDEX_Y] + prev_line_height;
        cursor[3][INDEX_WIDTH] = width;

        /* month */
        strncpy(reffub, buffer + dayname_len + 6, monthname_len);
        reffub[monthname_len] = '\0';
        lcd_getstringsize(reffub, &width, &height);
        cursor[4][INDEX_X] = weekday_width + separator_width +
                             cursor[3][INDEX_WIDTH] + separator_width;
        cursor[4][INDEX_Y] = cursor[0][INDEX_Y] + prev_line_height;
        cursor[4][INDEX_WIDTH] = width;

        /* day */
        strncpy(reffub, buffer + dayname_len + monthname_len + 7, 2);
        reffub[2] = '\0';
        lcd_getstringsize(reffub, &width, &height);
        cursor[5][INDEX_X] = weekday_width + separator_width +
                             cursor[3][INDEX_WIDTH] + separator_width +
                             cursor[4][INDEX_WIDTH] + separator_width;
        cursor[5][INDEX_Y] = cursor[0][INDEX_Y] + prev_line_height;
        cursor[5][INDEX_WIDTH] = width;

        lcd_set_drawmode(DRMODE_COMPLEMENT);
        lcd_fillrect(cursor[cursorpos][INDEX_X],
                     cursor[cursorpos][INDEX_Y] + lcd_getymargin(),
                     cursor[cursorpos][INDEX_WIDTH],
                     line_height);
        lcd_set_drawmode(DRMODE_SOLID);

        lcd_puts(0, 4, str(LANG_TIME_SET));
        lcd_puts(0, 5, str(LANG_TIME_REVERT));
#ifdef HAVE_LCD_BITMAP
        status_draw(true);
#endif
        lcd_update();

        /* calculate the minimum and maximum for the number under cursor */
        if(cursorpos!=lastcursorpos) {
            lastcursorpos=cursorpos;
            switch(cursorpos) {
                case 0: /* hour */
                    min = 0;
                    steps = 24;
                    valptr = &tm->tm_hour;
                    break;
                case 1: /* minute */
                    min = 0;
                    steps = 60;
                    valptr = &tm->tm_min;
                    break;
                case 2: /* second */
                    min = 0;
                    steps = 60;
                    valptr = &tm->tm_sec;
                    break;
                case 3: /* year */
                    min = 1;
                    steps = 200;
                    valptr = &tm->tm_year;
                    break;
                case 4: /* month */
                    min = 0;
                    steps = 12;
                    valptr = &tm->tm_mon;
                    break;
                case 5: /* day */
                    min = 1;
                    steps = daysinmonth[tm->tm_mon];
                    valptr = &tm->tm_mday;
                    break;
            }
            say_time(cursorpos, tm);
        }

        button = button_get_w_tmo(HZ/2);
        switch ( button ) {
            case BUTTON_LEFT:
                cursorpos = (cursorpos + 6 - 1) % 6;
                break;
            case BUTTON_RIGHT:
                cursorpos = (cursorpos + 6 + 1) % 6;
                break;
            case BUTTON_UP:
            case BUTTON_UP | BUTTON_REPEAT:
                *valptr = (*valptr + steps - min + 1) %
                    steps + min;
                if(*valptr == 0)
                    *valptr = min;
                say_time(cursorpos, tm);
                break;
            case BUTTON_DOWN:
            case BUTTON_DOWN | BUTTON_REPEAT:
                *valptr = (*valptr + steps - min - 1) %
                    steps + min;
                if(*valptr == 0)
                    *valptr = min;
                say_time(cursorpos, tm);
                break;

#ifdef BUTTON_ON
            case BUTTON_ON:
#elif defined BUTTON_MENU
            case BUTTON_MENU:
#endif
                done = true;
                break;

            case BUTTON_OFF:
                done = true;
                tm->tm_year = -1;
                break;

            default:
                if (default_event_handler(button) == SYS_USB_CONNECTED)
                    return true;
                break;
        }
    }

    lcd_set_drawmode(lastmode);
    return false;
}
#endif /* defined(HAVE_LCD_BITMAP) && defined (HAVE_RTC) */

#if (CONFIG_KEYPAD == RECORDER_PAD) && !defined(HAVE_SW_POWEROFF)
bool shutdown_screen(void)
{
    int button;
    bool done = false;

    lcd_stop_scroll();

    splash(0, true, str(LANG_CONFIRM_SHUTDOWN));

    while(!done)
    {
        button = button_get_w_tmo(HZ*2);
        switch(button)
        {
            case BUTTON_OFF:
                sys_poweroff(false);
                break;

            default:
                if(default_event_handler(button) == SYS_USB_CONNECTED)
                    return true;

                /* Return if any other button was pushed, or if there
                   was a timeout. We ignore RELEASE events, since we may
                   have been called by a button down event, and the user might
                   not have released the button yet.
                   We also ignore REPEAT events, since we don't want to
                   remove the splash when the user holds OFF to shut down. */
                if(!(button & (BUTTON_REL | BUTTON_REPEAT)))
                   done = true;
                break;
        }
    }
    return false;
}
#endif

bool browse_id3(void)
{
    struct mp3entry* id3 = audio_current_track();
    int button;
    int menu_pos = 0;
#if CONFIG_HWCODEC == MASNONE
    int menu_max = 12;
#else
    int menu_max = 10;
#endif
    bool exit = false;
    char scroll_text[MAX_PATH];

    if (!(audio_status() & AUDIO_STATUS_PLAY))
        return false;

    while (!exit)
    {
        lcd_clear_display();

        switch (menu_pos)
        {
            case 0:
                lcd_puts(0, 0, str(LANG_ID3_TITLE));
                lcd_puts_scroll(0, 1, id3->title ? id3->title :
                                (char*)str(LANG_ID3_NO_TITLE));
                break;

            case 1:
                lcd_puts(0, 0, str(LANG_ID3_ARTIST));
                lcd_puts_scroll(0, 1,
                                id3->artist ? id3->artist :
                                (char*)str(LANG_ID3_NO_ARTIST));
                break;

            case 2:
                lcd_puts(0, 0, str(LANG_ID3_ALBUM));
                lcd_puts_scroll(0, 1, id3->album ? id3->album :
                                (char*)str(LANG_ID3_NO_ALBUM));
                break;

            case 3:
                lcd_puts(0, 0, str(LANG_ID3_TRACKNUM));

                if (id3->track_string) {
                    lcd_puts_scroll(0, 1, id3->track_string);
                }
                else if (id3->tracknum) {
                    snprintf(scroll_text,sizeof(scroll_text), "%d",
                             id3->tracknum);
                    lcd_puts_scroll(0, 1, scroll_text);
                }
                else
                    lcd_puts_scroll(0, 1, str(LANG_ID3_NO_TRACKNUM));
                break;

            case 4:
                lcd_puts(0, 0, str(LANG_ID3_GENRE));

                if (id3->genre_string) {
                    lcd_puts_scroll(0, 1, id3->genre_string);
                }
                else {
                  lcd_puts_scroll(0, 1,
                                  id3_get_genre(id3) ?
                                  id3_get_genre(id3) :
                                  (char*)str(LANG_ID3_NO_INFO));
                }
                break;

            case 5:
                lcd_puts(0, 0, str(LANG_ID3_YEAR));
                if (id3->year_string) {
                    lcd_puts_scroll(0, 1, id3->year_string);
                }
                else if (id3->year) {
                    snprintf(scroll_text,sizeof(scroll_text), "%d",
                             id3->year);
                    lcd_puts_scroll(0, 1, scroll_text);
                }
                else
                    lcd_puts_scroll(0, 1, str(LANG_ID3_NO_INFO));
                break;

            case 6:
                lcd_puts(0, 0, str(LANG_ID3_LENGHT));
                wps_format_time(scroll_text, sizeof(scroll_text), id3->length);
                lcd_puts(0, 1, scroll_text);
                break;

            case 7:
                lcd_puts(0, 0, str(LANG_ID3_PLAYLIST));
                snprintf(scroll_text,sizeof(scroll_text), "%d/%d",
                         playlist_get_display_index(), playlist_amount());
                lcd_puts_scroll(0, 1, scroll_text);
                break;


            case 8:
                lcd_puts(0, 0, str(LANG_ID3_BITRATE));
                snprintf(scroll_text,sizeof(scroll_text), "%d kbps",
                         id3->bitrate);
                lcd_puts(0, 1, scroll_text);
                break;

            case 9:
                lcd_puts(0, 0, str(LANG_ID3_FRECUENCY));
                snprintf(scroll_text,sizeof(scroll_text), "%d Hz",
                         id3->frequency);
                lcd_puts(0, 1, scroll_text);
                break;

            case 10:
                lcd_puts(0, 0, str(LANG_ID3_PATH));
                lcd_puts_scroll(0, 1, id3->path);
                break;
#if CONFIG_HWCODEC == MASNONE
            case 11:
                lcd_puts(0, 0, str(LANG_ID3_TRACK_GAIN));
                lcd_puts(0, 1, id3->track_gain_string 
                    ? id3->track_gain_string
                    : (char*) str(LANG_ID3_NO_GAIN));
                break;

            case 12:
                lcd_puts(0, 0, str(LANG_ID3_ALBUM_GAIN));
                lcd_puts(0, 1, id3->album_gain_string
                    ? id3->album_gain_string
                    : (char*) str(LANG_ID3_NO_GAIN));
                break;
#endif
        }
        lcd_update();

        button = button_get(true);

        switch(button)
        {
            case SETTINGS_DEC:
                if (menu_pos > 0)
                    menu_pos--;
                else
                    menu_pos = menu_max;
                break;

            case SETTINGS_INC:
                if (menu_pos < menu_max)
                    menu_pos++;
                else
                    menu_pos = 0;
                break;

#ifdef SETTINGS_OK2
            case SETTINGS_OK2:
#endif
            case SETTINGS_CANCEL:
                lcd_stop_scroll();
                /* eat release event */
                button_get(true);
                exit = true;
                break;

            default:
                if(default_event_handler(button) ==  SYS_USB_CONNECTED)
                    return true;
                break;
        }
    }
    return false;
}

bool set_rating(void)
{
    struct mp3entry* id3 = audio_current_track();
    int button;
    bool exit = false;
    char rating_text[20];

    if (!(audio_status() & AUDIO_STATUS_PLAY)||id3==NULL)
        return false;
    while (!exit)
    {
        lcd_clear_display();
        lcd_puts(0, 0, str(LANG_RATING));
        snprintf(rating_text,sizeof(rating_text),"%d",id3->rating);
        lcd_puts(0,1,rating_text);
        lcd_update();
        button = button_get(true);

        switch(button)
        {
            case SETTINGS_DEC:
                if (id3->rating > 0)
                    id3->rating--;
                else
                    id3->rating = 10;
                break;

            case SETTINGS_INC:
                if (id3->rating < 10)
                    id3->rating++;
                else
                    id3->rating = 0;
                break;
            case SETTINGS_CANCEL:
#ifdef SETTINGS_OK2
            case SETTINGS_OK2:
#endif
                /* eat release event */
                button_get(true);
                exit = true;
                break;

            default:
                if(default_event_handler(button) ==  SYS_USB_CONNECTED)
                    return true;
                break;
        }
    }
    return false;
}
