newtx/sdk/sane_opt_json/device_opt.h

413 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// device_opt: option manager of device
//
// Created: 2023-09-07
//
#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_;
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);
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<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(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 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" : "<22>ֱ<EFBFBD><D6B1><EFBFBD>",
// "desc" : "<22><><EFBFBD><EFBFBD>ɨ<EFBFBD><C9A8>ͼ<EFBFBD><CDBC>ķֱ<C4B7><D6B1><EFBFBD>",
// "type" : "int",
// "fix-id" : 34840,
// "size" : 4,
// "cur" : 200,
// "default" : 200,
// "range" : {
// "min": 100,
// "max" : {
// "default": 600,
// "paper==<3D><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD><DFB4>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD> || paper==<3D><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD> || paper==<3D><><EFBFBD><EFBFBD><EFBFBD>Ծ<EFBFBD>" : 500
// },
// "step" : 1
// }
// },
//
// "paper": {
// "cat": "base",
// "group" : "base",
// "title" : "ֽ<>ųߴ<C5B3>",
// "desc" : "<22><><EFBFBD>ó<EFBFBD>ͼ<EFBFBD><CDBC>С",
// "type" : "string",
// "fix-id" : 34831,
// "size" : 44,
// "cur" : "ƥ<><C6A5>ԭʼ<D4AD>ߴ<EFBFBD>",
// "default" : "ƥ<><C6A5>ԭʼ<D4AD>ߴ<EFBFBD>",
// "range" : ["A3", "8<><38>", "A4", "16<31><36>", "A5", "A6", "B4", "B5", "B6", "Letter", "Double Letter", "LEGAL", "ƥ<><C6A5>ԭʼ<D4AD>ߴ<EFBFBD>", {
// "resolution<500": "<22><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD><DFB4>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>"
// }, {
// "resolution<500": "<22><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD>"
// }, {
// "resolution<500": "<22><><EFBFBD><EFBFBD><EFBFBD>Ծ<EFBFBD>"
// }]
// }
//}