newtx/sdk/sane_opt_json/device_opt.cpp

2378 lines
53 KiB
C++
Raw Permalink Blame History

#include "device_opt.h"
#include <huagao/hgscanner_error.h>
#include "base_opt.h"
#include <string.h>
#include <algorithm>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
static const char* empty_from_default_language(const char* deflang)
{
return deflang;
}
static const char*(*from_def_lang)(const char*) = empty_from_default_language;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// image-processing jsons ...
static bool refine_string_data(gb_json* jsn, void* value)
{
bool refined = false;
gb_json* range = nullptr;
std::string v((char*)value);
jsn->get_value("range", range);
if (range)
{
std::string vl(""), vu(""), s("");
if (range->get_value("min", vl))
{
if (v < vl)
{
strcpy((char*)value, vl.c_str());
refined = true;
}
else if (range->get_value("max", vu))
{
if (v > vu)
{
strcpy((char*)value, vu.c_str());
refined = true;
}
//else if (range->get_value("step", s))
//{
// T cur(*(TC*)value);
//
// vl = cur - vl;
// vl /= s;
// if (!IS_DOUBLE_EQUAL(vl, (int)vl))
// {
// vl += .5f;
// vl = (int)vl * s;
// if (vl > vu)
// vl = vu;
// cp((TC*)value, vl);
// refined = true;
// }
//}
}
}
else
{
gb_json* val = range->first_child();
bool found = false;
while (val)
{
if (val->value(vl))
{
if (v == vl)
{
found = true;
val->release();
break;
}
}
val->release();
val = range->next_child();
}
if (!found)
{
if (jsn->get_value("default", vl))
{
refined = true;
strcpy((char*)value, vl.c_str());
}
}
}
range->release();
}
return refined;
}
template<class T>
static std::string get_real_value(gb_json* jsn, bool def_val)
{
T v;
jsn->get_value(def_val ? "default" : "cur", v);
return std::move(std::string((char*)&v, sizeof(v)));
}
template<class T>
static std::string get_real_string(gb_json* jsn, bool def_val)
{
T v;
jsn->get_value(def_val ? "default" : "cur", v);
return std::move(v);
}
static std::string get_real_value(gb_json* jsn, const char* type)
{
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
{
bool v = false;
jsn->value(v);
return std::string((char*)&v, sizeof(v));
}
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
{
int v = 0;
jsn->value(v);
return std::string((char*)&v, sizeof(v));
}
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
{
double v = .0f;
jsn->value(v);
return std::string((char*)&v, sizeof(v));
}
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
{
std::string v("");
jsn->value(v);
return std::move(v);
}
return "";
}
static struct
{
std::string name;
std::string title; // in UTF8
}g_known_group_with_sn[] = { {"base", "\xE5\x9F\xBA\xE6\x9C\xAC\xE8\xAE\xBE\xE7\xBD\xAE"}
, {"bright", "\xE4\xBA\xAE\xE5\xBA\xA6"}
, {"imgp", "\xE5\x9B\xBE\xE5\x83\x8F\xE5\xA4\x84\xE7\x90\x86"}
, {"feeder", "\xE9\x80\x81\xE7\xBA\xB8\xE6\x96\xB9\xE5\xBC\x8F\xE8\xAE\xBE\xE7\xBD\xAE"} // <20><><EFBFBD>ã<EFBFBD>\xE8\xAE\xBE\xE7\xBD\xAE
, {"advance", "\xE9\xAB\x98\xE7\xBA\xA7\xE8\xAE\xBE\xE7\xBD\xAE"} // <20><><EFBFBD>ã<EFBFBD>\xE8\xAE\xBE\xE7\xBD\xAE
, {"user", "\xE7\x94\xA8\xE6\x88\xB7"} // <20><><EFBFBD>ã<EFBFBD>\xE8\xAE\xBE\xE7\xBD\xAE
, {"about", "\xE5\x85\xB3\xE4\xBA\x8E"} // <20><><EFBFBD>ã<EFBFBD>\xE8\xAE\xBE\xE7\xBD\xAE
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class device_opt ...
bool device_option::condition_value::set_value(gb_json* jsn, const char* type, device_option* parent) // jsn contains only ONE value or its object
{
bool ret = false;
clear();
if (!jsn)
{
// consistant value, and type is name ...
CONDVAL cv;
cv.logic = nullptr;
cv.value = type;
vals_.push_back(cv);
parent_ = parent;
return true;
}
if (jsn->is_leaf_node())
{
CONDVAL cv;
cv.logic = nullptr;
cv.value = get_real_value(jsn, type);
vals_.push_back(cv);
ret = true;
}
else
{
gb_json* item = jsn->first_child();
CONDVAL val0; // make the default value at last
ret = true;
val0.logic = nullptr;
val0.value = "";
while (item)
{
// "condition": value
std::string cond(item->key()), val("");
CONDVAL cv;
if (cond == "default")
{
val0.logic = nullptr;
val0.value = get_real_value(item, type);
}
else
{
int end = 0;
cv.logic = new simple_logic();
if (cv.logic->parse(cond.c_str(), &end, &device_option::init_condition, parent))
{
cv.value = get_real_value(item, type);
vals_.push_back(cv);
}
else
{
delete cv.logic;
item->release();
ret = false;
break;
}
}
item->release();
item = jsn->next_child();
}
if (!val0.value.empty())
vals_.push_back(val0);
}
return ret;
}
std::string device_option::condition_value::value(bool(*compare)(const char*, void*), void* param)
{
if (parent_)
return parent_->get_option_value(vals_[0].value.c_str(), SANE_ACTION_GET_VALUE);
for (auto& v : vals_)
{
if (!v.logic)
return v.value;
else if (v.logic->value(compare, param))
return v.value;
}
return "";
}
bool device_option::range_value::set_value(gb_json* jsn, const char* type, device_option* parent) // jsn contains all range object
{
bool ret = true;
parent->depend_opts_[jsn->key()];
clear();
if (jsn->is_array())
{
gb_json* item = jsn->first_child();
while (item)
{
device_option::condition_value* v = new device_option::condition_value();
if (v->set_value(item, type, parent))
{
vals_.push_back(v);
}
else
{
delete v;
item->release();
ret = false;
break;
}
item->release();
item = jsn->next_child();
}
}
else
{
condition_value* cv = nullptr;
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
{
cv = device_option::to_condition_value<bool>(jsn, "min", type, parent);
ret = false;
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<bool>(jsn, "max", type, parent);
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<bool>(jsn, "step", type, parent);
if (cv)
{
vals_.push_back(cv);
ret = true;
}
}
}
}
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
{
cv = device_option::to_condition_value<int>(jsn, "min", type, parent);
ret = false;
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<int>(jsn, "max", type, parent);
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<int>(jsn, "step", type, parent);
if (cv)
{
vals_.push_back(cv);
ret = true;
}
}
}
}
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
{
cv = device_option::to_condition_value<double>(jsn, "min", type, parent);
ret = false;
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<double>(jsn, "max", type, parent);
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<double>(jsn, "step", type, parent);
if (cv)
{
vals_.push_back(cv);
ret = true;
}
}
}
}
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
{
cv = device_option::to_condition_value<std::string>(jsn, "min", type, parent);
ret = false;
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<std::string>(jsn, "max", type, parent);
if (cv)
{
vals_.push_back(cv);
cv = device_option::to_condition_value<std::string>(jsn, "step", type, parent);
if (cv)
{
vals_.push_back(cv);
ret = true;
}
}
}
}
}
return ret;
}
device_option::device_option(bool no_group, std::function<bool(int)> user_priv
, std::function<void(const char*)> log)
: no_grp_(no_group), origin_(nullptr), now_(nullptr)
, user_(user_priv), log_(log)
{}
device_option::~device_option()
{
clear();
}
bool device_option::is_equal_b(gb_json* opt, void* val, void* v1, void* v2)
{
return *(bool*)val == *(bool*)v1;
}
bool device_option::is_equal_i(gb_json* opt, void* val, void* v1, void* v2)
{
return *(int*)val == *(int*)v1;
}
bool device_option::is_equal_f(gb_json* opt, void* val, void* v1, void* v2)
{
return IS_DOUBLE_EQUAL(*(double*)val, *(double*)v1);
}
bool device_option::is_equal_s(gb_json* opt, void* val, void* v1, void* v2)
{
return strcmp((char*)val, from_def_lang((char*)v1)) == 0;
}
bool device_option::is_less_b(gb_json* opt, void* val, void* v1, void* v2)
{
return *(bool*)val < *(bool*)v1;
}
bool device_option::is_less_i(gb_json* opt, void* val, void* v1, void* v2)
{
return *(int*)val < *(int*)v1;
}
bool device_option::is_less_f(gb_json* opt, void* val, void* v1, void* v2)
{
return *(double*)val < *(double*)v1;
}
bool device_option::is_less_s(gb_json* opt, void* val, void* v1, void* v2)
{
return strcmp((char*)val, from_def_lang((char*)v1)) < 0;
}
bool device_option::is_great_b(gb_json* opt, void* val, void* v1, void* v2)
{
return *(bool*)val > *(bool*)v1;
}
bool device_option::is_great_i(gb_json* opt, void* val, void* v1, void* v2)
{
return *(int*)val > *(int*)v1;
}
bool device_option::is_great_f(gb_json* opt, void* val, void* v1, void* v2)
{
return *(double*)val > *(double*)v1;
}
bool device_option::is_great_s(gb_json* opt, void* val, void* v1, void* v2)
{
return strcmp((char*)val, from_def_lang((char*)v1)) > 0;
}
bool device_option::is_between_b(gb_json* opt, void* val, void* v1, void* v2)
{
return *(bool*)val == *(bool*)v1 || *(bool*)val <= *(bool*)v2;
}
bool device_option::is_between_i(gb_json* opt, void* val, void* v1, void* v2)
{
return *(int*)val >= *(int*)v1 && *(int*)val <= *(int*)v2;
}
bool device_option::is_between_f(gb_json* opt, void* val, void* v1, void* v2)
{
return *(double*)val >= *(double*)v1 && *(double*)val <= *(double*)v2;
}
bool device_option::is_between_s(gb_json* opt, void* val, void* v1, void* v2)
{
return strcmp((char*)val, from_def_lang((char*)v1)) >= 0 &&
strcmp((char*)val, from_def_lang((char*)v2)) <= 0;
}
bool device_option::is_opt_enabled(gb_json* opt, void* val, void* v1, void* v2)
{
bool en = true;
if (!opt->get_value("enabled", en))
en = true;
return en;
}
bool device_option::get_equal(const char* type, bool(**f)(gb_json*, void*, void*, void*))
{
bool ret = true;
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
{
*f = &device_option::is_equal_b;
}
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
{
*f = &device_option::is_equal_i;
}
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
{
*f = &device_option::is_equal_f;
}
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
{
*f = &device_option::is_equal_s;
}
else
{
ret = false;
}
return ret;
}
bool device_option::get_less(const char* type, bool(**f)(gb_json*, void*, void*, void*))
{
bool ret = true;
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
{
*f = &device_option::is_less_b;
}
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
{
*f = &device_option::is_less_i;
}
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
{
*f = &device_option::is_less_f;
}
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
{
*f = &device_option::is_less_s;
}
else
{
ret = false;
}
return ret;
}
bool device_option::get_great(const char* type, bool(**f)(gb_json*, void*, void*, void*))
{
bool ret = true;
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
{
*f = &device_option::is_great_b;
}
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
{
*f = &device_option::is_great_i;
}
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
{
*f = &device_option::is_great_f;
}
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
{
*f = &device_option::is_great_s;
}
else
{
ret = false;
}
return ret;
}
bool device_option::get_between(const char* type, bool(**f)(gb_json*, void*, void*, void*))
{
bool ret = true;
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
{
*f = &device_option::is_between_b;
}
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
{
*f = &device_option::is_between_i;
}
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
{
*f = &device_option::is_between_f;
}
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
{
*f = &device_option::is_between_s;
}
else
{
ret = false;
}
return ret;
}
bool device_option::is_string_function(const char* expr, std::string& name, int& start, int& cnt)
{
bool ret = false;
std::string exp(expr);
size_t pos = exp.find(".");
cnt = 0;
if (pos != std::string::npos)
{
name = exp.substr(0, pos++);
exp.erase(0, pos);
if (exp.find("left(") == 0)
{
exp.erase(0, 5);
start = 0;
cnt = atoi(exp.c_str());
pos = 0;
}
else if (exp.find("right(") == 0)
{
exp.erase(0, 6);
start = -1 * atoi(exp.c_str());
cnt = -1;
pos = 0;
}
else if (exp.find("mid(") == 0)
{
exp.erase(0, 4);
start = atoi(exp.c_str());
pos = exp.find(",");
if (pos != std::string::npos)
{
exp.erase(0, pos + 1);
cnt = atoi(exp.c_str());
pos = 0;
}
}
if (pos == 0)
{
pos = exp.find(")");
ret = pos != std::string::npos && (cnt > 0 || cnt == -1);
}
}
return ret;
}
std::string device_option::from_text_value(const char* type, const char* text_val)
{
std::string real_v("");
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
{
bool v = STRICMP(text_val, "true") == 0;
real_v = std::string((char*)&v, sizeof(v));
}
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
{
int v = atoi(text_val);
real_v = std::string((char*)&v, sizeof(v));
}
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
{
double v = atof(text_val);
real_v = std::string((char*)&v, sizeof(v));
}
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
{
real_v = text_val;
}
return std::move(real_v);
}
bool device_option::parse_simple_logic_expression(gb_json* root, const char* expr, std::string* name, EXPRCALC& calc)
{
const char* opers[] = { ">", "<", "!", "=", "." }, * tag = nullptr, * dot = strstr(expr, ".");
for (auto& v : opers)
{
tag = strstr(expr, v);
if (tag)
break;
}
if (!tag)
return false;
if (dot && dot < tag) // is mode.!enable ?
{
tag = dot;
}
std::string n(expr, tag - expr);
bool ret = true;
gb_json *child = nullptr;
if (name)
*name = n;
calc.name = n;
root->get_value(n.c_str(), child);
if (!child)
return false;
if (!child->get_value("type", n))
{
child->release();
return false;
}
child->release();
if (*tag == '>')
{
if (tag[1] == '=')
{
if (!device_option::get_less(n.c_str(), &calc.compare))
return false;
calc.not_op = true;
tag++;
}
else
{
if (!device_option::get_great(n.c_str(), &calc.compare))
return false;
calc.not_op = false;
}
tag++;
}
else if (*tag == '<')
{
if (tag[1] == '=')
{
if (!device_option::get_great(n.c_str(), &calc.compare))
return false;
calc.not_op = true;
tag++;
}
else
{
if (!device_option::get_less(n.c_str(), &calc.compare))
return false;
calc.not_op = false;
}
tag++;
}
else if (*tag == '!')
{
if (tag[1] == '=')
{
if (!device_option::get_equal(n.c_str(), &calc.compare))
return false;
calc.not_op = true;
tag++;
}
else
{
return false;
}
tag++;
}
else if (*tag == '=')
{
if (tag[1] == '=')
{
if (!device_option::get_equal(n.c_str(), &calc.compare))
return false;
calc.not_op = false;
tag++;
}
else
{
return false;
}
tag++;
}
else if (*tag++ == '.')
{
calc.compare = &device_option::is_opt_enabled;
if (strcmp(tag, "enabled") == 0)
{
calc.not_op = false;
}
else if (strcmp(tag, "!enabled") == 0)
{
calc.not_op = true;
}
else
{
// substr function ...
if (strstr(tag, "left(") || strstr(tag, "right(") || strstr(tag, "mid("))
{
const char* end = nullptr, *start = strstr(tag, "(");
int l = string_util::find_end_of_pair(start + 1, '(', ')');
bool ret = start[++l] == ')';
if (ret)
{
std::string exp(calc.name + (start + l + 1)),
name1(tag, start + l - tag + 1);
name1.insert(0, calc.name + ".");
ret = device_option::parse_simple_logic_expression(root, exp.c_str(), name, calc);
if (ret)
calc.name = std::move(name1);
}
return ret;
}
return false;
}
tag = "";
}
else
{
return false;
}
// value ...
if (*tag)
{
if (*tag == '[')
{
const char* next = strstr(tag++, ",");
if (next++ == nullptr)
{
ret = false;
}
else
{
std::string v(tag, next - tag - 1);
calc.val1 = device_option::from_text_value(n.c_str(), v.c_str());
tag = strstr(next, "]");
if (!tag)
ret = false;
else
{
v = std::string(next, tag - next);
calc.val2 = device_option::from_text_value(n.c_str(), v.c_str());
ret = device_option::get_between(n.c_str(), &calc.compare);
}
}
}
else
{
calc.val1 = device_option::from_text_value(n.c_str(), tag);
}
}
return ret;
}
void device_option::init_condition(const char* expr, void* param)
{
if (((device_option*)param)->compare_.count(expr))
return;
std::string name("");
EXPRCALC calc;
if (device_option::parse_simple_logic_expression(((device_option*)param)->origin_, expr, &name, calc))
{
((device_option*)param)->compare_[expr] = calc;
if (std::find(((device_option*)param)->master_opts_.begin(), ((device_option*)param)->master_opts_.end(), name)
== ((device_option*)param)->master_opts_.end())
((device_option*)param)->master_opts_.push_back(name);
}
}
bool device_option::calc_simple_logic_expression(const char* expr, void* param)
{
device_option* obj = (device_option*)param;
bool ret = true;
if (obj->compare_.count(expr))
{
gb_json* child = nullptr;
bool substr = false;
int start = 0, cnt = 0;
if(obj->now_)
obj->now_->get_value(obj->compare_[expr].name.c_str(), child);
else
obj->origin_->get_value(obj->compare_[expr].name.c_str(), child);
if (!child)
{
// check string function ...
std::string name("");
substr = device_option::is_string_function(expr, name, start, cnt);
if (substr)
{
if (obj->now_)
obj->now_->get_value(name.c_str(), child);
else
obj->origin_->get_value(name.c_str(), child);
}
}
if (child)
{
bool bv = false;
int nv = 0;
double dv = .0f;
std::string sv("");
void* val = nullptr;
child->get_value("type", sv);
if (strcmp(sv.c_str(), JSON_SANE_TYPE_BOOL) == 0)
{
sv = get_real_value<bool>(child, false);
}
else if (strcmp(sv.c_str(), JSON_SANE_TYPE_INT) == 0)
{
sv = get_real_value<int>(child, false);
}
else if (strcmp(sv.c_str(), JSON_SANE_TYPE_FIXED) == 0)
{
sv = get_real_value<double>(child, false);
}
else if (strcmp(sv.c_str(), JSON_SANE_TYPE_STRING) == 0)
{
child->get_value("cur", sv);
if (substr)
{
if (start < 0)
start = sv.length() + start;
if (start > 0 && start < sv.length())
sv.erase(0, start);
if (cnt > 0 && cnt < sv.length())
sv.erase(cnt);
}
}
val = &sv[0];
ret = obj->compare_[expr].compare(child, val, &obj->compare_[expr].val1[0], &obj->compare_[expr].val2[0]) ^ obj->compare_[expr].not_op;
child->release();
}
}
return ret;
}
gb_json* device_option::meaningless_option(const char* group)
{
gb_json* ml = new gb_json();
ml->set_value("type", JSON_SANE_TYPE_BUTTON);
ml->set_value("group", group);
ml->set_value("enabled", false);
return ml;
}
void device_option::clear_for_reconstruct(void)
{
if (now_)
now_->release();
now_ = nullptr;
master_opts_.clear();
compare_.clear();
for (auto& v : slaver_)
delete v.second;
slaver_.clear();
for (auto& v : range_value_)
delete v.second;
range_value_.clear();
for (auto& v : init_value_)
delete v.second;
init_value_.clear();
for (auto& v : support_value_)
delete v.second;
support_value_.clear();
depend_opts_.clear();
}
gb_json* device_option::group_opt(const char* title)
{
gb_json* jsn = new gb_json();
jsn->set_value("title", title);
jsn->set_value("type", "group");
return jsn;
}
int device_option::next_group(gb_json* root, int start)
{
for (; start < root->children(); ++start)
{
gb_json* child = root->child(start);
std::string str("");
child->get_value("type", str);
child->release();
if (str == JSON_SANE_TYPE_GROUP)
break;
}
return start;
}
int device_option::insert_group(const char* title)
{
gb_json* group = nullptr;
int ind = origin_->children(), grpind = next_group(origin_, 0);
std::string name("grp-" + std::to_string(++grp_ind_)), t("");
while(grpind < origin_->children())
{
group = origin_->child(grpind);
group->get_value("title", t);
if(t == title || group->key() == title)
break;
group->release();
group = nullptr;
grpind = next_group(origin_, grpind + 1);
}
if (group)
{
ind = grpind;
group->release();
}
else
{
for (auto& v : g_known_group_with_sn)
{
if (v.title == title)
{
name = v.name;
break;
}
else if (v.name == title)
{
name = v.name;
title = v.title.c_str();
break;
}
gb_json* child = nullptr;
origin_->get_value(v.name.c_str(), child);
if (child)
{
if (group)
group->release();
group = child;
}
}
if (group)
{
ind = origin_->index(group) + 1;
group->release();
}
else
ind = 0;
ind = next_group(origin_, ind);
group = group_opt(title);
origin_->insert(ind, name.c_str(), group);
group->release();
}
return ind;
}
void device_option::insert_option(gb_json* opt, sane_opt_provider* from, const char* group)
{
// first compare version, reserve higher version
// second compare position, replace with last insertion
// last, sort by position
gb_json* existing = nullptr;
bool added = true;
origin_->get_value(opt->key().c_str(), existing);
if (existing)
{
int vo = 0, vn = 0;
opt->get_value("ver", vn);
existing->get_value("ver", vo);
if (vn > vo)
{
// replace and discard all following options ...
write_log("SANE-OPT: use %s::%s(ver: %d) replaced with %s::%s(ver: %d)\n", src_[existing->key()]->from(), existing->key().c_str(), vo
, from->from(), opt->key().c_str(), vn);
vo = origin_->index(existing);
origin_->remove(vo);
if (src_.count(existing->key()))
{
// disable discarded options ...
sane_opt_provider* next = src_[existing->key()]->get_following(existing->key().c_str());
src_[existing->key()]->enable(existing->key().c_str(), false);
while (next)
{
next->enable(existing->key().c_str(), false);
sane_opt_provider* n = next->get_following(existing->key().c_str());
next->release();
next = n;
}
src_[existing->key()]->release();
src_.erase(existing->key());
}
origin_->insert(vo, opt->key().c_str(), opt);
opt->add_ref();
src_[opt->key()] = from;
from->add_ref();
}
else if (vn == vo)
{
// insert into following queue ...
write_log("SANE-OPT: inserting %s::%s to provider queue ...\n", from->from(), opt->key().c_str());
opt->get_value("pos", vn);
existing->get_value("pos", vo);
if (vo < vn)
{
sane_opt_provider* prev = src_[existing->key()],
*que = src_[existing->key()]->get_following(existing->key().c_str());
prev->add_ref();
while (que)
{
std::string text(que->get_opt_json());
gb_json* jsn = new gb_json(), *child = nullptr;
jsn->attach_text(&text[0]);
jsn->get_value(existing->key().c_str(), child);
if (child)
{
child->get_value("ver", vo);
child->release();
}
jsn->release();
if (vo >= vn)
break;
prev->release();
prev = que;
que = que->get_following(existing->key().c_str());
}
prev->set_following_provider(opt->key().c_str(), from);
if (vo == vn)
{
// replace ...
if(que)
{
sane_opt_provider* next = que->get_following(opt->key().c_str());
que->set_following_provider(opt->key().c_str(), nullptr);
if (next)
{
from->set_following_provider(opt->key().c_str(), next);
next->release();
}
que->release(); // release get_following reference
}
}
else
{
// insert ...
from->set_following_provider(opt->key().c_str(), que);
if (que)
que->release();
}
prev->release();
}
else if (vo > vn)
{
// re-init existing ...
std::string text(opt->to_string()), fo(src_[existing->key()]->from());
existing->attach_text(&text[0]);
existing->key() = opt->key();
write_log("Re-Initialize option '%s' for new provider(%s - %d) is ahead of existing(%s - %d) ...\n", opt->key().c_str()
, from->from(), vn, fo.c_str(), vo);
from->set_following_provider(opt->key().c_str(), src_[existing->key()]);
src_[existing->key()]->release();
src_[opt->key()] = from;
from->add_ref();
}
else
{
// replace ...
vo = origin_->index(existing);
origin_->remove(vo);
if (src_.count(existing->key()))
{
sane_opt_provider* next = src_[existing->key()]->get_following(existing->key().c_str());
src_[existing->key()]->set_following_provider(existing->key().c_str(), nullptr);
src_[existing->key()]->release();
src_.erase(existing->key());
while (next)
{
sane_opt_provider* next1 = next->get_following(existing->key().c_str());
next->set_following_provider(existing->key().c_str(), nullptr);
next->release();
next = next1;
}
}
origin_->insert(vo, opt->key().c_str(), opt);
opt->add_ref();
src_[opt->key()] = from;
from->add_ref();
}
{
std::string logi("SANE-OPT: option '");
logi += opt->key() + "' queue: " + src_[opt->key().c_str()]->from();
sane_opt_provider* next = src_[opt->key().c_str()]->get_following(opt->key().c_str());
while (next)
{
logi += std::string(" -> ") + next->from();
sane_opt_provider* next1 = next;
next = next->get_following(opt->key().c_str());
next1->release();
}
write_log("%s\n", logi.c_str());
}
}
else
{
// discard new option ...
write_log("SANE-OPT: discard %s::%s(ver: %d) for %s::%s(ver: %d) existed!\n", from->from(), opt->key().c_str(), vn
, src_[existing->key()]->from(), existing->key().c_str(), vo);
// disable discarded option
from->enable(existing->key().c_str(), false);
added = false;
}
existing->release();
}
else
{
int index = -1, pos = -1;
if (group)
index = insert_group(group);
// insert poisition according to 'ui-pos'
if (!opt->get_value("ui-pos", pos) || pos == -1)
{
index = next_group(origin_, index + 1);
}
else
{
for (index++; index < origin_->children(); ++index)
{
gb_json* sib = origin_->child(index);
std::string t("");
int sit = -1;
if (!sib->get_value("type", t) || t == JSON_SANE_TYPE_GROUP)
{
sib->release();
break;
}
if (!sib->get_value("ui-pos", sit) || sit == -1)
{
sib->release();
break;
}
sib->release();
if (pos < sit)
break;
}
}
origin_->insert(index, opt->key().c_str(), opt);
src_[opt->key()] = from;
from->add_ref();
}
//if (added) // error occurs when condition default value, move to after to_now ...
//{
// // restore to default value ...
// int size = 0;
// bool can_auto = false;
// std::string val(get_option_value(opt->key().c_str(), SANE_ACTION_GET_DEFAULT_VALUE, &size));
//
// if (!opt->get_value("auto", can_auto) || can_auto)
// {
// std::string type("");
// opt->get_value("type", type);
// val.resize(size);
// type = sane_opt_provider::sane_value_2_readable_text(type.c_str(), &val[0]);
// write_log("Set option '%s' to default value: '%s'\n", opt->key().c_str(), type.c_str());
// from->set_value(opt->key().c_str(), &val[0]);
// sane_opt_provider::set_opt_value(opt, &val[0]);
// }
//}
}
bool device_option::arrange_raw_json(sane_opt_provider* sop)
{
std::vector<gb_json*> ungroup;
std::map<std::string, std::vector<gb_json*>> ingroup;
gb_json* jsn = new gb_json(), *child = nullptr;
std::string text(sop->get_opt_json()), str("");
bool ret = jsn->attach_text(&text[0]);
if (ret)
{
if (!origin_)
origin_ = new gb_json();
text.clear();
child = jsn->first_child();
while (child)
{
child->get_value("type", str);
if (str != JSON_SANE_TYPE_GROUP) // omit group
{
if (str == JSON_SANE_TYPE_FIXED)
{
// revise cur and default
device_option::revise_number_type(child, true);
}
if(no_grp_)
{
insert_option(child, sop);
}
else
{
child->get_value("group", str);
insert_option(child, sop, str.empty() ? nullptr : str.c_str());
}
}
child->release();
child = jsn->next_child();
}
}
jsn->release();
return ret;
}
void device_option::init_depends(gb_json* opt)
{
gb_json* range = nullptr;
std::string dpnd("");
if (opt->get_value("depend", dpnd) && !dpnd.empty())
{
simple_logic* logic = new simple_logic();
int pos = 0;
if (logic->parse(dpnd.c_str(), &pos, &device_option::init_condition, this))
slaver_[opt->key().c_str()] = logic;
else
delete logic;
}
// values ...
opt->get_value("range", range);
if (range)
{
range_value* val = new range_value();
opt->get_value("type", dpnd);
if (val->set_value(range, dpnd.c_str(), this))
range_value_[opt->key()] = val;
else
delete val;
range->release();
}
// default:
range = nullptr;
opt->get_value("default", range);
opt->get_value("type", dpnd);
if (range)
{
condition_value* rv = new condition_value();
if (rv->set_value(range, dpnd.c_str(), this))
init_value_[opt->key()] = rv;
else
delete rv;
range->release();
}
else if (opt->get_value("default", dpnd))
{
if (dpnd.find("=") == 0)
{
// consitant with another option ...
dpnd.erase(0, 1);
condition_value* rv = new condition_value();
rv->set_value(nullptr, dpnd.c_str(), this);
init_value_[opt->key()] = rv;
if (std::find(master_opts_.begin(), master_opts_.end(), dpnd)
== master_opts_.end())
master_opts_.push_back(dpnd);
}
}
// visible
range = nullptr;
opt->get_value("visible", range);
if(range)
{
condition_value* v = new condition_value();
if (v->set_value(range, JSON_SANE_TYPE_INT, this))
support_value_[opt->key()] = v;
else
delete v;
}
}
gb_json* device_option::copy_opt(gb_json* from, bool* changed_cur)
{
std::string text(from->to_string());
gb_json* to = new gb_json();
if (!to->attach_text(&text[0]))
{
to->release();
to = nullptr;
}
else
{
std::string val("");
to->key() = from->key();
// 1: language changed ... (title, description, string-list)
if (to->get_value("title", val))
to->set_value("title", from_def_lang(val.c_str()));
if (to->get_value("desc", val))
to->set_value("desc", from_def_lang(val.c_str()));
// 2: enabled ...
if (slaver_.count(to->key()))
{
bool enable = slaver_[to->key()]->value(&device_option::calc_simple_logic_expression, this);
to->set_value("enabled", enable);
if (src_.count(to->key()))
{
src_[to->key()]->enable(to->key().c_str(), enable);
sane_opt_provider* next = src_[to->key()]->get_following(to->key().c_str());
while (next)
{
next->enable(to->key().c_str(), enable);
sane_opt_provider* next1 = next->get_following(to->key().c_str());
next->release();
next = next1;
}
}
}
// 3: default value ...
if (init_value_.count(to->key()))
{
std::string val(init_value_[to->key()]->value(&device_option::calc_simple_logic_expression, this));
std::string type("");
bool apply_cur = false, rdo = false;
// should we change current value ??? - answer: add boolean field "bind" to specify
if (!from->get_value("bind", apply_cur))
apply_cur = false;
if (!from->get_value("readonly", rdo))
rdo = false;
if (changed_cur)
*changed_cur = apply_cur;
sane_opt_provider::set_opt_value(to, &val[0], "default");
if (apply_cur)
{
if (sane_opt_provider::set_opt_value(to, &val[0], "cur"))
update_provider_value(to->key().c_str(), &val[0], rdo);
else if (changed_cur)
*changed_cur = false;
}
}
// 4: range value ...
if (range_value_.count(to->key()))
{
gb_json* src = nullptr, * dst = nullptr;
from->get_value("range", src);
to->get_value("range", dst);
if (src && dst)
{
dst->clear(src->is_array());
dst->key() = src->key();
to->get_value("type", val);
if (val == JSON_SANE_TYPE_BOOL)
{
if (dst->is_array())
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
for (int i = 0; i < range_value_[to->key()]->count(); ++i)
{
if (!val.empty())
*dst += *(bool*)&val[0];
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
}
}
else
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("min", *(bool*)&val[0]);
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("max", *(bool*)&val[0]);
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("step", *(bool*)&val[0]);
}
}
else if (val == JSON_SANE_TYPE_INT)
{
if (dst->is_array())
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
for (int i = 0; i < range_value_[to->key()]->count(); ++i)
{
if (!val.empty())
*dst += *(int*)&val[0];
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
}
}
else
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("min", *(int*)&val[0]);
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("max", *(int*)&val[0]);
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("step", *(int*)&val[0]);
}
}
else if (val == JSON_SANE_TYPE_FIXED)
{
if (dst->is_array())
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
for (int i = 0; i < range_value_[to->key()]->count(); ++i)
{
if (!val.empty())
*dst += *(double*)&val[0];
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
}
}
else
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("min", *(double*)&val[0]);
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("max", *(double*)&val[0]);
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("step", *(double*)&val[0]);
}
}
else if (val == JSON_SANE_TYPE_STRING)
{
if (dst->is_array())
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
for(int i = 0; i < range_value_[to->key()]->count(); ++i)
{
if(!val.empty())
*dst += from_def_lang(val.c_str());
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
}
}
else
{
val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("min", from_def_lang(val.c_str()));
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("max", from_def_lang(val.c_str()));
val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this);
dst->set_value("step", from_def_lang(val.c_str()));
}
}
}
if (src)
src->release();
if (dst)
dst->release();
}
}
return to;
}
int device_option::visibility(gb_json* jsn)
{
int visb = OPT_VISIBLE_ALL;
if (!jsn->get_value("visible", visb))
{
if (support_value_.count(jsn->key()))
{
std::string val(support_value_[jsn->key()]->value(&device_option::calc_simple_logic_expression, this));
visb = *(int*)&val[0];
}
}
return visb;
}
bool device_option::to_now(bool init, bool* changed)
{
if (!origin_)
return false;
gb_json* from = nullptr, * to = nullptr, * tmp = new gb_json();
from = origin_->first_child();
while (from)
{
std::string name(from->key());
bool cur_chged = false;
if (init)
init_depends(from);
// check hidden option is moved to do when get all options json, here should add into, or else the option maybe in wrong status !!!
to = copy_opt(from, &cur_chged);
from->release();
if (to)
{
// copy cur value ...
if (now_ && !cur_chged)
{
gb_json* now = nullptr;
now_->get_value(to->key().c_str(), now);
if (now)
{
std::string type("");
now->get_value("type", type);
if (type == JSON_SANE_TYPE_BOOL)
{
bool v = false;
now->get_value("cur", v);
refine_data_to_range<bool>(to, &v);
to->set_value("cur", v);
}
else if (type == JSON_SANE_TYPE_INT)
{
int v = 0;
now->get_value("cur", v);
refine_data_to_range<int>(to, &v);
to->set_value("cur", v);
}
else if (type == JSON_SANE_TYPE_FIXED)
{
double v = .0f;
now->get_value("cur", v);
refine_data_to_range<double>(to, &v);
to->set_value("cur", v);
}
else if (type == JSON_SANE_TYPE_STRING)
{
std::string v("");
int size = 0, s1 = 0;
char *vv = nullptr;
to->get_value("size", size);
now->get_value("size", s1);
if (size < s1)
size = s1;
vv = new char[size + 4];
memset(vv, 0, size + 4);
now->get_value("cur", v);
strcpy(vv, v.c_str());
refine_string_data(to, vv);
to->set_value("cur", vv);
delete[] vv;
}
now->release();
}
}
tmp->set_value(name.c_str(), to);
to->release();
from = origin_->next_child();
}
else
{
tmp->release();
tmp = nullptr;
break;
}
}
if (changed)
{
if (!now_ || !tmp)
*changed = true;
else
*changed = *now_ != *tmp;
}
if (now_)
now_->release();
now_ = tmp;
return now_ != nullptr;
}
int device_option::update_provider_value(const char* name, void* value, bool skip_first)
{
int ret = SCANNER_ERR_OK;
if (src_.count(name))
{
sane_opt_provider* opt = nullptr;
if (skip_first)
{
opt = src_[name]->get_following(name);
}
else
{
opt = src_[name];
opt->add_ref();
}
while (opt)
{
int err = opt->set_value(name, value);
if (err == SCANNER_ERR_RELOAD_OPT_PARAM ||
err == SCANNER_ERR_CONFIGURATION_CHANGED)
{
ret = err;
}
sane_opt_provider* next = opt->get_following(name);
opt->release();
opt = next;
}
}
return ret;
}
void device_option::remove_provider(gb_json* root, sane_opt_provider* sop)
{
if (root)
{
gb_json* child = root->first_child();
while (child)
{
bool chk_child = src_.count(child->key()) == 0;
if (!chk_child)
{
sane_opt_provider* sp = src_[child->key()],
* next = sp->get_following(child->key().c_str());
sp->add_ref();
while (sp)
{
if (sp == sop)
{
if (next)
src_[child->key()] = next;
else
src_.erase(child->key());
sp->release(); // release from map: src_
chk_child = true;
break;
}
sp->release(); // release from get_following
sp = next;
next = sp->get_following(child->key().c_str());
}
}
if (chk_child && src_.count(child->key()) == 0)
{
utils::to_log(LOG_LEVEL_DEBUG, "remove_provider: Remove option '%s' from '%s'\n", child->key().c_str(), sop->from());
root->remove(child);
child->release();
child = root->first_child();
continue;
}
child->release();
child = root->next_child();
}
}
}
void device_option::remove_empty_group(gb_json* root)
{
int prev = -2, index = next_group(root, 0);
std::vector<int> grp0;
while(index < root->children())
{
if(index - prev == 1)
grp0.push_back(prev);
else if(prev >= 0)
{
// contains only-accessible option
int cnt = 0;
for (int i = prev + 1; i < index; ++i)
{
gb_json* child = root->child(i);
int visible = 0;
child->get_value("visible", visible);
child->release();
if (visible != OPT_VISIBLE_ACCESS)
{
cnt++;
break;
}
}
if(cnt == 0)
grp0.push_back(prev);
}
prev = index;
index = next_group(root, index + 1);
}
if (prev >= 0)
{
// contains only-accessible option
int cnt = 0;
for (int i = prev + 1; i < index; ++i)
{
gb_json* child = root->child(i);
int visible = 0;
child->get_value("visible", visible);
child->release();
if (visible != OPT_VISIBLE_ACCESS)
{
cnt++;
break;
}
}
if (cnt == 0)
grp0.push_back(prev);
}
for(int i = grp0.size() - 1; i >= 0; --i)
{
std::string title("");
gb_json *grp = root->child(grp0[i]);
grp->get_value("title", title);
grp->release();
utils::to_log(LOG_LEVEL_ALL, "remove empty group '%s'.\n", title.c_str());
root->remove(grp0[i]);
}
}
bool device_option::is_hidden_option(gb_json* opt)
{
bool hidden = false;
int auth = 0;
opt->get_value("auth", auth);
if (!user_ || !user_(auth))
{
hidden = true;
}
return hidden;
}
void device_option::revise_number_type(gb_json* opt, bool to_double)
{
gb_json* child = nullptr;
opt->get_value("cur", child);
if (child)
{
if (child->is_leaf_node())
child->revise_number_type(to_double);
//else
// no object now, fixed me if be object
child->release();
}
opt->get_value("default", child);
if (child)
{
if (child->is_leaf_node())
child->revise_number_type(to_double);
else
{
gb_json* v = child->first_child();
while (v)
{
if (v->is_leaf_node())
v->revise_number_type(to_double);
v->release();
v = child->next_child();
}
}
child->release();
}
opt->get_value("range", child);
if (child)
{
gb_json* v = child->first_child();
while (v)
{
if (v->is_leaf_node())
v->revise_number_type(to_double);
v->release();
v = child->next_child();
}
child->release();
}
}
std::string device_option::trans_group(const char* utf8, bool to_title)
{
if (to_title)
{
for (auto& v : g_known_group_with_sn)
{
if (v.name == utf8)
return v.title;
}
}
else
{
for (auto& v : g_known_group_with_sn)
{
if (v.title == utf8)
return v.name;
}
}
return utf8;
}
std::string device_option::get_group(int ind, bool title)
{
if (ind >= 0 && ind < _countof(g_known_group_with_sn))
return title ? g_known_group_with_sn[ind].title : g_known_group_with_sn[ind].name;
else
return "";
}
void device_option::set_from_default_language_api(const char*(*fdl)(const char*))
{
from_def_lang = fdl ? fdl : empty_from_default_language;
}
void device_option::clear(void)
{
clear_for_reconstruct();
if (origin_)
origin_->release();
origin_ = nullptr;
for (auto& v : src_)
v.second->release();
src_.clear();
grp_ind_ = 0;
}
bool device_option::add(sane_opt_provider* sop, bool apply_default_val)
{
bool ret = false;
clear_for_reconstruct();
if (arrange_raw_json(sop))
{
ret = to_now(true, nullptr);
if (ret)
{
if(apply_default_val)
restore(nullptr);
}
else
clear();
}
else
{
clear();
}
return ret;
}
bool device_option::remove(sane_opt_provider* sop)
{
remove_provider(origin_, sop);
remove_provider(now_, sop);
return true;
}
bool device_option::refine_data(const char* name, void* value)
{
bool refined = false;
gb_json* child = nullptr;
now_->get_value(name, child);
if (child)
{
std::string type(""), org(""), result("");
child->get_value("type", type);
org = sane_opt_provider::sane_value_2_readable_text(type.c_str(), value);
if (type == JSON_SANE_TYPE_BOOL)
{
refined = refine_data_to_range<bool>(child, value);
}
else if (type == JSON_SANE_TYPE_INT)
{
refined = refine_data_to_range<int>(child, value);
}
else if (type == JSON_SANE_TYPE_FIXED)
{
refined = refine_data_to_range<double>(child, value);
}
else if (type == JSON_SANE_TYPE_STRING)
{
refined = refine_string_data(child, value);
}
if(refined)
{
result = sane_opt_provider::sane_value_2_readable_text(type.c_str(), value);
write_log("Refine value of '%s' from '%s' to '%s'.\n", name, org.c_str(), result.c_str());
}
child->release();
}
return refined;
}
int device_option::update_data(const char* name, void* value, bool reorder_if_need, bool from_user)
{
int err = SCANNER_ERR_NO_DATA;
if (!name)
{
to_now(false, nullptr);
err = SCANNER_ERR_RELOAD_OPT_PARAM;
}
else if(now_)
{
std::string type("");
gb_json* child = nullptr;
err = SCANNER_ERR_DEVICE_NOT_SUPPORT;
now_->get_value(name, child);
if (child)
{
bool ro = false;
if (from_user && ((child->get_value("readonly", ro) && ro)
|| (child->get_value("auth", err) && user_ && !user_(err))))
{
err = SCANNER_ERR_ACCESS_DENIED;
// following ...
update_provider_value(name, value, true);
}
else
{
std::string pre(sane_opt_provider::option_value(child, false));
bool changed = false;
// pass to sane_opt_provider ...
err = update_provider_value(name, value);
child->get_value("type", type);
write_log("set option '%s' value to '%s' = %d.\n", name, sane_opt_provider::sane_value_2_readable_text(type.c_str(), value).c_str(), err);
if (err == SCANNER_ERR_OK || err == SCANNER_ERR_NOT_EXACT
|| err == SCANNER_ERR_RELOAD_IMAGE_PARAM || err == SCANNER_ERR_RELOAD_OPT_PARAM
// || err == SCANNER_ERR_CONFIGURATION_CHANGED
)
{
gb_json* org = nullptr;
sane_opt_provider::set_opt_value(child, value);
origin_->get_value(name, org);
if (org)
{
sane_opt_provider::set_opt_value(org, value);
org->release();
}
changed = !sane_opt_provider::is_opt_value_equal(child, &pre[0], value);
// set paper-w and paper-h
if (strcmp(name, SANE_STD_OPT_NAME_PAPER) == 0 ||
strcmp(name, SANE_STD_OPT_NAME_LATERAL) == 0)
{
if (src_.count(SANE_STD_OPT_NAME_PAPER_W))
{
size_t wl = 0, hl = 0;
char *ws = src_[SANE_STD_OPT_NAME_PAPER_W]->get_value(SANE_STD_OPT_NAME_PAPER_W, nullptr, &wl),
*hs = src_[SANE_STD_OPT_NAME_PAPER_H]->get_value(SANE_STD_OPT_NAME_PAPER_H, nullptr, &hl);
std::string w(ws ? std::string(ws, wl) : ""),
h(hs ? std::string(hs, hl) : ""),
lateral(get_option_value(SANE_STD_OPT_NAME_LATERAL, SANE_ACTION_GET_VALUE));
gb_json *jsnl = nullptr;
if (ws)
free(ws);
if (hs)
free(hs);
now_->get_value(SANE_STD_OPT_NAME_LATERAL, jsnl);
if (jsnl)
{
if (device_option::is_opt_enabled(jsnl, nullptr, nullptr, nullptr))
{
refine_data(SANE_STD_OPT_NAME_LATERAL, &lateral[0]);
if (*(bool*)&lateral[0])
w.swap(h);
}
jsnl->release();
}
gb_json* pw = nullptr,
* ph = nullptr;
now_->get_value(SANE_STD_OPT_NAME_PAPER_W, pw);
now_->get_value(SANE_STD_OPT_NAME_PAPER_H, ph);
if (pw)
{
pw->set_value("cur", *(double*)&w[0]);
pw->release();
}
if (ph)
{
ph->set_value("cur", *(double*)&h[0]);
ph->release();
}
name = SANE_STD_OPT_NAME_PAPER; // ensure invoked refresh ...
}
}
if (reorder_if_need && changed && // value has changed
std::find(master_opts_.begin(), master_opts_.end(), name) != master_opts_.end()) // can affect others
{
changed = false;
do
{
changed = false;
to_now(false, &changed);
} while (changed);
err = SCANNER_ERR_RELOAD_OPT_PARAM;
}
} // provider has processed right
} // not read-only option
child->release();
} // has option named 'name'
} // has initialized
return err;
}
int device_option::restore(sane_opt_provider* holder)
{
gb_json* child = nullptr, * cur = now_;
if (cur)
{
cur->add_ref();
child = cur->first_child();
while (child)
{
std::string type("");
child->get_value("type", type);
if (type != JSON_SANE_TYPE_GROUP && type != JSON_SANE_TYPE_BUTTON
&& (!holder || src_.count(child->key()) && src_[child->key()] == holder)
// && is_auto_restore_default(child)
)
{
std::string val(sane_opt_provider::option_value(child, true));
update_data(child->key().c_str(), &val[0], false);
}
child->release();
child = cur->next_child();
}
cur->release();
bool changed = false;
do
{
changed = false;
to_now(false, &changed);
} while (changed);
}
return SCANNER_ERR_RELOAD_OPT_PARAM;
}
int device_option::count(void)
{
gb_json* jsn = now_ ? now_ : origin_;
if (jsn)
return jsn->children();
else
return 0;
}
bool device_option::is_auto_restore_default(gb_json* jsn)
{
bool support = true;
std::string type("");
jsn->get_value("type", type);
if(type == JSON_SANE_TYPE_BUTTON || type == JSON_SANE_TYPE_GROUP)
return false;
if (!jsn->get_value("auto", support))
support = true;
return support;
}
int device_option::sane_id_from_name(const char* name)
{
gb_json* jsn = now_ ? now_ : origin_;
int sn = -1;
if(jsn)
{
gb_json* child = nullptr;
jsn->get_value(name, child);
if(child)
{
sn = jsn->index(child) + 1;
child->release();
}
}
return sn;
}
std::string device_option::get_name_by_sane_id(int sane_ind)
{
std::string value("");
gb_json* jsn = now_ ? now_ : origin_;
if (sane_ind > 0 && sane_ind - 1 < jsn->children())
{
gb_json* child = now_->child(sane_ind - 1);
//child->get_value("name", value);
value = child->key();
child->release();
}
return std::move(value);
}
std::string device_option::get_option_value_type(const char* name, size_t* size)
{
std::string value("");
gb_json* jsn = now_ ? now_ : origin_;
if (jsn)
{
gb_json* child = nullptr;
jsn->get_value(name, child);
if (child)
{
child->get_value("type", value);
if (size)
{
int v = 0;
child->get_value("size", v);
*size = v;
}
child->release();
}
}
return std::move(value);
}
std::string device_option::get_option_value(const char* name, int type, int* size, void* in_data, SANE_Constraint_Type *constraint)
{
std::string value("");
gb_json* jsn = now_ ? now_ : origin_;
if (jsn)
{
if (!name)
{
gb_json *filter = new gb_json(),
*child = jsn->first_child();
int hidden = 0;
while(child)
{
if (is_hidden_option(child)) // The option count must stay the same in SANE protocol, replace hidden option with meaninless option here
{
char key[40] = { 0 };
child->get_value("group", value);
child->release();
child = device_option::meaningless_option(value.c_str());
sprintf(key, "%p-%d", this, ++hidden);
child->key() = key; // "hidden-" + std::to_string(++hidden); // the same hidden id ...
}
filter->set_value(child->key().c_str(), child);
child->release();
child = jsn->next_child();
}
if(!no_grp_)
remove_empty_group(filter);
value = filter->to_string();
filter->release();
// if (no_grp_ && origin_)
// value = origin_->to_string();
// else
// value = jsn->to_string();
}
else
{
gb_json* child = nullptr;
bool own_read = false;
jsn->get_value(name, child);
if (child)
{
if (type == SANE_ACTION_GET_ENTIRE_JSON)
value = child->to_string();
else if(type == SANE_ACTION_GET_ALL_VALUES)
{
value = sane_opt_provider::get_all_values(child, constraint, nullptr);
}
else if (child->get_value("ownread", own_read) && own_read)
{
if (src_.count(name))
{
size_t len = 0;
char* v = src_[name]->get_value(name, in_data, &len);
if (v)
{
value = std::string(v, len);
free(v);
}
}
}
else
value = sane_opt_provider::option_value(child, type == SANE_ACTION_GET_DEFAULT_VALUE);
if (size)
{
int n = 0;
child->get_value("size", n);
*size = n;
}
child->release();
}
}
}
return std::move(value);
}
std::string device_option::get_option_field_string(const char* name, const char* key)
{
std::string value("");
gb_json* jsn = now_ ? now_ : origin_;
if (jsn)
{
gb_json* child = nullptr;
jsn->get_value(name, child);
if (child)
{
child->get_value(key, value);
child->release();
}
}
return std::move(value);
}
std::string device_option::get_option_value_type(int sane_ind, size_t* size)
{
std::string value("");
gb_json* jsn = now_ ? now_ : origin_;
if (sane_ind > 0 && sane_ind - 1 < jsn->children())
{
gb_json* child = now_->child(sane_ind - 1);
child->get_value("type", value);
if (size)
{
int v = 0;
child->get_value("size", v);
*size = v;
}
child->release();
}
return std::move(value);
}
std::string device_option::get_option_value_by_id(int sane_ind, int type, int* size, void* in_data)
{
std::string value("");
if (now_)
{
if (sane_ind <= 0)
{
value = std::move(now_->to_string());
}
else if(sane_ind - 1 < now_->children())
{
gb_json* child = now_->child(sane_ind - 1);
std::string name(child->key());
child->release();
value = std::move(get_option_value(name.c_str(), type, size, in_data));
}
}
return std::move(value);
}