#ifndef DEVICE_MENU_H #define DEVICE_MENU_H #include #include #include "sane/sane_ex.h" #include "sane/sane_option_definitions.h" #include "../../../sdk/include/huagao/hgscanner_error.h" #include "cfg/gb_json.h" #include "lang/app_language.h" typedef struct _scanner { std::string name; std::string model; bool online; gb::scanner_cfg *cfg; }SCANNER; class dev_que { std::vector que_; std::string root_dir_; std::string opened_scanner_; std::string applied_scheme_; SANE_Handle handle_; static void trans_number(const char* name, std::string& val, void* param) { if (strcmp(name, "tl-x") == 0 || strcmp(name, "br-x") == 0 || strcmp(name, "tl-y") == 0 || strcmp(name, "br-y") == 0 || strcmp(name, "gamma") == 0 || strcmp(name, "search-hole-range-l") == 0 || strcmp(name, "search-hole-range-r") == 0 || strcmp(name, "search-hole-range-t") == 0 || strcmp(name, "search-hole-range-b") == 0 || strcmp(name, "feed-strength-value") == 0 ) { float v = atof(val.c_str()); SANE_Fixed f = SANE_FIX(v); val = std::string((char*)&f, sizeof(f)); } else if (strcmp(name, "binary-threshold") == 0 || strcmp(name, "bkg-color-range") == 0 || strcmp(name, "noise-size") == 0 || strcmp(name, "blank-sensitivity") == 0 || strcmp(name, "resolution") == 0 || strcmp(name, "brightness") == 0 || strcmp(name, "contrast") == 0 || strcmp(name, "threshold") == 0 || strcmp(name, "anti-noise-level") == 0 || strcmp(name, "margin") == 0 || strcmp(name, "scan-count") == 0 || strcmp(name, "askew-range") == 0 || strcmp(name, "dog-ear-size") == 0 ) { SANE_Int v = atoi(val.c_str()); val = std::string((char*)&v, sizeof(v)); } else if (strcmp(val.c_str(), "true") == 0) { SANE_Bool b = SANE_TRUE; val = std::string((char*)&b, sizeof(b)); } else if (strcmp(val.c_str(), "false") == 0) { SANE_Bool b = SANE_FALSE; val = std::string((char*)&b, sizeof(b)); } } static const char* title_2_name(const char* title, void* param) { struct { const char* name; const char* title; }g_opts[] = { {SANE_STD_OPT_NAME_RESTORE , OPTION_TITLE_HFMRSZ} , {SANE_STD_OPT_NAME_HELP , OPTION_TITLE_BZ} , {SANE_STD_OPT_NAME_IS_MULTI_OUT , OPTION_TITLE_DLSC} , {SANE_STD_OPT_NAME_MULTI_OUT_TYPE , OPTION_TITLE_DLSCLX} , {SANE_STD_OPT_NAME_COLOR_MODE , OPTION_TITLE_YSMS} , {SANE_STD_OPT_NAME_BINARY_THRESHOLD , OPTION_TITLE_HBTXYZ} , {SANE_STD_OPT_NAME_REVERSE_01 , OPTION_TITLE_HBTXFSSC} , {SANE_STD_OPT_NAME_FILTER , OPTION_TITLE_HDHHBTX_CSYZQ} , {SANE_STD_OPT_NAME_RID_MULTIOUT_RED , OPTION_TITLE_24WCSTX_DLSCCH} , {SANE_STD_OPT_NAME_RID_ANSWER_SHEET_RED , OPTION_TITLE_24WCSTX_DTKCH} , {SANE_STD_OPT_NAME_ERASE_BACKGROUND , OPTION_TITLE_BJYC} , {SANE_STD_OPT_NAME_BKG_COLOR_RANGE , OPTION_TITLE_BJSCFDFW} , {SANE_STD_OPT_NAME_SHARPEN , OPTION_TITLE_RHYMH} , {SANE_STD_OPT_NAME_RID_MORR , OPTION_TITLE_QCMW} , {SANE_STD_OPT_NAME_RID_GRID , OPTION_TITLE_CWW} , {SANE_STD_OPT_NAME_ERROR_EXTENSION , OPTION_TITLE_CWKS} , {SANE_STD_OPT_NAME_NOISE_OPTIMIZE , OPTION_TITLE_HBTXZDYH} , {SANE_STD_OPT_NAME_NOISE_SIZE , OPTION_TITLE_ZDYHCC} , {SANE_STD_OPT_NAME_PAPER , OPTION_TITLE_ZZCC} , {SANE_STD_OPT_NAME_CUSTOM_AREA , OPTION_TITLE_ZDYSMQY} , {SANE_STD_OPT_NAME_CUSTOM_AREA_LEFT , OPTION_TITLE_SMQYZCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_RIGHT , OPTION_TITLE_SMQYYCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_TOP , OPTION_TITLE_SMQYSCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_BOTTOM , OPTION_TITLE_SMQYXCmm} , {SANE_STD_OPT_NAME_SIZE_CHECK , OPTION_TITLE_CCJC} , {SANE_STD_OPT_NAME_PAGE , OPTION_TITLE_SMYM} , {SANE_STD_OPT_NAME_DISCARD_BLANK_SENS , OPTION_TITLE_TGKBYLMD} , {SANE_STD_OPT_NAME_RESOLUTION , OPTION_TITLE_FBL} , {SANE_STD_OPT_NAME_TIME_TO_SLEEP , OPTION_TITLE_XMSJ} , {SANE_STD_OPT_NAME_IMAGE_QUALITY , OPTION_TITLE_HZ} , {SANE_STD_OPT_NAME_EXCHANGE ,OPTION_TITLE_JHZFM} , {SANE_STD_OPT_NAME_SPLIT ,OPTION_TITLE_TXCF } , {SANE_STD_OPT_NAME_ANTI_SKEW , OPTION_TITLE_ZDJP} , {SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA , OPTION_TITLE_QYSDQX} , {SANE_STD_OPT_NAME_GAMMA , OPTION_TITLE_JMZ} , {SANE_STD_OPT_NAME_BRIGHTNESS , OPTION_TITLE_LDZ} , {SANE_STD_OPT_NAME_CONTRAST , OPTION_TITLE_DBD} , {SANE_STD_OPT_NAME_IS_PHOTO_MODE , OPTION_TITLE_ZPMS} , {SANE_STD_OPT_NAME_ERASE_BLACK_FRAME , OPTION_TITLE_XCHK} , {SANE_STD_OPT_NAME_DARK_SAMPLE , OPTION_TITLE_SSYZ} , {SANE_STD_OPT_NAME_THRESHOLD , OPTION_TITLE_YZ} , {SANE_STD_OPT_NAME_ANTI_NOISE_LEVEL , OPTION_TITLE_BJKZDJ} , {SANE_STD_OPT_NAME_MARGIN , OPTION_TITLE_BYSJ} , {SANE_STD_OPT_NAME_FILL_BKG_MODE , OPTION_TITLE_BJTCFS} , {SANE_STD_OPT_NAME_IS_ANTI_PERMEATE , OPTION_TITLE_FZST} , {SANE_STD_OPT_NAME_ANTI_PERMEATE_LEVEL , OPTION_TITLE_FZSTDJ} , {SANE_STD_OPT_NAME_RID_HOLE_L , OPTION_TITLE_CKYCZC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_L , OPTION_TITLE_ZCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_R , OPTION_TITLE_CKYCYC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_R , OPTION_TITLE_YCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_T , OPTION_TITLE_CKYCSC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_T , OPTION_TITLE_SCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_B , OPTION_TITLE_CKYCXC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_B , OPTION_TITLE_XCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_IS_FILL_COLOR , OPTION_TITLE_SCTC} , {SANE_STD_OPT_NAME_IS_ULTROSONIC_CHECK , OPTION_TITLE_CSBJC} , {SANE_STD_OPT_NAME_DOUBLE_FEED_HANDLE , OPTION_TITLE_SZTPCL} , {SANE_STD_OPT_NAME_IS_CHECK_STAPLE , OPTION_TITLE_ZDJC} , {SANE_STD_OPT_NAME_SCAN_MODE , OPTION_TITLE_SMZS} , {SANE_STD_OPT_NAME_SCAN_COUNT , OPTION_TITLE_SMSL} , {SANE_STD_OPT_NAME_TEXT_DIRECTION , OPTION_TITLE_WGFX} , {SANE_STD_OPT_NAME_IS_ROTATE_BKG_180 , OPTION_TITLE_BMXZ180} , {SANE_STD_OPT_NAME_IS_CHECK_DOG_EAR , OPTION_TITLE_ZJJC} , {SANE_STD_OPT_NAME_DOG_EAR_SIZE , OPTION_TITLE_ZJDX} , {SANE_STD_OPT_NAME_IS_CHECK_ASKEW , OPTION_TITLE_WXJC} , {SANE_STD_OPT_NAME_ASKEW_RANGE , OPTION_TITLE_WXRRD} , {SANE_STD_OPT_NAME_FEED_STRENGTH , OPTION_TITLE_FZQD} , {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , OPTION_TITLE_ZDFZQD} , {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , OPTION_TITLE_JZSBL} , {SANE_STD_OPT_NAME_WAIT_TO_SCAN , OPTION_TITLE_DZSM} , {SANE_STD_OPT_NAME_FOLD_TYPE , OPTION_TITLE_DZMS} }, g_discard[] = { {SANE_STD_OPT_NAME_REVERSE_01 , "\351\273\221\347\231\275\345\233\276\345\203\217\345\217\215\350\211\262\350\276\223\345\207\272\357\274\210\346\255\243\345\270\270\351\242\234\350\211\262\344\270\272\357\274\2320-\351\273\221\350\211\262\357\274\2331-\347\231\275\350\211\262\357\274\211"} // 黑白图像反色输出(正常颜色为:0-黑色;1-白色) , {SANE_STD_OPT_NAME_FILTER , "\347\201\260\345\272\246\346\210\226\351\273\221\347\231\275\345\233\276\345\203\217 - \351\231\244\350\211\262"} // 灰度或黑白图像 - 除色 , {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , "\350\207\252\345\212\250\346\220\223\347\272\270\345\274\272\345\272\246"} // 自动搓纸强度 , {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , "\346\220\223\347\272\270\351\230\210\345\200\274"} // " 搓纸阈值" }; while (*title == ' ') title++; for (size_t i = 0; i < _countof(g_opts); ++i) { if (strcmp(title, g_opts[i].title) == 0) return g_opts[i].name; } for (size_t i = 0; i < _countof(g_discard); ++i) { if (strcmp(title, g_discard[i].title) == 0) return g_discard[i].name; } return title; } public: dev_que() : handle_(nullptr) {} ~dev_que() { close_scanner(); for(auto& v : que_) v.cfg->release(); } static void update_old_cfg(const char* conf) { gb::scanner_cfg::UDF func; func.func_param = nullptr; func.title2name = &dev_que::title_2_name; func.trans_number = &dev_que::trans_number; gb::scanner_cfg::update(conf, &func); } static void apply_scheme(SANE_Handle h, gb::sane_config_schm* schm) { SANE_Int count = 0, none = 0; std::string name(""), val(""); none = sane_io_control(h, IO_CTRL_CODE_RESTORE_SETTINGS, NULL, NULL); if(schm && schm->id_from_name(SANE_STD_OPT_NAME_COLOR_MODE) == -1) { SANE_Int dev_options = 0; sane_control_option(h, 0, SANE_ACTION_GET_VALUE, &dev_options, nullptr); for(int i = 1; i < dev_options; ++i) { const SANE_Option_Descriptor* opt = sane_get_option_descriptor(h, i); if(!opt) continue; unsigned int n = i; if(opt->type == SANE_TYPE_BOOL) { SANE_Bool v = SANE_TRUE; sane_io_control(h, IO_CTRL_CODE_GET_DEFAULT_VALUE, &v, &n); schm->set_default_value(i, opt->name, (char*)&v, sizeof(v)); } else if (opt->type == SANE_TYPE_INT) { SANE_Int v = 0; sane_io_control(h, IO_CTRL_CODE_GET_DEFAULT_VALUE, &v, &n); schm->set_default_value(i, opt->name, (char*)&v, sizeof(v)); } else if(opt->type == SANE_TYPE_FIXED) { SANE_Fixed v = 0; sane_io_control(h, IO_CTRL_CODE_GET_DEFAULT_VALUE, &v, &n); schm->set_default_value(i, opt->name, (char*)&v, sizeof(v)); } else { char *buf = new char[opt->size + 4]; memset(buf, 0, opt->size + 4); sane_io_control(h, IO_CTRL_CODE_GET_DEFAULT_VALUE, buf, &n); std::string langCN(to_default_language(buf, nullptr)); schm->set_default_value(i, opt->name, &langCN[0], langCN.length()); delete[] buf; } } } if(schm && schm->first_config(name, val)) { do { int id = schm->id_from_name(name.c_str()); if(id == -1) { if(gb::sane_config_schm::is_option_data(name)) { if(name == SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA && val.length() == sizeof(SANE_Gamma)) { unsigned int l = val.length(); sane_io_control(h, IO_CTRL_CODE_SET_CUSTOM_GAMMA, &val[0], &l); } } } else { const SANE_Option_Descriptor* opt = reinterpret_cast(sane_get_option_descriptor(h, id)); if(opt) { if(opt->type == SANE_TYPE_STRING) { char *buf = new char[opt->size + 4]; memset(buf, 0, opt->size + 4); strcpy(buf, val.c_str()); std::string langCN(from_default_language(buf, nullptr)); sane_control_option(h, id, SANE_ACTION_SET_VALUE, &langCN[0], &none); delete[] buf; } else { sane_control_option(h, id, SANE_ACTION_SET_VALUE, &val[0], &none); } } } }while(schm->next_config(name, val)); } } public: void set_root_dir(const char* root) { root_dir_ = std::string(root) + PATH_SYMBOL; } void add_scanner(const char* sane_name) { bool found = false; for(auto& v: que_) { if(v.name == sane_name) { found = true; break; } } if(!found) { SCANNER s; size_t pos = 0; s.model = s.name = sane_name; s.cfg = nullptr; pos = s.model.find(" - "); if(pos != std::string::npos) { pos = s.model.find(" - ", pos + 3); if(pos != std::string::npos) s.model.erase(pos); } for(auto& v: que_) { if(v.model == s.model) { s.cfg = v.cfg; s.cfg->add_ref(); break; } } if(!s.cfg) { s.cfg = new gb::scanner_cfg(); s.cfg->load_file((root_dir_ + s.model + ".cfg").c_str()); } s.online = true; que_.push_back(s); } } void get_schemes(const char* scanner_name, std::vector& schemes) { schemes.clear(); for(auto& v : que_) { if(v.name == scanner_name) { v.cfg->get_all_schemes(schemes); break; } } } SANE_Handle handle(void) { return handle_; } std::string opened_scanner_name(void) { return opened_scanner_; } std::string applied_scheme(void) { return applied_scheme_; } int open_scanner(const char* scanner_name, const char* scheme = nullptr) { SANE_Status statu = SANE_STATUS_GOOD; close_scanner(); statu = sane_open(scanner_name, &handle_); if(statu == SANE_STATUS_GOOD && handle_) { opened_scanner_ = scanner_name; apply_scheme(scheme); } return statu; } int close_scanner(void) { if(handle_) { sane_close(handle_); handle_ = nullptr; opened_scanner_ = ""; applied_scheme_ = ""; } return SANE_STATUS_GOOD; } int scanners(void) { return que_.size(); } SCANNER get_at(int pos) { SCANNER s; s.name = s.model = ""; s.cfg = nullptr; if(pos >= 0 && pos < que_.size()) { s = que_[pos]; s.cfg->add_ref(); } return s; } bool is_online(const char* scanner = nullptr) { if(!scanner) scanner = opened_scanner_.c_str(); for(auto& v : que_) { if(v.name == scanner) return v.online; } return false; } void set_online(bool online, const char* scanner = nullptr) { if(!scanner) scanner = opened_scanner_.c_str(); for(auto& v : que_) { if(v.name == scanner) { v.online = online; break; } } } void get_scanners(std::vector& que) { for(auto& v: que_) que.push_back(v.name); } int apply_scheme(const char* scheme_name) { if(!handle_) return SCANNER_ERR_NOT_OPEN; for(auto& v: que_) { if(v.name == opened_scanner_) { gb::sane_config_schm* schm = v.cfg->get_scheme(scheme_name); dev_que::apply_scheme(handle_, schm); if(schm) { v.cfg->select_scheme(schm->get_scheme_name().c_str()); schm->release(); } else { v.cfg->select_scheme(scheme_name); } applied_scheme_ = v.cfg->get_current_scheme_name(); v.cfg->save(); break; } } return SCANNER_ERR_OK; } }; class device_menu : public QMenu { Q_OBJECT typedef struct _pop_menu { QMenu* menu; std::vector actions; bool operator==(const QString& menu_title) { return menu->title() == menu_title; } }POPMENU; std::vector menus_; QAction* cur_action_; QAction* none_action_; QActionGroup* group_action_; std::mutex mutex_; QMenu* find_device_menu(const QString& dev_name); QAction* find_device_config(const QString& dev_name, const QString& cfg_name); public: device_menu(QWidget* parent = nullptr); device_menu(const QString& title, QWidget* parent = nullptr); void deviceMenuUpdate(dev_que* que); void connectedDevice(const QString& device); void disconnectedDevice(const QString& device); void setOptionChecked(const QString& device, const QString& opt, bool checked); void get_online_devices(QList& dev_names); signals: void scanOptionsChanged(const QString& device, const QString& opt, bool checked_now); private slots: void on_act_triggered(QAction* act); }; #endif // DEVICE_MENU_H