387 lines
11 KiB
C++
387 lines
11 KiB
C++
//
|
|
// 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 <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <functional>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
#include "simple_logic.h"
|
|
#include <json/gb_json.h>
|
|
#include <base/utils.h>
|
|
#include <sane/sane_ex.h>
|
|
|
|
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<std::string, sane_opt_provider*> src_;
|
|
std::vector<std::string> master_opts_; // options that value changed will affect others
|
|
std::map<std::string, simple_logic*> slaver_;
|
|
std::function<bool(int)> user_;
|
|
std::function<void(const char*)> 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<std::string, EXPRCALC> compare_; // simple condition compare
|
|
|
|
class condition_value
|
|
{
|
|
typedef struct _cond_val
|
|
{
|
|
simple_logic *logic;
|
|
std::string value;
|
|
}CONDVAL;
|
|
std::vector<CONDVAL> 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<condition_value*> 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<std::string, range_value*> range_value_;
|
|
std::map<std::string, condition_value*> init_value_;
|
|
std::map<std::string, condition_value*> support_value_;
|
|
std::map<std::string, std::vector<std::string> > depend_opts_; // values that depend on other option's current value <master option name, [slaver-option-nam.field ...]>
|
|
|
|
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<class T>
|
|
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<class T>
|
|
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<class T>
|
|
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<T>(range, "min", vl))
|
|
{
|
|
if (*(T*)value < vl)
|
|
{
|
|
*(T*)value = vl;
|
|
refined = true;
|
|
}
|
|
else if (get_range<T>(range, "max", vu))
|
|
{
|
|
if (*(T*)value > vu)
|
|
{
|
|
*(T*)value = vu;
|
|
refined = true;
|
|
}
|
|
else if (get_range<T>(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<typename ... Args>
|
|
void write_log(const char* fmt, Args ... args)
|
|
{
|
|
if (log_)
|
|
{
|
|
size_t size = snprintf(nullptr, 0, fmt, args ...) + 2;
|
|
std::unique_ptr<char[]> buf(new char[size]);
|
|
|
|
snprintf(buf.get(), size, fmt, args ...);
|
|
log_(buf.get());
|
|
}
|
|
}
|
|
|
|
|
|
public:
|
|
device_option(bool no_group = false, std::function<bool(int)> user_priv = std::function<bool(int)>()
|
|
, std::function<void(const char*)> log = std::function<void(const char*)>());
|
|
~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
|
|
};
|
|
|