// // device_opt: option manager of device // // Created: 2023-09-07 // // Design: all option-JSONs should added into device_option object // // and all option-logics execute in it; // // Exception: if some sane_opt_providers has its own manager object 'device_option' // // and I cannot get the origin JSON from the provider, how to resolve this ??? // // Solution: return origin JSON if device_option is below other device_option! #pragma once #include #include #include #include #include #include #include "simple_logic.h" #include #include #include class sane_opt_provider; class device_option : public refer { gb_json* origin_; gb_json* now_; bool no_grp_ = false; // i am a lower option manager, need not group std::map src_; std::vector master_opts_; // options that value changed will affect others std::map slaver_; std::function user_; std::function log_; typedef struct _expr_calc { std::string name; std::string val1; std::string val2; bool not_op; bool(*compare)(gb_json*, void* val, void* v1, void* v2); }EXPRCALC; std::map compare_; // simple condition compare class condition_value { typedef struct _cond_val { simple_logic *logic; std::string value; }CONDVAL; std::vector vals_; device_option* parent_; // if this value was valid, the condition value is a consistant value with vals_[0].value void clear(void) { for (auto& v : vals_) { if (v.logic) delete v.logic; } vals_.clear(); parent_ = nullptr; } public: condition_value() : parent_(nullptr) {} ~condition_value() { clear(); } public: bool set_value(gb_json* jsn, const char* type, device_option* parent); // jsn contains only ONE value or its object, or nullptr for a consistant value std::string value(bool(*compare)(const char*, void*), void* param); }; class range_value { bool is_range_; // true - range; false - list int val_ind_; std::vector vals_; void clear(void) { for (auto& v : vals_) delete v; vals_.clear(); } public: range_value() : is_range_(false), val_ind_(0) {} ~range_value() { clear(); } public: bool set_value(gb_json* jsn, const char* type, device_option *parent); // jsn contains all range object int count(void) { return vals_.size(); } bool is_range(void) { return is_range_; } // return first element in list-value or min-value of range std::string first_value(bool(*compare)(const char*, void*), void* param) { val_ind_ = 0; if (val_ind_ < count()) return vals_[val_ind_]->value(compare, param); else return ""; } // return next element in list-value or max-value of range std::string next_value(bool(*compare)(const char*, void*), void* param) { if (++val_ind_ < count()) return vals_[val_ind_]->value(compare, param); else return ""; } }; std::map range_value_; std::map init_value_; std::map support_value_; std::map > depend_opts_; // values that depend on other option's current value static bool is_equal_b(gb_json* opt, void* val, void* v1, void* v2); static bool is_equal_i(gb_json* opt, void* val, void* v1, void* v2); static bool is_equal_f(gb_json* opt, void* val, void* v1, void* v2); static bool is_equal_s(gb_json* opt, void* val, void* v1, void* v2); static bool is_less_b(gb_json* opt, void* val, void* v1, void* v2); static bool is_less_i(gb_json* opt, void* val, void* v1, void* v2); static bool is_less_f(gb_json* opt, void* val, void* v1, void* v2); static bool is_less_s(gb_json* opt, void* val, void* v1, void* v2); static bool is_great_b(gb_json* opt, void* val, void* v1, void* v2); static bool is_great_i(gb_json* opt, void* val, void* v1, void* v2); static bool is_great_f(gb_json* opt, void* val, void* v1, void* v2); static bool is_great_s(gb_json* opt, void* val, void* v1, void* v2); static bool is_between_b(gb_json* opt, void* val, void* v1, void* v2); static bool is_between_i(gb_json* opt, void* val, void* v1, void* v2); static bool is_between_f(gb_json* opt, void* val, void* v1, void* v2); static bool is_between_s(gb_json* opt, void* val, void* v1, void* v2); static bool is_opt_enabled(gb_json* opt, void* val, void* v1, void* v2); static bool get_equal(const char* type, bool(**f)(gb_json*, void*, void*, void*)); static bool get_less(const char* type, bool(**f)(gb_json*, void*, void*, void*)); static bool get_great(const char* type, bool(**f)(gb_json*, void*, void*, void*)); static bool get_between(const char* type, bool(**f)(gb_json*, void*, void*, void*)); // Function: parse string function - .left(cnt), .right(cnt), .mid(start, cnt) // // Parameter: expr - expression of string function, e.g. mode.left(2) // // name - to receive the final option name, e.g. mode // // start - to receive the starting position of the sub-string, negative is for right() // // cnt - to receive the length of the sub string, -1 is to end // // Return: true if was string function static bool is_string_function(const char* expr, std::string& name, int& start, int& cnt); static std::string from_text_value(const char* type, const char* text_val); static bool parse_simple_logic_expression(gb_json* root, const char* expr, std::string* name, EXPRCALC& calc); static void init_condition(const char* expr, void* param); static bool calc_simple_logic_expression(const char* expr, void* param); void clear_for_reconstruct(void); gb_json* group_opt(const char* title); int next_group(int start); // return index of the next group int insert_group(const char* name, const char* title); // return index of the group void insert_option(gb_json* opt, sane_opt_provider* from, const char* group = nullptr); bool arrange_raw_json(sane_opt_provider* sop); // create origin_ and re-arrange groups void init_depends(gb_json* opt); gb_json* copy_opt(gb_json* from, bool *changed_cur = nullptr); int visibility(gb_json* jsn); bool to_now(bool init, bool* changed); int update_provider_value(const char* name, void* value, bool skip_first = false/*readonly value should skip first*/); void remove_provider(gb_json* root, sane_opt_provider* sop); protected: static std::string option_value(gb_json* jsn, bool def_val); static void revise_number_type(gb_json* opt, bool to_double); template static condition_value* to_condition_value(gb_json* jsn, const char* key, const char* type, device_option* parent) { condition_value* ret = nullptr; gb_json* child = nullptr; if (!jsn->get_value(key, child)) { T v; if(jsn->get_value(key, v)) child = new gb_json("", v); else { std::string sv(""); if (jsn->get_value(key, sv)) { // consistant with another option ... ret = new condition_value(); ret->set_value(nullptr, sv.c_str(), parent); if (std::find(parent->master_opts_.begin(), parent->master_opts_.end(), sv) == parent->master_opts_.end()) parent->master_opts_.push_back(sv); } } } if (child) { ret = new condition_value(); if (!ret->set_value(child, type, parent)) { delete ret; ret = nullptr; } child->release(); } return ret; } template bool get_range(gb_json* jsn, const char* key, T& val) { if (jsn->get_value(key, val)) return true; std::string optn(""); if (!jsn->get_value(key, optn)) return false; gb_json* opt = nullptr; if (now_) now_->get_value(optn.c_str(), opt); if (!opt && origin_) origin_->get_value(optn.c_str(), opt); if (!opt) return false; bool ret = opt->get_value("cur", val); opt->release(); return ret; } template bool refine_data_to_range(gb_json* jsn, void* value) { bool refined = false; gb_json* range = nullptr; jsn->get_value("range", range); if (range) { T vl, vu, s; if (get_range(range, "min", vl)) { if (*(T*)value < vl) { *(T*)value = vl; refined = true; } else if (get_range(range, "max", vu)) { if (*(T*)value > vu) { *(T*)value = vu; refined = true; } else if (get_range(range, "step", s)) { // step check, FIXED me ... T cur(*(T*)value); cur -= vl; cur /= s; if (!IS_DOUBLE_EQUAL(cur, (int)cur)) { cur *= s; cur += vl; if (cur > vu) cur = vu; refined = !IS_DOUBLE_EQUAL(cur, *(T*)value); *(T*)value = cur; } } } } else { gb_json* val = range->first_child(); bool found = false; while (val) { if (val->value(vl)) { if (*(T*)value == vl) { found = true; val->release(); break; } } val->release(); val = range->next_child(); } if (!found) { if (jsn->get_value("default", vl)) { refined = true; *(T*)value = vl; } } } range->release(); } return refined; } template void write_log(const char* fmt, Args ... args) { if (log_) { size_t size = snprintf(nullptr, 0, fmt, args ...) + 2; std::unique_ptr buf(new char[size]); snprintf(buf.get(), size, fmt, args ...); log_(buf.get()); } } public: device_option(bool no_group = false, std::function user_priv = std::function() , std::function log = std::function()); ~device_option(); static std::string trans_group(const char* utf8, bool to_title); static std::string get_group(int ind, bool title); static void set_from_default_language_api(const char*(*fdl)(const char*)); public: void clear(void); bool add(sane_opt_provider* sop, bool apply_default_val = true); bool remove(sane_opt_provider* sop); bool refine_data(const char* name, void* value); // return true if the 'value' is out of range and refined it in the range int update_data(const char* name, void* value, bool reorder_if_need = true); // return scanner_err. name and value would be null if invoked for language changed int restore(sane_opt_provider* holder); // int count(void); // return option count bool is_auto_restore_default(gb_json* jsn); std::string get_name_by_sane_id(int sane_ind); std::string get_option_value_type(const char* name, size_t* size = nullptr); std::string get_option_value_type(int sane_ind, size_t* size = nullptr); std::string get_option_field_string(const char* name, const char* key); std::string get_option_value(const char* name, int type/*OPT_VAL_xxx*/, int* size = nullptr, void* in_data = nullptr); // return whole json-text if name was null std::string get_option_value(int sane_ind, int type/*OPT_VAL_xxx*/, int* size = nullptr, void* in_data = nullptr); // return whole json-text if name was null };