8143e4437b
Patch by Igor Poretsky Change-Id: I1bb94965cb18efaa0787d7780f6cf22814231144
613 lines
21 KiB
C
613 lines
21 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 by Kevin Ferrare
|
|
* Copyright (C) 2007 by Jonathan Gordon
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include <stdlib.h>
|
|
#include "string-extra.h"
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "option_select.h"
|
|
#include "kernel.h"
|
|
#include "lang.h"
|
|
#include "talk.h"
|
|
#include "settings_list.h"
|
|
#include "sound.h"
|
|
#include "list.h"
|
|
#include "action.h"
|
|
#include "misc.h"
|
|
#include "splash.h"
|
|
#include "menu.h"
|
|
#include "quickscreen.h"
|
|
|
|
|
|
static int selection_to_val(const struct settings_list *setting, int selection);
|
|
int option_value_as_int(const struct settings_list *setting)
|
|
{
|
|
int type = (setting->flags & F_T_MASK);
|
|
int temp = 0;
|
|
if (type == F_T_BOOL)
|
|
temp = *(bool*)setting->setting?1:0;
|
|
else if (type == F_T_UINT || type == F_T_INT)
|
|
temp = *(int*)setting->setting;
|
|
return temp;
|
|
}
|
|
|
|
/* return an auto ranged time string, unit_idx specifies lowest or
|
|
base index of the passed value -- flag F_TIME_SETTING calls this */
|
|
static const char *option_get_timestring(char *buf, int buf_len,
|
|
int val, int unit_idx)
|
|
{
|
|
return format_time_auto(buf, buf_len, val, unit_idx | UNIT_TRIM_ZERO, false);
|
|
}
|
|
|
|
/* 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;
|
|
const char *option_get_valuestring(const struct settings_list *setting,
|
|
char *buffer, int buf_len,
|
|
intptr_t temp_var)
|
|
{
|
|
const char* str = buffer;
|
|
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
|
|
{
|
|
bool val = (bool)temp_var;
|
|
strlcpy(buffer, str(val? setting->bool_setting->lang_yes :
|
|
setting->bool_setting->lang_no), buf_len);
|
|
}
|
|
#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;
|
|
int info_unit;
|
|
const char *str_unit;
|
|
const char* (*formatter)(char*, size_t, int, const char*);
|
|
if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
|
|
{
|
|
formatter = int_info->formatter;
|
|
info_unit = int_info->unit;
|
|
}
|
|
else
|
|
{
|
|
formatter = tbl_info->formatter;
|
|
info_unit = tbl_info->unit;
|
|
}
|
|
|
|
if ((setting->flags & F_TIME_SETTING) == F_TIME_SETTING)
|
|
str = option_get_timestring(buffer, buf_len, (long)temp_var, info_unit);
|
|
|
|
str_unit = unit_strings_core[info_unit];
|
|
if (formatter)
|
|
str = formatter(buffer, buf_len, (int)temp_var, str_unit);
|
|
else if ((setting->flags & F_TIME_SETTING) != F_TIME_SETTING)
|
|
snprintf(buffer, buf_len, "%d %s", (int)temp_var, str_unit?str_unit:"");
|
|
}
|
|
else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
|
|
{
|
|
format_sound_value(buffer, buf_len,
|
|
setting->sound_setting->setting,
|
|
temp_var);
|
|
}
|
|
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)
|
|
{
|
|
strlcpy(buffer, str(info->talks[(int)temp_var]), buf_len);
|
|
}
|
|
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]);
|
|
strlcpy(buffer, val, buf_len);
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
void option_talk_value(const struct settings_list *setting, int value, bool enqueue)
|
|
{
|
|
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
|
|
{
|
|
bool val = (value==1);
|
|
talk_id(val? setting->bool_setting->lang_yes :
|
|
setting->bool_setting->lang_no, enqueue);
|
|
}
|
|
#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(value, unit), enqueue);
|
|
else if ((setting->flags & F_TIME_SETTING) == F_TIME_SETTING)
|
|
talk_time_intervals(value, unit, enqueue);
|
|
else
|
|
talk_value(value, unit, enqueue);
|
|
}
|
|
else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
|
|
{
|
|
int talkunit = UNIT_INT;
|
|
int sound_setting = setting->sound_setting->setting;
|
|
const char *unit = sound_unit(sound_setting);
|
|
int decimals = sound_numdecimals(sound_setting);
|
|
int phys = sound_val2phys(sound_setting, value);
|
|
if (!strcmp(unit, "dB"))
|
|
talkunit = UNIT_DB;
|
|
else if (!strcmp(unit, "%"))
|
|
talkunit = UNIT_PERCENT;
|
|
else if (!strcmp(unit, "Hz"))
|
|
talkunit = UNIT_HERTZ;
|
|
talk_value_decimal(phys, talkunit, decimals, enqueue);
|
|
}
|
|
else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
|
|
{
|
|
if (setting->flags & F_CHOICETALKS)
|
|
{
|
|
talk_id(setting->choice_setting->talks[value], enqueue);
|
|
}
|
|
else
|
|
{
|
|
talk_id(P2ID(setting->choice_setting->desc[value]), enqueue);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
option_talk_value(setting, temp_var, false);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(HAVE_QUICKSCREEN) || defined(HAVE_RECORDING) || defined(HAVE_TOUCHSCREEN)
|
|
/* only the quickscreen and recording trigger needs this */
|
|
void option_select_next_val(const struct settings_list *setting,
|
|
bool previous, bool apply)
|
|
{
|
|
int val = 0;
|
|
int *value = setting->setting;
|
|
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
|
|
{
|
|
*(bool*)value = !*(bool*)value;
|
|
if (apply && setting->bool_setting->option_callback)
|
|
setting->bool_setting->option_callback(*(bool*)value);
|
|
return;
|
|
}
|
|
else if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
|
|
{
|
|
struct int_setting *info = (struct int_setting *)setting->int_setting;
|
|
bool neg_step = (info->step < 0);
|
|
if (!previous)
|
|
{
|
|
val = *value + info->step;
|
|
if (neg_step ? (val < info->max) : (val > info->max))
|
|
val = info->min;
|
|
}
|
|
else
|
|
{
|
|
val = *value - info->step;
|
|
if (neg_step ? (val > info->min) : (val < info->min))
|
|
val = info->max;
|
|
}
|
|
*value = val;
|
|
if (apply && info->option_callback)
|
|
info->option_callback(val);
|
|
}
|
|
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);
|
|
if (!previous)
|
|
{
|
|
val = *value + steps;
|
|
if (val >= max)
|
|
val = min;
|
|
}
|
|
else
|
|
{
|
|
val = *value - steps;
|
|
if (val < min)
|
|
val = max;
|
|
}
|
|
*value = val;
|
|
if (apply)
|
|
sound_set(setting_id, val);
|
|
}
|
|
else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
|
|
{
|
|
struct choice_setting *info = (struct choice_setting *)setting->choice_setting;
|
|
if (!previous)
|
|
{
|
|
val = *value + 1;
|
|
if (val >= info->count)
|
|
val = 0;
|
|
}
|
|
else
|
|
{
|
|
val = *value - 1;
|
|
if (val < 0)
|
|
val = info->count-1;
|
|
}
|
|
*value = val;
|
|
if (apply && info->option_callback)
|
|
info->option_callback(val);
|
|
}
|
|
else if ((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING)
|
|
{
|
|
const struct table_setting *tbl_info = setting->table_setting;
|
|
int i, add;
|
|
add = previous?tbl_info->count-1:1;
|
|
for (i=0; i<tbl_info->count;i++)
|
|
{
|
|
if ((*value == tbl_info->values[i]) ||
|
|
(settings->flags&F_ALLOW_ARBITRARY_VALS &&
|
|
*value < tbl_info->values[i]))
|
|
{
|
|
val = tbl_info->values[(i+add)%tbl_info->count];
|
|
break;
|
|
}
|
|
}
|
|
*value = val;
|
|
if (apply && tbl_info->option_callback)
|
|
tbl_info->option_callback(val);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int selection_to_val(const struct settings_list *setting, int selection)
|
|
{
|
|
/* rockbox: comment 'set but unused' variables
|
|
int min = 0;
|
|
*/
|
|
int 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 = (setting_id == SOUND_VOLUME) ?
|
|
global_settings.volume_limit : 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 const char * value_setting_get_name_cb(int selected_item,
|
|
void * data,
|
|
char *buffer,
|
|
size_t buffer_len)
|
|
{
|
|
selected_item = selection_to_val(data, selected_item);
|
|
return option_get_valuestring(data, buffer, buffer_len, 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);
|
|
}
|
|
|
|
static void val_to_selection(const struct settings_list *setting, int oldvalue,
|
|
int *nb_items, int *selected,
|
|
void (**function)(int))
|
|
{
|
|
int var_type = setting->flags&F_T_MASK;
|
|
/* 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 = (setting_id == SOUND_VOLUME) ?
|
|
global_settings.volume_limit : 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;
|
|
}
|
|
}
|
|
|
|
bool option_screen(const struct settings_list *setting,
|
|
struct viewport parent[NB_SCREENS],
|
|
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 */
|
|
push_current_activity(ACTIVITY_OPTIONSELECT);
|
|
gui_synclist_init(&lists, value_setting_get_name_cb,
|
|
(void*)setting, false, 1, parent);
|
|
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);
|
|
|
|
val_to_selection(setting, oldvalue, &nb_items, &selected, &function);
|
|
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, HZ, /* HZ so the status bar redraws */
|
|
&lists, &action,
|
|
allow_wrap? LIST_WRAP_UNLESS_HELD: LIST_WRAP_OFF))
|
|
{
|
|
/* setting changed */
|
|
selected = gui_synclist_get_sel_pos(&lists);
|
|
*variable = selection_to_val(setting, selected);
|
|
if (var_type == F_T_BOOL && !use_temp_var)
|
|
*(bool*)setting->setting = (*variable==1);
|
|
}
|
|
else if (action == ACTION_NONE)
|
|
continue;
|
|
else if (action == ACTION_STD_CANCEL)
|
|
{
|
|
/* setting canceled, restore old value if changed */
|
|
if (*variable != oldvalue)
|
|
{
|
|
*variable = oldvalue;
|
|
if (var_type == F_T_BOOL && !use_temp_var)
|
|
*(bool*)setting->setting = (oldvalue==1);
|
|
splash(HZ/2, ID2P(LANG_CANCEL));
|
|
}
|
|
done = true;
|
|
}
|
|
else if (action == ACTION_STD_CONTEXT)
|
|
{
|
|
/* reset setting to default */
|
|
reset_setting(setting, variable);
|
|
if (var_type == F_T_BOOL && !use_temp_var)
|
|
*(bool*)setting->setting = (*variable==1);
|
|
val_to_selection(setting, *variable, &nb_items,
|
|
&selected, &function);
|
|
gui_synclist_select_item(&lists, selected);
|
|
gui_synclist_draw(&lists);
|
|
gui_synclist_speak_item(&lists);
|
|
}
|
|
else if (action == ACTION_STD_OK)
|
|
{
|
|
/* setting accepted, store now if it used a temp var */
|
|
if (use_temp_var)
|
|
{
|
|
if (var_type == F_T_INT || var_type == F_T_UINT)
|
|
*(int*)setting->setting = *variable;
|
|
else
|
|
*(bool*)setting->setting = (*variable==1);
|
|
}
|
|
settings_save();
|
|
done = true;
|
|
}
|
|
else if(default_event_handler(action) == SYS_USB_CONNECTED)
|
|
return true;
|
|
/* callback */
|
|
if ( function )
|
|
function(*variable);
|
|
/* if the volume is changing we need to let the skins know */
|
|
if (function == sound_get_fn(SOUND_VOLUME))
|
|
global_status.last_volume_change = current_tick;
|
|
}
|
|
pop_current_activity();
|
|
return false;
|
|
}
|
|
|
|
int get_setting_info_for_bar(int setting_id, int *count, int *val)
|
|
{
|
|
const struct settings_list *setting = &settings[setting_id];
|
|
int var_type = setting->flags&F_T_MASK;
|
|
void (*function)(int) = NULL;
|
|
int oldvalue;
|
|
|
|
if (var_type == F_T_INT || var_type == F_T_UINT)
|
|
{
|
|
oldvalue = *(int*)setting->setting;
|
|
}
|
|
else if (var_type == F_T_BOOL)
|
|
{
|
|
oldvalue = *(bool*)setting->setting?1:0;
|
|
}
|
|
else
|
|
{
|
|
*val = 0;
|
|
*count = 1;
|
|
return false; /* only int/bools can go here */
|
|
}
|
|
|
|
val_to_selection(setting, oldvalue, count, val, &function);
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
void update_setting_value_from_touch(int setting_id, int selection)
|
|
{
|
|
const struct settings_list *setting = &settings[setting_id];
|
|
int new_val = selection_to_val(setting, selection);
|
|
int var_type = setting->flags&F_T_MASK;
|
|
|
|
if (var_type == F_T_INT || var_type == F_T_UINT)
|
|
{
|
|
*(int*)setting->setting = new_val;
|
|
}
|
|
else if (var_type == F_T_BOOL)
|
|
{
|
|
*(bool*)setting->setting = new_val ? true : false;
|
|
}
|
|
}
|
|
#endif
|