beginning of the new menu system. This commit shouldnt break anything, 
but comming ones might.. report bugs in 
http://forums.rockbox.org/index.php?topic=8703.0 and more info at 
http://www.rockbox.org/twiki/bin/view/Main/SettingsRecode


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12227 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/SOURCES b/apps/SOURCES
index 2304ee7..d47e2bd 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -11,6 +11,14 @@
 main.c
 main_menu.c
 menu.c
+menus/display_menu.c
+menus/main_menu.c
+menus/playlist_menu.c
+#ifdef HAVE_RECORDING
+menus/recording_menu.c
+#endif
+menus/settings_menu.c
+menus/sound_menu.c
 misc.c
 onplay.c
 playlist.c
diff --git a/apps/action.h b/apps/action.h
index 5a8168e..9519bd0 100644
--- a/apps/action.h
+++ b/apps/action.h
@@ -154,6 +154,10 @@
     ACTION_REC_F3,
     
     /* main menu */
+    ACTION_REQUEST_MENUITEM,
+    ACTION_EXIT_MENUITEM,
+    ACTION_ENTER_MENUITEM,
+    ACTION_MENU_WPS,
     
     /* id3db */
     
diff --git a/apps/main_menu.c b/apps/main_menu.c
index 7e20656..078e766 100644
--- a/apps/main_menu.c
+++ b/apps/main_menu.c
@@ -281,16 +281,6 @@
     return false;
 }
 
-static bool plugin_browse(void)
-{
-    return rockbox_browse(PLUGIN_DIR, SHOW_PLUGINS);
-}
-
-static bool custom_theme_browse(void)
-{
-    return rockbox_browse(THEME_DIR, SHOW_CFG);
-}
-
 #ifdef HAVE_RECORDING
 
 static bool rec_menu_recording_screen(void)
@@ -340,7 +330,7 @@
 }
 #endif
 
-static bool info_menu(void)
+bool info_menu(void)
 {
     int m;
     bool result;
@@ -368,6 +358,7 @@
     return result;
 }
 
+#if 0
 #ifdef HAVE_LCD_CHARCELLS
 static bool do_shutdown(void)
 {
@@ -375,7 +366,6 @@
     return false;
 }
 #endif
-
 bool main_menu(void)
 {
     int m;
@@ -445,7 +435,7 @@
 
     return result;
 }
-
+#endif
 /* -----------------------------------------------------------------
  * vim: et sw=4 ts=8 sts=4 tw=78
  */
diff --git a/apps/menu.c b/apps/menu.c
index 261286b..9c5afbe 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -35,12 +35,14 @@
 #include "usb.h"
 #include "panic.h"
 #include "settings.h"
+#include "settings_list.h"
 #include "status.h"
 #include "screens.h"
 #include "talk.h"
 #include "lang.h"
 #include "misc.h"
 #include "action.h"
+#include "menus/exported_menus.h"
 
 #ifdef HAVE_LCD_BITMAP
 #include "icons.h"
@@ -356,3 +358,337 @@
 #endif
     }
 }
+
+/******************************************************************/
+/*              New menu stuff here!!
+ ******************************************************************/
+
+
+/* used to allow for dynamic menus */
+#define MAX_MENU_SUBITEMS 64
+static int current_subitems[MAX_MENU_SUBITEMS];
+static int current_subitems_count = 0;
+
+void get_menu_callback(const struct menu_item_ex *m,
+                        menu_callback_type menu_callback) 
+{
+    if (m->flags&MENU_HAS_DESC)
+        menu_callback= m->callback_and_desc->menu_callback;
+    else menu_callback = m->menu_callback;
+}
+
+static int get_menu_selection(int selected_item, const struct menu_item_ex *menu)
+{
+    int type = (menu->flags&MENU_TYPE_MASK);
+    if (type == MT_MENU && (selected_item<current_subitems_count))
+        return current_subitems[selected_item];
+    return selected_item;
+}
+
+static char * get_menu_item_name(int selected_item,void * data, char *buffer)
+{
+    const struct menu_item_ex *menu = (const struct menu_item_ex *)data;
+    int type = (menu->flags&MENU_TYPE_MASK);
+    selected_item = get_menu_selection(selected_item, menu);
+    
+    (void)buffer;
+    /* only MT_MENU or MT_RETURN_ID is allowed in here */
+    if (type == MT_RETURN_ID)
+    {
+        return (char*)menu->strings[selected_item];
+    }
+    
+    menu = menu->submenus[selected_item];
+    type = (menu->flags&MENU_TYPE_MASK);
+    if (type == MT_SETTING)
+    {
+        const struct settings_list *v
+                = find_setting(menu->variable);
+        if (v)
+            return str(v->lang_id);
+        else return "Not Done yet!";
+    }
+    return P2STR(menu->callback_and_desc->desc);
+}
+
+static void init_menu_lists(const struct menu_item_ex *menu,
+                     struct gui_synclist *lists, int selected, bool callback)
+{
+    int i, count = (menu->flags&MENU_COUNT_MASK)>>MENU_COUNT_SHIFT;
+    menu_callback_type menu_callback = NULL;
+    current_subitems_count = 0;
+    for (i=0; i<count; i++)
+    {
+        get_menu_callback(menu->submenus[i],menu_callback);
+        if (menu_callback)
+        {
+            if (menu_callback(ACTION_REQUEST_MENUITEM,menu->submenus[i])
+                    != ACTION_EXIT_MENUITEM)
+            {
+                current_subitems[current_subitems_count] = i;
+                current_subitems_count++;
+            }
+        }
+        else 
+        {
+            current_subitems[current_subitems_count] = i;
+            current_subitems_count++;
+        }
+    }
+    
+    gui_synclist_init(lists,get_menu_item_name,(void*)menu,false,1);
+    gui_synclist_set_title(lists, P2STR(menu->callback_and_desc->desc), NOICON);
+    gui_synclist_set_icon_callback(lists,NULL);
+    gui_synclist_set_nb_items(lists,current_subitems_count);
+    gui_synclist_limit_scroll(lists,true);
+    gui_synclist_select_item(lists, selected);
+    
+    get_menu_callback(menu,menu_callback);
+    if (callback && menu_callback)
+        menu_callback(ACTION_ENTER_MENUITEM,menu);
+}
+
+static void talk_menu_item(const struct menu_item_ex *menu,
+                    struct gui_synclist *lists)
+{
+    int id = -1;
+    if (global_settings.talk_menu)
+    {
+        int sel = get_menu_selection(gui_synclist_get_sel_pos(lists),menu);
+        if ((menu->flags&MENU_TYPE_MASK) == MT_MENU)
+        {
+           if ((menu->submenus[sel]->flags&MENU_TYPE_MASK) == MT_SETTING)
+               talk_setting(menu->submenus[sel]->variable);
+           else 
+           {
+               id = P2ID(menu->submenus[sel]->callback_and_desc->desc);
+               if (id != -1)
+                   talk_id(id,false);
+           }
+        }
+    }
+}
+#define MAX_OPTIONS 32
+int do_menu(const struct menu_item_ex *start_menu)
+{
+    int action;
+    int selected = 0;
+    struct gui_synclist lists;
+    const struct menu_item_ex *temp, *menu;
+    int ret = 0;
+    
+    const struct menu_item_ex *menu_stack[MAX_MENUS];
+    int menu_stack_selected_item[MAX_MENUS];
+    int stack_top = 0;
+    bool in_stringlist;
+    menu_callback_type menu_callback = NULL;
+    if (start_menu == NULL)
+        menu = &main_menu_;
+    else menu = start_menu;
+
+    init_menu_lists(menu,&lists,selected,true);
+    in_stringlist = ((menu->flags&MENU_TYPE_MASK) == MT_RETURN_ID);
+    
+    talk_menu_item(menu, &lists);
+    
+    gui_synclist_draw(&lists);
+    gui_syncstatusbar_draw(&statusbars, true);
+    action_signalscreenchange();
+    
+    while (ret == 0)
+    {
+        action = get_action(CONTEXT_MAINMENU,HZ); 
+        /* HZ so the status bar redraws corectly */
+        if (action == ACTION_NONE)
+        {
+            gui_syncstatusbar_draw(&statusbars, true);
+            continue;
+        }
+
+        get_menu_callback(menu,menu_callback);
+        if (menu_callback)
+        {
+            action = menu_callback(action,menu);
+        }
+
+        if (gui_synclist_do_button(&lists,action,LIST_WRAP_UNLESS_HELD))
+        {
+            talk_menu_item(menu, &lists);
+        }
+        else if (action == ACTION_MENU_WPS)
+        {
+            ret = MENU_RETURN_TO_WPS;
+        }
+        else if ((action == ACTION_STD_CANCEL) ||
+                 (action == ACTION_STD_MENU))
+        {
+            if (in_stringlist)
+                in_stringlist = false;
+            if (stack_top > 0)
+            {
+                get_menu_callback(menu,menu_callback);
+                if (menu_callback)
+                {
+                    if (menu_callback(action,menu) ==
+                            ACTION_EXIT_MENUITEM)
+                        break;
+                }
+                stack_top--;
+                menu = menu_stack[stack_top];
+                init_menu_lists(menu, &lists, 
+                                 menu_stack_selected_item[stack_top], false);
+                talk_menu_item(menu, &lists);
+            }
+            else 
+            {
+                break;
+            }
+        }
+        else if (action == ACTION_STD_OK)
+        {
+            int type;
+            selected = get_menu_selection(gui_synclist_get_sel_pos(&lists), menu);
+            temp = menu->submenus[selected];
+            if (in_stringlist)
+                type = (menu->flags&MENU_TYPE_MASK);
+            else type = (temp->flags&MENU_TYPE_MASK);
+            get_menu_callback(temp,menu_callback);
+            if (menu_callback)
+            {
+                action = menu_callback(ACTION_ENTER_MENUITEM,temp);
+                if (action == ACTION_EXIT_MENUITEM)
+                    break;
+            }
+            switch (type)
+            {
+                case MT_MENU:
+                    if (stack_top < MAX_MENUS)
+                    {
+                        menu_stack[stack_top] = menu;
+                        menu_stack_selected_item[stack_top]
+                                = gui_synclist_get_sel_pos(&lists);
+                        stack_top++;
+                        init_menu_lists(temp, &lists, 0, true);
+                        menu = temp;
+                        talk_menu_item(menu, &lists);
+                    }
+                    break;
+                case MT_FUNCTION_CALL:
+                    action_signalscreenchange();
+                    temp->function();
+                    break;
+                case MT_FUNCTION_WITH_PARAM:
+                    action_signalscreenchange();
+                    temp->func_with_param->function(
+                                    temp->func_with_param->param);
+                    break;
+                case MT_SETTING:
+                {
+                    const struct settings_list *setting = find_setting(
+                                                               temp->variable);
+                    if (setting)
+                    {
+                        if ((setting->flags&F_BOOL_SETTING) == F_BOOL_SETTING)
+                        {
+                            bool temp_var, *var;
+                            if (setting->flags&F_TEMPVAR)
+                            {
+                                temp_var = *(bool*)setting->setting;
+                                var = &temp_var;
+                            }
+                            else
+                            {
+                                var = (bool*)setting->setting;
+                            }
+                            set_bool_options(str(setting->lang_id),var,
+                                        STR(setting->bool_setting->lang_yes),
+                                        STR(setting->bool_setting->lang_no),
+                                        setting->bool_setting->option_callback);
+                            if (setting->flags&F_TEMPVAR)
+                                *(bool*)setting->setting = temp_var;
+                        }
+                        else if (setting->flags&F_T_SOUND)
+                        {
+                            set_sound(str(setting->lang_id), setting->setting,
+                                        setting->sound_setting->setting);
+                        }
+                        else /* other setting, must be an INT type */
+                        {
+                            int temp_var, *var;
+                            if (setting->flags&F_TEMPVAR)
+                            {
+                                temp_var = *(int*)setting->setting;
+                                var = &temp_var;
+                            }
+                            else
+                            {
+                                var = (int*)setting->setting;
+                            }
+                            DEBUGF("%x\n",setting->flags);
+                            if (setting->flags&F_INT_SETTING)
+                            {DEBUGF("boo");
+                                set_int(str(setting->lang_id),
+                                        str(setting->int_setting->unit),
+                                        setting->int_setting->unit,var,
+                                        setting->int_setting->option_callback,
+                                        setting->int_setting->step,
+                                        setting->int_setting->min,
+                                        setting->int_setting->max,
+                                        setting->int_setting->formatter);
+                            }
+                            else if (setting->flags&F_CHOICE_SETTING)
+                            {
+                                static struct opt_items options[MAX_OPTIONS];
+                                int i,j, count = setting->choice_setting->count;
+                                for (i=0, j=0; i<count && i<MAX_OPTIONS; i++)
+                                {
+                                    options[j].string =
+                                        P2STR(setting->choice_setting->desc[i]);
+                                    options[j].voice_id = 
+                                        P2ID(setting->choice_setting->desc[i]);
+                                    j++;
+                                }
+                                set_option(str(setting->lang_id), var, INT,
+                                            options,count,
+                                            setting->choice_setting->option_callback);
+                            }
+                            if (setting->flags&F_TEMPVAR)
+                                *(int*)setting->setting = temp_var;
+                        }
+                    }
+                    break;
+                }
+                case MT_RETURN_ID:
+                    if (in_stringlist)
+                    {
+                        action_signalscreenchange();
+                        return selected;
+                    }
+                    else if (stack_top < MAX_MENUS)
+                    {
+                        menu_stack[stack_top] = menu;
+                        menu_stack_selected_item[stack_top] = selected;
+                        stack_top++;
+                        menu = temp;
+                        init_menu_lists(menu,&lists,0,false);
+                        in_stringlist = true;
+                    }
+                    break;
+            }
+            get_menu_callback(temp,menu_callback);
+            if (type != MT_MENU && menu_callback)
+                menu_callback(ACTION_EXIT_MENUITEM,temp);
+        }
+        else if(default_event_handler(action) == SYS_USB_CONNECTED)
+            ret = MENU_ATTACHED_USB;
+        gui_syncstatusbar_draw(&statusbars, true);
+        gui_synclist_draw(&lists);
+    }
+    action_signalscreenchange();
+    return ret;
+}
+
+int main_menu(void)
+{
+    return do_menu(NULL);
+}
diff --git a/apps/menu.h b/apps/menu.h
index f9a3d1f..b0a1b4c 100644
--- a/apps/menu.h
+++ b/apps/menu.h
@@ -22,101 +22,14 @@
 
 #include <stdbool.h>
 
-/* button definitions */
-#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
-    (CONFIG_KEYPAD == IRIVER_H300_PAD)
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT2      BUTTON_OFF
-#define MENU_EXIT_MENU  BUTTON_MODE
-#define MENU_ENTER      BUTTON_RIGHT
-#define MENU_ENTER2     BUTTON_SELECT
-
-#define MENU_RC_EXIT        BUTTON_RC_STOP
-#define MENU_RC_EXIT_MENU   BUTTON_RC_MODE
-#define MENU_RC_ENTER       BUTTON_RC_ON
-#define MENU_RC_ENTER2      BUTTON_RC_MENU
-
-
-#elif CONFIG_KEYPAD == RECORDER_PAD
-
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT2      BUTTON_OFF
-#define MENU_EXIT_MENU  BUTTON_F1
-#define MENU_ENTER      BUTTON_RIGHT
-#define MENU_ENTER2     BUTTON_PLAY
-
-#define MENU_RC_EXIT    BUTTON_RC_STOP
-#define MENU_RC_ENTER   BUTTON_RC_PLAY
-
-#elif CONFIG_KEYPAD == PLAYER_PAD
-#define MENU_EXIT       BUTTON_STOP
-#define MENU_EXIT_MENU  BUTTON_MENU
-#define MENU_ENTER      BUTTON_PLAY
-
-#define MENU_RC_EXIT    BUTTON_RC_STOP
-#define MENU_RC_ENTER   BUTTON_RC_PLAY
-
-#elif CONFIG_KEYPAD == ONDIO_PAD
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT_MENU  BUTTON_MENU
-#define MENU_ENTER      BUTTON_RIGHT
-
-#elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD)
-
-/* TODO: Check menu button assignments */
-
-#define MENU_NEXT       BUTTON_DOWN
-#define MENU_PREV       BUTTON_UP
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT_MENU  BUTTON_MENU
-#define MENU_ENTER      BUTTON_RIGHT
-#define MENU_ENTER2     BUTTON_SELECT
-
-#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
-
-#define MENU_NEXT       BUTTON_DOWN
-#define MENU_PREV       BUTTON_UP
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT_MENU  BUTTON_PLAY
-#define MENU_ENTER      BUTTON_RIGHT
-
-#elif CONFIG_KEYPAD == IAUDIO_X5_PAD
-
-#define MENU_NEXT       BUTTON_DOWN
-#define MENU_PREV       BUTTON_UP
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT_MENU  BUTTON_REC
-#define MENU_ENTER      BUTTON_RIGHT
-#define MENU_ENTER2     BUTTON_SELECT
-
-#elif CONFIG_KEYPAD == GIGABEAT_PAD
-
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT2      BUTTON_A
-#define MENU_EXIT_MENU  BUTTON_MENU
-#define MENU_ENTER      BUTTON_RIGHT
-#define MENU_ENTER2     BUTTON_SELECT
-#define MENU_NEXT       BUTTON_DOWN
-#define MENU_PREV       BUTTON_UP
-
-#elif CONFIG_KEYPAD == IRIVER_H10_PAD
-
-#define MENU_NEXT       BUTTON_SCROLL_DOWN
-#define MENU_PREV       BUTTON_SCROLL_UP
-#define MENU_EXIT       BUTTON_LEFT
-#define MENU_EXIT_MENU  BUTTON_REW
-#define MENU_ENTER      BUTTON_RIGHT
-#define MENU_ENTER2     BUTTON_FF
-
-#endif
-
 struct menu_item {
     unsigned char *desc; /* string or ID */
     bool (*function) (void); /* return true if USB was connected */
 };
 
-int menu_init(const struct menu_item* mitems, int count, int (*callback)(int, int),
-              const char *button1, const char *button2, const char *button3);
+int menu_init(const struct menu_item* mitems, int count,
+                int (*callback)(int, int),
+                const char *button1, const char *button2, const char *button3);
 void menu_exit(int menu);
 
 void put_cursorxy(int x, int y, bool on);
@@ -125,6 +38,8 @@
 int menu_show(int m);
 #define MENU_ATTACHED_USB -1
 #define MENU_SELECTED_EXIT -2
+#define MENU_EXIT_ALL -3
+#define MENU_RETURN_TO_WPS -4
 
 bool menu_run(int menu);
 int menu_cursor(int menu);
@@ -138,4 +53,87 @@
 void menu_set_cursor(int menu, int position);
 void menu_talk_selected(int m);
 
+
+enum menu_item_type {
+    MT_MENU = 0,
+    MT_SETTING,
+    MT_FUNCTION_CALL, /* used when the standard code wont work */
+    MT_FUNCTION_WITH_PARAM,
+    MT_RETURN_ID, /* returns the position of the selected item (starting at 0)*/
+};
+
+typedef int (*menu_function)(void);
+struct menu_func_with_param {
+    int (*function)(void* param);
+    void *param;
+};
+
+#define MENU_TYPE_MASK 0xF /* MT_* type */
+#define MENU_HAS_DESC   0x10
+#define MENU_COUNT_MASK (~(MENU_TYPE_MASK|MENU_HAS_DESC)) /* unless we need more flags*/
+#define MENU_COUNT_SHIFT 5
+
+struct menu_item_ex {
+    int flags; /* above defines */
+    union {
+        const struct menu_item_ex **submenus; /* used with MT_MENU */
+        void *variable; /* used with MT_SETTING,
+                           must be in the settings_list.c list */
+        int (*function)(void); /* used with MT_FUNCTION_CALL */
+        const struct menu_func_with_param 
+                                *func_with_param; /* MT_FUNCTION_WITH_PARAM */
+        const char **strings; /* used with MT_RETURN_ID */
+    };
+    union {
+        int (*menu_callback)(int action, const struct menu_item_ex *this_item);
+        const struct menu_callback_with_desc {
+            int (*menu_callback)(int action, 
+                                 const struct menu_item_ex *this_item);
+            unsigned char *desc; /* string or ID */
+        } *callback_and_desc;
+    };
+};
+
+typedef int (*menu_callback_type)(int action,
+                                  const struct menu_item_ex *this_item);
+int do_menu(const struct menu_item_ex *menu);
+
+#define MENU_ITEM_COUNT(c) (c<<MENU_COUNT_SHIFT)
+
+#define MENUITEM_SETTING(name,var,callback)                  \
+    static const struct menu_item_ex name =                  \
+        {MT_SETTING, {.variable = (void*)var},{callback}};
+
+#define MAKE_MENU( name, str, cb, ... )                                 \
+    static const struct menu_item_ex *name##_[]  = {__VA_ARGS__};       \
+    static const struct menu_callback_with_desc name##__ = {cb,str};    \
+    const struct menu_item_ex name =                                    \
+        {MT_MENU|MENU_HAS_DESC|                                         \
+         MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)),            \
+            { (void*)name##_},{.callback_and_desc = & name##__}};
+
+#define MENUITEM_STRINGLIST(name, str, callback, ... )                  \
+    static const char *name##_[] = {__VA_ARGS__};                       \
+    static const struct menu_callback_with_desc name##__ = {cb,str};    \
+    static const struct menu_item_ex name =                             \
+        {MT_RETURN_ID|MENU_HAS_DESC|                                    \
+         MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)),            \
+            { .submenus = name##_},{.callback_and_desc = & name##__}};
+/* This one should be static'ed also, 
+   but cannot be done untill sound and playlist menus are done */
+#define MENUITEM_FUNCTION(name, str, func, cb)                          \
+    static const struct menu_callback_with_desc name##_ = {cb,str};     \
+    const struct menu_item_ex name   =                                  \
+        { MT_FUNCTION_CALL|MENU_HAS_DESC, { .function = func},          \
+        {.callback_and_desc = & name##_}};
+
+#define MENUITEM_FUNCTION_WPARAM(name, str, func, param, callback)          \
+    static const struct menu_callback_with_desc name##_ = {callback,str};   \
+    static const struct menu_func_with_param name##__ = {func, param};      \
+    static const struct menu_item_ex name   =                               \
+        { MT_FUNCTION_WITH_PARAM|MENU_HAS_DESC,                             \
+            { .func_with_param = &name##__},                                \
+            {.callback_and_desc = & name##_}};
+
+
 #endif /* End __MENU_H__ */
diff --git a/apps/menus/display_menu.c b/apps/menus/display_menu.c
new file mode 100644
index 0000000..30d321d
--- /dev/null
+++ b/apps/menus/display_menu.c
@@ -0,0 +1,32 @@
+
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:  $
+ *
+ * Copyright (C) 2007 Jonathan Gordon
+ *
+ * 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 <stddef.h>
+#include <limits.h>
+#include "config.h"
+#include "lang.h"
+#include "action.h"
+#include "settings.h"
+#include "menu.h"
+#include "settings_menu.h"
+
+bool display_settings_menu(void); /* from ../settings_menu.c */
+MENUITEM_FUNCTION(display_menu,ID2P(LANG_DISPLAY),(menu_function)display_settings_menu,NULL);
diff --git a/apps/menus/exported_menus.h b/apps/menus/exported_menus.h
new file mode 100644
index 0000000..3d6a11a
--- /dev/null
+++ b/apps/menus/exported_menus.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: $
+ *
+ * Copyright (C) 2006 Jonathan Gordon
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef _EXPORTED_MENUS_H
+#define _EXPORTED_MENUS_H
+
+#include "menu.h"
+/* not needed for plugins */
+#ifndef PLUGIN 
+
+extern const struct menu_item_ex 
+        main_menu_,                 /* main_menu.c      */
+        display_menu,               /* display_menu.c   */
+//        playback_settings,          /* playback_menu.c  */
+#ifdef HAVE_RECORDING
+        recording_settings_menu,    /* recording_menu.c */
+#endif
+        sound_settings,             /* sound_menu.c     */
+        settings_menu_item,         /* settings_menu.c  */
+        playlist_menu_item;         /* playlist_menu.c  */
+
+
+
+#endif /* ! PLUGIN */
+#endif /*_EXPORTED_MENUS_H */
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
new file mode 100644
index 0000000..e830f4b
--- /dev/null
+++ b/apps/menus/main_menu.c
@@ -0,0 +1,125 @@
+
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:  $
+ *
+ * Copyright (C) 2007 Jonathan Gordon
+ *
+ * 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 <stddef.h>
+#include <limits.h>
+#include "config.h"
+#include "lang.h"
+#include "action.h"
+#include "settings.h"
+#include "powermgmt.h"
+#include "menu.h"
+#include "settings_menu.h"
+#include "exported_menus.h"
+#include "tree.h"
+#ifdef CONFIG_TUNER
+#include "radio.h"
+#endif
+#ifdef HAVE_RECORDING
+#include "recording.h"
+#endif
+#include "bookmark.h"
+
+/* lazy coders can use this function if the needed callback 
+	is just to say if the item is shown or not */
+int dynamicitem_callback(int action,const struct menu_item_ex *this_item);
+
+/***********************************/
+/*    MAIN MENU                    */
+
+struct browse_folder_info {
+	const char* dir;
+	int show_options;
+};
+static struct browse_folder_info theme = {THEME_DIR, SHOW_CFG};
+static struct browse_folder_info rocks = {PLUGIN_DIR, SHOW_PLUGINS};
+static int browse_folder(void *param)
+{
+	const struct browse_folder_info *info =
+		(const struct browse_folder_info*)param;
+    return rockbox_browse(info->dir, info->show_options);
+}
+MENUITEM_FUNCTION_WPARAM(browse_themes, ID2P(LANG_CUSTOM_THEME), 
+		browse_folder, (void*)&theme, NULL);
+MENUITEM_FUNCTION_WPARAM(browse_plugins, ID2P(LANG_PLUGINS),
+		browse_folder, (void*)&rocks, NULL);
+
+#ifdef CONFIG_TUNER
+MENUITEM_FUNCTION(load_radio_screen, ID2P(LANG_FM_RADIO),
+                   (menu_function)radio_screen, dynamicitem_callback);
+#endif
+
+#include "settings_menu.h"
+MENUITEM_FUNCTION(manage_settings_menu_item, ID2P(LANG_MANAGE_MENU),
+                (menu_function)manage_settings_menu, NULL);
+bool info_menu(void); /* from apps/main_menu.c TEMP*/
+MENUITEM_FUNCTION(info_menu_item, ID2P(LANG_INFO),
+                (menu_function)info_menu, NULL);
+MENUITEM_FUNCTION(mrb_bookmarks, ID2P(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS),
+                   (menu_function)bookmark_mrb_load, NULL);
+
+#ifdef HAVE_LCD_CHARCELLS
+static int do_shutdown(void)
+{
+    sys_poweroff();
+    return 0;
+}
+MENUITEM_FUNCTION(do_shutdown_item, ID2P(LANG_SHUTDOWN), do_shutdown, NULL);
+#endif
+
+/* NOTE: This title will be translatable once we decide what to call this menu
+         when the root menu comes in... hopefully in the next few days */
+MAKE_MENU(main_menu_, "ROCKbox Main Menu", NULL,
+        &mrb_bookmarks, &sound_settings,
+        &settings_menu_item, &manage_settings_menu_item, &browse_themes,
+#ifdef CONFIG_TUNER
+        &load_radio_screen,
+#endif
+#ifdef HAVE_RECORDING
+        &recording_settings_menu,
+#endif
+        &playlist_menu_item, &browse_plugins, &info_menu_item
+#ifdef HAVE_LCD_CHARCELLS
+        ,&do_shutdown_item
+#endif
+        );
+/*    MAIN MENU                    */
+/***********************************/
+
+/* lazy coders can use this function if the needed 
+   callback is just to say if the item is shown or not */
+int dynamicitem_callback(int action,const struct menu_item_ex *this_item)
+{
+    if (action != ACTION_ENTER_MENUITEM)
+        return action;
+	
+#ifdef CONFIG_TUNER
+    if (this_item == &load_radio_screen)
+    {
+        if (radio_hardware_present() == 0)
+            return ACTION_EXIT_MENUITEM;
+    }
+#else
+    (void)this_item;
+#endif
+
+    return action;
+}
diff --git a/apps/menus/playlist_menu.c b/apps/menus/playlist_menu.c
new file mode 100644
index 0000000..b38870e
--- /dev/null
+++ b/apps/menus/playlist_menu.c
@@ -0,0 +1,32 @@
+
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:  $
+ *
+ * Copyright (C) 2007 Jonathan Gordon
+ *
+ * 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 <stddef.h>
+#include <limits.h>
+#include "config.h"
+#include "lang.h"
+#include "action.h"
+#include "settings.h"
+#include "menu.h"
+#include "playlist_menu.h"
+
+MENUITEM_FUNCTION(playlist_menu_item, ID2P(LANG_PLAYLIST_MENU), (menu_function)playlist_menu, NULL);
+
diff --git a/apps/menus/recording_menu.c b/apps/menus/recording_menu.c
new file mode 100644
index 0000000..d1aed69
--- /dev/null
+++ b/apps/menus/recording_menu.c
@@ -0,0 +1,41 @@
+
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:  $
+ *
+ * Copyright (C) 2007 Jonathan Gordon
+ *
+ * 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 <stddef.h>
+#include <limits.h>
+#include "config.h"
+#include "lang.h"
+#include "action.h"
+#include "settings.h"
+#include "menu.h"
+#include "recording.h"
+
+#ifdef HAVE_RECORDING
+MENUITEM_FUNCTION(rec_menu_recording_screen_item, ID2P(LANG_RECORDING_MENU),
+                   (menu_function)recording_screen, NULL);
+/* TEMP */
+bool recording_menu(bool no_source); /* from apps/sound_menu.h */
+MENUITEM_FUNCTION_WPARAM(recording_settings, ID2P(LANG_RECORDING_MENU),
+                          (int (*)(void*))recording_menu,0, NULL);
+
+MAKE_MENU(recording_settings_menu,ID2P(LANG_RECORDING),0,
+          &rec_menu_recording_screen_item, &recording_settings);
+#endif
diff --git a/apps/menus/recording_settings_menu.c b/apps/menus/recording_settings_menu.c
new file mode 100644
index 0000000..1bc38dd
--- /dev/null
+++ b/apps/menus/recording_settings_menu.c
@@ -0,0 +1,33 @@
+
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:  $
+ *
+ * Copyright (C) 2007 Jonathan Gordon
+ *
+ * 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 <stddef.h>
+#include <limits.h>
+#include "config.h"
+#include "lang.h"
+#include "action.h"
+#include "settings.h"
+#include "menu.h"
+#include "settings_menu.h"
+#include "playback_menu.h"
+
+bool display_settings_menu(void); /* from ../settings_menu.c */
+MENUITEM_FUNCTION(display_menu,ID2P(LANG_DISPLAY),display_settings_menu,NULL);
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c
new file mode 100644
index 0000000..3ea3532
--- /dev/null
+++ b/apps/menus/settings_menu.c
@@ -0,0 +1,33 @@
+
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:  $
+ *
+ * Copyright (C) 2007 Jonathan Gordon
+ *
+ * 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 <stddef.h>
+#include <limits.h>
+#include "config.h"
+#include "lang.h"
+#include "action.h"
+#include "settings.h"
+#include "menu.h"
+#include "sound_menu.h"
+
+#include "settings_menu.h"
+MENUITEM_FUNCTION(settings_menu_item, ID2P(LANG_GENERAL_SETTINGS),
+                   (menu_function)settings_menu, NULL);
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
new file mode 100644
index 0000000..6d656da
--- /dev/null
+++ b/apps/menus/sound_menu.c
@@ -0,0 +1,32 @@
+
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:  $
+ *
+ * Copyright (C) 2007 Jonathan Gordon
+ *
+ * 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 <stddef.h>
+#include <limits.h>
+#include "config.h"
+#include "lang.h"
+#include "action.h"
+#include "settings.h"
+#include "menu.h"
+#include "sound_menu.h"
+
+MENUITEM_FUNCTION(sound_settings, ID2P(LANG_SOUND_SETTINGS), (menu_function)sound_menu, NULL);
+
diff --git a/apps/recorder/recording.h b/apps/recorder/recording.h
index a67337b..e78af50 100644
--- a/apps/recorder/recording.h
+++ b/apps/recorder/recording.h
@@ -18,6 +18,7 @@
  ****************************************************************************/
 #ifndef RECORDING_H
 #define RECORDING_H
+#include "audio.h"
 
 bool recording_screen(bool no_source);
 char *rec_create_filename(char *buf);
diff --git a/apps/settings.c b/apps/settings.c
index 2cdab77..4d4a96c 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -19,6 +19,7 @@
  ****************************************************************************/
 #include <stdio.h>
 #include <stddef.h>
+#include <stdlib.h>
 #include <limits.h>
 #include "inttypes.h"
 #include "config.h"
@@ -922,6 +923,78 @@
 }
 
 /** Changing setting values **/
+const struct settings_list* find_setting(void* variable)
+{
+    int i;
+    for(i=0;i<nb_settings;i++)
+    {
+        if (settings[i].setting == variable)
+            return &settings[i];
+    }
+    return NULL;
+}
+
+void talk_setting(void *global_settings_variable)
+{
+    const struct settings_list *setting;
+    if (global_settings.talk_menu == 0)
+        return;
+    setting = find_setting(global_settings_variable);
+    if (setting == NULL)
+        return;
+    if (setting->lang_id)
+        talk_id(setting->lang_id,false);
+}
+static int selected_setting; /* Used by the callback */
+static void dec_sound_formatter(char *buffer, int buffer_size, 
+        int val, const char *unit)
+{
+    val = sound_val2phys(selected_setting, val);
+    char sign = ' ';
+    if(val < 0)
+    {
+        sign = '-';
+        val = abs(val);
+    }
+    int integer = val / 10;
+    int dec = val % 10;
+    snprintf(buffer, buffer_size, "%c%d.%d %s", sign, integer, dec, unit);
+}
+bool set_sound(const unsigned char * string,
+               int* variable,
+               int setting)
+{
+    int talkunit = UNIT_INT;
+    const char* unit = sound_unit(setting);
+    int numdec = sound_numdecimals(setting);
+    int steps = sound_steps(setting);
+    int min = sound_min(setting);
+    int max = sound_max(setting);
+    sound_set_type* sound_callback = sound_get_fn(setting);
+    if (*unit == 'd') /* crude reconstruction */
+        talkunit = UNIT_DB;
+    else if (*unit == '%')
+        talkunit = UNIT_PERCENT;
+    else if (*unit == 'H')
+        talkunit = UNIT_HERTZ;
+    if(!numdec)
+#if CONFIG_CODEC == SWCODEC
+        /* We need to hijack this one and send it off to apps/dsp.c instead of
+           firmware/sound.c */
+        if (setting == SOUND_STEREO_WIDTH)
+            return set_int(string, unit, talkunit, variable, &stereo_width_set,
+                           steps, min, max, NULL );
+        else
+#endif   
+        return set_int(string, unit, talkunit,  variable, sound_callback,
+                       steps, min, max, NULL );
+    else
+    {/* Decimal number */
+        selected_setting=setting;
+        return set_int(string, unit, talkunit,  variable, sound_callback,
+                       steps, min, max, &dec_sound_formatter );
+    }
+}
 
 bool set_bool(const char* string, bool* variable )
 {
diff --git a/apps/settings.h b/apps/settings.h
index 379084b..8244163 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -224,6 +224,10 @@
     unsigned const char* string;
     long voice_id;
 };
+const struct settings_list* find_setting(void* variable);
+void talk_setting(void *global_settings_variable);
+bool set_sound(const unsigned char * string,
+               int* variable, int setting);
 bool set_bool_options(const char* string, bool* variable,
                       const char* yes_str, int yes_voice,
                       const char* no_str, int no_voice,
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 8641cd8..5f784d4 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -21,10 +21,12 @@
 #include <stdbool.h>
 
 #include "lang.h"
+#include "talk.h"
 #include "lcd.h"
 #include "settings.h"
 #include "settings_list.h"
 #include "sound.h"
+#include "dsp.h"
 
 /* some sets of values which are used more than once, to save memory */
 static const char off_on[] = "off,on";
@@ -92,24 +94,39 @@
 #define NODEFAULT INT(0)
 
 #define SOUND_SETTING(flags,var,lang_id,setting) \
-        {flags|F_T_INT|F_T_SOUND, GS(var),lang_id, NODEFAULT,#var,NULL,\
-            {.sound_setting=(struct sound_setting[]){{setting}}} }
+            {flags|F_T_INT|F_T_SOUND, GS(var),lang_id, NODEFAULT,#var,NULL,\
+                {.sound_setting=(struct sound_setting[]){{setting}}} }
 
 #define BOOL_SETTING(flags,var,lang_id,default,name,cfgvals,yes,no,opt_cb) \
-        {flags|F_T_BOOL, GS(var),lang_id, BOOL(default),name,cfgvals,  \
-            {.bool_setting=(struct bool_setting[]){{opt_cb,yes,no}}} }
+            {flags|F_BOOL_SETTING, GS(var),lang_id, BOOL(default),name,cfgvals,\
+                {.bool_setting=(struct bool_setting[]){{opt_cb,yes,no}}} }
 
 #define OFFON_SETTING(flags,var,lang_id,default,name,cb) \
-        {flags|F_T_BOOL, GS(var),lang_id, BOOL(default),name,off_on,  \
-            {.bool_setting=(struct bool_setting[])             \
-            {{cb,LANG_SET_BOOL_YES,LANG_SET_BOOL_NO}}} }
+            {flags|F_BOOL_SETTING, GS(var),lang_id, BOOL(default),name,off_on, \
+                {.bool_setting=(struct bool_setting[])             \
+                {{cb,LANG_SET_BOOL_YES,LANG_SET_BOOL_NO}}} }
 
 #define SYSTEM_SETTING(flags,var,default) \
-        {flags|F_T_INT, &global_status.var,-1, INT(default), NULL, NULL, UNUSED}
+            {flags|F_T_INT, &global_status.var,-1, INT(default),    \
+                NULL, NULL, UNUSED}
         
 #define FILENAME_SETTING(flags,var,name,default,prefix,suffix,len) \
-        {flags|F_T_UCHARPTR, GS(var),-1, CHARPTR(default),name,NULL,\
-            {.filename_setting=(struct filename_setting[]){{prefix,suffix,len}}} }
+            {flags|F_T_UCHARPTR, GS(var),-1, CHARPTR(default),name,NULL,\
+                {.filename_setting=                                        \
+                    (struct filename_setting[]){{prefix,suffix,len}}} }
+                    
+#define CHOICE_SETTING(flags,var,lang_id,default,name,cfg_vals,cb,count,...)  \
+            {flags|F_CHOICE_SETTING|F_T_INT,  GS(var), lang_id,   \
+                INT(default), name, cfg_vals,                     \
+                {.choice_setting = (struct choice_setting[]){    \
+                    {cb,count,(unsigned char*[]){__VA_ARGS__}}}}}
+
+#define INT_SETTING(flags, var, lang_id, default, name, cfg_vals, \
+                    unit, min, max, step, formatter, cb)  \
+            {flags|F_INT_SETTING|F_T_INT, GS(var), lang_id, INT(default),   \
+                name, cfg_vals, {.int_setting = (struct int_setting[]){    \
+                    {cb, unit, min, max, step, formatter}}}}
+                    
 const struct settings_list settings[] = {
     /* sound settings */
     SOUND_SETTING(0,volume, LANG_VOLUME, SOUND_VOLUME),
@@ -122,10 +139,17 @@
          "off,20ms,2,4,8", UNUSED },
     OFFON_SETTING(0,superbass,LANG_SUPERBASS,false,"superbass",NULL),
 #endif
-    { F_T_INT, GS(channel_config), LANG_CHANNEL, INT(0), "channels",
-         "stereo,mono,custom,mono left,mono right,karaoke", UNUSED },
-    { F_T_INT, GS(stereo_width),LANG_STEREO_WIDTH,
-         INT(100), "stereo width", NULL, UNUSED },
+    CHOICE_SETTING(0,channel_config,LANG_CHANNEL,0,"channels",
+         "stereo,mono,custom,mono left,mono right,karaoke", 
+#if CONFIG_CODEC == SWCODEC
+         channels_set,
+#else
+         sound_set_channels,
+#endif
+         6, ID2P(LANG_CHANNEL_STEREO), ID2P(LANG_CHANNEL_MONO),
+            ID2P(LANG_CHANNEL_CUSTOM), ID2P(LANG_CHANNEL_LEFT),
+            ID2P(LANG_CHANNEL_RIGHT), ID2P(LANG_CHANNEL_KARAOKE)),
+    SOUND_SETTING(0,stereo_width, LANG_STEREO_WIDTH, SOUND_STEREO_WIDTH),
     /* playback */
     OFFON_SETTING(0, resume, LANG_RESUME, false, "resume", NULL),
     OFFON_SETTING(0, playlist_shuffle, LANG_SHUFFLE, false, "shuffle", NULL),
@@ -174,8 +198,8 @@
     SYSTEM_SETTING(NVRAM(4),runtime,0),
     SYSTEM_SETTING(NVRAM(4),topruntime,0),
 #if MEM > 1
-    {F_T_INT,GS(max_files_in_playlist),LANG_MAX_FILES_IN_PLAYLIST, 
-        INT(10000),"max files in playlist",NULL,UNUSED},
+    INT_SETTING(0,max_files_in_playlist,LANG_MAX_FILES_IN_PLAYLIST,10000,
+                "max files in playlist", NULL, UNIT_INT,1000,20000,1000,NULL,NULL),
     {F_T_INT,GS(max_files_in_dir),LANG_MAX_FILES_IN_DIR,
         INT(400),"max files in dir",NULL,UNUSED},
 #else
diff --git a/apps/settings_list.h b/apps/settings_list.h
index 195703b..663d9db 100644
--- a/apps/settings_list.h
+++ b/apps/settings_list.h
@@ -53,7 +53,7 @@
     int lang_yes;
     int lang_no;
 };
-#define F_BOOL_SETTING F_T_BOOL|0x10
+#define F_BOOL_SETTING (F_T_BOOL|0x10)
 #define F_RGB 0x20
 
 struct filename_setting {
@@ -64,29 +64,42 @@
 #define F_FILENAME 0x40
 
 struct int_setting {
-    void (*option_callback)(int);
-    int min;
-    int max;
-    int step;
+  void (*option_callback)(int);
+  int unit;
+  int min;
+  int max;
+  int step;
+  void (*formatter)(char*, int, int, const char*);
 };
+#define F_INT_SETTING 0x80
+
+struct choice_setting {
+  void (*option_callback)(int);
+  int count;
+  unsigned char **desc;
+};
+#define F_CHOICE_SETTING 0x100
+
 /* these use the _isfunc_type type for the function */
 /* typedef int (*_isfunc_type)(void); */
 #define F_MIN_ISFUNC    0x100000 /* min(above) is function pointer to above type */
 #define F_MAX_ISFUNC    0x200000 /* max(above) is function pointer to above type */
 #define F_DEF_ISFUNC    0x400000 /* default_val is function pointer to above type */
 
-#define F_NVRAM_BYTES_MASK     0xE00 /*0-4 bytes can be stored */
-#define F_NVRAM_MASK_SHIFT     9
+#define F_THEMESETTING  0x800000
+
+#define F_NVRAM_BYTES_MASK     0xE000 /*0-4 bytes can be stored */
+#define F_NVRAM_MASK_SHIFT     13
 #define NVRAM_CONFIG_VERSION 2
 /* Above define should be bumped if
 - a new NVRAM setting is added between 2 other NVRAM settings
 - number of bytes for a NVRAM setting is changed
 - a NVRAM setting is removed
 */
-#define F_THEMESETTING	0x800000
+#define F_TEMPVAR    0x200 /* used if the setting should be set using a temp var */
 
 struct settings_list {
-    uint32_t             flags;   /* ____ ____ TFFF ____ ____ NNN_ IFRB STTT */
+    uint32_t             flags;   /* ____ ____ TFFF ____ NNN_ __TC IFRB STTT */
     void                *setting;
     int                  lang_id; /* -1 for none */
     union storage_type   default_val;
@@ -98,6 +111,8 @@
         struct sound_setting *sound_setting; /* use F_T_SOUND for this */
         struct bool_setting  *bool_setting; /* F_BOOL_SETTING */
         struct filename_setting *filename_setting; /* use F_FILENAME */
+        struct int_setting *int_setting; /* use F_INT_SETTING */
+        struct choice_setting *choice_setting; /* F_CHOICE_SETTING */
     };
 };
 
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index 8e6fbd0..054dd14 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -1767,7 +1767,7 @@
 }
 #endif
 
-static bool playback_settings_menu(void)
+bool playback_settings_menu(void)
 {
     int m;
     bool result;
@@ -2061,7 +2061,7 @@
 #endif
 
 
-static bool display_settings_menu(void)
+bool display_settings_menu(void)
 {
     int m;
     bool result;
diff --git a/apps/sound_menu.c b/apps/sound_menu.c
index 19dc57b..f7ddc43 100644
--- a/apps/sound_menu.c
+++ b/apps/sound_menu.c
@@ -61,57 +61,6 @@
 #endif
 #include "action.h"
 
-static int selected_setting; /* Used by the callback */
-static void dec_sound_formatter(char *buffer, int buffer_size, int val, const char *unit)
-{
-    val = sound_val2phys(selected_setting, val);
-    char sign = ' ';
-    if(val < 0)
-    {
-        sign = '-';
-        val = abs(val);
-    }
-    int integer = val / 10;
-    int dec = val % 10;
-    snprintf(buffer, buffer_size, "%c%d.%d %s", sign, integer, dec, unit);
-}
-
-bool set_sound(const unsigned char * string,
-               int* variable,
-               int setting)
-{
-    int talkunit = UNIT_INT;
-    const char* unit = sound_unit(setting);
-    int numdec = sound_numdecimals(setting);
-    int steps = sound_steps(setting);
-    int min = sound_min(setting);
-    int max = sound_max(setting);
-    sound_set_type* sound_callback = sound_get_fn(setting);
-    if (*unit == 'd') /* crude reconstruction */
-        talkunit = UNIT_DB;
-    else if (*unit == '%')
-        talkunit = UNIT_PERCENT;
-    else if (*unit == 'H')
-        talkunit = UNIT_HERTZ;
-    if(!numdec)
-#if CONFIG_CODEC == SWCODEC
-        /* We need to hijack this one and send it off to apps/dsp.c instead of
-           firmware/sound.c */
-        if (setting == SOUND_STEREO_WIDTH)
-            return set_int(string, unit, talkunit,  variable, &stereo_width_set,
-                           steps, min, max, NULL );
-        else
-#endif   
-        return set_int(string, unit, talkunit,  variable, sound_callback,
-                       steps, min, max, NULL );
-    else
-    {/* Decimal number */
-        selected_setting=setting;
-        return set_int(string, unit, talkunit,  variable, sound_callback,
-                       steps, min, max, &dec_sound_formatter );
-    }
-}
-
 static bool volume(void)
 {
     return set_sound(str(LANG_VOLUME), &global_settings.volume, SOUND_VOLUME);
diff --git a/apps/sound_menu.h b/apps/sound_menu.h
index 4e85b9c..4d295b0 100644
--- a/apps/sound_menu.h
+++ b/apps/sound_menu.h
@@ -24,6 +24,5 @@
 bool sound_menu(void);
 bool recording_menu(bool no_source);
 bool rectrigger(void);
-bool set_sound(const unsigned char * string, int* variable, int setting);
 
 #endif