code_device/hgsane/sane_opt/sane_opts.cpp

536 lines
11 KiB
C++

#include "sane_opts.h"
#include <json/gb_json.h>
#include <algorithm>
#include <string.h>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// sane_opt
sane_opt::sane_opt() : fix_id_(-1), enabled_(true), visible_(true)
{
memset(&opt_desc_, 0, sizeof(opt_desc_));
}
sane_opt::~sane_opt()
{
clear();
}
std::string sane_opt::readable_text(const char* type, void* value)
{
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
return sane_opt::readable_text(SANE_TYPE_BOOL, value);
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
return sane_opt::readable_text(SANE_TYPE_INT, value);
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
return sane_opt::readable_text(SANE_TYPE_FIXED, value);
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
return sane_opt::readable_text(SANE_TYPE_STRING, value);
else
return "";
}
std::string sane_opt::readable_text(SANE_Value_Type type, void* value)
{
if (type == SANE_TYPE_BOOL)
return *(bool*)value ? "true" : "false";
else if (type == SANE_TYPE_INT)
return std::to_string(*(int*)value);
else if (type == SANE_TYPE_FIXED)
return std::to_string(SANE_UNFIX(*(SANE_Fixed*)value));
else if (type == SANE_TYPE_STRING)
return (char*)value;
else
return "";
}
void sane_opt::clear()
{
if (opt_desc_.desc)
delete[] opt_desc_.desc;
if (opt_desc_.name)
delete[] opt_desc_.name;
if (opt_desc_.title)
delete[] opt_desc_.title;
if (opt_desc_.type == SANE_TYPE_STRING && opt_desc_.constraint_type == SANE_CONSTRAINT_STRING_LIST && opt_desc_.constraint.string_list)
delete[] opt_desc_.constraint.string_list;
else if (opt_desc_.type == SANE_TYPE_INT || opt_desc_.type == SANE_TYPE_FIXED)
{
if (opt_desc_.constraint_type == SANE_CONSTRAINT_RANGE && opt_desc_.constraint.range)
delete[] opt_desc_.constraint.range;
else if (opt_desc_.constraint_type == SANE_CONSTRAINT_WORD_LIST && opt_desc_.constraint.word_list)
delete[] opt_desc_.constraint.word_list;
}
memset(&opt_desc_, 0, sizeof(opt_desc_));
fix_id_ = -1;
enabled_ = true;
}
void sane_opt::set_opt_desc_string_value(char** buf, const char* val)
{
int l = strlen(val) + 4;
l += 4;
l /= 4;
l *= 4;
*buf = new char[l];
if (*buf)
{
memset(*buf, 0, l);
strcpy(*buf, val);
}
}
void sane_opt::init_cap(gb_json* jsn)
{
bool bv = false;
opt_desc_.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
if (jsn->get_value("readonly", bv) && bv)
SET_CAP_READONLY(opt_desc_.cap);
if (jsn->get_value("enabled", bv) && !bv)
SET_CAP_ACTIVE(opt_desc_.cap, bv);
if (jsn->get_value("auto", bv) && !bv)
opt_desc_.cap &= ~SANE_CAP_AUTOMATIC;
std::string cat("");
if (jsn->get_value("cat", cat))
{
if (cat == "advanced")
opt_desc_.cap |= SANE_CAP_ADVANCED;
}
int visb = 0;
if (jsn->get_value("visible", visb))
{
visible_ = visb == OPT_VISIBLE_ALL;
}
}
void sane_opt::init_range(gb_json* jsn)
{
gb_json* child = nullptr;
if (jsn->get_value("range", child) && child)
{
if (opt_desc_.type == SANE_TYPE_STRING)
{
gb_json* val = child->first_child();
std::vector<std::string> vals;
int len = 0;
char *buf = nullptr, *oper = nullptr;
while (val)
{
std::string v("");
if (val->value(v))
{
vals.push_back(v);
len += ALIGN_TO(v.length() + 1, 4);
}
val->release();
val = child->next_child();
}
len += (vals.size() + 1) * sizeof(char*);
opt_desc_.constraint_type = SANE_CONSTRAINT_STRING_LIST;
buf = new char[len];
opt_desc_.constraint.string_list = (SANE_String_Const*)buf;
memset(buf, 0, len);
oper = buf + (vals.size() + 1) * sizeof(char*);
len = 0;
for (auto& v : vals)
{
((char**)buf)[len++] = oper;
strcpy(oper, v.c_str());
oper += ALIGN_TO(v.length() + 1, 4);
}
}
else if (opt_desc_.type == SANE_TYPE_INT)
{
int v = 0;
if (child->get_value("min", v))
{
SANE_Range* range = new SANE_Range;
opt_desc_.constraint_type = SANE_CONSTRAINT_RANGE;
opt_desc_.constraint.range = range;
range->min = v;
child->get_value("max", range->max);
child->get_value("step", range->quant);
}
else
{
std::vector<int> vals;
gb_json* val = child->first_child();
while (val)
{
if (val->value(v))
vals.push_back(v);
val->release();
val = child->next_child();
}
SANE_Word* lst = new SANE_Word[vals.size() + 1];
int ind = 0;
lst[ind++] = vals.size();
for (auto& e : vals)
lst[ind++] = e;
opt_desc_.constraint_type = SANE_CONSTRAINT_WORD_LIST;
opt_desc_.constraint.word_list = lst;
}
}
else if (opt_desc_.type == SANE_TYPE_FIXED)
{
double v = 0;
if (child->get_value("min", v))
{
SANE_Range* range = new SANE_Range;
opt_desc_.constraint_type = SANE_CONSTRAINT_RANGE;
opt_desc_.constraint.range = range;
range->min = SANE_FIX(v);
if(child->get_value("max", v))
range->max = SANE_FIX(v);
if(child->get_value("step", v))
range->quant = SANE_FIX(v);
}
else
{
std::vector<double> vals;
gb_json* val = child->first_child();
while (val)
{
if (val->value(v))
vals.push_back(v);
val->release();
val = child->next_child();
}
SANE_Word* lst = new SANE_Word[vals.size() + 1];
int ind = 0;
lst[ind++] = vals.size();
for (auto& e : vals)
lst[ind++] = SANE_FIX(e);
opt_desc_.constraint_type = SANE_CONSTRAINT_WORD_LIST;
opt_desc_.constraint.word_list = lst;
}
}
child->release();
}
}
int sane_opt::get_fix_id(void)
{
return fix_id_;
}
SANE_Option_Descriptor* sane_opt::get_descriptor(void)
{
return &opt_desc_;
}
const char* sane_opt::name(void)
{
return opt_desc_.name;
}
bool sane_opt::is_enabled(void)
{
return enabled_;
}
bool sane_opt::is_visible(void)
{
return visible_;
}
bool sane_opt::from_json_text(const char* key, const char* json, void(*err_msg)(const char*))
{
bool ret = false;
std::string text(json);
gb_json *jsn = new gb_json();
if (jsn->attach_text(&text[0]))
{
jsn->key() = key;
ret = from_json_object(jsn, err_msg);
}
jsn->release();
return ret;
}
bool sane_opt::from_json_object(gb_json* jsn, void(*err_msg)(const char*))
{
bool ret = false;
std::string text("");
clear();
while (1)
{
if (!jsn->get_value("fix-id", fix_id_))
fix_id_ = -1;
if (!jsn->get_value("enabled", enabled_))
enabled_ = true;
set_opt_desc_string_value((char**)&opt_desc_.name, jsn->key().c_str());
if (jsn->get_value("title", text))
{
set_opt_desc_string_value((char**)&opt_desc_.title, text.c_str());
}
if (jsn->get_value("desc", text))
{
set_opt_desc_string_value((char**)&opt_desc_.desc, text.c_str());
}
if (!jsn->get_value("type", text))
break;
if (text == "bool")
{
opt_desc_.type = SANE_TYPE_BOOL;
}
else if (text == "int")
{
opt_desc_.type = SANE_TYPE_INT;
}
else if (text == "float")
{
opt_desc_.type = SANE_TYPE_FIXED;
}
else if (text == "string")
{
opt_desc_.type = SANE_TYPE_STRING;
}
else if (text == "group")
opt_desc_.type = SANE_TYPE_GROUP;
else if (text == "button")
opt_desc_.type = SANE_TYPE_BUTTON;
else
break;
opt_desc_.unit = SANE_UNIT_NONE;
if (jsn->get_value("unit", text))
{
if (text == "pixel")
opt_desc_.unit = SANE_UNIT_PIXEL;
else if (text == "bit")
opt_desc_.unit = SANE_UNIT_BIT;
else if (text == "mm")
opt_desc_.unit = SANE_UNIT_MM;
else if (text == "DPI")
opt_desc_.unit = SANE_UNIT_DPI;
else if (text == "%")
opt_desc_.unit = SANE_UNIT_PERCENT;
else if (text == "microsec")
opt_desc_.unit = SANE_UNIT_MICROSECOND;
else
opt_desc_.unit = SANE_UNIT_NONE;
}
jsn->get_value("size", opt_desc_.size);
// cap ...
init_cap(jsn);
// range ...
init_range(jsn);
ret = true;
break;
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// sane_opt
sane_options::sane_options() : opt_cnt_(0)
{
memset(&opt_0_, 0, sizeof(opt_0_));
opt_0_.cap = SANE_CAP_SOFT_DETECT;
opt_0_.name = "option-count";
opt_0_.desc = "option count of this device";
opt_0_.title = "option count";
opt_0_.type = SANE_TYPE_INT;
opt_0_.size = sizeof(SANE_Int);
}
sane_options::~sane_options()
{
clear();
}
void sane_options::clear(void)
{
std::vector<sane_opt*> tofree;
for (auto& v : opts_)
{
if (std::find(tofree.begin(), tofree.end(), v.second) == tofree.end())
tofree.push_back(v.second);
}
for (auto& v : tofree)
delete v;
tofree.clear();
opts_.clear();
opt_cnt_ = 0;
}
bool sane_options::init_from(const char* jsn_text/*all options*/, void(*err_msg)(const char*), bool clear_prev)
{
bool ret = false;
gb_json* jsn = new gb_json();
std::string str(jsn_text);
int sn = 0;
if(clear_prev)
clear(); // keep the SANE_Option_Descriptor*
if (jsn->attach_text(&str[0]))
{
gb_json* child = jsn->first_child();
str = "";
while (child)
{
sn++;
ret = add_or_replace_opt(sn, child, err_msg);
child->release();
if (!ret)
{
if (clear_prev)
clear();
break;
}
child = jsn->next_child();
}
}
jsn->release();
return ret;
}
bool sane_options::add_or_replace_opt(int& sn, const char* name, const char* jsn_text, void(*err_msg)(const char*))
{
bool ret = false;
std::string text(jsn_text);
gb_json *jsn = new gb_json();
if (jsn->attach_text(&text[0]))
{
jsn->key() = name;
ret = add_or_replace_opt(sn, jsn, err_msg);
}
jsn->release();
return ret;
}
bool sane_options::add_or_replace_opt(int& sn, gb_json* jsn, void(*err_msg)(const char*))
{
int id = sn;
if (!jsn->get_value("fix-id", id))
id = sn;
if (opts_.count(id))
{
bool ret = opts_[id]->from_json_object(jsn, err_msg);
if (!opts_[id]->is_visible())
sn--;
return ret;
}
sane_opt* opt = new sane_opt();
if (opt->from_json_object(jsn, err_msg))
{
if (opt->is_visible())
{
opts_[sn] = opt;
opt_cnt_++;
}
else
sn--;
if (opt->get_fix_id() > SANE_OPT_ID_BASE)
{
opts_[opt->get_fix_id()] = opt;
}
else if (!opt->is_visible()) // no fix-id and SANE_Int number will not be existence
delete opt;
return true;
}
else
{
delete opt;
return false;
}
}
SANE_Option_Descriptor* sane_options::get_opt_descriptor(const void* opt, int* fix_id, int ind_base)
{
if (fix_id)
*fix_id = -1;
if (opt == nullptr)
{
return &opt_0_;
}
if (IS_PTR_NUMBER(opt))
{
int sn = (int)(long)opt;
if(sn < SANE_OPT_ID_BASE)
sn -= ind_base;
if (opts_.count(sn))
{
if (fix_id)
*fix_id = opts_[sn]->get_fix_id();
return opts_[sn]->get_descriptor();
}
}
else
{
const char* name = (const char*)opt;
for (auto& v : opts_)
{
if (v.second->name() == name)
{
if (fix_id)
*fix_id = v.second->get_fix_id();
return v.second->get_descriptor();
}
}
}
return nullptr;
}
int sane_options::get_option_count(void)
{
return opt_cnt_;
}
bool sane_options::enum_invisible_fix_ids(struct _fix_id_cb* fcb) // return whether the callback stopped the enumeration
{
bool stopped = false;
for (auto& v : opts_)
{
if (!v.second->is_visible())
{
if (!fcb->cb(v.second->get_fix_id(), fcb->param))
{
stopped = true;
break;
}
}
}
return stopped;
}