/*************************************************************************** * __________ __ ___. * 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 #include #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 && iflags&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; } /****************************************************** Compatability functions *******************************************************/ #define MAX_OPTIONS 32 const struct opt_items *set_option_options; void set_option_formatter(char* buf, size_t size, int item, const char* unit) { (void)unit; const unsigned char *text = set_option_options[item].string; snprintf(buf, size, "%s", P2STR(text)); } int32_t set_option_get_talk_id(int value, int unit) { (void)unit; return set_option_options[value].voice_id; } bool set_option(const char* string, void* variable, enum optiontype type, const struct opt_items* options, int numoptions, void (*function)(int)) { int temp; struct settings_list item; struct int_setting data = { function, UNIT_INT, 0, numoptions-1, 1, set_option_formatter, set_option_get_talk_id }; set_option_options = options; item.int_setting = &data; item.flags = F_INT_SETTING|F_T_INT; item.lang_id = -1; item.cfg_vals = (char*)string; item.setting = &temp; if (type == BOOL) temp = *(bool*)variable? 1: 0; else temp = *(int*)variable; if (!option_screen(&item, false, NULL)) { if (type == BOOL) *(bool*)variable = (temp == 1? true: false); else *(int*)variable = temp; return false; } return true; } bool set_int_ex(const unsigned char* string, const char* unit, int voice_unit, int* variable, void (*function)(int), int step, int min, int max, void (*formatter)(char*, size_t, int, const char*), int32_t (*get_talk_id)(int, int)) { (void)unit; struct settings_list item; struct int_setting data = { function, voice_unit, min, max, step, formatter, get_talk_id }; item.int_setting = &data; item.flags = F_INT_SETTING|F_T_INT; item.lang_id = -1; item.cfg_vals = (char*)string; item.setting = variable; return option_screen(&item, false, NULL); } /* 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)); }