rockbox/apps/gui/option_select.c

583 lines
19 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 by Kevin Ferrare
* Copyright (C) 2007 by 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 <stdlib.h>
#include <string.h>
#include "config.h"
#include "option_select.h"
#include "sprintf.h"
#include "kernel.h"
#include "lang.h"
#include "talk.h"
#include "settings_list.h"
#include "sound.h"
#include "list.h"
#include "action.h"
#include "statusbar.h"
#include "misc.h"
#include "splash.h"
#if defined (HAVE_SCROLLWHEEL) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD) || \
(CONFIG_KEYPAD == PLAYER_PAD)
/* Define this if your target makes sense to have
smaller values at the top of the list increasing down the list */
#define ASCENDING_INT_SETTINGS
#endif
static int selection_to_val(struct settings_list *setting, int selection);
static const char *unit_strings[] =
{
[UNIT_INT] = "", [UNIT_MS] = "ms",
[UNIT_SEC] = "s", [UNIT_MIN] = "min",
[UNIT_HOUR]= "hr", [UNIT_KHZ] = "kHz",
[UNIT_DB] = "dB", [UNIT_PERCENT] = "%",
[UNIT_MAH] = "mAh", [UNIT_PIXEL] = "px",
[UNIT_PER_SEC] = "per sec",
[UNIT_HERTZ] = "Hz",
[UNIT_MB] = "MB", [UNIT_KBIT] = "kb/s",
[UNIT_PM_TICK] = "units/10ms",
};
/* these two vars are needed so arbitrary values can be added to the
TABLE_SETTING settings if the F_ALLOW_ARBITRARY_VALS flag is set */
static int table_setting_oldval = 0, table_setting_array_position = 0;
static char *option_get_valuestring(struct settings_list *setting,
char *buffer, int buf_len,
intptr_t temp_var)
{
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
{
bool val = (bool)temp_var;
snprintf(buffer, buf_len, "%s",
str(val? setting->bool_setting->lang_yes :
setting->bool_setting->lang_no));
}
#if 0 /* probably dont need this one */
else if ((setting->flags & F_FILENAME) == F_FILENAME)
{
struct filename_setting *info = setting->filename_setting;
snprintf(buffer, buf_len, "%s%s%s", info->prefix,
(char*)temp_var, info->suffix);
}
#endif
else if (((setting->flags & F_INT_SETTING) == F_INT_SETTING) ||
((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING))
{
const struct int_setting *int_info = setting->int_setting;
const struct table_setting *tbl_info = setting->table_setting;
const char *unit;
void (*formatter)(char*, size_t, int, const char*);
if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
{
formatter = int_info->formatter;
unit = unit_strings[int_info->unit];
}
else
{
formatter = tbl_info->formatter;
unit = unit_strings[tbl_info->unit];
}
if (formatter)
formatter(buffer, buf_len, (int)temp_var, unit);
else
snprintf(buffer, buf_len, "%d %s", (int)temp_var, unit?unit:"");
}
else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
{
char sign = ' ', *unit;
unit = (char*)sound_unit(setting->sound_setting->setting);
if (sound_numdecimals(setting->sound_setting->setting))
{
int integer, dec;
int val = sound_val2phys(setting->sound_setting->setting,
(int)temp_var);
if(val < 0)
{
sign = '-';
val = abs(val);
}
integer = val / 10; dec = val % 10;
snprintf(buffer, buf_len, "%c%d.%d %s", sign, integer, dec, unit);
}
else
snprintf(buffer, buf_len, "%d %s", (int)temp_var, unit);
}
else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
{
if (setting->flags & F_CHOICETALKS)
{
int setting_id;
const struct choice_setting *info = setting->choice_setting;
if (info->talks[(int)temp_var] < LANG_LAST_INDEX_IN_ARRAY)
{
snprintf(buffer, buf_len, "%s", str(info->talks[(int)temp_var]));
}
else
{
find_setting(setting->setting, &setting_id);
cfg_int_to_string(setting_id, (int)temp_var, buffer, buf_len);
}
}
else
{
int value= (int)temp_var;
char *val = P2STR(setting->choice_setting->desc[value]);
snprintf(buffer, buf_len, "%s", val);
}
}
return buffer;
}
static int option_talk(int selected_item, void * data)
{
struct settings_list *setting = (struct settings_list *)data;
int temp_var = selection_to_val(setting, selected_item);
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
{
bool val = temp_var==1?true:false;
talk_id(val? setting->bool_setting->lang_yes :
setting->bool_setting->lang_no, false);
}
#if 0 /* probably dont need this one */
else if ((setting->flags & F_FILENAME) == F_FILENAME)
{
}
#endif
else if (((setting->flags & F_INT_SETTING) == F_INT_SETTING) ||
((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING))
{
const struct int_setting *int_info = setting->int_setting;
const struct table_setting *tbl_info = setting->table_setting;
int unit;
int32_t (*get_talk_id)(int, int);
if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
{
unit = int_info->unit;
get_talk_id = int_info->get_talk_id;
}
else
{
unit = tbl_info->unit;
get_talk_id = tbl_info->get_talk_id;
}
if (get_talk_id)
talk_id(get_talk_id((int)temp_var, unit), false);
else
talk_value((int)temp_var, unit, false);
}
else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
{
int talkunit = UNIT_INT;
const char *unit = sound_unit(setting->sound_setting->setting);
if (!strcmp(unit, "dB"))
talkunit = UNIT_DB;
else if (!strcmp(unit, "%"))
talkunit = UNIT_PERCENT;
else if (!strcmp(unit, "Hz"))
talkunit = UNIT_HERTZ;
talk_value((int)temp_var, talkunit, false);
}
else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
{
int value = (int)temp_var;
if (setting->flags & F_CHOICETALKS)
{
talk_id(setting->choice_setting->talks[value], false);
}
else
{
talk_id(P2ID(setting->choice_setting->desc[value]), false);
}
}
return 0;
}
#if 0
int option_select_next_val(struct settings_list *setting,
intptr_t temp_var)
{
int val = 0;
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
{
val = (bool)temp_var ? 0 : 1;
}
else if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
{
struct int_setting *info = setting->int_setting;
val = (int)temp_var + info->step;
if (val > info->max)
val = info->min;
}
else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
{
int setting_id = setting->sound_setting->setting;
int steps = sound_steps(setting_id);
int min = sound_min(setting_id);
int max = sound_max(setting_id);
val = (int)temp_var + steps;
if (val > max)
val = min;
}
else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
{
struct choice_setting *info = setting->choice_setting;
val = (int)temp_var;
if (val > info->count)
val = 0;
}
return val;
}
int option_select_prev_val(struct settings_list *setting,
intptr_t temp_var)
{
int val = 0;
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
{
val = (bool)temp_var ? 0 : 1;
}
else if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
{
struct int_setting *info = setting->int_setting;
val = (int)temp_var - info->step;
if (val < info->min)
val = info->max;
}
else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
{
int setting_id = setting->sound_setting->setting;
int steps = sound_steps(setting_id);
int min = sound_min(setting_id);
int max = sound_max(setting_id);
val = (int)temp_var -+ steps;
if (val < min)
val = max;
}
else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
{
struct choice_setting *info = setting->choice_setting;
val = (int)temp_var;
if (val < 0)
val = info->count - 1;
}
return val;
}
#endif
static int selection_to_val(struct settings_list *setting, int selection)
{
int min = 0, max = 0, step = 1;
if (((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING) ||
((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING))
return selection;
else if ((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING)
{
const struct table_setting *info = setting->table_setting;
if (setting->flags&F_ALLOW_ARBITRARY_VALS &&
table_setting_array_position != -1 &&
(selection >= table_setting_array_position))
{
if (selection == table_setting_array_position)
return table_setting_oldval;
return info->values[selection-1];
}
else
return info->values[selection];
}
else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
{
int setting_id = setting->sound_setting->setting;
#ifndef ASCENDING_INT_SETTINGS
step = sound_steps(setting_id);
max = sound_max(setting_id);
min = sound_min(setting_id);
#else
step = -sound_steps(setting_id);
min = sound_max(setting_id);
max = sound_min(setting_id);
#endif
}
else if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
{
const struct int_setting *info = setting->int_setting;
#ifndef ASCENDING_INT_SETTINGS
min = info->min;
max = info->max;
step = info->step;
#else
max = info->min;
min = info->max;
step = -info->step;
#endif
}
return max- (selection * step);
}
static char * value_setting_get_name_cb(int selected_item,
void * data, char *buffer)
{
selected_item = selection_to_val(data, selected_item);
return option_get_valuestring(data, buffer, MAX_PATH, selected_item);
}
/* wrapper to convert from int param to bool param in option_screen */
static void (*boolfunction)(bool);
static void bool_funcwrapper(int value)
{
if (value)
boolfunction(true);
else
boolfunction(false);
}
bool option_screen(struct settings_list *setting,
bool use_temp_var, unsigned char* option_title)
{
int action;
bool done = false;
struct gui_synclist lists;
int oldvalue, nb_items = 0, selected = 0, temp_var;
int *variable;
bool allow_wrap = setting->flags & F_NO_WRAP ? false : true;
int var_type = setting->flags&F_T_MASK;
void (*function)(int) = NULL;
char *title;
if (var_type == F_T_INT || var_type == F_T_UINT)
{
variable = use_temp_var ? &temp_var: (int*)setting->setting;
temp_var = oldvalue = *(int*)setting->setting;
}
else if (var_type == F_T_BOOL)
{
/* bools always use the temp variable...
if use_temp_var is false it will be copied to setting->setting every change */
variable = &temp_var;
temp_var = oldvalue = *(bool*)setting->setting?1:0;
}
else return false; /* only int/bools can go here */
gui_synclist_init(&lists, value_setting_get_name_cb,
(void*)setting, false, 1);
if (setting->lang_id == -1)
title = (char*)setting->cfg_vals;
else
title = P2STR(option_title);
gui_synclist_set_title(&lists, title, Icon_Questionmark);
gui_synclist_set_icon_callback(&lists, NULL);
if(global_settings.talk_menu)
gui_synclist_set_voice_callback(&lists, option_talk);
/* set the number of items and current selection */
if (var_type == F_T_INT || var_type == F_T_UINT)
{
if (setting->flags&F_CHOICE_SETTING)
{
nb_items = setting->choice_setting->count;
selected = oldvalue;
function = setting->choice_setting->option_callback;
}
else if (setting->flags&F_TABLE_SETTING)
{
const struct table_setting *info = setting->table_setting;
int i;
nb_items = info->count;
selected = -1;
table_setting_array_position = -1;
for (i=0;selected==-1 && i<nb_items;i++)
{
if (setting->flags&F_ALLOW_ARBITRARY_VALS &&
(oldvalue < info->values[i]))
{
table_setting_oldval = oldvalue;
table_setting_array_position = i;
selected = i;
nb_items++;
}
else if (oldvalue == info->values[i])
selected = i;
}
function = info->option_callback;
}
else if (setting->flags&F_T_SOUND)
{
int setting_id = setting->sound_setting->setting;
int steps = sound_steps(setting_id);
int min = sound_min(setting_id);
int max = sound_max(setting_id);
nb_items = (max-min)/steps + 1;
#ifndef ASCENDING_INT_SETTINGS
selected = (max - oldvalue) / steps;
#else
selected = (oldvalue - min) / steps;
#endif
function = sound_get_fn(setting_id);
}
else
{
const struct int_setting *info = setting->int_setting;
int min, max, step;
max = info->max;
min = info->min;
step = info->step;
nb_items = (max-min)/step + 1;
#ifndef ASCENDING_INT_SETTINGS
selected = (max - oldvalue) / step;
#else
selected = (oldvalue - min) / step;
#endif
function = info->option_callback;
}
}
else if (var_type == F_T_BOOL)
{
selected = oldvalue;
nb_items = 2;
boolfunction = setting->bool_setting->option_callback;
if (boolfunction)
function = bool_funcwrapper;
}
gui_synclist_set_nb_items(&lists, nb_items);
gui_synclist_select_item(&lists, selected);
gui_synclist_limit_scroll(&lists, true);
gui_synclist_draw(&lists);
/* talk the item */
gui_synclist_speak_item(&lists);
while (!done)
{
if (list_do_action(CONTEXT_LIST, TIMEOUT_BLOCK,
&lists, &action,
allow_wrap? LIST_WRAP_UNLESS_HELD: LIST_WRAP_OFF))
{
selected = gui_synclist_get_sel_pos(&lists);
*variable = selection_to_val(setting, selected);
if (var_type == F_T_BOOL)
{
if (!use_temp_var)
*(bool*)setting->setting = selected==1?true:false;
}
}
else if (action == ACTION_NONE)
continue;
else if (action == ACTION_STD_CANCEL)
{
bool show_cancel = false;
if (use_temp_var)
show_cancel = true;
else if (var_type == F_T_INT || var_type == F_T_UINT)
{
if (*variable != oldvalue)
{
show_cancel = true;
*variable = oldvalue;
}
}
else
{
if (*variable != oldvalue)
{
show_cancel = true;
if (!use_temp_var)
*(bool*)setting->setting = oldvalue==1?true:false;
*variable = oldvalue;
}
}
if (show_cancel)
gui_syncsplash(HZ/2, ID2P(LANG_CANCEL));
done = true;
}
else if (action == ACTION_STD_OK)
{
done = true;
}
else if(default_event_handler(action) == SYS_USB_CONNECTED)
return true;
gui_syncstatusbar_draw(&statusbars, false);
/* callback */
if ( function )
function(*variable);
}
if (oldvalue != *variable && (action != ACTION_STD_CANCEL))
{
if (use_temp_var)
{
if (var_type == F_T_INT || var_type == F_T_UINT)
*(int*)setting->setting = *variable;
else
*(bool*)setting->setting = *variable?true:false;
}
settings_save();
}
return false;
}
/* to be replaced */
void option_select_init_items(struct option_select * opt,
const char * title,
int selected,
const struct opt_items * items,
int nb_items)
{
opt->title=title;
opt->min_value=0;
opt->max_value=nb_items;
opt->option=selected;
opt->items=items;
}
void option_select_next(struct option_select * opt)
{
if(opt->option + 1 >= opt->max_value)
{
if(opt->option==opt->max_value-1)
opt->option=opt->min_value;
else
opt->option=opt->max_value-1;
}
else
opt->option+=1;
}
void option_select_prev(struct option_select * opt)
{
if(opt->option - 1 < opt->min_value)
{
/* the dissimilarity to option_select_next() arises from the
* sleep timer problem (bug #5000 and #5001):
* there we have min=0, step = 5 but the value itself might
* not be a multiple of 5 -- as time elapsed;
* We need to be able to set timer to 0 (= Off) nevertheless. */
if(opt->option!=opt->min_value)
opt->option=opt->min_value;
else
opt->option=opt->max_value-1;
}
else
opt->option-=1;
}
const char * option_select_get_text(struct option_select * opt)
{
return(P2STR(opt->items[opt->option].string));
}