// // device_opt: option manager of device // // Created: 2023-09-07 // #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_; 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); int visibility(gb_json* jsn); bool to_now(bool init, bool* changed); protected: static std::string option_value(gb_json* jsn, bool def_val); 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(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 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(const char* name); 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 }; //{ // "resolution": { // "cat": "base", // "group" : "base", // "title" : "�ֱ���", // "desc" : "����ɨ��ͼ��ķֱ���", // "type" : "int", // "fix-id" : 34840, // "size" : 4, // "cur" : 200, // "default" : 200, // "range" : { // "min": 100, // "max" : { // "default": 600, // "paper==���ɨ��ߴ��Զ����� || paper==���ɨ��ߴ� || paper==�����Ծ�" : 500 // }, // "step" : 1 // } // }, // // "paper": { // "cat": "base", // "group" : "base", // "title" : "ֽ�ųߴ�", // "desc" : "���ó�ͼ��С", // "type" : "string", // "fix-id" : 34831, // "size" : 44, // "cur" : "ƥ��ԭʼ�ߴ�", // "default" : "ƥ��ԭʼ�ߴ�", // "range" : ["A3", "8��", "A4", "16��", "A5", "A6", "B4", "B5", "B6", "Letter", "Double Letter", "LEGAL", "ƥ��ԭʼ�ߴ�", { // "resolution<500": "���ɨ��ߴ��Զ�����" // }, { // "resolution<500": "���ɨ��ߴ�" // }, { // "resolution<500": "�����Ծ�" // }] // } //}