diff --git a/device/win_usb/win_usb.cpp b/device/win_usb/win_usb.cpp index ee452e3..0501eeb 100644 --- a/device/win_usb/win_usb.cpp +++ b/device/win_usb/win_usb.cpp @@ -74,6 +74,20 @@ std::wstring ansi2unicode(const char* ansi, UINT cp = CP_ACP) return unic; } +std::string unicode2ansi(const wchar_t* unic, UINT cp = CP_ACP) +{ + int len = WideCharToMultiByte(cp, 0, unic, lstrlenW(unic), NULL, 0, NULL, NULL); + char *buf = new char[len + 4]; + + memset(buf, 0, len + 4); + len = WideCharToMultiByte(cp, 0, unic, lstrlenW(unic), buf, len, NULL, NULL); + buf[len--] = 0; + + std::string ansi(buf); + delete[] buf; + + return ansi; +} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // OVERLAPPED ... ovl_cls::ovl_cls(uint32_t type) : ref_(1), io_bytes_(0), type_(type) @@ -388,6 +402,16 @@ bool usb_device::init(void) COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, iSerialNumber); COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bNumConfigurations); } + PSTRING_DESCRIPTOR_NODE strdesc = info->StringDescs; + while (strdesc) + { + STRCFG sc; + sc.id = strdesc->DescriptorIndex; + sc.val = strdesc->StringDescriptor ? unicode2ansi(strdesc->StringDescriptor->bString) : ""; + str_cfg_.push_back(sc); + + strdesc = strdesc->Next; + } if (info->ConfigDesc) { libusb_config_descriptor* desc = NULL; @@ -470,6 +494,7 @@ void usb_device::clear(void) { close(); + str_cfg_.clear(); if (dev_desc_) delete dev_desc_; dev_desc_ = NULL; @@ -535,6 +560,23 @@ int usb_device::get_config_descriptor(int index, libusb_config_descriptor** desc return LIBUSB_SUCCESS; } +int usb_device::get_config_string(uint8_t desc_index, unsigned char* data, int length) +{ + for (auto& v : str_cfg_) + { + if (v.id == desc_index) + { + if (length < v.val.length()) + memcpy(data, v.val.c_str(), length); + else + strcpy((char*)data, v.val.c_str()); + + return LIBUSB_SUCCESS; + } + } + + return LIBUSB_ERROR_NOT_FOUND; +} int usb_device::open(libusb_device_handle** dev_handle) { if (handle_) @@ -679,7 +721,26 @@ int usb_device::transfer_bulk(unsigned endpoint, unsigned char* data, int* lengt ret = LIBUSB_SUCCESS; } else - ret = LIBUSB_ERROR_TIMEOUT; + { + if (!GetOverlappedResult(h, oc->over_lapped(), oc->io_bytes(), TRUE)) + { + utils::to_log(LOG_LEVEL_DEBUG, "Bulk-Transfer of endpoint 0x%02x failed Wait timeout and failed in GetOverlappedResult, set error TIMEOUT.\n", endpoint); + ret = LIBUSB_ERROR_TIMEOUT; + } + else + { + *length = *oc->io_bytes(); + if (*length == 0 && oc->over_lapped()->Internal != ERROR_SUCCESS) + { + ret = oc->over_lapped()->Internal == STATUS_CANCELLED ? /*LIBUSB_ERROR_TRY_AGAIN*/LIBUSB_ERROR_INTERRUPTED : oc->over_lapped()->Internal; + if (ret == ERROR_NO_MORE_ITEMS) + ret = LIBUSB_ERROR_TIMEOUT; + utils::to_log(LOG_LEVEL_WARNING, "Bulk-Transfer of endpoint 0x%02x failed with code 0x%08X\n", endpoint, oc->over_lapped()->Internal); + } + else + ret = LIBUSB_SUCCESS; + } + } } else { @@ -815,11 +876,11 @@ UINT usb_monitor::find_usb_timer_ = 101; usb_monitor::usb_monitor() : wnd_monitor_(NULL), handle_msg_id_(0), run_(true) { #ifdef USE_SAFE_THREAD - auto tf = [this](void*) -> void + auto tf = [this](void) -> void { thread_handle_device_change_msg(); }; - handle_msg_.start(tf, nullptr, "usb_monitor::thread_handle_device_change_msg"); + handle_msg_.start(tf, "usb_monitor::thread_handle_device_change_msg"); #else handle_msg_.reset(new std::thread(&usb_monitor::thread_handle_device_change_msg, this)); #endif @@ -1220,7 +1281,7 @@ void usb_monitor::thread_handle_device_change_msg(void) else if(handled) *handled = false; - return true; + return run_; }; handle_msg_id_ = GetCurrentThreadId(); @@ -1230,6 +1291,7 @@ void usb_monitor::quit(void) { KillTimer(wnd_monitor_, usb_monitor::find_usb_timer_); run_ = false; + PostThreadMessageW(handle_msg_id_, WM_QUIT, 0, 0); if (IsWindow(wnd_monitor_)) { PostMessage(wnd_monitor_, WM_QUIT, 0, 0); @@ -1239,7 +1301,6 @@ void usb_monitor::quit(void) #ifndef USE_SAFE_THREAD if (handle_msg_.get()) { - PostThreadMessageW(handle_msg_id_, WM_QUIT, 0, 0); if (handle_msg_->joinable()) handle_msg_->join(); handle_msg_.reset(); @@ -1505,3 +1566,8 @@ int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer* transfer/*in wind { return ((usb_device*)transfer)->cancel_io(); } +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle* dev_handle, + uint8_t desc_index, unsigned char* data, int length) +{ + return ((usb_device*)dev_handle)->get_config_string(desc_index, data, length); +} diff --git a/device/win_usb/win_usb.h b/device/win_usb/win_usb.h index 76ac787..3b6e416 100644 --- a/device/win_usb/win_usb.h +++ b/device/win_usb/win_usb.h @@ -106,6 +106,13 @@ class usb_device // consider as libusb_device libusb_device_descriptor *dev_desc_; std::vector cfg_desc_; + typedef struct _str_cfg + { + int id; + std::string val; + }STRCFG; + std::vector str_cfg_; + typedef struct _usb_pipe { UCHAR address; @@ -151,6 +158,7 @@ public: void clear(void); int get_descriptor(libusb_device_descriptor* desc); int get_config_descriptor(int index, libusb_config_descriptor** desc); + int get_config_string(uint8_t desc_index, unsigned char* data, int length); int open(libusb_device_handle** dev_handle); int close(void); int set_timeout(unsigned milliseconds); diff --git a/sln/hgscanner.sln b/sln/hgscanner.sln index 8d1268d..83dff2f 100644 --- a/sln/hgscanner.sln +++ b/sln/hgscanner.sln @@ -26,6 +26,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{53E5EDD9 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hgjson", "..\..\doc_and_tools\tools\apps\hgjson\hgjson.vcxproj", "{D3579C48-F5AB-4F15-9B49-A2970FBA76C5}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usb_tools", "usb_tools\usb_tools.vcxproj", "{4C912767-88BE-463E-BFFC-AF994A92E425}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_TWAIN_App", "..\..\doc_and_tools\tools\apps\test_TWAIN_App\test_TWAIN_App.vcxproj", "{8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -72,6 +76,22 @@ Global {D3579C48-F5AB-4F15-9B49-A2970FBA76C5}.Release|x64.ActiveCfg = Release|Win32 {D3579C48-F5AB-4F15-9B49-A2970FBA76C5}.Release|x86.ActiveCfg = Release|Win32 {D3579C48-F5AB-4F15-9B49-A2970FBA76C5}.Release|x86.Build.0 = Release|Win32 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Debug|x64.ActiveCfg = Debug|x64 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Debug|x64.Build.0 = Debug|x64 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Debug|x86.ActiveCfg = Debug|Win32 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Debug|x86.Build.0 = Debug|Win32 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Release|x64.ActiveCfg = Release|x64 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Release|x64.Build.0 = Release|x64 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Release|x86.ActiveCfg = Release|Win32 + {4C912767-88BE-463E-BFFC-AF994A92E425}.Release|x86.Build.0 = Release|Win32 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Debug|x64.ActiveCfg = Debug|x64 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Debug|x64.Build.0 = Debug|x64 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Debug|x86.ActiveCfg = Debug|Win32 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Debug|x86.Build.0 = Debug|Win32 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Release|x64.ActiveCfg = Release|x64 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Release|x64.Build.0 = Release|x64 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Release|x86.ActiveCfg = Release|Win32 + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -82,6 +102,8 @@ Global {C3B47CE2-27CE-4509-AB59-3C0F194F0FCE} = {C43532EF-1B31-457F-B40B-61F22E91A13E} {7776AB6D-6296-4F7A-A6ED-E9A4D6290DD9} = {C43532EF-1B31-457F-B40B-61F22E91A13E} {D3579C48-F5AB-4F15-9B49-A2970FBA76C5} = {53E5EDD9-13BA-4F3C-A387-C73CB06C686D} + {4C912767-88BE-463E-BFFC-AF994A92E425} = {53E5EDD9-13BA-4F3C-A387-C73CB06C686D} + {8C3DEF78-4DF1-4C26-A11E-7031FF773E7D} = {53E5EDD9-13BA-4F3C-A387-C73CB06C686D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A89068FF-95C4-3C1E-B126-70B66C9824BB} diff --git a/sln/usb_tools/DlgScanner.cpp b/sln/usb_tools/DlgScanner.cpp new file mode 100644 index 0000000..07197da --- /dev/null +++ b/sln/usb_tools/DlgScanner.cpp @@ -0,0 +1,1484 @@ +// DlgScanner.cpp: 实现文件 +// + +#include "pch.h" +#include "usb_tools.h" +#include "DlgScanner.h" +#include "afxdialogex.h" + +#include +#include +#include +#include + +#define TIMER_ID_REFRESH_BULK 1001 + +HMODULE g_my_inst; + +namespace sane +{ +#define ALIGN_INTEGER(v) ALIGN_INT(v, sizeof(int)) + + static std::vector g_opts; + SANE_Option_Descriptor g_opt0; + + namespace local_utility + { + void* acquire_memory(size_t bytes, const char* msg) + { + char* buf = new char[bytes]; + + memset(buf, 0, bytes); + + return buf; + } + } + + SANE_Option_Descriptor* string_option_to_SANE_descriptor(const char* name, const char* title, const char* desc + , const std::vector& values) + { + int bytes = sizeof(SANE_Option_Descriptor) + sizeof(char*); + SANE_Option_Descriptor* sod = NULL; + char* str = NULL, ** str_arr = NULL; + + bytes += ALIGN_INTEGER(strlen(name) + 1); + bytes += ALIGN_INTEGER(strlen(title) + 1); + bytes += ALIGN_INTEGER(strlen(desc) + 1); + bytes += sizeof(SANE_Option_Descriptor); + bytes += sizeof(char*); + for (size_t i = 0; i < values.size(); ++i) + bytes += ALIGN_INTEGER(values[i].length() + 1); + bytes += sizeof(char*) * (values.size() + 1); + sod = (SANE_Option_Descriptor*)local_utility::acquire_memory(bytes, "string_option_to_SANE_descriptor"); + bzero(sod, bytes); + str = (char*)sod; + str += sizeof(SANE_Option_Descriptor); + + sod->name = str; + strcpy(str, name); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->title = str; + strcpy(str, title); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->desc = str; + strcpy(str, desc); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->type = SANE_TYPE_STRING; + sod->unit = SANE_UNIT_NONE; + sod->size = values.size(); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT // 软件可设置选项 + | SANE_CAP_AUTOMATIC; // 硬件可设置默认�? + if (values.size()) + { + sod->constraint_type = SANE_CONSTRAINT_STRING_LIST; + sod->constraint.string_list = (char**)str; + str_arr = (char**)str; + str += (values.size() + 1) * sizeof(char*); + for (size_t i = 0; i < values.size(); ++i) + { + str_arr[i] = str; + strcpy(str, values[i].c_str()); + + str += ALIGN_INTEGER(values[i].length() + 1); + } + } + + //VLOG_MINI_2(LOG_LEVEL_ALL, "Memory usage: %u/%u\n", str - (char*)sod, bytes); + + return sod; + } + SANE_Option_Descriptor* number_option_to_SANE_descriptor(const char* name, const char* title, const char* desc + , bool double_val, double* lower, double* upper, double* step) + { + int bytes = sizeof(SANE_Option_Descriptor) + sizeof(SANE_Range); + SANE_Option_Descriptor* sod = NULL; + char* str = NULL; + + bytes += ALIGN_INTEGER(strlen(name) + 1); + bytes += ALIGN_INTEGER(strlen(title) + 1); + bytes += ALIGN_INTEGER(strlen(desc) + 1); + bytes += sizeof(SANE_Option_Descriptor); + bytes += sizeof(SANE_Range*) + sizeof(SANE_Range); + sod = (SANE_Option_Descriptor*)local_utility::acquire_memory(bytes, "number_option_to_SANE_descriptor"); + bzero(sod, bytes); + str = (char*)sod; + str += sizeof(SANE_Option_Descriptor); + + sod->name = str; + strcpy(str, name); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->title = str; + strcpy(str, title); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->desc = str; + strcpy(str, desc); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->type = double_val ? SANE_TYPE_FIXED : SANE_TYPE_INT; + sod->unit = SANE_UNIT_NONE; + sod->size = sizeof(SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT + | SANE_CAP_AUTOMATIC; + if (lower || upper) + { + sod->size = sizeof(SANE_Range); + sod->constraint_type = SANE_CONSTRAINT_RANGE; + sod->constraint.range = (SANE_Range*)str; + if (lower) + { + if (double_val) + (*(SANE_Range*)str).min = SANE_FIX(*lower); + else + (*(SANE_Range*)str).min = (SANE_Word)*lower; + } + if (upper) + { + if (double_val) + (*(SANE_Range*)str).max = SANE_FIX(*upper); + else + (*(SANE_Range*)str).max = (SANE_Word)*upper; + } + (*(SANE_Range*)str).quant = 0; + if (step) + { + if (double_val) + (*(SANE_Range*)str).quant = SANE_FIX(*step); + else + (*(SANE_Range*)str).quant = (SANE_Word)(*step); + } + + str = (char*)((SANE_Range*)str + 1); + } + //VLOG_MINI_2(LOG_LEVEL_ALL, "Memory usage: %u/%u\n", str - (char*)sod, bytes); + + return sod; + } + SANE_Option_Descriptor* number_option_to_SANE_descriptor(const char* name, const char* title, const char* desc + , const std::vector& values) + { + int bytes = sizeof(SANE_Option_Descriptor) + sizeof(SANE_Range); + SANE_Option_Descriptor* sod = NULL; + char* str = NULL; + + bytes += ALIGN_INTEGER(strlen(name) + 1); + bytes += ALIGN_INTEGER(strlen(title) + 1); + bytes += ALIGN_INTEGER(strlen(desc) + 1); + bytes += sizeof(SANE_Option_Descriptor); + bytes += sizeof(SANE_Word*) + sizeof(SANE_Word) * (values.size() + 1); + sod = (SANE_Option_Descriptor*)local_utility::acquire_memory(bytes, "number_option_to_SANE_descriptor"); + bzero(sod, bytes); + str = (char*)sod; + str += sizeof(SANE_Option_Descriptor); + + sod->name = str; + strcpy(str, name); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->title = str; + strcpy(str, title); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->desc = str; + strcpy(str, desc); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->type = SANE_TYPE_INT; + sod->unit = SANE_UNIT_NONE; + sod->size = sizeof(SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT // 软件可设置选项 + | SANE_CAP_AUTOMATIC; // 硬件可设置默认�? + + if (values.size()) + { + SANE_Word* val = (SANE_Word*)str; + sod->constraint.word_list = val; + sod->constraint_type = SANE_CONSTRAINT_WORD_LIST; + *val++ = values.size(); + for (size_t i = 0; i < values.size(); ++i) + val[i] = values[i]; + + str = (char*)(val + values.size()); + } + //VLOG_MINI_2(LOG_LEVEL_ALL, "Memory usage: %u/%u\n", str - (char*)sod, bytes); + + return sod; + } + SANE_Option_Descriptor* number_option_to_SANE_descriptor(const char* name, const char* title, const char* desc + , const std::vector& values) + { + int bytes = sizeof(SANE_Option_Descriptor) + sizeof(SANE_Range); + SANE_Option_Descriptor* sod = NULL; + char* str = NULL; + + bytes += ALIGN_INTEGER(strlen(name) + 1); + bytes += ALIGN_INTEGER(strlen(title) + 1); + bytes += ALIGN_INTEGER(strlen(desc) + 1); + bytes += sizeof(SANE_Option_Descriptor); + bytes += sizeof(SANE_Word*) + sizeof(SANE_Word) * (values.size() + 1); + sod = (SANE_Option_Descriptor*)local_utility::acquire_memory(bytes, "number_option_to_SANE_descriptor"); + bzero(sod, bytes); + str = (char*)sod; + str += sizeof(SANE_Option_Descriptor); + + sod->name = str; + strcpy(str, name); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->title = str; + strcpy(str, title); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->desc = str; + strcpy(str, desc); + str += ALIGN_INTEGER(strlen(str) + 1); + + sod->type = SANE_TYPE_FIXED; + sod->unit = SANE_UNIT_NONE; + sod->size = sizeof(SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT // 软件可设置选项 + | SANE_CAP_AUTOMATIC; // 硬件可设置默认�? + + if (values.size()) + { + SANE_Word* val = (SANE_Word*)str; + sod->constraint.word_list = val; + sod->constraint_type = SANE_CONSTRAINT_WORD_LIST; + *val++ = values.size(); + for (size_t i = 0; i < values.size(); ++i) + val[i] = SANE_FIX(values[i]); + + str = (char*)(val + values.size()); + } + //VLOG_MINI_2(LOG_LEVEL_ALL, "Memory usage: %u/%u\n", str - (char*)sod, bytes); + + return sod; + } + SANE_Option_Descriptor* trans_json_to_opt_desc(gb_json* jsn) + { + std::string title(""), + desc(""), + name(""), + val(""); + std::vector constraints; + double lower = .0f, upper = .0f, step = .0f; + bool db_val = false; + SANE_Option_Descriptor *ret = NULL; + + jsn->get_value("title", title); + jsn->get_value("desc", desc); + name = jsn->key(); + if (!jsn->get_value("type", val)) + return NULL; + + if (val == "string") + { + gb_json* range = NULL, * child = NULL; + + jsn->get_value("range", range); + if (range) + { + child = range->first_child(); + while (child) + { + if (child->value(val)) + constraints.push_back(val); + child->release(); + child = range->next_child(); + } + range->release(); + } + + ret = string_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , constraints); + } + else if (val == "int" || val == "float") + { + gb_json* range = NULL; + + jsn->get_value("range", range); + if (range) + { + if (val == "int") + { + int l = 0; + if (range->get_value("min", l)) + { + int u = 0, s = 1; + range->get_value("max", u); + range->get_value("step", s); + lower = l; + upper = u; + step = s; + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , false, &lower, &upper, &step); + } + else + { + std::vector constraints; + gb_json* child = range->first_child(); + + while(child) + { + int val = 0; + if(child->value(val)) + constraints.push_back(val); + child->release(); + child = range->next_child(); + } + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , constraints); + } + } + else + { + if (range->get_value("min", lower)) + { + range->get_value("max", upper); + step = (upper - lower) / 10.0f; + range->get_value("step", step); + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , true, &lower, &upper, &step); + } + else + { + std::vector constraints; + gb_json* child = range->first_child(); + + while(child) + { + double val = .0f; + if(child->value(val)) + constraints.push_back(val); + child->release(); + child = range->next_child(); + } + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , constraints); + } + } + range->release(); + } + else + { + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , false, NULL, NULL, NULL); + } + } + else if (val == "bool") + { + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , false, NULL, NULL, NULL); + ret->type = SANE_TYPE_BOOL; + } + else if (val == "button") + { + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , false, NULL, NULL, NULL); + ret->type = SANE_TYPE_BUTTON; + } + else if (val == "group") + { + ret = number_option_to_SANE_descriptor(name.c_str(), title.c_str(), desc.c_str() + , false, NULL, NULL, NULL); + ret->type = SANE_TYPE_GROUP; + } + + // fill the 'size' field, for SANE_ACTION_GET action ... + if (ret) + { + int bytes = 0; + bool bv = false; + + jsn->get_value("size", bytes); + ret->size = bytes; + + if (jsn->get_value("readonly", bv) && bv) + SET_CAP_READONLY(ret->cap) + else if (jsn->get_value("hwonly", bv) && bv) + SET_CAP_DEVICE_SETTABLE(ret->cap, true) + + val = ""; + jsn->get_value("group", val); + if (val == "advance") + { + ret->cap |= SANE_CAP_ADVANCED; + } + + val = ""; + if (jsn->get_value("unit", val)) + { + if (val == "DPI") + ret->unit = SANE_UNIT_DPI; + else if (val == "pixel") + ret->unit = SANE_UNIT_PIXEL; + else if (val == "mm") + ret->unit = SANE_UNIT_MM; + else if (val == "bit") + ret->unit = SANE_UNIT_BIT; + else if (val == "microsec") + ret->unit = SANE_UNIT_MICROSECOND; + else if (val == "%") + ret->unit = SANE_UNIT_PERCENT; + else + ret->unit = SANE_UNIT_NONE; + } + + bool enabled = true; + if (jsn->get_value("enabled", enabled) && !enabled) + ret->cap |= SANE_CAP_INACTIVE; + + // 关联�? + gb_json* depend = NULL; + } + + return ret; + } + + void reset_opts(const char* json_txt) + { + for (auto& v : g_opts) + delete[] v; + g_opts.clear(); + + g_opt0.cap = CAPABILITY_READONLY; + g_opt0.name = "option-count"; + g_opt0.title = ""; + g_opt0.desc = "Number of options"; + g_opt0.type = SANE_TYPE_INT; + g_opt0.size = sizeof(SANE_TYPE_INT); + + if (json_txt) + { + gb_json* jsn = new gb_json(), * child = NULL; + if (jsn->attach_text((char*)json_txt)) + { + child = jsn->first_child(); + if (child) + { + child->release(); + while ((child = jsn->next_child())) + { + SANE_Option_Descriptor* desc = trans_json_to_opt_desc(child); + if (desc) + g_opts.push_back(desc); + child->release(); + } + } + } + jsn->release(); + } + } + + SANE_Status sane_get_devices_api(const SANE_Device*** device_list, SANE_Bool local_only) + { + return SANE_STATUS_INVAL; + } + SANE_Status sane_open_api(SANE_String_Const devicename, SANE_Handle* handle) + { + return SANE_STATUS_INVAL; + } + void sane_close_api(SANE_Handle handle) + {} + const SANE_Option_Descriptor* sane_get_option_descriptor_api(SANE_Handle handle, SANE_Int option) + { + if (option == 0) + return &g_opt0; + else if (option <= g_opts.size()) + return g_opts[option - 1]; + else + return NULL; + } + SANE_Status sane_control_option_api(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info) + { + CDlgScanner* dlg = (CDlgScanner*)handle; + + if (action == SANE_ACTION_GET_VALUE && option > 0 && option <= g_opts.size()) + { + if (g_opts[option - 1]->type == SANE_TYPE_FIXED) + { + double val = .0f; + dlg->get_option(g_opts[option - 1]->name, &val, sizeof(val)); + *(SANE_Fixed*)value = SANE_FIX(val); + } + else + { + dlg->get_option(g_opts[option - 1]->name, value, g_opts[option - 1]->size); + } + + return SANE_STATUS_GOOD; + } + else if(action == SANE_ACTION_SET_VALUE && option > 0 && option <= g_opts.size()) + { + size_t val_size = 0; + double val = .0f; + void* buf = value; + + if (g_opts[option - 1]->type == SANE_TYPE_BOOL) + val_size = sizeof(bool); + else if (g_opts[option - 1]->type == SANE_TYPE_INT) + val_size = sizeof(int); + else if (g_opts[option - 1]->type == SANE_TYPE_FIXED) + { + val_size = sizeof(val); + val = SANE_UNFIX(*(SANE_Int*)value); + buf = &val; + } + else + val_size = strlen((char*)value); + + SANE_Int after = 0; + SANE_Status ret = (SANE_Status)dlg->set_option(g_opts[option - 1]->name, buf, g_opts[option - 1]->type, val_size, g_opts[option - 1]->size, (int*)&after); + + if (info) + *info = after; + + if (buf == &val) + { + *(SANE_Int*)value = SANE_FIX(val); + } + + if (after & SANE_INFO_RELOAD_OPTIONS) + { + std::string str(""); + if (dlg->get_all_option(str) == 0) + { + reset_opts(str.c_str()); + } + } + + return ret; + } + + return SANE_STATUS_INVAL; + } + SANE_Status sane_get_parameters_api(SANE_Handle handle, SANE_Parameters* params) + { + return SANE_STATUS_INVAL; + } + SANE_Status sane_start_api(SANE_Handle handle) + { + return SANE_STATUS_INVAL; + } + SANE_Status sane_read_api(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length) + { + return SANE_STATUS_INVAL; + } + void sane_cancel_api(SANE_Handle handle) + {} + SANE_Status sane_set_io_mode_api(SANE_Handle handle, SANE_Bool non_blocking) + { + return SANE_STATUS_INVAL; + } + SANE_Status sane_get_select_fd_api(SANE_Handle handle, SANE_Int* fd) + { + return SANE_STATUS_INVAL; + } + SANE_String_Const sane_strstatus_api(SANE_Status status) + { + return ""; + } + SANE_Status sane_io_control_api(SANE_Handle h, unsigned long code, void* data, unsigned* len) + { + return SANE_STATUS_INVAL; + } + SANE_Status sane_init_api(SANE_Int* version_code, SANE_Auth_Callback authorize) + { + return SANE_STATUS_INVAL; + } + void sane_exit_api(void) + {} +}; + +#define RETURN_EQUAL(v, e) \ + if(v == e) \ + return L###e; + +template +static int msg_box(HWND owner, UINT type, const wchar_t* title, const wchar_t* fmt, Args ... args) +{ + size_t size = _snwprintf(nullptr, 0, fmt, args ...) + 1; + std::unique_ptr buf(new wchar_t[size]); + + _snwprintf(buf.get(), size, fmt, args ...); + + return ::MessageBoxW(owner, buf.get(), title, type); +} +const wchar_t* peer_bulk_status(int s, wchar_t unk[20]) +{ + RETURN_EQUAL(s, WORKER_STATUS_NOT_START); + RETURN_EQUAL(s, WORKER_STATUS_IDLE); + RETURN_EQUAL(s, WORKER_STATUS_BUSY); + RETURN_EQUAL(s, WORKER_STATUS_ERROR); + RETURN_EQUAL(s, WORKER_STATUS_RESET); + RETURN_EQUAL(s, WORKER_STATUS_WAIT_RESOURCE); + + swprintf_s(unk, 18, L"%x", s); + + return unk; +} +const wchar_t* scanner_status(int s, wchar_t unk[20]) +{ + RETURN_EQUAL(s, SCANNER_STATUS_READY); + RETURN_EQUAL(s, SCANNER_STATUS_NOT_OPEN); + RETURN_EQUAL(s, SCANNER_STATUS_LOST_CONNECT); + RETURN_EQUAL(s, SCANNER_STATUS_RESET_BULK); + RETURN_EQUAL(s, SCANNER_STATUS_START_SCANNING); + RETURN_EQUAL(s, SCANNER_STATUS_SCANNING); + RETURN_EQUAL(s, SCANNER_STATUS_SCAN_FINISHED); + RETURN_EQUAL(s, SCANNER_STATUS_BUSY); + RETURN_EQUAL(s, SCANNER_STATUS_COVER_OPENNED); + RETURN_EQUAL(s, SCANNER_STATUS_COVER_CLOSED); + RETURN_EQUAL(s, SCANNER_STATUS_SLEEPING); + RETURN_EQUAL(s, SCANNER_STATUS_WAKED_UP); + RETURN_EQUAL(s, SCANNER_STATUS_COUNT_MODE); + RETURN_EQUAL(s, SCANNER_STATUS_DOUBLE_FEEDED); + RETURN_EQUAL(s, SCANNER_STATUS_PAPER_JAMMED); + RETURN_EQUAL(s, SCANNER_STATUS_PAPER_ASKEW); + RETURN_EQUAL(s, SCANNER_STATUS_FEED_FAILED); + RETURN_EQUAL(s, SCANNER_STATUS_NO_PAPER); + RETURN_EQUAL(s, SCANNER_STATUS_PAPER_ON); + RETURN_EQUAL(s, SCANNER_STATUS_STAPLE_ON); + RETURN_EQUAL(s, SCANNER_STATUS_SIZE_ERR); + RETURN_EQUAL(s, SCANNER_STATUS_DOGEAR); + RETURN_EQUAL(s, SCANNER_STATUS_CFG_CHANGED); + + swprintf_s(unk, 18, L"0x%x", s); + + return unk; +} + +static DWORD thread_open_id_ = 0; +static safe_fifo images_("images"); +static DWORD WINAPI thread_open_image(void* lp) +{ + while (1) + { + std::string file(""); + if (images_.take(file, true)) + { + if (file.empty()) + break; + + ShellExecuteA(NULL, "Open", file.c_str(), NULL, NULL, SW_SHOWNORMAL); + } + } + return 0; + + MSG msg = { 0 }; + BOOL ret = TRUE; + + while ((ret = GetMessage(&msg, NULL, 0, 0))) + { + if ((DWORD)ret == -1) + break; + + if (msg.message == WM_USER + 1001) + break; + + if (msg.message == WM_USER + 1) + { + std::string* file = (std::string*)msg.lParam; + + ShellExecuteA(NULL, "Open", file->c_str(), NULL, NULL, SW_SHOWNORMAL); + delete file; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} + +// CDlgScanner 对话框 + +IMPLEMENT_DYNAMIC(CDlgScanner, CDialogEx) + +CDlgScanner::CDlgScanner(CWnd* pParent /*=nullptr*/) + : CDialogEx(IDD_SCANNER, pParent) + , scanner_(NULL), auto_tx_file_(-1), auto_tx_(false) + , setting_ui_(NULL), img_cnt_(0), paper_cnt_(0), max_sent_(0), max_cmd_(0) +{ + g_my_inst = GetModuleHandle(NULL); + + parent_ = pParent ? pParent->m_hWnd : NULL; + auto_wait_ = CreateEvent(NULL, TRUE, FALSE, NULL); + + HANDLE h = CreateThread(NULL, 0, thread_open_image, NULL, 0, &thread_open_id_); + CloseHandle(h); + { + } +} + +CDlgScanner::~CDlgScanner() +{ + ::PostThreadMessage(thread_open_id_, WM_USER + 1001, 0, 0); + images_.save("", true); + if (scanner_) + { + scanner_->close(); + scanner_->release(); + } +} + +void CDlgScanner::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + // DDX_Control(pDX, IDC_TAB_OPT, tab_opt_); + DDX_Control(pDX, IDC_TAB_OPER, tab_oper_); + DDX_Control(pDX, IDC_COMBO_BUF_SIZE, buf_); +} + +typedef struct _prog_data +{ + CDlgScanner* obj; + std::string file; +}PROGD, *LPPROGD; +void CDlgScanner::set_device(ONLNSCANNER* pnp) +{ + if (!pnp) + { + auto_tx_ = false; + SetEvent(auto_wait_); + } + ((CButton*)GetDlgItem(IDC_CHECK_REPEAT))->SetCheck(BST_UNCHECKED); + + KillTimer(TIMER_ID_REFRESH_BULK); + ((CButton*)GetDlgItem(IDC_CHECK_AUTO))->SetCheck(BST_UNCHECKED); + enable_buttons(pnp != NULL); + + if (scanner_) + { + scanner_->close(); + scanner_->release(); + scanner_ = NULL; + } + + if (setting_ui_) + { + delete setting_ui_; + setting_ui_ = NULL; + } + + max_cmd_ = max_sent_ = 0; + buf_.SetCurSel(0); + buf_.EnableWindow(pnp != NULL); + if (pnp) + { + int err = 0; + + OnDeviceStatus(0, (LPARAM)SCANNER_STATUS_READY); + scanner_ = new hg_scanner(pnp); + err = scanner_->status(); + if (err) + { + scanner_->release(); + scanner_ = NULL; + msg_box(m_hWnd, MB_OK, L"Error", L"Open %04X:%04X failed with error %d.", pnp->vid, pnp->pid, err); + enable_buttons(false); + buf_.EnableWindow(FALSE); + OnDeviceStatus(0, (LPARAM)SCANNER_STATUS_NOT_OPEN); + } + else + { + wchar_t buf[128] = { 0 }; + uint16_t ver = PROTOCOL_VER; + + swprintf_s(buf, _countof(buf) - 1, L"%04X:%04X", pnp->vid, pnp->pid); + ::SetWindowTextW(m_hWnd, buf); + + //err = scanner_->get_protocol_version(&ver); + if (err || ver != PROTOCOL_VER) + { + if(err) + msg_box(m_hWnd, MB_OK, L"Unsupported Scanner", L"Failed to get protocol version with error %d.", err); + else + msg_box(m_hWnd, MB_OK, L"Unsupported Scanner", L"Protocol version is mismatch: expect %u.%u but return %u.%u", HIBYTE(PROTOCOL_VER), LOBYTE(PROTOCOL_VER), HIBYTE(ver), LOBYTE(ver)); + if (scanner_) + { + scanner_->release(); + scanner_ = NULL; + } + enable_buttons(false); + buf_.EnableWindow(FALSE); + OnDeviceStatus(0, (LPARAM)SCANNER_STATUS_NOT_OPEN); + } + else + { + swprintf_s(buf, _countof(buf) - 1, L"%u.%u", HIBYTE(ver), LOBYTE(ver)); + SetDlgItemTextW(IDC_EDIT_PROTOCOL_VER, buf); + + refresh_bulk_status(); + //SetTimer(TIMER_ID_REFRESH_BULK, 1000, NULL); + RECT r = { 0 }; + std::string all(""); + + if (get_all_option(all) == ETIMEDOUT) + { + // reset it ... + msg_box(m_hWnd, MB_OK, L"Bulk Trouble", L"Bulk communication TIMEOUTED, will try reset bulk ..."); + refresh_bulk_status(); + get_all_option(all); + } + sane::reset_opts(all.c_str()); + + GetDlgItem(IDC_STATIC_OPTS)->GetWindowRect(&r); + ScreenToClient(&r); + //ClientToScreen(&r); + setting_ui_ = new dlg_setting(m_hWnd, &sane_api_, (SANE_Handle)this, &r); + setting_ui_->show(true); + } + } + } + else + { + OnDeviceStatus(0, (LPARAM)SCANNER_STATUS_LOST_CONNECT); + ::SetDlgItemTextW(m_hWnd, IDC_BUTTON_SCAN, L"Scan"); + } + GetDlgItem(IDC_CHECK_AUTO)->EnableWindow(scanner_ != NULL); + GetDlgItem(IDC_BUTTON_RESET_BULK)->EnableWindow(scanner_ != NULL); + GetDlgItem(IDC_BUTTON_REFRESH)->EnableWindow(scanner_ != NULL); +} +bool CDlgScanner::is_online(void) +{ + return scanner_ != NULL; +} +void CDlgScanner::get_option(const char* name, void* value, size_t size) +{ + size_t len = 0; + char* cont = scanner_->get_value(name, value, &len); + + if (cont) + { + memcpy(value, cont, len); + free(cont); + } +} +int CDlgScanner::get_all_option(std::string& opts_json) +{ + device_option* opt = scanner_->get_device_opt(); + if (opt) + { + opts_json = opt->get_option_value(NULL, SANE_ACTION_GET_ENTIRE_JSON); + opt->release(); + } + + return 0; +} +int CDlgScanner::set_option(const char* name, void* value, int type, size_t len, size_t max_len, int* after) +{ + return scanner_->set_value(name, value); +} + +int CDlgScanner::refresh_bulk_status(void) +{ + EP0REPLYSTATUS s = { 0 }; + wchar_t buf[128] = { 0 }; + int err = scanner_->status(&s); + + if (err == 0) + { + SetDlgItemTextW(IDC_EDIT_BUILK_IN, peer_bulk_status(s.in_status, buf)); + if (s.in_status == WORKER_STATUS_BUSY) + { + swprintf_s(buf, _countof(buf) - 1, L"WORKER_STATUS_BUSY(Want: %x)", s.bytes_to_sent); + SetDlgItemTextW(IDC_EDIT_BUILK_IN, buf); + } + SetDlgItemTextW(IDC_EDIT_BULK_OUT, peer_bulk_status(s.out_status, buf)); + if (s.out_status == WORKER_STATUS_BUSY) + { + swprintf_s(buf, _countof(buf) - 1, L"WORKER_STATUS_BUSY(Need: %x)", s.task_required_bytes); + SetDlgItemTextW(IDC_EDIT_BULK_OUT, buf); + } + + if (max_sent_ < s.packets_to_sent) + max_sent_ = s.packets_to_sent; + if (max_cmd_ < s.task_cnt) + max_cmd_ = s.task_cnt; + + swprintf_s(buf, _countof(buf) - 1, L"%u (max: %u)", s.packets_to_sent, max_sent_); + SetDlgItemTextW(IDC_EDIT_SENT_QUE, buf); + + swprintf_s(buf, _countof(buf) - 1, L"%u (max: %u)", s.task_cnt, max_cmd_); + SetDlgItemTextW(IDC_EDIT_CMD_QUE, buf); + } + + return err; +} +void CDlgScanner::thread_auto_tx_file(void) +{ + char loc[256] = { 0 }, + remt[256] = { 0 }; + uint32_t ind = 0, err = 0; + std::string file(""), prev(""), ext(""); + std::wstring oper(L""); + chronograph watch; + + ::GetDlgItemTextA(m_hWnd, IDC_EDIT_LOCAL, loc, _countof(loc) - 1); + ::GetDlgItemTextA(m_hWnd, IDC_EDIT_REMOTE, remt, _countof(remt) - 1); + prev = loc; + ind = prev.rfind('.'); + ext = ")" + prev.substr(ind); + prev.erase(ind); + file = loc; + prev += " ("; + + ind = file.rfind('\\'); + file.erase(ind + 1); + file += "tx"; + mkdir(file.c_str()); + file = loc; + prev.insert(ind, "\\tx"); + + ind = 0; + while (auto_tx_) + { + double *prog = NULL, prevg = .0f; + ResetEvent(auto_wait_); + if (auto_tx_ & FILE_TX_SEND) + { + oper = L"Send file"; + err = scanner_->file_transfer(file.c_str(), remt, true); + if (err) + break; + + watch.reset(); + while ((prog = (double*)scanner_->get_value("tx-prog", nullptr, nullptr, (int*)&err))) + { + double val = *prog; + free(prog); + if (IS_DOUBLE_EQUAL(val, prevg)) + { + if (IS_DOUBLE_EQUAL(val, 1.0f)) + break; + if (watch.elapse_s() >= 120) + { + err = ETIMEDOUT; + break; + } + } + else + { + prevg = val; + watch.reset(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + if (err) + break; + if (!auto_tx_) + break; + + Sleep(1000); + ResetEvent(auto_wait_); + } + + if (auto_tx_ & FILE_TX_RECEIVE) + { + file = prev + std::to_string(++ind) + ext; + oper = L"Receive file"; + err = scanner_->file_transfer(file.c_str(), remt, false); + if (err) + break; + watch.reset(); + prevg = .0f; + while ((prog = (double*)scanner_->get_value("tx-prog", nullptr, nullptr, (int*)&err))) + { + double val = *prog; + free(prog); + if (IS_DOUBLE_EQUAL(val, prevg)) + { + if (IS_DOUBLE_EQUAL(val, 1.0f)) + break; + if (watch.elapse_s() >= 3) + { + err = ETIMEDOUT; + break; + } + } + else + { + prevg = val; + watch.reset(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + if (err) + break; + Sleep(1000); + } + if (auto_tx_ != (FILE_TX_RECEIVE | FILE_TX_SEND)) + break; + } + + if (err) + { + msg_box(m_hWnd, MB_OK | MB_ICONSTOP, oper.c_str(), L"Failed with error code : %d", err); + set_check(IDC_CHECK_REPEAT, false); + click_repeat(); + } + + set_check(IDC_CHECK_REPEAT, false); + auto_tx_ = 0; + //enable_buttons(scanner_ != NULL); + click_repeat(true); + // msg_box(m_hWnd, MB_OK, L"Repeat S/R", L"exited."); +} +void CDlgScanner::enable_buttons(bool enable) +{ + //GetDlgItem(IDC_BUTTON_RESET_BULK)->EnableWindow(enable); + //GetDlgItem(IDC_CHECK_AUTO)->EnableWindow(enable); + + GetDlgItem(IDC_BUTTON_SCAN)->EnableWindow(enable); + GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(enable); + GetDlgItem(IDC_BUTTON_RECEIVE)->EnableWindow(enable); + GetDlgItem(IDC_CHECK_REPEAT)->EnableWindow(enable); + GetDlgItem(IDC_BUTTON_START_PROG)->EnableWindow(enable); + GetDlgItem(IDC_BUTTON_SEND_EP0)->EnableWindow(enable); +} + +void CDlgScanner::set_text(UINT id, const wchar_t* text) +{ + std::wstring* str = new std::wstring(text); + + if (!::PostMessageW(m_hWnd, WM_SET_TEXT, id, (LPARAM)str)) + delete str; +} +bool CDlgScanner::is_checked(UINT id) +{ + bool chk = false; + + ::SendMessage(m_hWnd, WM_IS_BUTTON_CHECKED, id, (LPARAM)&chk); + + return chk; +} +void CDlgScanner::set_check(UINT id, bool checked) +{ + ::PostMessage(m_hWnd, WM_SET_BUTTON_CHECK, id, checked); +} +void CDlgScanner::click_repeat(bool enable_buttons, bool enable) +{ + if (enable_buttons) + ::PostMessage(m_hWnd, WM_ENABLE_CTRLS, 1, enable); + else + ::PostMessage(m_hWnd, WM_ENABLE_CTRLS, 0, enable); +} + + +BEGIN_MESSAGE_MAP(CDlgScanner, CDialogEx) + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_OPER, &CDlgScanner::OnTcnSelchangeTabOper) + ON_BN_CLICKED(IDOK, &CDlgScanner::OnBnClickedOk) + ON_BN_CLICKED(IDC_BUTTON_RESET_BULK, &CDlgScanner::OnBnClickedButtonResetBulk) + ON_BN_CLICKED(IDC_BUTTON_BROWSE_IMG_PATH, &CDlgScanner::OnBnClickedButtonBrowseSavingPath) + ON_BN_CLICKED(IDC_BUTTON_SCAN, &CDlgScanner::OnBnClickedButtonScan) + ON_BN_CLICKED(IDC_BUTTON_BROWSE_LOCAL, &CDlgScanner::OnBnClickedButtonBrowseFile) + ON_BN_CLICKED(IDC_BUTTON_SEND, &CDlgScanner::OnBnClickedButtonSendFile) + ON_BN_CLICKED(IDC_BUTTON_RECEIVE, &CDlgScanner::OnBnClickedButtonRecvFile) + ON_BN_CLICKED(IDC_BUTTON_START_PROG, &CDlgScanner::OnBnClickedButtonStartProgram) + ON_BN_CLICKED(IDC_BUTTON_SEND_EP0, &CDlgScanner::OnBnClickedButtonSendEp0) + ON_WM_TIMER() + ON_BN_CLICKED(IDC_CHECK_AUTO, &CDlgScanner::OnBnClickedCheckAuto) + ON_BN_CLICKED(IDC_BUTTON_REFRESH, &CDlgScanner::OnBnClickedButtonRefresh) + ON_BN_CLICKED(IDC_CHECK_REPEAT, &CDlgScanner::OnBnClickedCheckRepeat) + ON_MESSAGE(WM_SET_TEXT, &CDlgScanner::OnSetText) + ON_MESSAGE(WM_IS_BUTTON_CHECKED, &CDlgScanner::OnIsButtonChecked) + ON_MESSAGE(WM_SET_BUTTON_CHECK, &CDlgScanner::OnSetButtonChecked) + ON_MESSAGE(WM_ENABLE_CTRLS, &CDlgScanner::OnEnableCtrls) + ON_MESSAGE(WM_DEVICE_STATTUS, &CDlgScanner::OnDeviceStatus) + ON_CBN_SELCHANGE(IDC_COMBO_BUF_SIZE, &CDlgScanner::OnCbnSelchangeComboBufSize) + ON_WM_DESTROY() +END_MESSAGE_MAP() + + +// CDlgScanner 消息处理程序 +BOOL CDlgScanner::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 + // 执行此操作 + + // TODO: 在此添加额外的初始化代码 + int ind = 0; + tab_oper_.InsertItem(ind++, TEXT("Status")); + tab_oper_.InsertItem(ind++, TEXT("Scan")); + tab_oper_.InsertItem(ind++, TEXT("File")); + tab_oper_.InsertItem(ind++, TEXT("Prog")); + tab_oper_.InsertItem(ind++, TEXT("Ctrl")); + tab_oper_.SetCurSel(0); + OnTcnSelchangeTabOper(NULL, (LRESULT*)&ind); + + SetDlgItemText(IDC_EDIT_LOCAL, TEXT("D:\\boxroom\\usb-tx-file\\VMwareworkstation.exe")); + SetDlgItemText(IDC_EDIT_REMOTE, TEXT("/root/.HuaGoScan/Log/VMwareworkstation.exe")); + + sane_api_.sane_get_devices_api = &sane::sane_get_devices_api; + sane_api_.sane_open_api = &sane::sane_open_api; + sane_api_.sane_close_api = &sane::sane_close_api; + sane_api_.sane_get_option_descriptor_api = &sane::sane_get_option_descriptor_api; + sane_api_.sane_control_option_api = &sane::sane_control_option_api; + sane_api_.sane_get_parameters_api = &sane::sane_get_parameters_api; + sane_api_.sane_start_api = &sane::sane_start_api; + sane_api_.sane_read_api = &sane::sane_read_api; + sane_api_.sane_cancel_api = &sane::sane_cancel_api; + sane_api_.sane_set_io_mode_api = &sane::sane_set_io_mode_api; + sane_api_.sane_get_select_fd_api = &sane::sane_get_select_fd_api; + sane_api_.sane_strstatus_api = &sane::sane_strstatus_api; + sane_api_.sane_io_control_api = &sane::sane_io_control_api; + sane_api_.sane_init_api = &sane::sane_init_api; + sane_api_.sane_exit_api = &sane::sane_exit_api; + + wchar_t path[MAX_PATH] = { 0 }; + std::wstring root(L""); + + GetModuleFileNameW(NULL, path, _countof(path) - 1); + wcsrchr(path, L'\\')[1] = 0; + root = path; + if (GetPrivateProfileStringW(L"config", L"img-path", L"", path, _countof(path) - 1, (root + L"config.txt").c_str())) + { + img_root_ = path; + } + else + { + img_root_ = root + L"imgs"; + } + STR_SIMPLIFY_PATH(img_root_); + file_util::force_create_folder(img_root_.c_str(), NULL, false); + if(img_root_[img_root_.length() - 1] != L'\\') + img_root_ += L"\\"; + ::SetDlgItemTextW(m_hWnd, IDC_EDIT_IMG_PATH, img_root_.c_str()); + buf_.SetCurSel(0); + + return FALSE; // 除非将焦点设置到控件,否则返回 TRUE +} +BOOL CDlgScanner::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_SHOWWINDOW && pMsg->wParam == 0 && pMsg->lParam == SW_PARENTCLOSING) + return TRUE; + + return FALSE; +} + + +void CDlgScanner::OnTcnSelchangeTabOper(NMHDR* pNMHDR, LRESULT* pResult) +{ + // TODO: 在此添加控件通知处理程序代码 + UINT statu[] = {IDC_STATIC_BULK_IN, IDC_STATIC_BULK_OUT, IDC_STATIC_CMD_QUE, IDC_STATIC_SENT_QUE + , IDC_EDIT_BUILK_IN, IDC_EDIT_BULK_OUT, IDC_EDIT_CMD_QUE, IDC_EDIT_SENT_QUE + , IDC_BUTTON_RESET_BULK, IDC_CHECK_AUTO, IDC_BUTTON_REFRESH}, + scan[] = {IDC_STATIC_IMG_PATH, IDC_STATIC_COUNT, IDC_EDIT_IMG_PATH, IDC_EDIT_COUNT, IDC_BUTTON_BROWSE_IMG_PATH + , IDC_CHECK_AUTO_OPEN_IMG, IDC_BUTTON_SCAN}, + file[] = {IDC_STATIC_LOCAL, IDC_STATIC_REMOTE, IDC_EDIT_LOCAL, IDC_EDIT_REMOTE, IDC_BUTTON_BROWSE_LOCAL + , IDC_BUTTON_SEND, IDC_BUTTON_RECEIVE, IDC_CHECK_REPEAT}, + prog[] = {IDC_STATIC_CMD, IDC_STATIC_PARAM, IDC_EDIT_CMD, IDC_EDIT_PARAM, IDC_BUTTON_START_PROG}, + ctrl[] = {IDC_STATIC_TYPE, IDC_STATIC_REQ, IDC_STATIC_IND, IDC_STATIC_VAL, IDC_STATIC_LEN, IDC_STATIC_DATA + , IDC_EDIT_TYPE, IDC_EDIT_REQ, IDC_EDIT_IND, IDC_EDIT_VAL, IDC_EDIT_LEN, IDC_EDIT_DATA + , IDC_BUTTON_SEND_EP0}; + int sel = tab_oper_.GetCurSel(), show = sel-- == 0 ? SW_SHOW : SW_HIDE; + + for (auto& v : statu) + GetDlgItem(v)->ShowWindow(show); + + show = sel-- == 0 ? SW_SHOW : SW_HIDE; + for (auto& v : scan) + GetDlgItem(v)->ShowWindow(show); + + show = sel-- == 0 ? SW_SHOW : SW_HIDE; + for (auto& v : file) + GetDlgItem(v)->ShowWindow(show); + + show = sel-- == 0 ? SW_SHOW : SW_HIDE; + for (auto& v : prog) + GetDlgItem(v)->ShowWindow(show); + + show = sel-- == 0 ? SW_SHOW : SW_HIDE; + for (auto& v : ctrl) + GetDlgItem(v)->ShowWindow(show); + + *pResult = 0; +} + + +void CDlgScanner::OnBnClickedOk() +{ + // TODO: 在此添加控件通知处理程序代码 + if (IsWindow(parent_)) + ::PostMessage(parent_, WM_OPENNING_DLG_CLOSED, 0, (LPARAM)this); + + CDialogEx::OnOK(); +} + + +void CDlgScanner::OnBnClickedButtonResetBulk() +{ + // TODO: 在此添加控件通知处理程序代码 + //int err = scanner_->reset_message_que(); + //msg_box(m_hWnd, MB_OK, L"Reset-Bulk", L"Result = %d", err); + //if (err == 0) + //{ + // enable_buttons(true); + // auto_tx_ = false; + // SetEvent(auto_wait_); + //} +} +void CDlgScanner::OnBnClickedButtonBrowseSavingPath() +{ + // TODO: 在此添加控件通知处理程序代码 + LPITEMIDLIST pidRoot = NULL; + BROWSEINFOW bi = { 0 }; + wchar_t path[MAX_PATH] = { 0 }; + + SHGetSpecialFolderLocation(m_hWnd, CSIDL_DRIVES, &pidRoot); + bi.hwndOwner = m_hWnd; + bi.pidlRoot = pidRoot; + bi.lpszTitle = L"Select folder to save scanning images"; + bi.pszDisplayName = path; + bi.ulFlags = BIF_RETURNONLYFSDIRS; + pidRoot = SHBrowseForFolderW(&bi); + if (pidRoot) + { + SHGetPathFromIDListW(pidRoot, path); + img_root_ = path; + img_root_ += L"\\"; + STR_SIMPLIFY_PATH(img_root_); + ::SetDlgItemTextW(m_hWnd, IDC_EDIT_IMG_PATH, img_root_.c_str()); + + std::wstring root(L""); + + GetModuleFileNameW(NULL, path, _countof(path) - 1); + wcsrchr(path, L'\\')[1] = 0; + root = path; + WritePrivateProfileStringW(L"config", L"img-path", img_root_.c_str(), (root + L"config.txt").c_str()); + } +} +void CDlgScanner::OnBnClickedButtonScan() +{ + // TODO: 在此添加控件通知处理程序代码 + wchar_t title[40] = { 0 }; + + ::GetDlgItemTextW(m_hWnd, IDC_BUTTON_SCAN, title, _countof(title) - 1); + if (wcsicmp(title, L"Scan") == 0) + { + img_cnt_ = 0; + paper_cnt_ = 0; + SetDlgItemInt(IDC_EDIT_COUNT, img_cnt_); + if (scanner_) + { + int err = scanner_->start(); + utils::to_log(LOG_LEVEL_DEBUG, "Start to scan = %s\r\n", usb::u2a(scanner_status(err, title)).c_str()); + if (err) + msg_box(m_hWnd, MB_OK, L"Error", L"Failed in startin scanning with code %s", scanner_status(err, title)); + else + { + ::SetDlgItemTextW(m_hWnd, IDC_BUTTON_SCAN, L"Stop"); + OnDeviceStatus(0, (LPARAM)SCANNER_STATUS_SCANNING); + } + } + } + else + { + if (scanner_) + { + int err = scanner_->stop(); + if (err) + msg_box(m_hWnd, MB_OK, L"Error", L"Failed to stop scanning with code %d", err); + } + } +} +void CDlgScanner::OnBnClickedButtonBrowseFile() +{ + // TODO: 在此添加控件通知处理程序代码 +} +void CDlgScanner::OnBnClickedButtonSendFile() +{ + // TODO: 在此添加控件通知处理程序代码 + GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE); + GetDlgItem(IDC_BUTTON_RECEIVE)->EnableWindow(FALSE); + threads_.stop("CDlgScanner::thread_auto_tx_file"); + auto_tx_ = FILE_TX_SEND; + auto txf = [this](void) -> void + { + thread_auto_tx_file(); + }; + auto_tx_file_ = threads_.start(txf, "CDlgScanner::thread_auto_tx_file", NULL); + //char l[256] = { 0 }, + // r[256] = { 0 }; + //int err = 0; + + //::GetDlgItemTextA(m_hWnd, IDC_EDIT_LOCAL, l, _countof(l) - 1); + //::GetDlgItemTextA(m_hWnd, IDC_EDIT_REMOTE, r, _countof(r) - 1); + //err = scanner_->file_transfer(l, r, true); + //if(err) + // msg_box(m_hWnd, MB_OK, L"Send File", L"Result = %d", err); +} +void CDlgScanner::OnBnClickedButtonRecvFile() +{ + // TODO: 在此添加控件通知处理程序代码 + GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE); + GetDlgItem(IDC_BUTTON_RECEIVE)->EnableWindow(FALSE); + threads_.stop("CDlgScanner::thread_auto_tx_file"); + auto_tx_ = FILE_TX_RECEIVE; + auto txf = [this](void) -> void + { + thread_auto_tx_file(); + }; + auto_tx_file_ = threads_.start(txf, "CDlgScanner::thread_auto_tx_file", NULL); + //char l[256] = { 0 }, + // r[256] = { 0 }; + //int err = 0; + + //::GetDlgItemTextA(m_hWnd, IDC_EDIT_LOCAL, l, _countof(l) - 1); + //::GetDlgItemTextA(m_hWnd, IDC_EDIT_REMOTE, r, _countof(r) - 1); + //err = scanner_->file_transfer(l, r, false); + //if (err) + // msg_box(m_hWnd, MB_OK, L"Receive File", L"Result = %d", err); +} +void CDlgScanner::OnBnClickedButtonStartProgram() +{ + // TODO: 在此添加控件通知处理程序代码 + //char key[80] = { 0 }, val[80] = { 0 }; + //int err = 0; + + //::GetDlgItemTextA(m_hWnd, IDC_EDIT_CMD, key, _countof(key) - 1); + //err = scanner_->option_value_get(key, val, 40); + //if (err == 0) + // ::SetDlgItemTextA(m_hWnd, IDC_EDIT_PARAM, val); +} +void CDlgScanner::OnBnClickedButtonSendEp0() +{ + // TODO: 在此添加控件通知处理程序代码 +} + + +void CDlgScanner::OnTimer(UINT_PTR nIDEvent) +{ + // TODO: 在此添加消息处理程序代码和/或调用默认值 + if(nIDEvent == TIMER_ID_REFRESH_BULK) + refresh_bulk_status(); + + CDialogEx::OnTimer(nIDEvent); +} + + +void CDlgScanner::OnBnClickedCheckAuto() +{ + // TODO: 在此添加控件通知处理程序代码 + bool checked = ((CButton*)GetDlgItem(IDC_CHECK_AUTO))->GetCheck() == BST_CHECKED; + + GetDlgItem(IDC_BUTTON_REFRESH)->EnableWindow(!checked); + if (checked) + SetTimer(TIMER_ID_REFRESH_BULK, 1000, NULL); + else + KillTimer(TIMER_ID_REFRESH_BULK); +} + + +void CDlgScanner::OnBnClickedButtonRefresh() +{ + // TODO: 在此添加控件通知处理程序代码 + refresh_bulk_status(); +} + + +void CDlgScanner::OnBnClickedCheckRepeat() +{ + // TODO: 在此添加控件通知处理程序代码 + bool start = ((CButton*)GetDlgItem(IDC_CHECK_REPEAT))->GetCheck() == BST_CHECKED; + + enable_buttons(!start); + auto_tx_ = 0; + threads_.stop("CDlgScanner::thread_auto_tx_file"); + GetDlgItem(IDC_CHECK_REPEAT)->EnableWindow(TRUE); + GetDlgItem(IDC_CHECK_AUTO)->EnableWindow(TRUE); + if (start) + { + auto txf = [this](void) -> void + { + thread_auto_tx_file(); + }; + auto_tx_ = FILE_TX_RECEIVE | FILE_TX_SEND; + auto_tx_file_ = threads_.start(txf, "CDlgScanner::thread_auto_tx_file", NULL); + } + else + { + if (auto_tx_file_ != -1) + { + SetEvent(auto_wait_); + threads_.stop("CDlgScanner::thread_auto_tx_file"); + auto_tx_file_ = -1; + } + } +} + +LRESULT CDlgScanner::OnSetText(WPARAM wp, LPARAM lp) +{ + std::wstring* str = (std::wstring*)lp; + + ::SetDlgItemTextW(m_hWnd, wp, str->c_str()); + delete str; + + return 0; +} +LRESULT CDlgScanner::OnIsButtonChecked(WPARAM wp, LPARAM lp) +{ + *(bool*)lp = ((CButton*)GetDlgItem(wp))->GetCheck() == BST_CHECKED; + + return 0; +} +LRESULT CDlgScanner::OnSetButtonChecked(WPARAM wp, LPARAM lp) +{ + ((CButton*)GetDlgItem(wp))->SetCheck(lp ? BST_CHECKED : BST_UNCHECKED); + + return 0; +} +LRESULT CDlgScanner::OnEnableCtrls(WPARAM wp, LPARAM lp) +{ + if (wp == 0) + OnBnClickedCheckRepeat(); + else + enable_buttons(lp); + + return 0; +} +LRESULT CDlgScanner::OnDeviceStatus(WPARAM wp, LPARAM lp) +{ + wchar_t unk[20] = { 0 }; + + ::SetDlgItemTextW(m_hWnd, IDC_EDIT_STATUS, scanner_status((int)lp, unk)); + + return 0; +} + + +void CDlgScanner::OnCbnSelchangeComboBufSize() +{ + // TODO: 在此添加控件通知处理程序代码 + int sel = buf_.GetCurSel(); + + sel = 1 << sel; + //if (scanner_->set_io_buffer_size(sel)) + //{ + // int cur = 0; + + // sel = scanner_->get_io_buffer_size() >> 1; + // while (sel) + // { + // cur++; + // sel >>= 1; + // } + // buf_.SetCurSel(cur); + //} +} + + +void CDlgScanner::OnDestroy() +{ + CDialogEx::OnDestroy(); + + // TODO: 在此处添加消息处理程序代码 + set_device(NULL); +} diff --git a/sln/usb_tools/DlgScanner.h b/sln/usb_tools/DlgScanner.h new file mode 100644 index 0000000..7a6faea --- /dev/null +++ b/sln/usb_tools/DlgScanner.h @@ -0,0 +1,132 @@ +#pragma once + + +// CDlgScanner 对话框 +#include "../../../code_device/hgdriver/hgdev/hg_scanner.h" +#include +#include "opt_ui/DlgSetting.h" +#include +#include + +class CDlgScanner; + +namespace usb +{ + typedef struct _end_point + { + BYTE iface = -1; + BYTE type = -1; + BYTE addr = -1; + size_t packet = 0; + }USBEP; + typedef struct _usb_pnp + { + libusb_context* ctx = NULL; + libusb_device* device = NULL; + libusb_device_handle* handle = NULL; + CDlgScanner* dlg = NULL; + WORD vid = 0; + WORD pid = 0; + bool arrive = true; + std::vector eps; + }USBPNP, * LPUSBPNP; + + std::wstring now(void); + const wchar_t* error_name(int err, wchar_t* unk = NULL); + std::wstring a2u(const char* ansi); + std::string u2a(const wchar_t* unic); + void enum_endpoints(libusb_device* device, std::vector& eps); + const wchar_t* ep_type(BYTE type); + BYTE ep_type(const wchar_t* str); +} +enum +{ + WM_USB_PNP = WM_USER + 1001, + WM_RCV_MSG, + WM_OPENNING_DLG_CLOSED, + WM_TRAY_MSG, + WM_TIMER_CANCEL_TOPMOST, +}; + +class CDlgScanner : public CDialogEx +{ + DECLARE_DYNAMIC(CDlgScanner) + enum + { + FILE_TX_RECEIVE = 1 << 0, + FILE_TX_SEND = 1 << 1, + }; + + HWND parent_; + hg_scanner* scanner_; + volatile int auto_tx_ = 0; + uint32_t auto_tx_file_; + HANDLE auto_wait_; + dlg_setting *setting_ui_; + SANEAPI sane_api_; + std::wstring img_root_; + uint32_t img_cnt_; + uint32_t paper_cnt_; + uint32_t max_sent_; + uint32_t max_cmd_; + safe_thread threads_; + +public: + CDlgScanner(CWnd* pParent = nullptr); // 标准构造函数 + virtual ~CDlgScanner(); + +// 对话框数据 +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_SCANNER }; +#endif + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 + virtual BOOL OnInitDialog(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + + DECLARE_MESSAGE_MAP() + + int refresh_bulk_status(void); + void thread_auto_tx_file(void); + void enable_buttons(bool enable); + + void set_text(UINT id, const wchar_t* text); + bool is_checked(UINT id); + void set_check(UINT id, bool checked); + void click_repeat(bool enable_buttons = false, bool enable = true); + +public: + void set_device(ONLNSCANNER* pnp); + bool is_online(void); + void get_option(const char* name, void* value, size_t size); + int get_all_option(std::string& opts_json); + int set_option(const char* name, void* value, int type, size_t len, size_t max_len, int* after); + +public: + CTabCtrl tab_opt_; + CTabCtrl tab_oper_; + afx_msg void OnTcnSelchangeTabOper(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBnClickedOk(); + afx_msg void OnBnClickedButtonResetBulk(); + afx_msg void OnBnClickedButtonBrowseSavingPath(); + afx_msg void OnBnClickedButtonScan(); + afx_msg void OnBnClickedButtonBrowseFile(); + afx_msg void OnBnClickedButtonSendFile(); + afx_msg void OnBnClickedButtonRecvFile(); + afx_msg void OnBnClickedButtonStartProgram(); + afx_msg void OnBnClickedButtonSendEp0(); + afx_msg void OnTimer(UINT_PTR nIDEvent); + afx_msg void OnBnClickedCheckAuto(); + afx_msg void OnBnClickedButtonRefresh(); + afx_msg void OnBnClickedCheckRepeat(); + + afx_msg LRESULT OnSetText(WPARAM wp, LPARAM lp); + afx_msg LRESULT OnIsButtonChecked(WPARAM wp, LPARAM lp); + afx_msg LRESULT OnSetButtonChecked(WPARAM wp, LPARAM lp); + afx_msg LRESULT OnEnableCtrls(WPARAM wp, LPARAM lp); + afx_msg LRESULT OnDeviceStatus(WPARAM wp, LPARAM lp); + afx_msg void OnCbnSelchangeComboBufSize(); + CComboBox buf_; + afx_msg void OnDestroy(); +}; diff --git a/sln/usb_tools/framework.h b/sln/usb_tools/framework.h new file mode 100644 index 0000000..aec2a45 --- /dev/null +++ b/sln/usb_tools/framework.h @@ -0,0 +1,57 @@ +#pragma once + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // 从 Windows 头中排除极少使用的资料 +#endif + +#include "targetver.h" + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 构造函数将是显式的 + +// 关闭 MFC 的一些常见且经常可放心忽略的隐藏警告消息 +#define _AFX_ALL_WARNINGS + +// #define WIN32_LEAN_AND_MEAN +//#define _WINSOCK2API_ +//#define _WS2IPDEF_ +////#define IPV6STRICT +//#define __IPHLPAPI_H__ +//#define _WINDNS_INCLUDED_ + + +#include // MFC 核心组件和标准组件 +#include // MFC 扩展 + + +#include // MFC 自动化类 + + + +#ifndef _AFX_NO_OLE_SUPPORT +#include // MFC 对 Internet Explorer 4 公共控件的支持 +#endif +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC 对 Windows 公共控件的支持 +#endif // _AFX_NO_AFXCMN_SUPPORT + +#include // MFC 支持功能区和控制条 + + + + + + + + + +#ifdef _UNICODE +#if defined _M_IX86 +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif +#endif + + diff --git a/sln/usb_tools/opt_ui/DlgArea.cpp b/sln/usb_tools/opt_ui/DlgArea.cpp new file mode 100644 index 0000000..bf2eadc --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgArea.cpp @@ -0,0 +1,813 @@ +// DlgIndicator.cpp: 实现文件 +// + +#include "DlgArea.h" +#include "../resource.h" +#include "mem_dc.h" + +// CDlgIndicator 对话框 +#define MM_PER_INCH 25.4f + +int dlg_area::area_min_pixel = 50; + +dlg_area::dlg_area(HWND parent) : dlg_base(parent, IDD_AREA) + , unit_(PAPER_UNIT_MM), paper_w_(210), paper_h_(297), dpi_(200) + , x_(0), y_(0), w_(paper_w_), h_(paper_h_) + , paper_(L"A4") + , paper_w_0_(paper_w_), paper_h_0_(paper_h_), cursor_(NULL), drag_(DRAG_POS_NONE) + , in_set_func_(false) +{ + std::wstring title(local_trans::lang_trans_between_hz936(CONST_STRING_CUSTOM_AREA)); + + create(); + + SetWindowTextW(hwnd(), title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_UNIT_MM) + L" (mm)"; + SendMessageW(get_item(IDC_UNIT), CB_ADDSTRING, 0, (LPARAM)&title[0]); + title = local_trans::lang_trans_between_hz936(CONST_STRING_UNIT_INCH) + L" (inch)"; + SendMessageW(get_item(IDC_UNIT), CB_ADDSTRING, 0, (LPARAM)&title[0]); + title = local_trans::lang_trans_between_hz936(CONST_STRING_UNIT_PIXEL) + L" (px)"; + SendMessageW(get_item(IDC_UNIT), CB_ADDSTRING, 0, (LPARAM)&title[0]); + SendMessage(get_item(IDC_UNIT), CB_SETCURSEL, 0, 0); + + + if (!dlg_base::is_language_pack_default_code_page()) + { + int width_diff = 0, w = 0; + RECT r = { 0 }, rp = { 0 }; + + title = local_trans::lang_trans_between_hz936(CONST_STRING_OPT_PAPER) + L":"; + set_item_text(IDC_STATIC_PAPER, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_UNIT) + L":"; + set_item_text(IDC_STATIC_UNIT, title.c_str()); + title = L"DPI (" + local_trans::lang_trans_between_hz936(CONST_STRING_UNIT_PIXEL) + L"/" + local_trans::lang_trans_between_hz936(CONST_STRING_UNIT_INCH) + L"):"; + set_item_text(IDC_STATIC_DPI, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_AREA_SET) + L":"; + set_item_text(IDC_STATIC_AREA, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_LEFT) + L":"; + set_item_text(IDC_STATIC_LEFT, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_TOP) + L":"; + set_item_text(IDC_STATIC_TOP, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_WIDTH) + L":"; + set_item_text(IDC_STATIC_W, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_HEIGHT) + L":"; + set_item_text(IDC_STATIC_H, title.c_str()); + + title = local_trans::lang_trans_between_hz936(CONST_STRING_RESTORE_AREA); + set_item_text(IDC_BUTTON_RESET, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_CANCEL); + set_item_text(IDCANCEL, title.c_str()); + title = local_trans::lang_trans_between_hz936(CONST_STRING_OK); + set_item_text(IDOK, title.c_str()); + + layout(); + } +} +dlg_area::~dlg_area() +{ +} + +float dlg_area::mm_2_pixel(float mm, float dpi) +{ + return inches_2_pixel(mm_2_inches(mm), dpi); +} +float dlg_area::mm_2_inches(float mm) +{ + return mm / MM_PER_INCH; +} +float dlg_area::inches_2_pixel(float inch, float dpi) +{ + return inch * dpi; +} +float dlg_area::inches_2_mm(float inch) +{ + return inch * MM_PER_INCH; +} +float dlg_area::pixel_2_mm(float px, float dpi) +{ + return inches_2_mm(pixel_2_inches(px, dpi)); +} +float dlg_area::pixel_2_inches(float px, float dpi) +{ + return px / dpi; +} + +BOOL dlg_area::handle_message(UINT msg, WPARAM wp, LPARAM lp) +{ + wchar_t text[40] = { 0 }; + BOOL ret = TRUE; + + switch (msg) + { + case WM_INITDIALOG: + UpdateWindow(hwnd()); + break; + case WM_COMMAND: + handle_command(HIWORD(wp), LOWORD(wp), (HWND)lp); + break; + case WM_PAINT: + { + PAINTSTRUCT ps = { 0 }; + HDC hdc = BeginPaint(hwnd(), &ps); + { + compatible_dc dc(hdc); + on_paint(dc.get_dc()); + } + EndPaint(hwnd(), &ps); + } + break; + case WM_MOUSEMOVE: + on_mouse_move(wp, LOWORD(lp), HIWORD(lp)); + break; + case WM_LBUTTONDOWN: + on_lbutton_down(LOWORD(lp), HIWORD(lp)); + break; + case WM_LBUTTONUP: + on_lbutton_up(LOWORD(lp), HIWORD(lp)); + break; + default: + ret = FALSE; + } + return ret; +} +void dlg_area::handle_command(WORD code, WORD id, HANDLE ctrl) +{ + if (id == IDC_UNIT) + { + if (code == CBN_SELCHANGE) + { + on_unit_changed((HWND)ctrl); + } + } + else if (id == IDCANCEL) + { + quit_modal(id); + } + else if (id == IDOK) + { + quit_modal(id); + } + else if (id == IDC_BUTTON_RESET) + { + clear_area(); + } + else if (id == IDC_EDIT_x || + id == IDC_EDIT_y || + id == IDC_EDIT_W || + id == IDC_EDIT_H) + { + if (code == EN_CHANGE && !in_set_func_) + { + wchar_t val[80] = { 0 }; + float num = .0f; + + GetWindowTextW((HWND)ctrl, val, _countof(val) - 1); + num = (float)_wtof(val); + if (id == IDC_EDIT_x) + { + if (num < .0f) + num = .0f; + if (num > paper_w_ - dlg_area::area_min_pixel) + num = paper_w_ - dlg_area::area_min_pixel; + x_ = num; + user_sel_.left = whole_.left + (LONG)(x_ / ratio_); + if (x_ + w_ > paper_w_) + w_ = paper_w_ - x_; + user_sel_.right = user_sel_.left + (LONG)(w_ / ratio_); + refresh_paper_info(); + InvalidateRect(hwnd(), &whole_, FALSE); + } + else if (id == IDC_EDIT_y) + { + if (num < .0f) + num = .0f; + if (num > paper_h_ - dlg_area::area_min_pixel) + num = paper_h_ - dlg_area::area_min_pixel; + y_ = num; + user_sel_.top = whole_.top + (LONG)(y_ / ratio_); + if (y_ + h_ > paper_h_) + h_ = paper_h_ - y_; + user_sel_.bottom = user_sel_.top + (LONG)(h_ / ratio_); + refresh_paper_info(); + InvalidateRect(hwnd(), &whole_, FALSE); + } + else if (id == IDC_EDIT_W) + { + if (num < dlg_area::area_min_pixel) + num = (float)dlg_area::area_min_pixel; + if (num > paper_w_) + num = paper_w_; + w_ = num; + user_sel_.right = user_sel_.left + (LONG)(w_ / ratio_); + if (user_sel_.right > whole_.right) + { + OffsetRect(&user_sel_, whole_.right - user_sel_.right, 0); + if (user_sel_.left < whole_.left) + { + user_sel_.left = whole_.left; + x_ = 0; + } + } + refresh_paper_info(); + InvalidateRect(hwnd(), &whole_, FALSE); + } + else if (id == IDC_EDIT_H) + { + if (num < dlg_area::area_min_pixel) + num = (float)dlg_area::area_min_pixel; + if (num > paper_h_) + num = paper_h_; + h_ = num; + user_sel_.bottom = user_sel_.top + (LONG)(h_ / ratio_); + if (user_sel_.bottom > whole_.bottom) + { + OffsetRect(&user_sel_, 0, whole_.bottom - user_sel_.bottom); + if (user_sel_.top < whole_.top) + { + user_sel_.top = whole_.top; + y_ = 0; + } + } + refresh_paper_info(); + InvalidateRect(hwnd(), &whole_, FALSE); + } + } + } +} +void dlg_area::layout(void) +{ + RECT r = { 0 }; + int w_dif = get_width_diff_as_text_length(IDC_STATIC_LEFT), + top1 = get_width_diff_as_text_length(IDC_STATIC_PAPER), + top21 = get_width_diff_as_text_length(IDC_STATIC_UNIT), + top22 = get_width_diff_as_text_length(IDC_STATIC_DPI); + +#define CHECK_CTRL_W(id) \ + if(w_dif < get_width_diff_as_text_length(id)) \ + w_dif = get_width_diff_as_text_length(id); + + CHECK_CTRL_W(IDC_STATIC_TOP); + CHECK_CTRL_W(IDC_STATIC_W); + CHECK_CTRL_W(IDC_STATIC_H); + CHECK_CTRL_W(IDC_BUTTON_RESET); + if (w_dif < top21 + top22) + w_dif = top21 + top22; + + // layout ... + if (w_dif > 0) + { + GetWindowRect(hwnd(), &r); + r.right += w_dif; + MoveWindow(hwnd(), r.left, r.top, RECT_W(r), RECT_H(r), FALSE); + + expand_item(IDC_STATIC_PAPER, top1, 0); + offset_item(IDC_EDIT_PAPER, top1, 0); + expand_item(IDC_EDIT_PAPER, w_dif - top1, 0); + + expand_item(IDC_STATIC_UNIT, top21, 0); + offset_item(IDC_UNIT, top21, 0); + + offset_item(IDC_STATIC_DPI, w_dif - top22, 0); + expand_item(IDC_STATIC_DPI, top22, 0); + offset_item(IDC_EDIT_DPI, top21 + top22, 0); + + expand_item(IDC_STATIC_AREA, w_dif, 0); + + expand_item(IDC_STATIC_LEFT, w_dif, 0); + offset_item(IDC_EDIT_x, w_dif, 0); + + expand_item(IDC_STATIC_TOP, w_dif, 0); + offset_item(IDC_EDIT_y, w_dif, 0); + + expand_item(IDC_STATIC_W, w_dif, 0); + offset_item(IDC_EDIT_W, w_dif, 0); + + expand_item(IDC_STATIC_H, w_dif, 0); + offset_item(IDC_EDIT_H, w_dif, 0); + + top1 = get_width_diff_as_text_length(IDC_BUTTON_RESET); + expand_item(IDC_BUTTON_RESET, top1, 0); + offset_item(IDC_BUTTON_RESET, (w_dif - top1) / 2, 0); + + offset_item(IDOK, w_dif, 0); + offset_item(IDCANCEL, w_dif, 0); + } +} + +float dlg_area::as_mm(float v) +{ + if (unit_ == PAPER_UNIT_INCH) + return dlg_area::inches_2_mm(v); + else if (unit_ == PAPER_UNIT_PIXEL) + return dlg_area::pixel_2_mm(v, dpi_); + else + return v; +} +float dlg_area::from_mm(float v) +{ + if (unit_ == PAPER_UNIT_INCH) + return dlg_area::mm_2_inches(v); + else if (unit_ == PAPER_UNIT_PIXEL) + return dlg_area::mm_2_pixel(v, dpi_); + else + return v; +} +float dlg_area::as_inches(float v) +{ + if (unit_ == PAPER_UNIT_MM) + return dlg_area::mm_2_inches(v); + else if (unit_ == PAPER_UNIT_PIXEL) + return dlg_area::pixel_2_inches(v, dpi_); + else + return v; +} +float dlg_area::as_pixels(float v) +{ + if (unit_ == PAPER_UNIT_MM) + return dlg_area::mm_2_pixel(v, dpi_); + else if (unit_ == PAPER_UNIT_INCH) + return dlg_area::inches_2_pixel(v, dpi_); + else + return v; +} +std::wstring dlg_area::format_number(float v) +{ + wchar_t str[40] = { 0 }; + + if (unit_ == PAPER_UNIT_PIXEL) + swprintf_s(str, _countof(str) - 1, L"%d", (int)(v + .5f)); + else + swprintf_s(str, _countof(str) - 1, L"%.2f", v); + + return str; +} +void dlg_area::refresh_paper_info(void) +{ + SetWindowTextW(get_item(IDC_EDIT_PAPER), (paper_ + L"(" + format_number(paper_w_) + local_trans::lang_trans_between_hz936(CONST_STRING_MULTIPLE) + format_number(paper_h_) + L")").c_str()); + SetDlgItemInt(hwnd(), IDC_EDIT_DPI, UINT(dpi_), FALSE); + + in_set_func_ = true; + SetWindowTextW(get_item(IDC_EDIT_x), format_number(x_).c_str()); + SetWindowTextW(get_item(IDC_EDIT_y), format_number(y_).c_str()); + SetWindowTextW(get_item(IDC_EDIT_W), format_number(w_).c_str()); + SetWindowTextW(get_item(IDC_EDIT_H), format_number(h_).c_str()); + in_set_func_ = false; + + InvalidateRect(hwnd(), &whole_, FALSE); +} +void dlg_area::to_unit(paper_unit unit) +{ + float(dlg_area:: * conv)(float) = NULL; + + if (unit == PAPER_UNIT_INCH) + { + conv = &dlg_area::as_inches; + paper_w_ = dlg_area::mm_2_inches(paper_w_0_); + paper_h_ = dlg_area::mm_2_inches(paper_h_0_); + } + else if (unit == PAPER_UNIT_PIXEL) + { + conv = &dlg_area::as_pixels; + paper_w_ = dlg_area::mm_2_pixel(paper_w_0_, dpi_); + paper_h_ = dlg_area::mm_2_pixel(paper_h_0_, dpi_); + } + else // if (unit == PAPER_UNIT_MM) + { + conv = &dlg_area::as_mm; + paper_w_ = paper_w_0_; + paper_h_ = paper_h_0_; + } + +#define CONV_UNIT(v) \ + v = (this->*conv)(v); + + CONV_UNIT(x_); + CONV_UNIT(y_); + CONV_UNIT(w_); + CONV_UNIT(h_); + unit_ = unit; + refresh_paper_info(); +} +void dlg_area::clear_area(void) +{ + x_ = y_ = 0; + w_ = paper_w_; + h_ = paper_h_; + user_sel_ = whole_; + + refresh_paper_info(); +} +void dlg_area::drag_blocks(std::vector& blocks) +{ + int l = 5; + DRAGRECT r; + + r.left = user_sel_.left; + r.top = user_sel_.top; + r.right = r.left + l; + r.bottom = r.top + l; + r.pos = DRAG_POS_LT; + + blocks.clear(); + blocks.push_back(r); + OffsetRect(&r, (user_sel_.right - user_sel_.left) / 2 - l / 2, 0); r.pos = DRAG_POS_MT; + blocks.push_back(r); + OffsetRect(&r, (user_sel_.right - r.right), 0); r.pos = DRAG_POS_RT; + blocks.push_back(r); + OffsetRect(&r, 0, (user_sel_.bottom - user_sel_.top) / 2 - l / 2); r.pos = DRAG_POS_RM; + blocks.push_back(r); + OffsetRect(&r, 0, (user_sel_.bottom - r.bottom)); r.pos = DRAG_POS_RB; + blocks.push_back(r); + OffsetRect(&r, -((user_sel_.right - user_sel_.left) / 2 - l / 2), 0); r.pos = DRAG_POS_MB; + blocks.push_back(r); + OffsetRect(&r, user_sel_.left - r.left, 0); r.pos = DRAG_POS_LB; + blocks.push_back(r); + OffsetRect(&r, 0, -((user_sel_.bottom - user_sel_.top) / 2 - l / 2)); r.pos = DRAG_POS_LM; + blocks.push_back(r); +} +float dlg_area::pos_2_area(int val, bool x) +{ + float r = x ? (float)(val - whole_.left) : (float)(val - whole_.top); + + r *= ratio_; + if (unit_ == PAPER_UNIT_INCH) + r = dlg_area::mm_2_inches(r); + else if (unit_ == PAPER_UNIT_PIXEL) + r = dlg_area::mm_2_pixel(r, dpi_); + + return r; +} +void dlg_area::valid_x(int& x, bool left) +{ + if (left) + { + if (x < whole_.left) + x = whole_.left; + if (x > user_sel_.right - dlg_area::area_min_pixel) + x = user_sel_.right - dlg_area::area_min_pixel; + } + else + { + if (x > whole_.right) + x = whole_.right; + if (x < user_sel_.left + dlg_area::area_min_pixel) + x = user_sel_.left + dlg_area::area_min_pixel; + } +} +void dlg_area::valid_y(int& y, bool top) +{ + if (top) + { + if (y < whole_.top) + y = whole_.top; + if (y > user_sel_.bottom - dlg_area::area_min_pixel) + y = user_sel_.bottom - dlg_area::area_min_pixel; + } + else + { + if (y > whole_.bottom) + y = whole_.bottom; + if (y < user_sel_.top + dlg_area::area_min_pixel) + y = user_sel_.top + dlg_area::area_min_pixel; + } +} + +void dlg_area::on_unit_changed(HWND wnd) +{ + wchar_t text[80] = { 0 }; + + GetWindowTextW(wnd, text, _countof(text) - 1); + if (wcsstr(text, L"mm")) + { + if (unit_ != PAPER_UNIT_MM) + { + to_unit(PAPER_UNIT_MM); + } + } + else if (wcsstr(text, L"inch")) + { + if (unit_ != PAPER_UNIT_INCH) + { + to_unit(PAPER_UNIT_INCH); + } + } + else + { + if (unit_ != PAPER_UNIT_PIXEL) + { + to_unit(PAPER_UNIT_PIXEL); + } + } +} +void dlg_area::on_paint(HDC hdc) +{ + HBRUSH brsh_all = CreateSolidBrush(RGB(192, 192, 192)), + brsh_drag = CreateSolidBrush(RGB(255, 0, 0)), + brsh_sel = CreateSolidBrush(RGB(255, 255, 255)); + HPEN pen_border = CreatePen(PS_SOLID, 1, RGB(0, 255, 0)), + pen_ruler = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)), + pen_old = (HPEN)SelectObject(hdc, pen_border); + int step = 3, minor = 3, major = 5, count = 1, len = 0; + + FillRect(hdc, &whole_, brsh_all); + FillRect(hdc, &user_sel_, brsh_sel); + Rectangle(hdc, user_sel_.left, user_sel_.top, user_sel_.right, user_sel_.bottom); + SelectObject(hdc, pen_ruler); + for (int i = whole_.left + step; i < whole_.right; i += step) + { + ++count; + len = (count % 5) == 0 ? major : minor; + MoveToEx(hdc, i, whole_.top, NULL); + LineTo(hdc, i, whole_.top + len); + MoveToEx(hdc, i, whole_.bottom, NULL); + LineTo(hdc, i, whole_.bottom - len); + } + count = 1; + for (int i = whole_.top + step; i < whole_.bottom; i += step) + { + ++count; + len = (count % 5) == 0 ? major : minor; + MoveToEx(hdc, whole_.left, i, NULL); + LineTo(hdc, whole_.left + len, i); + MoveToEx(hdc, whole_.right, i, NULL); + LineTo(hdc, whole_.right - len, i); + } + + std::vector r; + drag_blocks(r); + for(size_t i = 0; i < r.size(); ++i) + FillRect(hdc, &r[i], brsh_drag); + + SelectObject(hdc, pen_old); + DeleteObject(pen_border); + DeleteObject(pen_ruler); + DeleteObject(brsh_all); + DeleteObject(brsh_sel); + DeleteObject(brsh_drag); +} +void dlg_area::on_mouse_move(DWORD key, int x, int y) +{ + if (key == MK_LBUTTON) + { + if (drag_ == DRAG_POS_NONE) + return; + + float dif = .0f; + + SetCursor(LoadCursorW(NULL, cursor_)); + if (drag_ == DRAG_POS_LT) + { + valid_x(x, true); + valid_y(y, true); + user_sel_.left = x; + user_sel_.top = y; + dif = x_; + x_ = pos_2_area(x, true); + w_ -= x_ - dif; + dif = y_; + y_ = pos_2_area(y, false); + h_ -= y_ - dif; + } + else if (drag_ == DRAG_POS_MT) + { + valid_y(y, true); + user_sel_.top = y; + dif = y_; + y_ = pos_2_area(y, false); + h_ -= y_ - dif; + } + else if(drag_ == DRAG_POS_RT) + { + valid_x(x, false); + valid_y(y, true); + user_sel_.right = x; + user_sel_.top = y; + w_ = pos_2_area(x, true) - x_; + if (x_ + w_ > paper_w_) + w_ = paper_w_ - x_; + dif = y_; + y_ = pos_2_area(y, false); + h_ -= y_ - dif; + } + else if (drag_ == DRAG_POS_LM) + { + valid_x(x, true); + user_sel_.left = x; + dif = x_; + x_ = pos_2_area(x, true); + w_ -= x_ - dif; + } + else if (drag_ == DRAG_POS_RM) + { + valid_x(x, false); + user_sel_.right = x; + w_ = pos_2_area(x, true) - x_; + if (x_ + w_ > paper_w_) + w_ = paper_w_ - x_; + } + else if (drag_ == DRAG_POS_LB) + { + valid_x(x, true); + valid_y(y, false); + user_sel_.left = x; + user_sel_.bottom = y; + dif = x_; + x_ = pos_2_area(x, true); + w_ -= x_ - dif; + h_ = pos_2_area(y, false) - y_; + } + else if (drag_ == DRAG_POS_MB) + { + valid_y(y, false); + user_sel_.bottom = y; + h_ = pos_2_area(y, false) - y_; + if (y_ + h_ > paper_h_) + h_ = paper_h_ - y_; + } + else if (drag_ == DRAG_POS_RB) + { + valid_x(x, false); + valid_y(y, false); + user_sel_.right = x; + user_sel_.bottom = y; + w_ = pos_2_area(x, true) - x_; + if (x_ + w_ > paper_w_) + w_ = paper_w_ - x_; + h_ = pos_2_area(y, false) - y_; + if (y_ + h_ > paper_h_) + h_ = paper_h_ - y_; + } + else if (drag_ == DRAG_POS_MOVE) + { + x += move_.x; + y += move_.y; + OffsetRect(&user_sel_, x - user_sel_.left, y - user_sel_.top); + if (user_sel_.left < whole_.left) + OffsetRect(&user_sel_, whole_.left - user_sel_.left, 0); + if (user_sel_.right > whole_.right) + OffsetRect(&user_sel_, whole_.right - user_sel_.right, 0); + if (user_sel_.top < whole_.top) + OffsetRect(&user_sel_, 0, whole_.top - user_sel_.top); + if (user_sel_.bottom > whole_.bottom) + OffsetRect(&user_sel_, 0, whole_.bottom - user_sel_.bottom); + x_ = pos_2_area(user_sel_.left, true); + if (x_ + w_ > paper_w_) + x_ = paper_w_ - w_; + y_ = pos_2_area(user_sel_.top, false); + if (y_ + h_ > paper_h_) + y_ = paper_h_ - h_; + } + + refresh_paper_info(); + InvalidateRect(hwnd(), &whole_, FALSE); + return; + } + + POINT pt = { x, y }; + std::vector r; + bool handled = false; + + drag_blocks(r); + for (size_t i = 0; i < r.size(); ++i) + { + if (PtInRect(&r[i], pt)) + { + handled = true; + if (r[i].pos == DRAG_POS_LT || r[i].pos == DRAG_POS_RB) + cursor_ = IDC_SIZENWSE; + else if(r[i].pos == DRAG_POS_RT || r[i].pos == DRAG_POS_LB) + cursor_ = IDC_SIZENESW; + else if(r[i].pos == DRAG_POS_MT || r[i].pos == DRAG_POS_MB) + cursor_ = IDC_SIZENS; + else + cursor_ = IDC_SIZEWE; + + drag_ = r[i].pos; + SetCursor(LoadCursor(NULL, cursor_)); + break; + } + } + if (!handled) + { + if (PtInRect(&user_sel_, pt)) + { + drag_ = DRAG_POS_MOVE; + cursor_ = IDC_SIZEALL; + SetCursor(LoadCursor(NULL, cursor_)); + } + else + { + drag_ = DRAG_POS_NONE; + cursor_ = NULL; + } + } +} +void dlg_area::on_lbutton_down(int x, int y) +{ + move_.x = user_sel_.left - x; + move_.y = user_sel_.top - y; + if (cursor_) + SetCursor(LoadCursorW(NULL, cursor_)); + SetCapture(hwnd()); +} +void dlg_area::on_lbutton_up(int x, int y) +{ + drag_ = DRAG_POS_NONE; + cursor_ = NULL; + ReleaseCapture(); +} + + +void dlg_area::set_paper(const wchar_t* name, int width_px, int height_px, float dpi) +{ + wchar_t paper[40] = { 0 }; + + dpi_ = dpi; + paper_ = name; + + paper_w_0_ = dlg_area::pixel_2_mm(width_px, dpi); + paper_h_0_ = dlg_area::pixel_2_mm(height_px, dpi); + + if (unit_ == PAPER_UNIT_INCH) + { + paper_w_ = dlg_area::pixel_2_inches(width_px, dpi); + paper_h_ = dlg_area::pixel_2_inches(height_px, dpi); + } + else if (unit_ == PAPER_UNIT_MM) + { + paper_w_ = paper_w_0_; + paper_h_ = paper_h_0_; + } + else + { + paper_w_ = width_px; + paper_h_ = height_px; + } + + RECT r = { 0 }; + float xr = 1.0f, + yr = 1.0f; + + GetWindowRect(get_item(IDC_STATIC_PAINT), &r); + screen_2_client(&r); + xr = paper_w_0_ / (r.right - r.left); + yr = paper_h_0_ / (r.bottom - r.top); + ratio_ = xr >= yr ? xr : yr; + + xr = paper_w_0_ / ratio_; + xr = (r.right - r.left - xr) / 2; + yr = paper_h_0_ / ratio_; + yr = (r.bottom - r.top - yr) / 2; + whole_.left = r.left + (LONG)xr; whole_.right = r.right - (LONG)xr; + whole_.top = r.top + (LONG)yr; whole_.bottom = r.bottom - (LONG)yr; + + clear_area(); +} +void dlg_area::set_area(float x, float y, float w, float h) +{ + x_ = from_mm(x); + y_ = from_mm(y); + w_ = from_mm(w); + h_ = from_mm(h); + + if (x_ > paper_w_) + { + if (w_ < paper_w_) + x_ = paper_w_ - w_; + else + x_ = 0; + } + if (w_ + x_ > paper_w_) + w_ = paper_w_ - x_; + if (y_ > paper_h_) + { + if (h_ < paper_h_) + y_ = paper_h_ - h_; + else + y_ = 0; + } + if (h_ + y_ > paper_h_) + h_ = paper_h_ - y_; + user_sel_.left = whole_.left + (LONG)(x_ / ratio_); + user_sel_.top = whole_.top + (LONG)(y_ / ratio_); + user_sel_.right = user_sel_.left + (LONG)(w_ / ratio_); + user_sel_.bottom = user_sel_.top + (LONG)(h_ / ratio_); + + refresh_paper_info(); +} +float dlg_area::x_in_mm(void) +{ + return as_mm(x_); +} +float dlg_area::y_in_mm(void) +{ + return as_mm(y_); +} +float dlg_area::w_in_mm(void) +{ + return as_mm(w_); +} +float dlg_area::h_in_mm(void) +{ + return as_mm(h_); +} diff --git a/sln/usb_tools/opt_ui/DlgArea.h b/sln/usb_tools/opt_ui/DlgArea.h new file mode 100644 index 0000000..17562c3 --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgArea.h @@ -0,0 +1,98 @@ +#pragma once + +#include +#include + +#include "DlgPage.h" + +// CDlgIndicator 对话框 + +class dlg_area: public dlg_base +{ + enum paper_unit + { + PAPER_UNIT_MM = 0, + PAPER_UNIT_INCH, + PAPER_UNIT_PIXEL, + }; + enum drag_pos + { + DRAG_POS_NONE = 0, + DRAG_POS_LT, + DRAG_POS_MT, + DRAG_POS_RT, + DRAG_POS_LM, + DRAG_POS_RM, + DRAG_POS_LB, + DRAG_POS_MB, + DRAG_POS_RB, + DRAG_POS_MOVE, + }; + typedef struct _drag_block : RECT + { + drag_pos pos; + }DRAGRECT; + std::wstring paper_; + paper_unit unit_; + float paper_w_0_; + float paper_h_0_; + float paper_w_; + float paper_h_; + float dpi_; + float x_; + float y_; + float w_; + float h_; + + RECT whole_; + RECT user_sel_; + float ratio_; // mm / pixel + drag_pos drag_; + POINT move_; + bool in_set_func_; + const wchar_t* cursor_; + + static int area_min_pixel; + + BOOL handle_message(UINT msg, WPARAM wp, LPARAM lp) override; + void handle_command(WORD code, WORD id, HANDLE ctrl); + void layout(void); + + float as_mm(float v); + float from_mm(float v); + float as_inches(float v); + float as_pixels(float v); + std::wstring format_number(float v); + void refresh_paper_info(void); + void to_unit(paper_unit unit); + void clear_area(void); + void drag_blocks(std::vector& blocks); + float pos_2_area(int val, bool x); + void valid_x(int& x, bool left); + void valid_y(int& y, bool top); + + void on_unit_changed(HWND wnd); + void on_paint(HDC hdc); + void on_mouse_move(DWORD key, int x, int y); + void on_lbutton_down(int x, int y); + void on_lbutton_up(int x, int y); + +public: + dlg_area(HWND parent); + ~dlg_area(); + + static float mm_2_pixel(float mm, float dpi); + static float mm_2_inches(float mm); + static float inches_2_pixel(float inch, float dpi); + static float inches_2_mm(float inch); + static float pixel_2_mm(float px, float dpi); + static float pixel_2_inches(float px, float dpi); + +public: + void set_paper(const wchar_t* name, int width_px, int height_px, float dpi); + void set_area(float x, float y, float w, float h); + float x_in_mm(void); + float y_in_mm(void); + float w_in_mm(void); + float h_in_mm(void); +}; diff --git a/sln/usb_tools/opt_ui/DlgGamma.cpp b/sln/usb_tools/opt_ui/DlgGamma.cpp new file mode 100644 index 0000000..08dd5da --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgGamma.cpp @@ -0,0 +1,823 @@ +// DlgIndicator.cpp: 实现文件 +// + +#include "DlgGamma.h" +#include "../resource.h" +#include "mem_dc.h" + +#define MAX_KEY_POINTS 4 // not include (0, 0) and (255, 255) + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// calculator +#include +namespace calc +{ + void solve_matrix(double** a, int n, std::vector& coef) + { + int m = 0; + int i, j; + + coef.clear(); + for (j = 0; j < n; j++) { + double max = 0; + double imax = 0; + for (i = j; i < n; i++) { + if (imax < fabs(a[i][j])) { + imax = fabs(a[i][j]); + max = a[i][j];//得到各行中所在列最大元素 + m = i; + } + } + if (fabs(a[j][j]) != max) { + double b = 0; + for (int k = j; k < n + 1; k++) { + b = a[j][k]; + a[j][k] = a[m][k]; + a[m][k] = b; + } + } + for (int r = j; r < n + 1; r++) { + a[j][r] = a[j][r] / max;//让该行的所在列除以所在列的第一个元素,目的是让首元素为1 + } + for (i = j + 1; i < n; i++) { + double c = a[i][j]; + if (c == 0) continue; + for (int s = j; s < n + 1; s++) { + //double tempdata = a[i][s]; + a[i][s] = a[i][s] - a[j][s] * c;//前后行数相减,使下一行或者上一行的首元素为0 + } + } + } + for (i = n - 2; i >= 0; i--) { + for (j = i + 1; j < n; j++) { + a[i][n] = a[i][n] - a[j][n] * a[i][j]; + } + } + + for (int k = 0; k < n; k++) { + coef.push_back(a[k][n]); + } + } + int power(int m, int exp) + { + int val = 1; + for (int i = 0; i < exp; ++i) + val *= m; + + return val; + } + std::vector coefs_from_points(const std::vector& pt) + { + std::vector coef; + double** a = new double* [pt.size()]; + + for (int i = 0; i < (int)pt.size(); ++i) + a[i] = new double[pt.size() + 1]; + + for (int i = 0; i < (int)pt.size(); ++i) + { + for (int j = 0; j < (int)pt.size(); ++j) + a[i][j] = power(pt[i].x, pt.size() - j - 1); + a[i][pt.size()] = pt[i].y; + } + solve_matrix(a, pt.size(), coef); + for (int i = 0; i < (int)pt.size(); ++i) + delete[] a[i]; + delete[] a; + + return coef; + } +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// dlg_gamma +dlg_gamma::dlg_gamma(HWND parent, bool color) : dlg_base(parent, IDD_GAMMA), is_color_(color), bkgnd_(NULL) + , show_all_(false) +{ + std::wstring title(local_trans::lang_trans_between_hz936(CONST_STRING_CUSTOM_TONE)); + + create(); + SetWindowTextW(hwnd(), title.c_str()); + + if (!dlg_base::is_language_pack_default_code_page()) + { + title = local_trans::lang_trans_between_hz936(CONST_STRING_TONE_SCHEME); + set_item_text(IDC_STATIC_SCHEME, title.c_str()); + + title = local_trans::lang_trans_between_hz936(CONST_STRING_COLOR_CHANNEL); + set_item_text(IDC_STATIC_COLOR, title.c_str()); + + title = local_trans::lang_trans_between_hz936(CONST_STRING_INITIALIZE); + set_item_text(IDC_BUTTON_RESET, title.c_str()); + + title = local_trans::lang_trans_between_hz936(CONST_STRING_INPUT); + set_item_text(IDC_STATIC_INPUT, title.c_str()); + + title = local_trans::lang_trans_between_hz936(CONST_STRING_OUTPUT); + set_item_text(IDC_STATIC_OUTPUT, title.c_str()); + + title = local_trans::lang_trans_between_hz936(CONST_STRING_CANCEL); + set_item_text(IDCANCEL, title.c_str()); + + title = local_trans::lang_trans_between_hz936(CONST_STRING_OK); + set_item_text(IDOK, title.c_str()); + + layout(); + } +} +dlg_gamma::~dlg_gamma() +{ + DeleteObject(bkgnd_); +} + + +BOOL dlg_gamma::handle_message(UINT msg, WPARAM wp, LPARAM lp) +{ + wchar_t text[40] = { 0 }; + BOOL ret = TRUE; + + switch (msg) + { + case WM_INITDIALOG: + on_init_dlg(); + UpdateWindow(hwnd()); + break; + case WM_COMMAND: + handle_command(HIWORD(wp), LOWORD(wp), (HWND)lp); + break; + case WM_PAINT: + { + PAINTSTRUCT ps = { 0 }; + HDC hdc = BeginPaint(hwnd(), &ps); + { + compatible_dc dc(hdc); + on_paint(dc.get_dc()); + } + EndPaint(hwnd(), &ps); + } + break; + case WM_MOUSEMOVE: + on_mouse_move(wp, LOWORD(lp), HIWORD(lp)); + break; + case WM_LBUTTONDOWN: + on_lbutton_down(LOWORD(lp), HIWORD(lp)); + break; + case WM_LBUTTONUP: + on_lbutton_up(LOWORD(lp), HIWORD(lp)); + break; + default: + ret = FALSE; + } + return ret; +} +void dlg_gamma::handle_command(WORD code, WORD id, HANDLE ctrl) +{ + if (code == BN_CLICKED) + { + if (id == IDC_BUTTON_RESET) + { + init_curve(cur_, cur_->clr); + InvalidateRect(hwnd(), NULL, FALSE); + } + else + { + quit_modal(id); + } + } + else if (code == EN_CHANGE) + { + if (id == IDC_EDIT_INPUT) + { + wchar_t val[40] = { 0 }; + int y = 0; + + GetDlgItemTextW(hwnd(), id, val, _countof(val) - 1); + y = _wtoi(val); + if (y < cur_->points[0].x) + SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, cur_->points[0].x, FALSE); + if (y > cur_->points[1].x) + SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, cur_->points[1].x, FALSE); + else + { + y = calc_value(y); + SetDlgItemInt(hwnd(), IDC_EDIT_OUTPUT, y, FALSE); + } + } + } + else if (code == CBN_SELCHANGE) + { + on_combo_sel_changed(id, SendMessage((HWND)ctrl, CB_GETCURSEL, 0, 0)); + } +} +void dlg_gamma::layout(void) +{ + int r1 = get_width_diff_as_text_length(IDC_STATIC_SCHEME), + r21 = get_width_diff_as_text_length(IDC_STATIC_COLOR), + r22 = get_width_diff_as_text_length(IDC_BUTTON_RESET), + r31 = get_width_diff_as_text_length(IDC_STATIC_INPUT), + r32 = get_width_diff_as_text_length(IDC_STATIC_OUTPUT), + r33 = get_width_diff_as_text_length(IDCANCEL), + r34 = get_width_diff_as_text_length(IDOK), + diff = 0; + + if (diff < r1) + diff = r1; + if (diff < r21 + r22) + diff = r21 + r22; + if (diff < r31 + r32 + r33 + r34) + diff = r31 + r32 + r33 + r34; + + if (diff > 0) + { + RECT r = { 0 }; + + GetWindowRect(hwnd(), &r); + r.right += diff; + MoveWindow(hwnd(), r.left, r.top, RECT_W(r), RECT_H(r), FALSE); + + expand_item(IDC_STATIC_SCHEME, r1, 0); + offset_item(IDC_SCHEME, r1, 0); + expand_item(IDC_SCHEME, diff - r1, 0); + + expand_item(IDC_STATIC_COLOR, r21, 0); + offset_item(IDC_CHANNEL, r21, 0); + + expand_item(IDC_BUTTON_RESET, r22, 0); + offset_item(IDC_BUTTON_RESET, diff - r22, 0); + + expand_item(IDC_STATIC_INPUT, r31, 0); + offset_item(IDC_EDIT_INPUT, r31, 0); + + offset_item(IDC_STATIC_OUTPUT, r31, 0); + expand_item(IDC_STATIC_OUTPUT, r32, 0); + offset_item(IDC_EDIT_OUTPUT, r31 + r32, 0); + + offset_item(IDCANCEL, diff - r33, 0); + expand_item(IDCANCEL, r33, 0); + + offset_item(IDOK, diff - r34, 0); + expand_item(IDOK, r34, 0); + + expand_item(IDC_STATIC_PAINT, diff, 0); + } +} +void dlg_gamma::create_background(void) +{ + COLORREF bclr = RGB(128, 128, 128); + HDC hdc = GetWindowDC(hwnd()), + cdc = CreateCompatibleDC(hdc); + HBRUSH bkg = CreateSolidBrush(bclr); + HPEN grid = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)), + old = (HPEN)SelectObject(cdc, grid); + HBITMAP ob = NULL; + int w = paint_area_.right - paint_area_.left + 2, + h = paint_area_.bottom - paint_area_.top + 2; + RECT r = { 1, 1, w - 1, h - 1}; + + if (bkgnd_) + DeleteObject(bkgnd_); + + // create bitmap and select into DC + bkgnd_ = CreateCompatibleBitmap(hdc, w, h); + ob = (HBITMAP)SelectObject(cdc, bkgnd_); + SetBkColor(cdc, bclr); + + // background and grid + FillRect(cdc, &r, bkg); + MoveToEx(cdc, r.left, r.top, NULL); + LineTo(cdc, r.right, r.top); + LineTo(cdc, r.right, r.bottom); + LineTo(cdc, r.left, r.bottom); + LineTo(cdc, r.left, r.top); + SelectObject(cdc, old); + DeleteObject(grid); + grid = CreatePen(PS_DASH, 1, RGB(0, 0, 0)); + SelectObject(cdc, grid); + for (int i = 1; i < 6; ++i) + { + MoveToEx(cdc, r.left + i * 50, r.bottom, NULL); + LineTo(cdc, r.left + i * 50, r.top); + + MoveToEx(cdc, r.left, r.bottom - i * 50, NULL); + LineTo(cdc, r.right, r.bottom - i * 50); + } + + // default curve + MoveToEx(cdc, r.left, r.bottom, NULL); + LineTo(cdc, r.right, r.top); + + // free resource + SelectObject(cdc, old); + SelectObject(cdc, ob); + DeleteDC(cdc); + ReleaseDC(hwnd(), hdc); + + DeleteObject(bkg); + DeleteObject(grid); +} +void dlg_gamma::init_curve(GAMMACURVE* curv, COLORREF clr) +{ + curv->points.clear(); + curv->points.push_back({ 0, 0 }); + curv->points.push_back({ 255, 255 }); + curv->coefs = calc::coefs_from_points(curv->points); + curv->drag = -1; + curv->left = 0; + curv->right = 1; + curv->clr = clr; +} +int dlg_gamma::add_drag_point(int x, int y) +{ + int ind = -1; + + if (cur_->points.size() < MAX_KEY_POINTS + 2) + { + for (int i = 2; i < (int)cur_->points.size(); ++i) + { + if (x < cur_->points[i].x) + { + cur_->points.insert(cur_->points.begin() + i, { x, calc_value(x) }); + ind = i; + cur_->coefs = calc::coefs_from_points(cur_->points); + break; + } + else if (x == cur_->points[i].x) + { + ind = i; + break; + } + } + if (ind == -1) + { + ind = cur_->points.size(); + cur_->points.push_back({ x, calc_value(x) }); + cur_->coefs = calc::coefs_from_points(cur_->points); + } + cur_->left = ind - 1; + cur_->right = ind + 1; + if (cur_->left == 1) + cur_->left = 0; + if (cur_->right >= (int)cur_->points.size()) + cur_->right = 1; + } + else + { + for (int i = 2; i < (int)cur_->points.size(); ++i) + { + if (x == cur_->points[i].x) + { + ind = i; + cur_->left = ind - 1; + cur_->right = ind + 1; + if (cur_->left == 1) + cur_->left = 0; + if (cur_->right >= (int)cur_->points.size()) + cur_->right = 1; + break; + } + } + } + cur_->drag = ind; + + return ind; +} +BYTE dlg_gamma::calc_value(BYTE x) +{ + double y = .0f; + for(int j = 0; j < (int)cur_->points.size(); ++j) + y += cur_->coefs[j] * calc::power(x, cur_->points.size() - j - 1); + + if (y > 255.0f) + y = 255.0f; + if (y < .0f) + y = .0f; + + bool upper = cur_->points[1].y > cur_->points[0].y; + if (y < cur_->points[!upper].y) + y = cur_->points[!upper].y; + if (y > cur_->points[upper].y) + y = cur_->points[upper].y; + + return (BYTE)(y + .5f); +} +bool dlg_gamma::is_adjacent(POINT p1, POINT p2) +{ + bool neighbour = abs(p1.x - p2.x) <= 3 && abs(p1.y - p2.y) <= 3; + + return neighbour; +} +bool dlg_gamma::hit_test(int* x, int* y) +{ + for (int i = 2; i < (int)cur_->points.size(); ++i) + { + POINT pt = { *x, *y }; + if (is_adjacent(pt, cur_->points[i])) + { + *x = cur_->points[i].x; + *y = cur_->points[i].y; + return true; + } + } + + + int val = calc_value(*x), tolerant = 3; + bool hit = false; + + if (abs(val - *y) < tolerant) + { + *y = val; + hit = true; + } + else + { + int l = *x - tolerant, + u = *x + tolerant; + if (l < 0) + l = 0; + if (u > 255) + u = 255; + for (; l <= u; ++l) + { + val = calc_value(l); + if (abs(val - *y) < tolerant) + { + hit = true; + *x = l; + *y = val; + break; + } + } + } + + return hit; +} +void dlg_gamma::draw_ellipse(HDC hdc, POINT center, int xl, int yl) +{ + center.x += paint_area_.left; + center.y = paint_area_.bottom - center.y; + + HRGN rgn = CreateEllipticRgn(center.x - xl, center.y - yl, center.x + xl, center.y + yl); + HBRUSH brsh = CreateSolidBrush(RGB(255, 0, 255)); + FillRgn(hdc, rgn, brsh); + DeleteObject(brsh); + DeleteObject(rgn); +} +void dlg_gamma::draw_current_curve(HDC hdc) +{ + MoveToEx(hdc, paint_area_.left + cur_->points[0].x, paint_area_.bottom - cur_->points[0].y, NULL); + for (int i = 0; i < 256; ++i) + { + int y = calc_value(i); + LineTo(hdc, paint_area_.left + i, paint_area_.bottom - y); + } +} + +void dlg_gamma::on_init_dlg(void) +{ + HWND combo = get_item(IDC_SCHEME); + + GetWindowRect(get_item(IDC_STATIC_PAINT), &paint_area_); + screen_2_client(&paint_area_); + paint_area_.right = paint_area_.left + 255; + paint_area_.bottom = paint_area_.top + 255; + create_background(); + + init_curve(&rgb_gray_); + init_curve(&red_, RGB(255, 0, 0)); + init_curve(&green_, RGB(0, 255, 0)); + init_curve(&blue_, RGB(0, 0, 255)); + + std::wstring lang(local_trans::lang_trans_between_hz936(CONST_STRING_CUSTOM)); + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + lang = local_trans::lang_trans_between_hz936(CONST_STRING_NEG_PHOTO) + L" (RGB)"; + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + lang = local_trans::lang_trans_between_hz936(CONST_STRING_COLOR_NEG_PHOTO) + L" (RGB)"; + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + lang = local_trans::lang_trans_between_hz936(CONST_STRING_DARKER) + L" (RGB)"; + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + lang = local_trans::lang_trans_between_hz936(CONST_STRING_BRIGHTER) + L" (RGB)"; + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + SendMessage(combo, CB_SETCURSEL, 0, 0); + + combo = get_item(IDC_CHANNEL); + if (is_color_) + { + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"RGB"); + lang = local_trans::lang_trans_between_hz936(CONST_STRING_RED); + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + lang = local_trans::lang_trans_between_hz936(CONST_STRING_GREEN); + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + lang = local_trans::lang_trans_between_hz936(CONST_STRING_BLUE); + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + } + else + { + lang = local_trans::lang_trans_between_hz936(CONST_STRING_GRAY); + SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)&lang[0]); + } + SendMessage(combo, CB_SETCURSEL, 0, 0); + cur_ = &rgb_gray_; + + SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, 0, FALSE); + SetDlgItemInt(hwnd(), IDC_EDIT_OUTPUT, 0, FALSE); +} +void dlg_gamma::on_paint(HDC hdc) +{ + HPEN pen = CreatePen(PS_SOLID, 1, cur_->clr), + drag = CreatePen(PS_SOLID, 1, RGB(255, 0, 255)), + old = (HPEN)SelectObject(hdc, pen); + HDC cdc = CreateCompatibleDC(hdc); + HBITMAP ob = (HBITMAP)SelectObject(cdc, bkgnd_); + + BitBlt(hdc, paint_area_.left - 1, paint_area_.top - 1, paint_area_.right - paint_area_.left + 2, paint_area_.bottom - paint_area_.top + 2, + cdc, 0, 0, SRCCOPY); + SelectObject(cdc, ob); + DeleteDC(cdc); + + if (show_all_) + { + GAMMACURVE* prev = cur_; + HPEN now = NULL; + + cur_ = &red_; + now = CreatePen(PS_SOLID, 1, cur_->clr); + SelectObject(hdc, now); + draw_current_curve(hdc); + + cur_ = &green_; + SelectObject(hdc, old); + DeleteObject(now); + now = CreatePen(PS_SOLID, 1, cur_->clr); + SelectObject(hdc, now); + draw_current_curve(hdc); + + cur_ = &blue_; + SelectObject(hdc, old); + DeleteObject(now); + now = CreatePen(PS_SOLID, 1, cur_->clr); + SelectObject(hdc, now); + draw_current_curve(hdc); + + SelectObject(hdc, pen); + DeleteObject(now); + cur_ = prev; + } + else + draw_current_curve(hdc); + SelectObject(hdc, drag); + + for (int i = 0; i < (int)cur_->points.size(); ++i) + { + draw_ellipse(hdc, cur_->points[i], 3, 3); + } + SelectObject(hdc, old); + + DeleteObject(pen); +} +void dlg_gamma::on_mouse_move(DWORD key, int x, int y) +{ + POINT pt = { x, y }; + + if (PtInRect(&paint_area_, pt)) + { + x -= paint_area_.left; + y = paint_area_.bottom - y; + if (key == MK_LBUTTON && cur_->drag != -1) + { + if (x <= cur_->points[cur_->left].x) + x = cur_->points[cur_->left].x + 1; + else if (x >= cur_->points[cur_->right].x) + x = cur_->points[cur_->right].x - 1; + cur_->points[cur_->drag].x = x; + cur_->points[cur_->drag].y = y; + cur_->coefs = calc::coefs_from_points(cur_->points); + InvalidateRect(hwnd(), NULL, FALSE); + } + else if (key == MK_CONTROL) + { + y = calc_value(x); + pt.y = paint_area_.bottom - y; + ClientToScreen(hwnd(), &pt); + SetCursorPos(pt.x, pt.y); + } + SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, x, FALSE); + SetDlgItemInt(hwnd(), IDC_EDIT_OUTPUT, y, FALSE); + } +} +void dlg_gamma::on_lbutton_down(int x, int y) +{ + x -= paint_area_.left; + y = paint_area_.bottom - y; + if (hit_test(&x, &y)) + { + if(add_drag_point(x, y) != -1) + SetCapture(hwnd()); + } +} +void dlg_gamma::on_lbutton_up(int x, int y) +{ + cur_->drag = -1; + cur_->left = 0; + cur_->right = 1; + ReleaseCapture(); +} +void dlg_gamma::on_combo_sel_changed(int id, int sel) +{ + show_all_ = false; + if (id == IDC_SCHEME) + { + if (sel == 1) + { + cur_ = &rgb_gray_; + init_curve(cur_); + cur_->points.clear(); + cur_->points.push_back({ 0, 255 }); + cur_->points.push_back({ 255, 0 }); + cur_->coefs = calc::coefs_from_points(cur_->points); + } + else if (sel == 2) + { + show_all_ = true; + cur_ = &red_; + init_curve(cur_, cur_->clr); + cur_->points.clear(); + cur_->points.push_back({ 33, 255 }); + cur_->points.push_back({ 185, 0 }); + cur_->points.push_back({ 119, 127 }); + cur_->coefs = calc::coefs_from_points(cur_->points); + + cur_ = &green_; + init_curve(cur_, cur_->clr); + cur_->points.clear(); + cur_->points.push_back({ 28, 255 }); + cur_->points.push_back({ 132, 0 }); + cur_->points.push_back({ 77, 127 }); + cur_->coefs = calc::coefs_from_points(cur_->points); + + cur_ = &blue_; + init_curve(cur_, cur_->clr); + cur_->points.clear(); + cur_->points.push_back({ 25, 255 }); + cur_->points.push_back({ 108, 0 }); + cur_->points.push_back({ 60, 127 }); + cur_->coefs = calc::coefs_from_points(cur_->points); + + cur_ = &red_; + } + else if (sel == 3) + { + cur_ = &rgb_gray_; + init_curve(cur_); + cur_->points.push_back({ 130, 101 }); + cur_->coefs = calc::coefs_from_points(cur_->points); + } + else if (sel == 4) + { + cur_ = &rgb_gray_; + init_curve(cur_); + cur_->points.push_back({ 103, 125 }); + cur_->coefs = calc::coefs_from_points(cur_->points); + } + InvalidateRect(hwnd(), NULL, FALSE); + } + else if (id == IDC_CHANNEL) + { + GAMMACURVE* prev = cur_, * all[] = { &rgb_gray_, &red_, &green_, &blue_ }; + + cur_ = all[sel]; + if (prev != cur_) + { + if (cur_->points.size() == 2) + { + COLORREF clr = cur_->clr; + + *cur_ = *prev; + cur_->clr = clr; + } + InvalidateRect(hwnd(), NULL, FALSE); + } + } +} + +void dlg_gamma::get_gamma(SANE_Gamma* gamma) +{ + //gamma->apply_to_back = SANE_FALSE; + ////if (cur_ == &rgb_gray_) + //{ + // cur_ = &rgb_gray_; + // gamma->pt_count = (SANE_Byte)(cur_->points.size() - 2); + //// gamma->pt_count_r = gamma->pt_count_g = gamma->pt_count_b = 0; + // for (int i = 2; i < (int)cur_->points.size(); ++i) + // { + // gamma->keypoint[i - 2].x = (SANE_Byte)cur_->points[i].x; + // gamma->keypoint[i - 2].y = (SANE_Byte)cur_->points[i].y; + // } + + // for (int i = 0; i < 256; ++i) + // gamma->table[i] = calc_value(i); + //} + ////else + //{ + //// gamma->pt_count = 0; + + // cur_ = &red_; + // gamma->pt_count_r = (SANE_Byte)(cur_->points.size() - 2); + // for (int i = 2; i < (int)cur_->points.size(); ++i) + // { + // gamma->keypoint_r[i - 2].x = (SANE_Byte)cur_->points[i].x; + // gamma->keypoint_r[i - 2].y = (SANE_Byte)cur_->points[i].y; + // } + // for (int i = 0; i < 256; ++i) + // gamma->table[i] = calc_value(i); + + // cur_ = &green_; + // gamma->pt_count_g = (SANE_Byte)(cur_->points.size() - 2); + // for (int i = 2; i < (int)cur_->points.size(); ++i) + // { + // gamma->keypoint_g[i - 2].x = (SANE_Byte)cur_->points[i].x; + // gamma->keypoint_g[i - 2].y = (SANE_Byte)cur_->points[i].y; + // } + // for (int i = 0; i < 256; ++i) + // gamma->table[256 + i] = calc_value(i); + + // cur_ = &blue_; + // gamma->pt_count_b = (SANE_Byte)(cur_->points.size() - 2); + // for (int i = 2; i < (int)cur_->points.size(); ++i) + // { + // gamma->keypoint_b[i - 2].x = (SANE_Byte)cur_->points[i].x; + // gamma->keypoint_b[i - 2].y = (SANE_Byte)cur_->points[i].y; + // } + // for (int i = 0; i < 256; ++i) + // gamma->table[512 + i] = calc_value(i); + //} +} +void dlg_gamma::set_gamma(const SANE_Gamma* gamma, bool gray) +{ + //int sel = 0; + //std::wstring text(L""); + + //SendMessage(get_item(IDC_CHANNEL), CB_RESETCONTENT, 0, 0); + //is_color_ = !gray; + //cur_ = &rgb_gray_; + //init_curve(cur_); + //for (int i = 0; i < gamma->pt_count; ++i) + //{ + // POINT pt = { gamma->keypoint[i].x, gamma->keypoint[i].y }; + // cur_->points.push_back(pt); + //} + //cur_->coefs = calc::coefs_from_points(cur_->points); + //if (is_color_) + //{ + // SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)L"RGB"); + //} + //else + //{ + // text = local_trans::lang_trans_between_hz936(CONST_STRING_GRAY); + // SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)&text[0]); + //} + + //if(!gray) + //{ + // is_color_ = true; + // cur_ = &red_; + // init_curve(cur_); + // for (int i = 0; i < gamma->pt_count_r; ++i) + // { + // POINT pt = { gamma->keypoint_r[i].x, gamma->keypoint_r[i].y }; + // cur_->points.push_back(pt); + // } + // cur_->coefs = calc::coefs_from_points(cur_->points); + + // cur_ = &green_; + // init_curve(cur_); + // for (int i = 0; i < gamma->pt_count_g; ++i) + // { + // POINT pt = { gamma->keypoint_g[i].x, gamma->keypoint_g[i].y }; + // cur_->points.push_back(pt); + // } + // cur_->coefs = calc::coefs_from_points(cur_->points); + + // cur_ = &blue_; + // init_curve(cur_); + // for (int i = 0; i < gamma->pt_count_b; ++i) + // { + // POINT pt = { gamma->keypoint_b[i].x, gamma->keypoint_b[i].y }; + // cur_->points.push_back(pt); + // } + // cur_->coefs = calc::coefs_from_points(cur_->points); + + //// SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)L"RGB"); + // text = local_trans::lang_trans_between_hz936(CONST_STRING_RED); + // SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)&text[0]); + // text = local_trans::lang_trans_between_hz936(CONST_STRING_GREEN); + // SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)&text[0]); + // text = local_trans::lang_trans_between_hz936(CONST_STRING_BLUE); + // SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)&text[0]); + //} + //cur_ = &rgb_gray_; + //sel = 0; + //SendMessage(get_item(IDC_CHANNEL), CB_SETCURSEL, sel, 0); + //SendMessage(get_item(IDC_SCHEME), CB_SETCURSEL, 0, 0); +} diff --git a/sln/usb_tools/opt_ui/DlgGamma.h b/sln/usb_tools/opt_ui/DlgGamma.h new file mode 100644 index 0000000..e18ba4d --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgGamma.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#include "DlgPage.h" + +// CDlgIndicator 对话框 + +class dlg_gamma: public dlg_base +{ + bool is_color_; + bool show_all_; + RECT paint_area_; + HBITMAP bkgnd_; + + typedef struct _gamma_curve + { + std::vector points; + std::vector coefs; + int drag; + int left; + int right; + COLORREF clr; + }GAMMACURVE; + GAMMACURVE rgb_gray_; + GAMMACURVE red_; + GAMMACURVE green_; + GAMMACURVE blue_; + GAMMACURVE* cur_; + + BOOL handle_message(UINT msg, WPARAM wp, LPARAM lp) override; + void handle_command(WORD code, WORD id, HANDLE ctrl); + void layout(void); + void create_background(void); + void init_curve(GAMMACURVE* curv, COLORREF clr = RGB(255, 255, 255)); + int add_drag_point(int x, int y); + BYTE calc_value(BYTE x); + bool is_adjacent(POINT p1, POINT p2); + bool hit_test(int* x, int* y); + void draw_ellipse(HDC hdc, POINT center, int xl, int yl); + void draw_current_curve(HDC hdc); + + void on_init_dlg(void); + void on_paint(HDC hdc); + void on_mouse_move(DWORD key, int x, int y); + void on_lbutton_down(int x, int y); + void on_lbutton_up(int x, int y); + void on_combo_sel_changed(int id, int sel); + +public: + dlg_gamma(HWND parent, bool color); + ~dlg_gamma(); + +public: + void get_gamma(SANE_Gamma* gamma); + void set_gamma(const SANE_Gamma* gamma, bool gray); +}; diff --git a/sln/usb_tools/opt_ui/DlgPage.cpp b/sln/usb_tools/opt_ui/DlgPage.cpp new file mode 100644 index 0000000..340cf6d --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgPage.cpp @@ -0,0 +1,1741 @@ +// DlgIndicator.cpp: 实现文件 +// + +#include "DlgPage.h" +#include "../resource.h" +#include "DlgArea.h" +#include "DlgGamma.h" +#include + +// #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + +static IMPLEMENT_OPTION_STRING_COMPARE(is_sane_opt); + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// dlg_base 对话框 +extern HMODULE g_my_inst; + +std::wstring dlg_base::prop_name = L"dlg_base_object_prop_name"; + +dlg_base::dlg_base(HWND parent, UINT idd) : parent_(parent), hwnd_(NULL), idd_(idd), ui_event_notify_(NULL), ui_notify_param_(NULL) +{ +} +dlg_base::~dlg_base() +{ + if (IsWindow(hwnd_)) + { + SetPropW(hwnd_, dlg_base::prop_name.c_str(), NULL); + //if (GetCurrentThreadId() == GetWindowThreadProcessId(hwnd_, NULL)) + DestroyWindow(hwnd_); + } + EnableWindow(parent_, TRUE); + BringWindowToTop(parent_); +} + +BOOL CALLBACK dlg_base::dlg_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + if (msg == WM_INITDIALOG) + { + dlg_base* obj = (dlg_base*)lp; + + SetPropW(hwnd, dlg_base::prop_name.c_str(), (HANDLE)obj); + obj->hwnd_ = hwnd; + } + else if (msg == WM_DESTROY) + { + dlg_base* obj = (dlg_base*)GetPropW(hwnd, dlg_base::prop_name.c_str()); + SetPropW(hwnd, dlg_base::prop_name.c_str(), NULL); + + return FALSE; + } + + dlg_base *obj = (dlg_base*)GetPropW(hwnd, dlg_base::prop_name.c_str()); + BOOL ret = FALSE; + + if (obj) + { + ret = obj->handle_message(msg, wp, lp); + } + + return ret; +} + +void dlg_base::screen_2_client(HWND wnd, LPRECT r) +{ + POINT pt = { r->left, r->top }; + + ScreenToClient(wnd, &pt); + OffsetRect(r, pt.x - r->left, pt.y - r->top); +} +void dlg_base::client_2_screen(HWND wnd, LPRECT r) +{ + POINT pt = { r->left, r->top }; + + ClientToScreen(wnd, &pt); + OffsetRect(r, pt.x - r->left, pt.y - r->top); +} +bool dlg_base::get_max_size(SIZE& dst, const SIZE& src) +{ + bool changed = false; + + if (dst.cx < src.cx) + { + dst.cx = src.cx; + changed = true; + } + if (dst.cy < src.cy) + { + dst.cy = src.cy; + changed = true; + } + + return changed; +} +bool dlg_base::get_max_size(SIZE& dst, int cx, int cy) +{ + bool changed = false; + + if (dst.cx < cx) + { + dst.cx = cx; + changed = true; + } + if (dst.cy < cy) + { + dst.cy = cy; + changed = true; + } + + return changed; +} +int dlg_base::select_combo_text(HWND combo, const wchar_t* text) +{ + int ind = SendMessageW(combo, CB_SELECTSTRING, -1, (LPARAM)text), + ret = ind; + + while (ind >= 0) + { + wchar_t buf[256] = { 0 }; + GetWindowTextW(combo, buf, _countof(buf) - 1); + if (wcsicmp(buf, text) == 0) + break; + + ret = ind; + ind = SendMessageW(combo, CB_SELECTSTRING, ret, (LPARAM)text); + } + + return ret; +} +std::wstring dlg_base::get_wnd_text(HWND h) +{ + int len = GetWindowTextLengthW(h); + wchar_t* buf = new wchar_t[len + 8]; + std::wstring ret(L""); + + memset(buf, 0, (len + 8) * 2); + GetWindowTextW(h, buf, len + 2); + ret = buf; + delete[] buf; + + return std::move(ret); +} +bool dlg_base::is_language_pack_default_code_page(void) +{ + return lang_get_cur_code_page() == DEFAULT_CODE_PAGE; +} +std::wstring dlg_base::get_menu_text(HMENU menu, int ind) +{ + MENUITEMINFOW mi = { 0 }; + wchar_t text[128] = { 0 }; + + mi.cbSize = sizeof(mi); + mi.fMask = mi.fType = MIIM_STRING; // MFT_STRING; + mi.dwTypeData = text; + mi.cch = _countof(text) - 1; + GetMenuItemInfoW(menu, ind, MF_BYPOSITION, &mi); + + return text; +} +void dlg_base::center_window(HWND wnd, HWND parent) +{ + RECT rme = { 0 }, rp = { 0 }; + int x = 0, y = 0; + + GetWindowRect(wnd, &rme); + if (!IsWindow(parent)) + parent = GetDesktopWindow(); + GetWindowRect(parent, &rp); + x = rp.left + (RECT_W(rp) - RECT_W(rme)) / 2; + y = rp.top + (RECT_H(rp) - RECT_H(rme)) / 2; + OffsetRect(&rme, x - rme.left, y - rme.top); + MoveWindow(wnd, rme.left, rme.top, RECT_W(rme), RECT_H(rme), FALSE); +} +int dlg_base::list_insert_column(HWND list_wnd, const wchar_t* text, int cx, int ind) +{ + LVCOLUMNW col = { 0 }; + + if (ind == -1) + ind = dlg_base::list_get_column_count(list_wnd); + col.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + col.pszText = (LPWSTR)text; + col.iSubItem = 0; + col.cx = cx; + + return ListView_InsertColumn(list_wnd, ind, &col); +} +int dlg_base::list_insert_item(HWND list_wnd, const wchar_t* text, int ind) +{ + LV_ITEMW item = { 0 }; + + if (ind == -1) + ind = dlg_base::list_get_item_count(list_wnd); + item.mask = LVIF_TEXT; + item.pszText = (PWSTR)text; + item.iItem = ind; + + return ListView_InsertItem(list_wnd, &item); +} +int dlg_base::list_get_item_count(HWND list_wnd) +{ + return ListView_GetItemCount(list_wnd); +} +int dlg_base::list_get_column_count(HWND list_wnd) +{ + return ListView_GetItemCount((HWND)ListView_GetHeader(list_wnd)); +} +std::wstring dlg_base::list_get_text(HWND list_wnd, int ind, int sub) +{ + LV_ITEMW item = { 0 }; + wchar_t text[128] = { 0 }; + + item.mask = LVIF_TEXT; + item.pszText = (PWSTR)text; + item.iItem = ind; + item.iSubItem = sub; + item.cchTextMax = _countof(text) - 1; + ListView_GetItem(list_wnd, &item); + + return text; +} +void dlg_base::list_get_selected_items(HWND list_wnd, std::vector& sels) +{ + LV_ITEMW item = { 0 }; + + item.mask = LVIF_STATE; + for (int i = 0; i < dlg_base::list_get_item_count(list_wnd); ++i) + { + item.iItem = i; + item.stateMask = LVIS_SELECTED; + ListView_GetItem(list_wnd, &item); + if (item.state & LVIS_SELECTED) + sels.push_back(i); + } +} +int dlg_base::list_set_item_text(HWND list_wnd, int item, int sub_item, const wchar_t* text) +{ + LV_ITEMW lvi = { 0 }; + + lvi.iItem = item; + lvi.iSubItem = sub_item; + lvi.pszText = (LPWSTR)text; + lvi.mask = LVIF_TEXT; + + return ListView_SetItem(list_wnd, &lvi) ? 0 : 1; +} + +BOOL dlg_base::handle_message(UINT msg, WPARAM wp, LPARAM lp) +{ + return FALSE; +} +void dlg_base::on_font_changed(void) +{ + +} +void dlg_base::create(void) +{ + // InitCommonControls(); + hwnd_ = CreateDialogParamW(g_my_inst, MAKEINTRESOURCE(idd_), parent_, (DLGPROC)&dlg_base::dlg_proc, (LPARAM)this); +} +void dlg_base::notify_ui_event(int ev) +{ + if (ui_event_notify_) + ui_event_notify_(ev, this, ui_notify_param_); +} + +void dlg_base::set_ui_event_notify(void(__stdcall* notify)(int, void*, void*), void* param) +{ + ui_event_notify_ = notify; + ui_notify_param_ = param; +} +HWND dlg_base::hwnd(void) +{ + return hwnd_; +} +void dlg_base::show(bool visible, bool hold) +{ + UINT cmd = visible ? SW_SHOW : SW_HIDE; + DWORD style = GetWindowLong(hwnd_, GWL_STYLE); + + if (!(style & WS_CHILD)) + { + if (visible) + { + RECT r0 = { 0 }, rp = { 0 }, rme = { 0 }; + POINT pt = { 0 }; + HWND after = HWND_TOP; + + if (IsWindow(parent_)) + { + HMONITOR mon = MonitorFromWindow(parent_, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO mi = { 0 }; + + mi.cbSize = sizeof(mi); + GetMonitorInfoW(mon, &mi); + r0 = mi.rcWork; + GetWindowRect(parent_, &rp); + } + else + { + GetWindowRect(GetDesktopWindow(), &r0); + rp = r0; + after = HWND_TOPMOST; + } + GetWindowRect(hwnd_, &rme); + pt.x = rp.left + (RECT_W(rp) - RECT_W(rme)) / 2; + pt.y = rp.top + (RECT_H(rp) - RECT_H(rme)) / 2; + if (pt.x + RECT_W(rme) > r0.right) + pt.x = r0.right - RECT_W(rme); + if (pt.x < r0.left) + pt.x = r0.left; + if (pt.y + RECT_H(rme) > r0.bottom) + pt.y = r0.bottom - RECT_H(rme); + if (pt.y < r0.top) + pt.y = r0.top; + SetWindowPos(hwnd_, after, pt.x, pt.y, RECT_W(rme), RECT_H(rme), SWP_NOSIZE); + UpdateWindow(hwnd_); + } + EnableWindow(parent_, !visible); + } + ShowWindow(hwnd_, cmd); + + if (hold) + { + MSG msg = { 0 }; + BOOL ret = FALSE; + + abandon_hold_ = false; + while ((ret = GetMessageW(&msg, NULL, 0, 0))) + { + if (ret == -1 || abandon_hold_) + break; + + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } +} +int dlg_base::do_modal(HWND parent) +{ + BOOL enable_parent = FALSE, + got = TRUE; + MSG msg = { 0 }; + + modal_exit_ = 0; + if (IsWindow(parent) && parent != GetDesktopWindow()) + { + EnableWindow(parent, FALSE); + enable_parent = TRUE; + } + + dlg_base::center_window(hwnd(), parent); + ShowWindow(hwnd(), SW_SHOW); + while ((got = GetMessage(&msg, NULL, 0, 0))) + { + if ((DWORD)got == -1) + break; + + //if (enable_parent && msg.hwnd == parent && + // msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST && + // msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) + // continue; + + TranslateMessage(&msg); + DispatchMessage(&msg); + if (modal_exit_) + break; + } + ShowWindow(hwnd(), SW_HIDE); + + if (enable_parent) + { + EnableWindow(parent, TRUE); + } + + return modal_exit_; +} +void dlg_base::quit_modal(int non_zero_code) +{ + // assert ( non_zero_code ); + modal_exit_ = non_zero_code; +} +void dlg_base::enable(bool enable) +{ + EnableWindow(hwnd_, enable); +} +void dlg_base::screen_2_client(LPRECT r) +{ + POINT pt = { r->left, r->top }; + + ScreenToClient(hwnd_, &pt); + OffsetRect(r, pt.x - r->left, pt.y - r->top); +} +void dlg_base::client_2_screen(LPRECT r) +{ + POINT pt = { r->left, r->top }; + + ClientToScreen(hwnd_, &pt); + OffsetRect(r, pt.x - r->left, pt.y - r->top); +} +HWND dlg_base::get_item(UINT id) +{ + return GetDlgItem(hwnd_, id); +} +BOOL dlg_base::set_font(HFONT font) +{ + BOOL ret = SendMessage(hwnd_, WM_SETFONT, (WPARAM)font, 1) == 0; + + if (ret) + on_font_changed(); + + return ret; +} +HFONT dlg_base::get_font(void) +{ + return (HFONT)SendMessage(hwnd_, WM_GETFONT, 0, 0); +} +int dlg_base::get_string_width(const wchar_t* str, HWND wnd) +{ + if (!wnd) + wnd = hwnd_; + + HDC hdc = GetWindowDC(wnd); + SIZE size = { 0 }; + + GetTextExtentPointW(hdc, str, lstrlenW(str), &size); + ReleaseDC(wnd, hdc); + + return size.cx; +} +bool dlg_base::get_item_rect(UINT id, LPRECT r, bool client) +{ + if (client) + return GetClientRect(GetDlgItem(hwnd_, id), r) == TRUE; + else + return GetWindowRect(GetDlgItem(hwnd_, id), r) == TRUE; +} +std::wstring dlg_base::get_item_text(UINT id) +{ + return std::move(dlg_base::get_wnd_text(get_item(id))); +} +int dlg_base::get_width_diff_as_text_length(UINT id) +{ + RECT r = { 0 }; + + get_item_rect(id, &r); + + return get_string_width(get_item_text(id).c_str()) - RECT_W(r); +} +void dlg_base::offset_item(HWND wnd, int dx, int dy) +{ + RECT r = { 0 }; + + GetWindowRect(wnd, &r); + OffsetRect(&r, dx, dy); + screen_2_client(&r); + MoveWindow(wnd, r.left, r.top, RECT_W(r), RECT_H(r), FALSE); +} +void dlg_base::offset_item(UINT id, int dx, int dy) +{ + offset_item(get_item(id), dx, dy); +} +void dlg_base::expand_item(UINT id, int dx, int dy) +{ + RECT r = { 0 }; + + get_item_rect(id, &r, false); + r.right += dx; + r.bottom += dy; + screen_2_client(&r); + MoveWindow(get_item(id), r.left, r.top, RECT_W(r), RECT_H(r), FALSE); +} +bool dlg_base::set_item_text(UINT id, const wchar_t* text) +{ + return SetWindowTextW(GetDlgItem(hwnd_, id), text) == TRUE; +} +int dlg_base::set_item_fit_to_text(UINT id) +{ + HWND wnd = get_item(id); + std::wstring text(get_item_text(id)); + RECT r = { 0 }; + int w = get_string_width(text.c_str(), wnd); + + get_item_rect(id, &r, false); + if (w > RECT_W(r) || w <= RECT_W(r) / 2) + { + if (w > RECT_W(r)) + w -= RECT_W(r); + else + w -= RECT_W(r) / 2; + expand_item(id, w, 0); + } + else + w = 0; + + return w; +} +void dlg_base::show_scroll_bar(int bar, bool show) +{ + DWORD style = GetWindowLong(hwnd(), GWL_STYLE); + if (bar == SB_VERT || bar == SB_BOTH) + { + if (show) + style |= WS_VSCROLL; + else + style &= ~WS_VSCROLL; + SetWindowLong(hwnd(), GWL_STYLE, style); + } + else if (bar == SB_HORZ || bar == SB_BOTH) + { + if (show) + style |= WS_HSCROLL; + else + style &= ~WS_HSCROLL; + SetWindowLong(hwnd(), GWL_STYLE, style); + } +} +bool dlg_base::track_mouse_hover(void) +{ + return true; + TRACKMOUSEEVENT tme = { 0 }; + + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_HOVER; + tme.dwHoverTime = HOVER_DEFAULT; + tme.hwndTrack = hwnd_; + + return TrackMouseEvent(&tme); +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// tooltip_wnd +tooltip_wnd::tooltip_wnd() : hwnd_(NULL), parent_(NULL) +{ +} +tooltip_wnd::~tooltip_wnd() +{ + DestroyWindow(hwnd_); +} + +bool tooltip_wnd::create(HWND parent) +{ + if (!IsWindow(hwnd_)) + { + parent_ = parent; + hwnd_ = CreateWindowExW(NULL, TOOLTIPS_CLASSW, NULL, + WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + parent_, NULL, + GetModuleHandle(NULL), NULL); + SetWindowPos(hwnd_, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + + return IsWindow(hwnd_); +} +void tooltip_wnd::enable(bool enabled) +{ + if (enabled) + { + + } + else + { + + } +} +bool tooltip_wnd::add_tool_tip_for_rect(const RECT& r, const wchar_t* tips) +{ + TTTOOLINFOW toolInfo = { 0 }; + + toolInfo.cbSize = sizeof(toolInfo) - sizeof(toolInfo.lpReserved); // TOOLTIPS_CLASSW in old style, not with member 'lpReserved' + toolInfo.hwnd = parent_; + toolInfo.uFlags = TTF_SUBCLASS; + toolInfo.lpszText = (wchar_t*)(DWORD_PTR)tips; // LPSTR_TEXTCALLBACK - WM_NOTIFY.TTN_GETDISPINFO + toolInfo.hinst = GetModuleHandle(NULL); + memcpy(&toolInfo.rect, &r, sizeof(toolInfo.rect)); + + return SendMessageW(hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolInfo) == TRUE; +} + +bool tooltip_wnd::add_tool_tip_for_ctrl(HWND ctrl, const wchar_t* tips) +{ + TTTOOLINFOW toolInfo = { 0 }; + + toolInfo.cbSize = sizeof(toolInfo) - sizeof(toolInfo.lpReserved); // TOOLTIPS_CLASSW in old style, not with member 'lpReserved' + toolInfo.hwnd = parent_; + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolInfo.uId = (UINT_PTR)ctrl; + toolInfo.lpszText = (wchar_t*)(DWORD_PTR)tips; // LPSTR_TEXTCALLBACK - WM_NOTIFY.TTN_GETDISPINFO + toolInfo.hinst = GetModuleHandle(NULL); + + return SendMessageW(hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolInfo) == TRUE; +} + +bool tooltip_wnd::remove_tool_tip_for_ctrl(HWND ctrl) +{ + TTTOOLINFOW toolInfo = { 0 }; + + toolInfo.cbSize = sizeof(toolInfo) - sizeof(toolInfo.lpReserved); // TOOLTIPS_CLASSW in old style, not with member 'lpReserved' + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolInfo.hwnd = parent_; + toolInfo.uId = (UINT_PTR)ctrl; + + return SendMessageW(hwnd_, TTM_DELTOOL, 0, (LPARAM)&toolInfo) == TRUE; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// dlg_page 对话框 +std::wstring dlg_page::property_type = L"option_type"; +std::wstring dlg_page::property_host = L"option_host_wnd"; +std::wstring dlg_page::property_size = L"option_size"; +UINT dlg_page::dyn_id_base = 3000; +int dlg_page::gap_x = 20; +int dlg_page::gap_y = 15; +int dlg_page::spin_w = 8; +int dlg_page::sb_adden = 30; + +dlg_page::dlg_page(HWND parent, const wchar_t* name + , LPSANEAPI api, SANE_Handle dev) + : dlg_base(parent, IDD_PAGE), name_(name ? name : L""), ctrl_id_(0) + , sane_(*api), dev_(dev), done_(false) + , id_custom_area_(-1), id_custom_left_(-1), id_custom_right_(-1), id_custom_top_(-1), id_custom_bottom_(-1) + , id_custom_gamma_(-1), id_paper_(-1), paper_(L"A4"), id_dpi_(-1), dpi_(200), vsb_pos_(0), hsb_pos_(0) + , vsb_(false), hsb_(false), id_paper_w_(-1), id_paper_h_(-1), id_lateral_(-1) +{ + size_.cx = size_.cy = 0; + pos_.x = 12; + pos_.y = 15; + create(); + tips_wnd_.create(hwnd()); +} +dlg_page::~dlg_page() +{ + for (auto& v : ctrls_) + DestroyWindow(v); +} + +BOOL dlg_page::handle_message(UINT msg, WPARAM wp, LPARAM lp) +{ + BOOL ret = TRUE; + + switch (msg) + { + case WM_MOUSEWHEEL: + ret = on_mouse_wheel(LOWORD(wp), HIWORD(wp), LOWORD(lp), HIWORD(lp)); + break; + case WM_NOTIFY: + ret = on_notify((int)wp, (LPNMHDR)lp); + break; + case WM_COMMAND: + handle_command(HIWORD(wp), LOWORD(wp), (HWND)lp); + break; + case WM_VSCROLL: + if(vsb_) + on_vscroll(HIWORD(wp), LOWORD(wp)); + break; + case WM_HSCROLL: + if(hsb_) + on_hscroll(HIWORD(wp), LOWORD(wp)); + break; + case WM_MOUSEHOVER: + track_mouse_hover(); + on_mouse_hover(LOWORD(lp), HIWORD(lp), wp); + break; + default: + ret = FALSE; + } + + return ret; +} +void dlg_page::on_font_changed(void) +{ + HFONT font = get_font(); + LOGFONTW lf = { 0 }; +} +void dlg_page::align_second_control(bool ignore_single) +{ + int pos_2nd = 0, + xoff = 0, + cnt = 0, + prev_pos = 0, + sn = -1; + + // 1st: find the right-most position of the first control + for (auto& v : ctrls_) + { + int id = (int)GetWindowLong(v, GWL_ID); + + if (id != sn) + { + RECT r = { 0 }; + GetWindowRect(v, &r); + + if (ignore_single && cnt == 1) + { + pos_2nd = prev_pos; + } + prev_pos = pos_2nd; + sn = id; + cnt = 1; + if (r.right > pos_2nd) + pos_2nd = r.right; + } + else + cnt++; + } + + // 2nd: align the controls begin from position 2 + sn = -1; + for (auto& v : ctrls_) + { + int id = (int)GetWindowLong(v, GWL_ID); + RECT r = { 0 }; + + if (id != sn) + { + GetWindowRect(v, &r); + sn = id; + xoff = pos_2nd - r.right; + } + else + { + offset_item(v, xoff, 0); + GetWindowRect(v, &r); + screen_2_client(&r); + if (size_.cx < r.right + dlg_page::gap_x) + size_.cx = r.right + dlg_page::gap_x; + } + } +} + +HWND dlg_page::create_label(int sn, const wchar_t* title, int x, int y, SIZE size) +{ + HWND wnd = CreateWindowW(WC_STATICW, title, WS_CHILD | WS_VISIBLE, x, y, size.cx, size.cy, hwnd(), NULL, g_my_inst, NULL); + + ShowWindow(wnd, SW_SHOW); + SendMessage(wnd, WM_SETFONT, (WPARAM)get_font(), 1); + SetWindowLong(wnd, GWL_ID, dlg_page::dyn_id_base + sn); + ctrls_.push_back(wnd); + + return wnd; +} +HWND dlg_page::create_slider(int sn, int x, int y, double lower, double upper, double step, double pos, LPSIZE size, bool is_double) +{ + // lower slider upper + HWND wnd = NULL; + wchar_t limit[20] = { 0 }; + const wchar_t *fmt = is_double ? FLOAT_FORMAT : L"%d"; + int w = x, ticks_limit = is_double ? 500 : 5; + DWORD style = WS_CHILD | WS_VISIBLE | TBS_HORZ; + + if (IS_DOUBLE_EQUAL(step, 0)) + step = 1.0f; + if(is_double) + swprintf_s(limit, _countof(limit) - 1, fmt, lower); + else + swprintf_s(limit, _countof(limit) - 1, fmt, (int)lower); + size->cx = get_string_width(limit); + create_label(sn, limit, x, y, *size); + x += size->cx; + size->cx = (LONG)((upper - lower + step - 1) / step + .5f); + size->cx += 100; + size->cx /= 200; + size->cx *= 100; + if (size->cx < 100) + size->cx = 100; + if (size->cx > 200) + size->cx = 200; + if (upper > lower && size->cx / (upper - lower) < ticks_limit) + style |= TBS_NOTICKS; + else + style |= TBS_AUTOTICKS; + wnd = CreateWindowW(TRACKBAR_CLASSW, L"", style, x, y, size->cx, size->cy, hwnd(), NULL, g_my_inst, NULL); + if (is_double) + { + SendMessage(wnd, TBM_SETRANGEMIN, 1, (LPARAM)int(lower * 100.0f + .5f)); + SendMessage(wnd, TBM_SETRANGEMAX, 1, (LPARAM)int(upper * 100.0f + .5f)); + SendMessage(wnd, TBM_SETPOS, TRUE, (LPARAM)int(pos * 100.0f + .5f)); + } + else + { + SendMessage(wnd, TBM_SETRANGEMIN, 1, (LPARAM)int(lower * 1.0f + .5f)); + SendMessage(wnd, TBM_SETRANGEMAX, 1, (LPARAM)int(upper * 1.0f + .5f)); + SendMessage(wnd, TBM_SETPOS, TRUE, (LPARAM)int(pos * 1.0f + .5f)); + } + SetWindowLong(wnd, GWL_ID, dlg_page::dyn_id_base + sn); + ShowWindow(wnd, SW_SHOW); + ctrls_.push_back(wnd); + + x += size->cx; + if(is_double) + swprintf_s(limit, _countof(limit) - 1, fmt, upper); + else + swprintf_s(limit, _countof(limit) - 1, fmt, (int)upper); + size->cx = get_string_width(limit); + create_label(sn, limit, x, y, *size); + size->cx = x + size->cx - w; + + return wnd; +} +HWND dlg_page::create_edit(int sn, int x, int y, int h, int w) +{ + HWND edit = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, L"", WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, x, y, w, h, hwnd(), NULL, g_my_inst, NULL); + + SetWindowLong(edit, GWL_ID, dlg_page::dyn_id_base + sn); + SendMessage(edit, WM_SETFONT, (WPARAM)get_font(), 1); + ShowWindow(edit, SW_SHOW); + ctrls_.push_back(edit); + + return edit; +} +HWND dlg_page::create_combox(int sn, int x, int y, std::vector& vals, const wchar_t* cur_val, LPSIZE size) +{ + int h = vals.size() * 2 * size->cy; + HWND wnd = NULL; + + for (int i = 0; i < (int)vals.size(); ++i) + { + if (size->cx < get_string_width(vals[i].c_str())) + size->cx = get_string_width(vals[i].c_str()); + } + + size->cx += 20; + wnd = CreateWindowW(WC_COMBOBOXW, L"", WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL, x, y, size->cx, h, hwnd(), NULL, g_my_inst, NULL); + SendMessage(wnd, CB_SETDROPPEDWIDTH, size->cx, 0); + for (int i = 0; i < (int)vals.size(); ++i) + { + SendMessageW(wnd, CB_ADDSTRING, 0, (LPARAM)vals[i].c_str()); + if (vals[i] == cur_val) + SendMessageW(wnd, CB_SETCURSEL, i, 0); + } + SetWindowLong(wnd, GWL_ID, dlg_page::dyn_id_base + sn); + SendMessage(wnd, WM_SETFONT, (WPARAM)get_font(), 1); + ShowWindow(wnd, SW_SHOW); + ctrls_.push_back(wnd); + + return wnd; +} +HWND dlg_page::create_spin(int sn, HWND edit, double pos, double lower, double upper, bool is_double) +{ + RECT r = { 0 }; + HWND wnd = NULL; + + GetWindowRect(edit, &r); + screen_2_client(&r); + wnd = CreateWindowW(UPDOWN_CLASSW, L"", WS_CHILD | WS_VISIBLE | UDS_SETBUDDYINT, r.right - 1, r.top, dlg_page::spin_w, r.bottom - r.top, hwnd(), NULL, g_my_inst, NULL); + SetWindowLong(wnd, GWL_ID, dlg_page::dyn_id_base + sn); + SendMessage(wnd, WM_SETFONT, (WPARAM)get_font(), 1); + if (is_double) + { + SendMessage(wnd, UDM_SETRANGE32, WPARAM(lower * 100.f + .5f), LPARAM(upper * 100.0f + .5f)); + SendMessage(wnd, UDM_SETPOS32, 0, LPARAM(pos * 100.0f + .5f)); + } + else + { + SendMessage(wnd, UDM_SETRANGE32, (WPARAM)(int)lower, (LPARAM)(int)upper); + SendMessage(wnd, UDM_SETPOS32, 0, (LPARAM)(int)pos); + } + ShowWindow(wnd, SW_SHOW); + ctrls_.push_back(wnd); + SetPropW(wnd, dlg_page::property_host.c_str(), edit); + if(!is_double) + SendMessage(wnd, UDM_SETBUDDY, (WPARAM)edit, 0); + + return wnd; +} + +HWND dlg_page::create_control_bool(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) +{ + bool now = *(SANE_Bool*)cur_val == SANE_TRUE ? true : false; + HWND wnd = NULL; + + text_size->cx += 18 + dlg_page::gap_x; + wnd = CreateWindowW(WC_BUTTONW, title, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, pos_.x, pos_.y, text_size->cx, text_size->cy, hwnd(), NULL, g_my_inst, NULL); + SetWindowLong(wnd, GWL_ID, dlg_page::dyn_id_base + sn); + SendMessage(wnd, WM_SETFONT, (WPARAM)get_font(), 1); + ShowWindow(wnd, SW_SHOW); + ctrls_.push_back(wnd); + if (now) + SendMessage(wnd, BM_SETCHECK, (WPARAM)BST_CHECKED, 0); + + if (IS_CAP_READONLY(desc->cap)) + { + if (IsWindow(wnd)) + EnableWindow(wnd, FALSE); + } + + return wnd; +} +HWND dlg_page::create_control_int(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) +{ + // title: combox + // title: [slider] edit spin + HWND label = create_label(sn, title, pos_.x, pos_.y, *text_size), slider = NULL, wnd = NULL, spin = NULL; + int now = *(SANE_Int*)cur_val, + x = pos_.x, + w = 50; + SIZE size(*text_size); + wchar_t text[20] = { 0 }; + + x += text_size->cx + dlg_page::gap_x; + swprintf_s(text, _countof(text) - 1, L"%d", now); + if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST) + { + const SANE_Word *v = desc->constraint.word_list; + std::vector vals; + for (int i = 0; i < v[0]; ++i) + { + swprintf_s(text, _countof(text) - 1, L"%d", v[i + 1]); + vals.push_back(text); + } + swprintf_s(text, _countof(text) - 1, L"%d", now); + wnd = create_combox(sn, x, pos_.y - 1, vals, text, &size); + x += size.cx; + } + else + { + int lower = 0, upper = 10000; + if (desc->constraint_type == SANE_CONSTRAINT_RANGE) + { + lower = desc->constraint.range->min; + upper = desc->constraint.range->max; + slider = create_slider(sn, x, pos_.y, lower, upper, desc->constraint.range->quant, now, &size, false); + x += size.cx + dlg_page::gap_x; + } + wnd = create_edit(sn, x, pos_.y - 2, text_size->cy, w); + x += w; + SetWindowTextW(wnd, text); + if (IsWindow(slider)) + SetPropW(slider, dlg_page::property_host.c_str(), wnd); + + spin = create_spin(sn, wnd, now, lower, upper, false); + x += dlg_page::spin_w; + } + text_size->cx = x + dlg_page::gap_x; + + if (IS_CAP_READONLY(desc->cap)) + { + if (IsWindow(label)) + EnableWindow(label, FALSE); + if (IsWindow(slider)) + EnableWindow(slider, FALSE); + if (IsWindow(wnd)) + EnableWindow(wnd, FALSE); + if (IsWindow(spin)) + EnableWindow(spin, FALSE); + } + + return wnd; +} +HWND dlg_page::create_control_float(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) +{ + // title: combox + // title: [slider] edit spin + HWND label = create_label(sn, title, pos_.x, pos_.y, *text_size), slider = NULL, wnd = NULL, spin = NULL; + int now = *(SANE_Int*)cur_val, + x = pos_.x, + w = 50; + SIZE size(*text_size); + wchar_t text[20] = { 0 }; + + x += text_size->cx + dlg_page::gap_x; + swprintf_s(text, _countof(text) - 1, FLOAT_FORMAT, SANE_UNFIX(*(SANE_Word*)cur_val)); + if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST) + { + const SANE_Word *v = desc->constraint.word_list; + std::vector vals; + wchar_t cur[40] = { 0 }; + for (int i = 0; i < v[0]; ++i) + { + swprintf_s(text, _countof(text) - 1, FLOAT_FORMAT, SANE_UNFIX(v[i + 1])); + vals.push_back(text); + if (v[i + 1] == *(SANE_Word*)cur_val) + wcscpy_s(cur, _countof(cur) - 1, text); + } + wnd = create_combox(sn, x, pos_.y - 1, vals, cur, &size); + x += size.cx; + } + else + { + double lower = .0f, upper = 100.0f; + if (desc->constraint_type == SANE_CONSTRAINT_RANGE) + { + lower = SANE_UNFIX(desc->constraint.range->min); + upper = SANE_UNFIX(desc->constraint.range->max); + slider = create_slider(sn, x, pos_.y, lower, upper, SANE_UNFIX(desc->constraint.range->quant), SANE_UNFIX(*(SANE_Word*)cur_val), &size, true); + x += size.cx + dlg_page::gap_x; + } + wnd = create_edit(sn, x, pos_.y - 2, text_size->cy, w); + x += w; + SetWindowTextW(wnd, text); + if (IsWindow(slider)) + SetPropW(slider, dlg_page::property_host.c_str(), wnd); + + spin = create_spin(sn, wnd, SANE_UNFIX(*(SANE_Word*)cur_val), lower, upper, true); + x += dlg_page::spin_w; + } + text_size->cx = x + dlg_page::gap_x; + + if (IS_CAP_READONLY(desc->cap)) + { + if (IsWindow(label)) + EnableWindow(label, FALSE); + if (IsWindow(slider)) + EnableWindow(slider, FALSE); + if (IsWindow(wnd)) + EnableWindow(wnd, FALSE); + if (IsWindow(spin)) + EnableWindow(spin, FALSE); + } + + return wnd; +} +HWND dlg_page::create_control_string(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) +{ + HWND wnd = NULL, lable = NULL; + int x = pos_.x; + std::wstring now(local_trans::a2u((char*)cur_val, CP_UTF8)); + + lable = create_label(sn, title, x, pos_.y, *text_size); + x += text_size->cx + dlg_page::gap_x; + + if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST) + { + std::vector vals; + const SANE_String_Const *str = desc->constraint.string_list; + SIZE size(*text_size); + + for (int i = 0; str[i]; ++i) + { + std::wstring text(local_trans::a2u(str[i], CP_UTF8)); + vals.push_back(text); + } + wnd = create_combox(sn, x, pos_.y - 1, vals, now.c_str(), &size); + x += size.cx; + } + else + { + wnd = create_edit(sn, x, pos_.y, text_size->cy, 200); + SetWindowTextW(wnd, now.c_str()); + x += 200; + } + + if (IS_CAP_READONLY(desc->cap)) + { + //SendMessage(wnd, EM_SETREADONLY, 1, 0); + EnableWindow(lable, FALSE); + EnableWindow(wnd, FALSE); + } + text_size->cx = x + dlg_page::gap_x; + + return wnd; +} +HWND dlg_page::create_control_button(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) +{ + HWND wnd = NULL; + + return wnd; +} + +void dlg_page::handle_command(WORD code, WORD id, HANDLE ctrl) +{ + wchar_t cls[128] = { 0 }; + + GetClassNameW((HWND)ctrl, cls, _countof(cls) - 1); + if ((IS_EDIT(cls) && code != EN_KILLFOCUS) || // for BUG-363 + (IS_COMBOX(cls) && code != CBN_SELCHANGE)) + { + return; + } + if (IS_BUTTON(cls)) + { + HWND host = (HWND)GetPropW((HWND)ctrl, dlg_page::property_host.c_str()); + if (IsWindow(host)) + { + if (id == dlg_page::dyn_id_base + id_custom_area_) + { + unsigned int size = 0; + std::string utf8(local_trans::u2a(paper_.c_str(), CP_UTF8)); + dlg_area dlg(parent_); + double x = .0f, y = .0f; + int w = 0, h = 0, n = 0; + SANE_Fixed sf; + bool lateral = false; + + // FIXED me here ... + // all the size options, has it's unit, we should convert them to pixel according their units here ... + // now we consinder them all be pixels ... + sane_.sane_control_option_api(dev_, id_paper_w_, SANE_ACTION_GET_VALUE, &w, NULL); + sane_.sane_control_option_api(dev_, id_paper_h_, SANE_ACTION_GET_VALUE, &h, NULL); + sane_.sane_control_option_api(dev_, id_lateral_, SANE_ACTION_GET_VALUE, &lateral, NULL); + if (lateral) + { + const SANE_Option_Descriptor* desc = sane_.sane_get_option_descriptor_api(dev_, id_lateral_); + lateral &= SANE_OPTION_IS_ACTIVE(desc->cap); + } + dlg.set_paper(lateral ? (paper_ + L"\u6A2A\u5411").c_str() : paper_.c_str(), w, h, dpi_); + sane_.sane_control_option_api(dev_, id_custom_left_, SANE_ACTION_GET_VALUE, &n, NULL); + x = dlg_area::pixel_2_mm(n, dpi_); + sane_.sane_control_option_api(dev_, id_custom_top_, SANE_ACTION_GET_VALUE, &n, NULL); + y = dlg_area::pixel_2_mm(n, dpi_); + sane_.sane_control_option_api(dev_, id_custom_right_, SANE_ACTION_GET_VALUE, &n, NULL); + w = dlg_area::pixel_2_mm(n, dpi_); + sane_.sane_control_option_api(dev_, id_custom_bottom_, SANE_ACTION_GET_VALUE, &n, NULL); + h = dlg_area::pixel_2_mm(n, dpi_); + dlg.set_area(x, y, w, h); + if (dlg.do_modal(parent_) == IDOK) + { + SANE_Int val = dlg_area::mm_2_pixel(dlg.x_in_mm(), dpi_); + SANE_Int after = 0; + bool created = true; + + sane_.sane_control_option_api(dev_, id_custom_left_, SANE_ACTION_SET_VALUE, &val, &after); + + val = dlg_area::mm_2_pixel(dlg.y_in_mm(), dpi_); + sane_.sane_control_option_api(dev_, id_custom_top_, SANE_ACTION_SET_VALUE, &val, &after); + + val = dlg_area::mm_2_pixel(dlg.x_in_mm() + dlg.w_in_mm(), dpi_); + sane_.sane_control_option_api(dev_, id_custom_right_, SANE_ACTION_SET_VALUE, &val, &after); + + val = dlg_area::mm_2_pixel(dlg.y_in_mm() + dlg.h_in_mm(), dpi_); + sane_.sane_control_option_api(dev_, id_custom_bottom_, SANE_ACTION_SET_VALUE, &val, &after); + } + return; + } + else if (id == dlg_page::dyn_id_base + id_custom_gamma_) + { + dlg_gamma dlg(parent_, true); + SANE_Gamma gamma = { 0 }; + unsigned int len = sizeof(gamma); + bool created = true; + + sane_.sane_io_control_api(dev_, IO_CTRL_CODE_GET_CUSTOM_GAMMA, &gamma, &len); + dlg.set_gamma(&gamma, len < 2); + if (dlg.do_modal(parent_) == IDOK) + { + len = sizeof(gamma); + dlg.get_gamma(&gamma); + sane_.sane_io_control_api(dev_, IO_CTRL_CODE_SET_CUSTOM_GAMMA, &gamma, &len); + } + + return; + } + } + } + + control_action((HWND)ctrl); +} +BOOL dlg_page::on_notify(int ctrl_id, LPNMHDR pnmh) +{ + wchar_t cls[128] = { 0 }; + + pnmh->code == NM_TOOLTIPSCREATED; + GetClassNameW((HWND)pnmh->hwndFrom, cls, _countof(cls) - 1); + if (IS_TRACKBAR(cls)) + { + if (pnmh->code != NM_RELEASEDCAPTURE) + { + //if (pnmh->code == NM_CUSTOMDRAW && (GetAsyncKeyState(VK_LBUTTON) & 0x8000) && GetFocus() == pnmh->hwndFrom) // drag track ... + // ; + //else + return FALSE; + } + } + else if (IS_UPDOWN_ARROW(cls)) + { + HWND host = (HWND)GetPropW(pnmh->hwndFrom, dlg_page::property_host.c_str()); + if (IsWindow(host) && (int)GetPropW(host, dlg_page::property_type.c_str()) == SANE_TYPE_INT) + return FALSE; + } + else if (IS_BUTTON(cls)) + return FALSE; + + control_action(pnmh->hwndFrom); + + return TRUE; +} + +void* dlg_page::value_from_ctrl(HWND ctrl, SANE_Value_Type* type) +{ + void* ret = NULL; + HWND host = (HWND)GetPropW(ctrl, dlg_page::property_host.c_str()); + wchar_t cls[40] = { 0 }; + SANE_Value_Type tp = SANE_TYPE_INT; + int(__cdecl * cmpstr)(const wchar_t*, const wchar_t*) = wcscmp; + + if (!IsWindow(host)) + host = ctrl; + tp = (SANE_Value_Type)(DWORD)GetPropW(host, dlg_page::property_type.c_str()); + if (type) + *type = tp; + GetClassNameW(ctrl, cls, _countof(cls) - 1); + if (cmpstr(cls, WC_BUTTONW) == 0) + { + if (tp == SANE_TYPE_BOOL) + { + ret = new char[sizeof(SANE_Bool)]; + *(SANE_Bool*)ret = SendMessage(ctrl, BM_GETCHECK, 0, 0) == BST_CHECKED ? SANE_TRUE : SANE_FALSE; + } + } + else if (cmpstr(cls, TRACKBAR_CLASSW) == 0) + { + if (tp == SANE_TYPE_INT) + { + ret = new char[sizeof(SANE_Int)]; + *(SANE_Int*)ret = SendMessage(ctrl, TBM_GETPOS, 0, 0); + } + else if (tp == SANE_TYPE_FIXED) + { + double pos = (double)SendMessage(ctrl, TBM_GETPOS, 0, 0); + + ret = new char[sizeof(SANE_Fixed)]; + *(SANE_Fixed*)ret = SANE_FIX(pos / 100.0f); + } + } + else if (cmpstr(cls, WC_EDITW) == 0 || cmpstr(cls, WC_COMBOBOXW) == 0) + { + int len = (int)GetPropW(host, dlg_page::property_size.c_str()); + wchar_t* buf = new wchar_t[len + 8]; + + GetWindowTextW(ctrl, buf, len + 6); + if (tp == SANE_TYPE_INT) + { + ret = new char[sizeof(SANE_Int)]; + *(SANE_Int*)ret = _wtoi(buf); + } + else if (tp == SANE_TYPE_FIXED) + { + ret = new char[sizeof(SANE_Fixed)]; + *(SANE_Fixed*)ret = SANE_FIX(_wtof(buf)); + } + else if (tp == SANE_TYPE_STRING) + { + buf[len] = 0; + std::string utf8(local_trans::u2a(buf, CP_UTF8)); + ret = new char[len + 2]; + if ((int)utf8.length() > len) + utf8.erase(len); + strcpy((char*)ret, utf8.c_str()); + ((char*)ret)[len] = 0; + } + delete[] buf; + } + else if (cmpstr(cls, UPDOWN_CLASSW) == 0) + { + if (tp == SANE_TYPE_INT) + { + ret = new char[sizeof(SANE_Int)]; + *(SANE_Int*)ret = (int)SendMessage(ctrl, UDM_GETPOS, 0, 0); + } + else if (tp == SANE_TYPE_FIXED) + { + double pos = (double)(int)SendMessage(ctrl, UDM_GETPOS32, 0, 0); + + ret = new char[sizeof(SANE_Fixed)]; + *(SANE_Fixed*)ret = SANE_FIX(pos / 100.0f); + } + } + + return ret; +} +void dlg_page::set_ctrl_value(HWND ctrl, SANE_Value_Type type, void* val, bool only_me, bool skip_ctrl) +{ + if (only_me) + { + bool finish = done_; + wchar_t cls[128] = { 0 }; + + GetClassNameW(ctrl, cls, _countof(cls) - 1); + done_ = false; + if (IS_BUTTON(cls)) + { + if (type == SANE_TYPE_BOOL) + { + DWORD id = GetWindowLong(ctrl, GWL_ID) - dlg_page::dyn_id_base; + HWND host = (HWND)GetPropW(ctrl, dlg_page::property_host.c_str()); + + if (IsWindow(host) && (id == id_custom_area_ || id == id_custom_gamma_)) + EnableWindow(ctrl, *(SANE_Bool*)val == SANE_TRUE); + else + SendMessage(ctrl, BM_SETCHECK, *(SANE_Bool*)val == SANE_TRUE ? (WPARAM)BST_CHECKED : (WPARAM)BST_UNCHECKED, 0); + } + } + else if (IS_TRACKBAR(cls)) + { + if (type == SANE_TYPE_INT) + SendMessage(ctrl, TBM_SETPOS, 1, (LPARAM)*(SANE_Int*)val); + else if (type == SANE_TYPE_FIXED) + { + double pos = SANE_UNFIX(*(SANE_Fixed*)val); + SendMessage(ctrl, TBM_SETPOS, TRUE, (LPARAM)int(pos * 100.0f + .5f)); + } + } + else if (IS_EDIT(cls) || IS_COMBOX(cls)) + { + wchar_t buf[40] = { 0 }; + std::wstring text(L""); + if (type == SANE_TYPE_INT) + { + swprintf_s(buf, _countof(buf) - 1, L"%d", *(SANE_Int*)val); + text = buf; + } + else if (type == SANE_TYPE_FIXED) + { + swprintf_s(buf, _countof(buf) - 1, FLOAT_FORMAT, SANE_UNFIX(*(SANE_Fixed*)val)); + text = buf; + } + else if (type == SANE_TYPE_STRING) + { + text = local_trans::a2u((char*)val, CP_UTF8); + } + if (IS_EDIT(cls)) + SetWindowTextW(ctrl, text.c_str()); + else + dlg_base::select_combo_text(ctrl, text.c_str()); + } + else if (IS_UPDOWN_ARROW(cls)) + { + if (type == SANE_TYPE_INT) + { + SendMessage(ctrl, UDM_SETPOS32, 0, *(SANE_Int*)val); + } + else if (type == SANE_TYPE_FIXED) + { + double pos = SANE_UNFIX(*(SANE_Fixed*)val); + } + } + done_ = finish; + } + else + { + int id = GetWindowLong(ctrl, GWL_ID); + int ind = 0; + + for (; ind < (int)ctrls_.size(); ++ind) + { + if (GetWindowLong(ctrls_[ind], GWL_ID) == id) + break; + } + for (; ind < (int)ctrls_.size(); ++ind) + { + if (GetWindowLong(ctrls_[ind], GWL_ID) != id) + break; + if (skip_ctrl && ctrl == ctrls_[ind]) + continue; + set_ctrl_value(ctrls_[ind], type, val, true); + } + } +} +void dlg_page::free_ctrl_value(void* val) +{ + if (val) + { + char* v = (char*)val; + delete[] v; + } +} +int dlg_page::find_control_ind(HWND wnd) +{ + int ind = -1; + + for (int i = 0; i < (int)ctrls_.size(); ++i) + { + if (ctrls_[i] == wnd) + { + ind = i; +break; + } + } + + return ind; +} +void dlg_page::control_action(HWND wnd) +{ + if (done_) + { + SANE_Value_Type type = (SANE_Value_Type)-1; + int id = GetWindowLong(wnd, GWL_ID); + void* val = value_from_ctrl(wnd, &type); + + if (val) + { + SANE_Int after = 0; + SANE_Status statu = sane_.sane_control_option_api(dev_, id - dlg_page::dyn_id_base, SANE_ACTION_SET_VALUE, val, &after); + bool created = true; + done_ = false; + if (statu == SANE_STATUS_INVAL) + { + set_ctrl_value(wnd, type, val, true); + } + else + { + set_ctrl_value(wnd, type, val, false, !(after || statu)); + } + done_ = true; + if (id == dlg_page::dyn_id_base + id_dpi_) + { + if (type == SANE_TYPE_FIXED) + dpi_ = (float)SANE_UNFIX(*(SANE_Fixed*)val); + else + dpi_ = (float)(int)*(SANE_Int*)val; + } + else if (id == dlg_page::dyn_id_base + id_paper_) + paper_ = local_trans::a2u((char*)val, CP_UTF8); + + if (after || (statu && statu != SANE_STATUS_INVAL)) + PostMessage(parent_, WM_REFRESH_OPTION, id - dlg_page::dyn_id_base, 0); + free_ctrl_value(val); + } + } +} +BOOL dlg_page::on_mouse_wheel(WORD vkey, short delta, short x, short y) +{ + POINT pt = { x, y }; + HWND wnd = WindowFromPoint(pt); + BOOL handled = FALSE; + + if (IsWindow(wnd)) + { + wchar_t cls[128] = { 0 }; + GetClassNameW(wnd, cls, _countof(cls) - 1); + if (IS_EDIT(cls)) + { + SANE_Value_Type type = (SANE_Value_Type)(DWORD)GetPropW(wnd, dlg_page::property_type.c_str()); + if (type == SANE_TYPE_INT || type == SANE_TYPE_FIXED) + { + int s = delta < 0 ? -1 : 1; + GetWindowTextW(wnd, cls, _countof(cls) - 1); + handled = TRUE; + if (type == SANE_TYPE_INT) + swprintf_s(cls, _countof(cls) - 1, L"%d", _wtoi(cls) + 1 * s); + else + swprintf_s(cls, _countof(cls) - 1, FLOAT_FORMAT, _wtof(cls) + .01f * s); + SetWindowTextW(wnd, cls); + } + } + } + + return handled; +} +void dlg_page::on_vscroll(int pos, int sb_ev) +{ + int old = vsb_pos_; + if (sb_ev == SB_THUMBPOSITION || sb_ev == SB_THUMBTRACK) + { + vsb_pos_ = pos; + } + else if (sb_ev == SB_LINEUP || sb_ev == SB_TOP) + vsb_pos_--; + else if (sb_ev == SB_LINEDOWN || sb_ev == SB_BOTTOM) + vsb_pos_++; + else if (sb_ev == SB_PAGEUP) + vsb_pos_ -= size_view_.cy; + else if (sb_ev == SB_PAGEDOWN) + vsb_pos_ += size_view_.cy; + else + { + return; + } + + if (vsb_pos_ < 0) + vsb_pos_ = 0; + else if (vsb_pos_ > desired_size().cy - size_view_.cy + dlg_page::sb_adden) + vsb_pos_ = desired_size().cy - size_view_.cy + dlg_page::sb_adden; + + if (old != vsb_pos_) + { + ScrollWindow(hwnd(), 0, old - vsb_pos_, NULL, NULL); + SetScrollPos(hwnd(), SB_VERT, vsb_pos_, TRUE); + } +} +void dlg_page::on_hscroll(int pos, int sb_ev) +{ + int old = hsb_pos_; + if (sb_ev == SB_THUMBPOSITION || sb_ev == SB_THUMBTRACK) + { + hsb_pos_ = pos; + } + else if (sb_ev == SB_LINEUP || sb_ev == SB_TOP) + hsb_pos_--; + else if (sb_ev == SB_LINEDOWN || sb_ev == SB_BOTTOM) + hsb_pos_++; + else if (sb_ev == SB_PAGEUP) + hsb_pos_ -= size_view_.cx; + else if (sb_ev == SB_PAGEDOWN) + hsb_pos_ += size_view_.cx; + else + { + return; + } + + if (hsb_pos_ < 0) + hsb_pos_ = 0; + else if (hsb_pos_ > desired_size().cx - size_view_.cx + dlg_page::sb_adden) + hsb_pos_ = desired_size().cx - size_view_.cx + dlg_page::sb_adden; + + if (old != hsb_pos_) + { + ScrollWindow(hwnd(), old - hsb_pos_, 0, NULL, NULL); + SetScrollPos(hwnd(), SB_HORZ, hsb_pos_, TRUE); + } +} +void dlg_page::on_mouse_hover(int x, int y, int flag) +{ + POINT pt = { x, y }; + HWND ctrl = NULL; + + ClientToScreen(hwnd_, &pt); + ctrl = WindowFromPoint(pt); + if (IsWindow(ctrl)) + { + int id = GetWindowLong(ctrl, GWL_ID); + if (id > dlg_page::dyn_id_base) + { + id -= dlg_page::dyn_id_base; + const SANE_Option_Descriptor* desc = sane_.sane_get_option_descriptor_api(dev_, id); + if (desc) + { + std::wstring tips(local_trans::a2u(desc->title, CP_UTF8)); + // MessageBoxW(NULL, tips.c_str(), L"Tips", MB_OK); + } + } + } +} + +bool dlg_page::add_control(int sn, const SANE_Option_Descriptor* desc, void* cur_val) +{ + bool ret = false; + + struct + { + int sane_type; + HWND(dlg_page::* func)(int, const SANE_Option_Descriptor*, void*, const wchar_t*, LPSIZE); + }creat[] = { {SANE_TYPE_BOOL, &dlg_page::create_control_bool} + , {SANE_TYPE_INT, &dlg_page::create_control_int} + , {SANE_TYPE_FIXED, &dlg_page::create_control_float} + , {SANE_TYPE_STRING, &dlg_page::create_control_string} + , {SANE_TYPE_BUTTON, &dlg_page::create_control_button} + }; + if (strcmp(SANE_STD_OPT_NAME_CUSTOM_AREA_LEFT, desc->name) == 0) + id_custom_left_ = sn; + else if (strcmp(SANE_STD_OPT_NAME_CUSTOM_AREA_TOP, desc->name) == 0) + id_custom_top_ = sn; + else if (strcmp(SANE_STD_OPT_NAME_CUSTOM_AREA_BOTTOM, desc->name) == 0) + id_custom_bottom_ = sn; + else if (strcmp(SANE_STD_OPT_NAME_CUSTOM_AREA_RIGHT, desc->name) == 0) + id_custom_right_ = sn; + else + { + if (strcmp(desc->name, "paper-w") == 0) + id_paper_w_ = sn; + else if (strcmp(desc->name, "paper-h") == 0) + id_paper_h_ = sn; + else if (strcmp(desc->name, "lateral") == 0) + id_lateral_ = sn; + + for (int i = 0; i < _countof(creat); ++i) + { + if (creat[i].sane_type == desc->type) + { + std::wstring title(local_trans::a2u(desc->title, CP_UTF8)); + HDC hdc = GetWindowDC(hwnd()); + SIZE text = { 0 }; + HWND wnd = NULL; + int pos = ctrls_.size(); + + GetTextExtentPointW(hdc, title.c_str(), title.length(), &text); + ReleaseDC(hwnd(), hdc); + wnd = (this->*creat[i].func)(sn, desc, cur_val, title.c_str(), &text); + ret = IsWindow(wnd); + if (ret) + { + if (desc->desc && *desc->desc) + tips_wnd_.add_tool_tip_for_ctrl(wnd, local_trans::a2u(desc->desc, CP_UTF8).c_str()); + SetPropW(wnd, dlg_page::property_type.c_str(), (HANDLE)creat[i].sane_type); + SetPropW(wnd, dlg_page::property_size.c_str(), (HANDLE)desc->size); + if (desc->cap & SANE_CAP_INACTIVE) + { + for (; pos < (int)ctrls_.size(); ++pos) + EnableWindow(ctrls_[pos], FALSE); + } + if (strcmp(SANE_STD_OPT_NAME_CUSTOM_AREA, desc->name) == 0) + { + // custom area ... + int w = 69; + HWND host = wnd; + std::wstring t(local_trans::lang_trans_between_hz936(CONST_STRING_SET_AREA)); + + id_custom_area_ = sn; + text.cx += dlg_page::gap_x; + wnd = CreateWindowW(WC_BUTTONW, t.c_str(), WS_CHILD | WS_VISIBLE, pos_.x + text.cx, pos_.y - 1, w, text.cy + 3, hwnd(), NULL, g_my_inst, NULL); + text.cx += w + dlg_page::gap_x; + EnableWindow(wnd, (desc->cap & SANE_CAP_INACTIVE) == 0 && *(SANE_Bool*)cur_val == SANE_TRUE); + SetWindowLong(wnd, GWL_ID, dlg_page::dyn_id_base + sn); + SendMessage(wnd, WM_SETFONT, (WPARAM)get_font(), 1); + SetPropW(wnd, dlg_page::property_host.c_str(), host); + ctrls_.push_back(wnd); + } + else if (strcmp(SANE_STD_OPT_NAME_PAPER, desc->name) == 0) + { + paper_ = local_trans::a2u((char*)cur_val, CP_UTF8); + id_paper_ = sn; + } + else if (strcmp(SANE_STD_OPT_NAME_RESOLUTION, desc->name) == 0) + { + if (desc->type == SANE_TYPE_FIXED) + dpi_ = (float)SANE_UNFIX(*(SANE_Fixed*)cur_val); + else + dpi_ = (float)(int)*(SANE_Int*)cur_val; + id_dpi_ = sn; + } + else if (strcmp(SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA, desc->name) == 0) + { + // custom gamma control ... + int w = 78; + HWND host = wnd; + std::wstring t(local_trans::lang_trans_between_hz936(CONST_STRING_SET_TONE)); + + id_custom_gamma_ = sn; + text.cx += dlg_page::gap_x; + wnd = CreateWindowW(WC_BUTTONW, t.c_str(), WS_CHILD | WS_VISIBLE, pos_.x + text.cx, pos_.y - 1, w, text.cy + 3, hwnd(), NULL, g_my_inst, NULL); + text.cx += w + dlg_page::gap_x; + EnableWindow(wnd, (desc->cap& SANE_CAP_INACTIVE) == 0 && *(SANE_Bool*)cur_val == SANE_TRUE); + SetWindowLong(wnd, GWL_ID, dlg_page::dyn_id_base + sn); + SendMessage(wnd, WM_SETFONT, (WPARAM)get_font(), 1); + SetPropW(wnd, dlg_page::property_host.c_str(), host); + ctrls_.push_back(wnd); + } + if (size_.cx < pos_.x + text.cx + 10) + size_.cx = pos_.x + text.cx + 10; + pos_.y += text.cy + dlg_page::gap_y; + } + break; + } + } + } + size_.cy = pos_.y; + size_view_ = size_; + + return ret; +} +void dlg_page::add_control_done(void) +{ + done_ = true; + align_second_control(true); +} +SIZE dlg_page::desired_size(void) +{ + return size_; +} +void dlg_page::show(void) +{ + ShowWindow(hwnd_, SW_SHOW); +} +void dlg_page::hide(void) +{ + ShowWindow(hwnd_, SW_HIDE); +} +bool dlg_page::refresh(int sn, const SANE_Option_Descriptor* desc, void* cur_val) +{ + bool found = false; + int ind = 0; + + sn += dlg_page::dyn_id_base; + for (; ind < (int)ctrls_.size(); ++ind) + { + if (GetWindowLong(ctrls_[ind], GWL_ID) == sn) + { + found = true; + break; + } + } + done_ = false; + for (; ind < (int)ctrls_.size(); ++ind) + { + if (GetWindowLong(ctrls_[ind], GWL_ID) != sn) + break; + + BOOL en = ((desc->cap & SANE_CAP_INACTIVE) != SANE_CAP_INACTIVE) && !(IS_CAP_READONLY(desc->cap)); + set_ctrl_value(ctrls_[ind], desc->type, cur_val, true); + EnableWindow(ctrls_[ind], en); + HWND host = (HWND)GetPropW(ctrls_[ind], dlg_page::property_host.c_str()); + if (IsWindow(host)) + { + BOOL checked = SendMessage(host, BM_GETCHECK, 0, 0) == BST_CHECKED; + checked &= en; + if (sn - dlg_page::dyn_id_base == id_custom_area_) + { + EnableWindow(ctrls_[ind], checked); + } + else if (sn - dlg_page::dyn_id_base == id_custom_gamma_) + { + EnableWindow(ctrls_[ind], checked); + } + } + } + done_ = true; + + return found; +} +const wchar_t* dlg_page::name(void) +{ + return name_.c_str(); +} +void dlg_page::set_view_size(SIZE size) +{ + int h = size.cy; + vsb_ = h < desired_size().cy + dlg_page::sb_adden; + if (!vsb_) + { + show_scroll_bar(SB_VERT, false); + } + else + { + size_view_.cy = h; + show_scroll_bar(SB_VERT, true); + SetScrollRange(hwnd(), SB_VERT, 0, desired_size().cy - h + dlg_page::sb_adden, FALSE); + } + + int w = size.cx; + hsb_ = w < desired_size().cx + dlg_page::sb_adden; + if (!hsb_) + { + show_scroll_bar(SB_HORZ, false); + } + else + { + size_view_.cx = w; + show_scroll_bar(SB_HORZ, true); + SetScrollRange(hwnd(), SB_HORZ, 0, desired_size().cx - w + dlg_page::sb_adden, FALSE); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// diff --git a/sln/usb_tools/opt_ui/DlgPage.h b/sln/usb_tools/opt_ui/DlgPage.h new file mode 100644 index 0000000..b5ad231 --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgPage.h @@ -0,0 +1,240 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "./const_str.h" + +// CDlgIndicator 对话框 +#define FLOAT_FORMAT L"%.2f" +#define IS_STR_EQUAL(s1, s2) (wcscmp(s1, s2) == 0) +#define IS_EDIT(cls) IS_STR_EQUAL(cls, WC_EDITW) +#define IS_COMBOX(cls) IS_STR_EQUAL(cls, WC_COMBOBOXW) +#define IS_BUTTON(cls) IS_STR_EQUAL(cls, WC_BUTTONW) +#define IS_TRACKBAR(cls) IS_STR_EQUAL(cls, TRACKBAR_CLASSW) +#define IS_UPDOWN_ARROW(cls) IS_STR_EQUAL(cls, UPDOWN_CLASSW) + +#define RECT_W(r) (r.right - r.left) +#define RECT_H(r) (r.bottom - r.top) + +#define WM_SCAN_WORKING WM_USER + 301 // WPARAM: unused; LPARAM: unsed +#define WM_USB_PACKET_RECEIVED WM_USER + 302 +#define WM_IMAGE_RECEIVED WM_USER + 303 +#define WM_SCAN_FINISHED WM_USER + 304 // WPARAM: std::string* msg; LPARAM: boo err +#define WM_REFRESH_OPTION WM_USER + 311 // WPARAM: source option SN, LPARAM: unused now +#define WM_GET_CONFIG_OBJ WM_USER + 312 // WPARAM: bool*, [in]create new if NULL; [out]created, LPARAM: to receive the gb::sane_config_schm* object + +#define WM_SET_TEXT WM_USER + 320 // WPARAM: id; LPARAM: std::wstring* +#define WM_IS_BUTTON_CHECKED WM_USER + 321 // WPARAM: id; LPARAM: bool* +#define WM_SET_BUTTON_CHECK WM_USER + 322 // WPARAM: id; LPARAM: bool +#define WM_ENABLE_CTRLS WM_USER + 323 // WPARAM: id (0 is click repeat button); LPARAM: bool +#define WM_DEVICE_STATTUS WM_USER + 324 // WPARAM: unused; LPARAM: scanner_status + + +extern HMODULE g_my_inst; +namespace local_trans +{ + std::string u2a(const wchar_t* unic, UINT cp = CP_ACP); + std::wstring a2u(const char* asc, UINT cp = CP_ACP); + + std::wstring lang_trans_between_hz936(const wchar_t* in, bool from_hz = true); + const char* __stdcall lang_trans_between_hz936(const char* in, bool from_hz, void* param); +} + +class dlg_base +{ + int modal_exit_; // set non-ZERO code to exit modal dialog + +protected: + HWND hwnd_; + HWND parent_; + UINT idd_; + bool abandon_hold_; + void(__stdcall* ui_event_notify_)(int uev, void* sender, void* param); + void* ui_notify_param_; + static std::wstring prop_name; + static BOOL CALLBACK dlg_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); + + virtual BOOL handle_message(UINT msg, WPARAM wp, LPARAM lp); + virtual void on_font_changed(void); + void create(void); + void notify_ui_event(int ev); + +public: + dlg_base(HWND parent, UINT idd); + virtual ~dlg_base(); + + static void screen_2_client(HWND wnd, LPRECT r); + static void client_2_screen(HWND wnd, LPRECT r); + static bool get_max_size(SIZE& dst, const SIZE& src); // return whether changed dst + static bool get_max_size(SIZE& dst, int cx, int cy); // return whether changed dst + static int select_combo_text(HWND combo, const wchar_t* text); + static std::wstring get_wnd_text(HWND h); + static bool is_language_pack_default_code_page(void); + static std::wstring get_menu_text(HMENU menu, int ind); + static void center_window(HWND wnd, HWND parent); + + static int list_insert_column(HWND list_wnd, const wchar_t* text, int cx = 20, int ind = -1); + static int list_insert_item(HWND list_wnd, const wchar_t* text, int ind = -1); + static int list_get_item_count(HWND list_wnd); + static int list_get_column_count(HWND list_wnd); + static std::wstring list_get_text(HWND list_wnd, int ind, int sub = 0); + static void list_get_selected_items(HWND list_wnd, std::vector& sels); + static int list_set_item_text(HWND list_wnd, int item, int sub_item, const wchar_t* text); + +public: + void set_ui_event_notify(void(__stdcall* notify)(int, void*, void*), void* param); + HWND hwnd(void); + void show(bool visible, bool hold = false); + int do_modal(HWND parent); + void quit_modal(int non_zero_code); + void enable(bool enable); + void screen_2_client(LPRECT r); + void client_2_screen(LPRECT r); + HWND get_item(UINT id); + BOOL set_font(HFONT font); + HFONT get_font(void); + int get_string_width(const wchar_t* str, HWND wnd = NULL); + bool get_item_rect(UINT id, LPRECT r, bool client = true); + std::wstring get_item_text(UINT id); + int get_width_diff_as_text_length(UINT id); // + void offset_item(HWND wnd, int dx, int dy); + void offset_item(UINT id, int dx, int dy); + void expand_item(UINT id, int dx, int dy); + bool set_item_text(UINT id, const wchar_t* text); + int set_item_fit_to_text(UINT id); // return difference + void show_scroll_bar(int bar = SB_VERT, bool show = true); + bool track_mouse_hover(void); +}; + +class tooltip_wnd +{ + HWND hwnd_; + HWND parent_; + WNDPROC org_proc_; + + typedef struct _tip_ele + { + HWND ctrl; + RECT rect; + std::wstring tips; + + struct _tip_ele() + { + ctrl = NULL; + memset(&rect, 0, sizeof(rect)); + tips = L""; + } + bool operator==(const HWND& wnd) + { + return ctrl == wnd; + } + bool operator==(const RECT& r) + { + return memcmp(&rect, &r, sizeof(r)) == 0; + } + }TIPELEM; + std::vector elements_; + +public: + tooltip_wnd(); + ~tooltip_wnd(); + +public: + bool create(HWND parent); + void enable(bool enabled); + bool add_tool_tip_for_rect(const RECT& r, const wchar_t* tips); + bool add_tool_tip_for_ctrl(HWND ctrl, const wchar_t* tips); + bool remove_tool_tip_for_ctrl(HWND ctrl); +}; + +class dlg_page : public dlg_base +{ + std::wstring name_; + SIZE size_; + UINT ctrl_id_; + POINT pos_; + SANEAPI sane_; + SANE_Handle dev_; + bool done_; + std::vector ctrls_; + tooltip_wnd tips_wnd_; + + int id_dpi_; + float dpi_; + int id_paper_; + std::wstring paper_; + + int id_custom_area_; + int id_custom_left_; + int id_custom_right_; + int id_custom_top_; + int id_custom_bottom_; + int id_paper_w_; + int id_paper_h_; + int id_lateral_; + int id_custom_gamma_; + + static std::wstring property_type; + static std::wstring property_host; + static std::wstring property_size; + static UINT dyn_id_base; + static int gap_x; + static int gap_y; + static int spin_w; + static int sb_adden; + + SIZE size_view_; + int vsb_pos_; + int hsb_pos_; + bool vsb_; + bool hsb_; + + BOOL handle_message(UINT msg, WPARAM wp, LPARAM lp) override; + void on_font_changed(void) override; + void align_second_control(bool ignore_single = false); // align the second control of a option controls + + HWND create_label(int sn, const wchar_t* title, int x, int y, SIZE size); + HWND create_slider(int sn, int x, int y, double lower, double upper, double step, double pos, LPSIZE size, bool is_double); + HWND create_edit(int sn, int x, int y, int h, int w = 50); + HWND create_combox(int sn, int x, int y, std::vector& vals, const wchar_t* cur_val, LPSIZE size); + HWND create_spin(int sn, HWND edit, double pos, double lower, double upper, bool is_double); + + HWND create_control_bool(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size); + HWND create_control_int(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size); + HWND create_control_float(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size); + HWND create_control_string(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size); + HWND create_control_button(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size); + + void handle_command(WORD code, WORD id, HANDLE ctrl); + BOOL on_notify(int ctrl_id, LPNMHDR pnmh); + + void* value_from_ctrl(HWND ctrl, SANE_Value_Type* type); // call free_ctrl_value to free the returned value, data according to SANE-standard + void set_ctrl_value(HWND ctrl, SANE_Value_Type type, void* val, bool only_me, bool skip_ctrl = false); + void free_ctrl_value(void* val); + int find_control_ind(HWND wnd); + void control_action(HWND wnd); + BOOL on_mouse_wheel(WORD vkey, short delta, short x, short y); + void on_vscroll(int pos, int sb_ev); + void on_hscroll(int pos, int sb_ev); + void on_mouse_hover(int x, int y, int flag); + +public: + dlg_page(HWND parent, const wchar_t* name, LPSANEAPI api, SANE_Handle dev); + ~dlg_page(); + +public: + bool add_control(int sn, const SANE_Option_Descriptor* desc, void* cur_val); + void add_control_done(void); + SIZE desired_size(void); + void show(void); + void hide(void); + bool refresh(int sn, const SANE_Option_Descriptor* desc, void* cur_val); + const wchar_t* name(void); + void set_view_size(SIZE size); +}; + + diff --git a/sln/usb_tools/opt_ui/DlgSetting.cpp b/sln/usb_tools/opt_ui/DlgSetting.cpp new file mode 100644 index 0000000..a02984e --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgSetting.cpp @@ -0,0 +1,490 @@ +// DlgIndicator.cpp: 实现文件 +// + +#include "DlgSetting.h" +#include "../resource.h" + +#include + +// CDlgIndicator 对话框 + +#define MENU_CMD_0 ((unsigned short)0x8888) + +static IMPLEMENT_OPTION_STRING_COMPARE(cmp_sane_opt); + +namespace local_trans +{ + std::string u2a(const wchar_t* u, UINT cp) + { + std::string a(""); + + if (u) + { + char* ansi = NULL; + int len = 0; + + len = WideCharToMultiByte(cp, 0, u, lstrlenW(u), NULL, 0, NULL, NULL); + ansi = new char[len + 2]; + len = WideCharToMultiByte(cp, 0, u, lstrlenW(u), ansi, len, NULL, NULL); + ansi[len--] = 0; + a = ansi; + delete[] ansi; + } + + return std::move(a); + } + std::wstring a2u(const char* asc, UINT cp) + { + std::wstring u(L""); + + if (asc) + { + wchar_t* buf = NULL; + int len = 0; + + len = MultiByteToWideChar(cp, 0, asc, lstrlenA(asc), NULL, 0); + buf = new wchar_t[len + 2]; + len = MultiByteToWideChar(cp, 0, asc, lstrlenA(asc), buf, len); + buf[len--] = 0; + u = buf; + delete[] buf; + } + + return std::move(u); + } + + std::wstring lang_trans_between_hz936(const wchar_t* in, bool from_hz) + { + std::string a(u2a(in, CP_UTF8)); + + if (from_hz) + a = from_default_language(a.c_str(), nullptr); + else + a = to_default_language(a.c_str(), nullptr); + + return std::move(a2u(a.c_str(), CP_UTF8)); + } + const char* __stdcall lang_trans_between_hz936(const char* in, bool from_hz, void* param) + { + return from_hz ? from_default_language(in, nullptr) : to_default_language(in, nullptr); + } +} +namespace known_name +{ + struct + { + std::wstring name; + std::wstring title; + } + g_group[] = { {L"base", L"\u57FA\u672C\u8BBE\u7F6E"}, {L"imgp", L"\u56FE\u50CF\u5904\u7406"}, {L"feeder", L"\u9001\u7EB8\u65B9\u5F0F"}, {L"advance", L"\u9AD8\u7EA7\u8BBE\u7F6E"} }, + g_cats[] = { {L"imgp", L"\u56FE\u50CF\u5904\u7406"} }; + + static std::wstring group_name(const wchar_t* grp_title) + { + for (auto& v : g_group) + { + if (v.title == grp_title) + return v.name; + } + + return grp_title; + } + static std::wstring group_title(const wchar_t* grp_name) + { + if (wcscmp(grp_name, L"imgproc") == 0) + grp_name = L"imgp"; + + for (auto& v : g_group) + { + if (v.name == grp_name) + return v.title; + } + + return grp_name; + } + + static std::wstring category_name(const wchar_t* cat_title) + { + for (auto& v : g_cats) + { + if (v.title == cat_title) + return v.name; + } + + return cat_title; + } + static std::wstring category_title(const wchar_t* cat_name) + { + if (wcscmp(cat_name, L"imgproc") == 0) + cat_name = L"imgp"; + + for (auto& v : g_cats) + { + if (v.name == cat_name) + return v.title; + } + + return cat_name; + } +}; + +dlg_setting::dlg_setting(HWND parent, LPSANEAPI api, SANE_Handle dev, LPRECT area) : dlg_base(parent, IDD_SETTING) + , sane_api_(*api), sane_dev_(dev), area_(*area) + , papers_(0), images_(0), err_(false), tab_(NULL) +{ + std::wstring setting(local_trans::lang_trans_between_hz936(CONST_STRING_SETTING)); + + create(); +} +dlg_setting::~dlg_setting() +{ + if (IsWindow(tab_)) + { + for (int i = 0; i < get_tab_count(); ++i) + { + TCITEMW item = { 0 }; + + item.mask = TCIF_PARAM; + TabCtrl_GetItem(tab_, i, &item); + if (item.lParam) + delete (dlg_page*)item.lParam; + } + DestroyWindow(tab_); + } +} + +BOOL dlg_setting::handle_message(UINT msg, WPARAM wp, LPARAM lp) +{ + wchar_t text[40] = { 0 }; + BOOL ret = TRUE; + + switch (msg) + { + case WM_INITDIALOG: + on_init_dialog(); + break; + case WM_COMMAND: + handle_command(HIWORD(wp), LOWORD(wp), (HWND)lp); + break; + case WM_NOTIFY: + ret = on_notify((int)wp, (LPNMHDR)lp); + break; + case WM_REFRESH_OPTION: + refresh_controls((int)wp); + break; + default: + ret = FALSE; + break; + } + + return ret; +} +void dlg_setting::handle_command(WORD code, WORD id, HANDLE ctrl) +{ + //if (id == IDOK) + //{ + // notify_over(); + //} + //else if (id == IDC_BUTTON_HELP) + //{ + // SANE_Int after = 0; + // SANE_Status statu = sane_api_.sane_control_option_api(sane_dev_, id_help_, SANE_ACTION_SET_VALUE, &after, &after); + //} + //else if (id == IDC_BUTTON_RESTORE) + //{ + // SANE_Int after = 0; + // SANE_Status statu = sane_api_.sane_control_option_api(sane_dev_, id_restore_, SANE_ACTION_SET_VALUE, &after, &after); + // refresh_controls(id_restore_); + //} + //else if (id == IDC_BUTTON_SCAN) + //{ + // // enable(false); + // notify_ui_event(SANE_EVENT_UI_SCAN_COMMAND); + //} +} +void dlg_setting::notify_over(void) +{ + notify_ui_event(SANE_EVENT_UI_CLOSE_SETTING); +} +void dlg_setting::on_init_dialog(void) +{ + dlg_page* page = NULL; + SANE_Int sn = 1; + SIZE size = { 0 }; + RECT r = area_, rme = { 0 }; + int y = 0; + const SANE_Option_Descriptor* desc = sane_api_.sane_get_option_descriptor_api(sane_dev_, sn++); + + MoveWindow(hwnd(), r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE); + while (desc) + { + if (desc->type == SANE_TYPE_GROUP) + { + if (page) + { + page->add_control_done(); + dlg_base::get_max_size(size, page->desired_size()); + } + page = add_tab(desc->title); + } + else if (page) + { + char* buf = new char[desc->size + 4]; + SANE_Int info = 0; + + memset(buf, 0, desc->size + 4); + sane_api_.sane_control_option_api(sane_dev_, sn - 1, SANE_ACTION_GET_VALUE, buf, &info); + page->add_control(sn - 1, desc, buf); + delete[] buf; + } + else if(desc->type == SANE_TYPE_BUTTON) + { + //if (strcmp(SANE_STD_OPT_NAME_HELP, desc->name) == 0) + //{ + // ShowWindow(GetDlgItem(hwnd_, IDC_BUTTON_HELP), SW_SHOW); + // id_help_ = sn - 1; + //} + //else if (strcmp(SANE_STD_OPT_NAME_RESTORE, desc->name) == 0) + //{ + // ShowWindow(GetDlgItem(hwnd_, IDC_BUTTON_RESTORE), SW_SHOW); + // id_restore_ = sn - 1; + //} + } + desc = sane_api_.sane_get_option_descriptor_api(sane_dev_, sn++); + } + if (page) + { + page->add_control_done(); + dlg_base::get_max_size(size, page->desired_size()); + } + + if (size.cx || size.cy || IsWindow(tab_)) + { + // resize ... + //GetClientRect(hwnd(), &rme); + rme = area_; + if (size.cx < rme.right - rme.left) + size.cx = rme.right - rme.left; + + if (IsWindow(tab_)) + { + GetWindowRect(tab_, &r); + y = r.bottom - r.top; + size.cy += y; + r.right = r.left + size.cx; + screen_2_client(&r); + MoveWindow(tab_, r.left, r.top, r.right - r.left, y, TRUE); + } + + RECT desk = area_; + int diff = 0; + + //GetClientRect(GetDesktopWindow(), &desk); + //GetWindowRect(hwnd(), &r); + //r.right += size.cx - (rme.right - rme.left); + //r.bottom += size.cy; + r = area_; + diff = size.cy - RECT_H(r); + //if (r.bottom - r.top > desk.bottom - desk.top) + //{ + // diff = (r.bottom - r.top) - (desk.bottom - desk.top) + 100; + // r.top = desk.top; + // r.bottom = desk.bottom - 100; + //} + OffsetRect(&r, area_.left - r.left, area_.top - r.top); + MoveWindow(hwnd(), r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE); + + size.cy -= diff; + size.cx = RECT_W(r); + for (int i = 0; page = get_page(i); ++i) + { + MoveWindow(page->hwnd(), 0, y, size.cx, size.cy - y, TRUE); + page->set_view_size(size); + } + + //offset_item(IDC_BUTTON_SCAN, 0, size.cy); + //offset_item(IDC_BUTTON_CONFIG_MGR, size.cx - RECT_W(rme), size.cy); + //offset_item(IDC_BUTTON_CONFIG_MENU, size.cx - RECT_W(rme), size.cy); + //offset_item(IDC_BUTTON_HELP, size.cx - RECT_W(rme), size.cy); + //offset_item(IDC_BUTTON_RESTORE, size.cx - RECT_W(rme), size.cy); + //offset_item(IDOK, size.cx - RECT_W(rme), size.cy); + // + //ShowWindow(get_item(IDC_BUTTON_CONFIG_MGR), SW_SHOW); + //ShowWindow(get_item(IDC_BUTTON_CONFIG_MENU), SW_SHOW); + } + //if (with_scan_) + // ShowWindow(get_item(IDC_BUTTON_SCAN), SW_SHOW); + // + //if (!dlg_base::is_language_pack_default_code_page()) + //{ + // std::wstring title(local_trans::lang_trans_between_hz936(CONST_STRING_CFG_MANAGER)); + // + // ::SetDlgItemTextW(hwnd(), IDC_BUTTON_CONFIG_MGR, title.c_str()); + // y = set_item_fit_to_text(IDC_BUTTON_CONFIG_MGR); + // if (y) + // offset_item(IDC_BUTTON_CONFIG_MGR, -y, 0); + // + // ::SetDlgItemTextW(hwnd(), IDC_BUTTON_SCAN, local_trans::lang_trans_between_hz936(CONST_STRING_SCAN).c_str()); + // ::SetDlgItemTextW(hwnd(), IDC_BUTTON_RESTORE, local_trans::lang_trans_between_hz936(CONST_STRING_RESTORE).c_str()); + // ::SetDlgItemTextW(hwnd(), IDC_BUTTON_HELP, local_trans::lang_trans_between_hz936(CONST_STRING_HELP).c_str()); + // ::SetDlgItemTextW(hwnd(), IDOK, local_trans::lang_trans_between_hz936(CONST_STRING_OK).c_str()); + //} + + select_page(0); + UpdateWindow(hwnd()); +} +BOOL dlg_setting::on_notify(int ctrl_id, LPNMHDR pnmh) +{ + BOOL ret = TRUE; + + if (pnmh->hwndFrom == tab_) + { + if (pnmh->code == TCN_SELCHANGING) + ret = FALSE; + else if (pnmh->code == TCN_SELCHANGE) + select_page(TabCtrl_GetCurSel(tab_)); + } + + return ret; +} +int dlg_setting::get_tab_count(void) +{ + int count = 0; + + if (IsWindow(tab_)) + count = TabCtrl_GetItemCount(tab_); + + return count; +} +dlg_page* dlg_setting::add_tab(const char* utf8_title) +{ + std::wstring title(known_name::group_title(local_trans::a2u(utf8_title, CP_UTF8).c_str())); + dlg_page *page = new dlg_page(hwnd(), title.c_str(), &sane_api_, sane_dev_); + HFONT font = (HFONT)SendMessage(get_item(IDOK), WM_GETFONT, 0, 0); + LOGFONTW lf = { 0 }; + + GetObjectW(font, sizeof(lf), &lf); + page->set_font(font); + if (!IsWindow(tab_)) + { + HDC hdc = GetWindowDC(hwnd()); + SIZE text = { 0 }; + + GetTextExtentPointW(hdc, title.c_str(), title.length(), &text); + ReleaseDC(hwnd(), hdc); + tab_ = CreateWindowW(L"SysTabControl32", L"pages", WS_CHILD | WS_VISIBLE, 0, 0, 100, text.cy + 6, hwnd(), NULL, g_my_inst, NULL); + wcscpy(lf.lfFaceName, L"微软雅黑"); + lf.lfHeight = 18; + font = CreateFontIndirectW(&lf); + SendMessage(tab_, WM_SETFONT, (WPARAM)font, 1); + SetWindowLong(tab_, GWL_ID, 1234); + ShowWindow(tab_, SW_SHOW); + } + + TC_ITEMW item = { 0 }; + + item.mask = TCIF_PARAM | TCIF_TEXT; + item.lParam = (LPARAM)page; + item.pszText = &title[0]; + TabCtrl_InsertItem(tab_, get_tab_count(), &item); + page->hide(); + + return page; +} +dlg_page* dlg_setting::get_page(int index) +{ + dlg_page* page = NULL; + + if (IsWindow(tab_) && index >= 0 && index < get_tab_count()) + { + TCITEMW item = { 0 }; + + item.mask = TCIF_PARAM; + TabCtrl_GetItem(tab_, index, &item); + page = (dlg_page*)item.lParam; + } + + return page; +} +dlg_page* dlg_setting::get_page(const char* utf8_title) +{ + dlg_page* page = NULL; + std::wstring unic(local_trans::a2u(utf8_title, CP_UTF8)); + + for (int i = 0; i < get_tab_count(); ++i) + { + TCITEMW item = { 0 }; + wchar_t buf[80] = { 0 }; + + item.mask = TCIF_TEXT | TCIF_PARAM; + item.pszText = buf; + item.cchTextMax = _countof(buf) - 1; + TabCtrl_GetItem(tab_, i, &item); + if (unic == buf) + { + page = (dlg_page*)item.lParam; + break; + } + } + + return page; +} +dlg_page* dlg_setting::select_page(int index) +{ + dlg_page* ret = NULL, *cur = NULL; + + for (int i = 0; cur = get_page(i); ++i) + { + if (i == index) + { + ret = cur; + cur->show(); + } + else + cur->hide(); + } + + return ret; +} +void dlg_setting::refresh_controls(int src_sn) +{ + int sn = 1; + const SANE_Option_Descriptor* desc = sane_api_.sane_get_option_descriptor_api(sane_dev_, sn++); + + while (desc) + { + if (desc->type != SANE_TYPE_BUTTON && desc->type != SANE_TYPE_GROUP) + { + char* buf = new char[desc->size + 8]; + SANE_Int info = 0; + dlg_page* page = NULL; + memset(buf, 0, desc->size + 8); + sane_api_.sane_control_option_api(sane_dev_, sn - 1, SANE_ACTION_GET_VALUE, buf, &info); + for (int i = 0; page = get_page(i); ++i) + { + if (page->refresh(sn - 1, desc, buf)) + break; + } + delete[] buf; + } + desc = sane_api_.sane_get_option_descriptor_api(sane_dev_, sn++); + } +} + +HWND dlg_setting::window(void) +{ + return hwnd_; +} +HWND dlg_setting::parent(void) +{ + return parent_; +} +void dlg_setting::hide(void) +{ + ShowWindow(hwnd_, SW_HIDE); +} +void dlg_setting::notify_scan_over(void) +{ + enable(true); +} +// CDlgIndicator 消息处理程序 diff --git a/sln/usb_tools/opt_ui/DlgSetting.h b/sln/usb_tools/opt_ui/DlgSetting.h new file mode 100644 index 0000000..6945699 --- /dev/null +++ b/sln/usb_tools/opt_ui/DlgSetting.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +#include "DlgPage.h" + +// CDlgIndicator 对话框 +//#define USE_SOLE_WIN_THREAD + +#ifdef USE_SOLE_WIN_THREAD +#include +#include +#endif + +class dlg_setting : public dlg_base +{ + SANEAPI sane_api_; + SANE_Handle sane_dev_; + unsigned int papers_; + unsigned int images_; + bool err_; + RECT area_; + HWND tab_; + +#ifdef USE_SOLE_WIN_THREAD + std::unique_ptr thread_; +#endif + + BOOL handle_message(UINT msg, WPARAM wp, LPARAM lp) override; + void handle_command(WORD code, WORD id, HANDLE ctrl); + void notify_over(void); + void on_init_dialog(void); + BOOL on_notify(int ctrl_id, LPNMHDR pnmh); + int get_tab_count(void); + dlg_page* add_tab(const char* utf8_title); + dlg_page* get_page(int index); + dlg_page* get_page(const char* utf8_title); + dlg_page* select_page(int index); + void refresh_controls(int src_sn); + +public: + dlg_setting(HWND parent, LPSANEAPI api, SANE_Handle dev, LPRECT area); + ~dlg_setting(); + +public: + HWND window(void); + HWND parent(void); + //void show(void); + void hide(void); + void notify_scan_over(void); +}; diff --git a/sln/usb_tools/opt_ui/const_str.h b/sln/usb_tools/opt_ui/const_str.h new file mode 100644 index 0000000..c52c879 --- /dev/null +++ b/sln/usb_tools/opt_ui/const_str.h @@ -0,0 +1,94 @@ +// For: const string list +// +// Date: 2023-02-07 +// +// Format: all strings are defined in unicode +// + +#pragma once + +#define CONST_STRING_CUSTOM_AREA L"\u81EA\u5B9A\u4E49\u626B\u63CF\u533A\u57DF" +#define CONST_STRING_UNIT L"\u5355\u4F4D" +#define CONST_STRING_UNIT_MM L"\u6BEB\u7C73" +#define CONST_STRING_UNIT_INCH L"\u82F1\u5BF8" +#define CONST_STRING_UNIT_PIXEL L"\u50CF\u7D20" +#define CONST_STRING_AREA_SET L"\u533A\u57DF\u8BBE\u7F6E" +#define CONST_STRING_LEFT L"\u5DE6" +#define CONST_STRING_TOP L"\u4E0A" +#define CONST_STRING_WIDTH L"\u5BBD" +#define CONST_STRING_HEIGHT L"\u9AD8" +#define CONST_STRING_RESTORE_AREA L"\u6062\u590D\u533A\u57DF" +#define CONST_STRING_OK L"\u786E\u5B9A" +#define CONST_STRING_CANCEL L"\u53D6\u6D88" +#define CONST_STRING_MULTIPLE L"\u00d7" +#define CONST_STRING_OPT_PAPER L"\u7EB8\u5F20\u5C3A\u5BF8" + +#define CONST_STRING_CFG_MANAGER L"\u914D\u7F6E\u7BA1\u7406" +#define CONST_STRING_CFG_SCHEME L"\u914D\u7F6E\u65B9\u6848" +#define CONST_STRING_DEL_SELECTED L"\u5220\u9664\u9009\u4E2D\u9879" +#define CONST_STRING_DEL_ALL L"\u5168\u90E8\u5220\u9664" +#define CONST_STRING_CFG_CONTENT L"\u914D\u7F6E\u5185\u5BB9" +#define CONST_STRING_CFG_NAME L"\u914D\u7F6E\u540D\u79F0" + +#define CONST_STRING_CUSTOM_TONE L"\u81EA\u5B9A\u4E49\u8272\u8C03\u66F2\u7EBF" +#define CONST_STRING_TONE_SCHEME L"\u8272\u8C03\u65B9\u6848" +#define CONST_STRING_COLOR_CHANNEL L"\u989C\u8272\u901A\u9053" +#define CONST_STRING_INITIALIZE L"\u521D\u59CB\u5316" +#define CONST_STRING_INPUT L"\u8F93\u5165" +#define CONST_STRING_OUTPUT L"\u8F93\u51FA" +#define CONST_STRING_CUSTOM L"\u81EA\u5B9A\u4E49" +#define CONST_STRING_NEG_PHOTO L"\u8d1f\u7247" +#define CONST_STRING_COLOR_NEG_PHOTO L"\u5f69\u8272\u8d1f\u7247" +#define CONST_STRING_DARKER L"\u8f83\u6697" +#define CONST_STRING_BRIGHTER L"\u8f83\u4eae" +#define CONST_STRING_RED L"\u7ea2" +#define CONST_STRING_GREEN L"\u7eff" +#define CONST_STRING_BLUE L"\u84dd" +#define CONST_STRING_GRAY L"\u7070" + +#define CONST_STRING_SCANNING L"\u6B63\u5728\u626B\u63CF\u2026\u2026" +#define CONST_STRING_PAPER L"\u7EB8\u5F20" +#define CONST_STRING_IMAGE L"\u56FE\u7247" +#define CONST_STRING_TOTAL_SCANNED L"\u603b\u8ba1\u626b\u63cf\u56fe\u7247" +#define CONST_STRING_CLOSE L"\u5173\u95ed" +#define CONST_STRING_SCAN_OVER L"\u626b\u63cf\u7ed3\u675f" + +#define CONST_STRING_CHOOSE_DEVICE L"\u8BF7\u9009\u62E9\u8BBE\u5907" +#define CONST_STRING_DEVICE_NAME L"\u8BBE\u5907\u540D\u79F0" +#define CONST_STRING_SERIAL_NUM L"\u5E8F\u5217\u53F7" + +#define CONST_STRING_INPUT_VAL L"\u8BF7\u8F93\u5165\u65B0\u503C" +#define CONST_STRING_RE_INPUT L"\u8BF7\u91CD\u65B0\u8F93\u5165" +#define CONST_STRING_NO_INPUT L"\u6CA1\u6709\u8F93\u5165\u5185\u5BB9" +#define CONST_STRING_ALREADY_EXISTS L"\u5DF2\u7ECF\u5B58\u5728" + +#define CONST_STRING_NOT_SUPPORT_SET L"\u6B64\u8BBE\u5907\u4E0D\u652F\u6301\u5C06" +#define CONST_STRING_SET_TO_BE L"\u8BBE\u7F6E\u4E3A" +#define CONST_STRING_PARAMETER_ERROR L"\u53C2\u6570\u9519\u8BEF" +#define CONST_STRING_PARAMETER_ORIGIN L"\u539F\u53C2\u6570" +#define CONST_STRING_PARAMETER_INEXACT L"\u4E0D\u7CBE\u786E\u6216\u8BBE\u5907\u4E0D\u652F\u6301\uFF0C\u5DF2\u8C03\u6574\u4E3A" +#define CONST_STRING_PARAMETER_ADJUSTED L"\u53C2\u6570\u8C03\u6574" +#define CONST_STRING_SET_AREA L"\u8bbe\u7f6e\u533a\u57df" +#define CONST_STRING_SET_TONE L"\u8bbe\u7f6e\u8272\u8c03\u66f2\u7ebf" + +#define CONST_STRING_SETTING L"\u8BBE\u7F6E" +#define CONST_STRING_SAVE_CUR_CFG_AS L"\u5F53\u524D\u914D\u7F6E\u53E6\u5B58\u4E3A" +#define CONST_STRING_SCAN L"\u626B\u63CF" +#define CONST_STRING_RESTORE L"\u6062\u590D\u9ED8\u8BA4\u503C" +#define CONST_STRING_HELP L"\u5E2E\u52A9" + + + +#define CONST_STRING_OPEN_FAILED L"\u6253\u5F00\u5931\u8D25" +#define CONST_STRING_START_FAILED L"\u542F\u52A8\u5931\u8D25" +#define CONST_STRING_ERROR L"\u9519\u8BEF" + + +#define CONST_STRING_SAVE_TITLE L"\u5F53\u524D\u914D\u7F6E\u5DF2\u7ECF\u66F4\u6539\uFF0C\u8BF7\u9009\u62E9\u4FDD\u5B58\u65B9\u5F0F" +#define CONST_STRING_SAVE_DISCARD L"\u4E0D\u4FDD\u5B58\uFF0C\u4EC5\u7528\u4E8E\u8BE5\u6B21\u626B\u63CF\u6216\u653E\u5F03\u66F4\u6539" +#define CONST_STRING_SAVE_OVERWRITE L"\u8986\u76D6\u73B0\u6709\u65B9\u6848" +#define CONST_STRING_SAVE_NEW L"\u4FDD\u5B58\u4E3A\u65B0\u65B9\u6848" +#define CONST_STRING_REINPUT_SCHEME_NAME L"\u8BF7\u8F93\u5165\u65B0\u914D\u7F6E\u65B9\u6848\u540D\u79F0\u3002" +#define CONST_STRING_LEFT_QUOTE L"\u201C" +#define CONST_STRING_RIGHT_QUOTE L"\u201D" +#define CONST_STRING_REINPUT_WHEN_EXISTING L"\u5DF2\u7ECF\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u3002" \ No newline at end of file diff --git a/sln/usb_tools/opt_ui/mem_dc.cpp b/sln/usb_tools/opt_ui/mem_dc.cpp new file mode 100644 index 0000000..742e161 --- /dev/null +++ b/sln/usb_tools/opt_ui/mem_dc.cpp @@ -0,0 +1,39 @@ +// DlgIndicator.cpp: 实现文件 +// + +#include "mem_dc.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// compatible_dc +compatible_dc::compatible_dc(HDC src) : src_(src), old_(NULL) +{ + HWND hwnd = WindowFromDC(src); + RECT r = { 0 }; + + if (!IsWindow(hwnd)) + hwnd = GetDesktopWindow(); + GetWindowRect(hwnd, &r); + size_.cx = r.right - r.left; + size_.cy = r.bottom - r.top; + bmp_ = CreateCompatibleBitmap(src, size_.cx, size_.cy); + hdc_ = CreateCompatibleDC(src); + old_ = (HBITMAP)SelectObject(hdc_, bmp_); + BitBlt(hdc_, 0, 0, size_.cx, size_.cy, src, 0, 0, SRCCOPY); +} +compatible_dc::~compatible_dc() +{ + BitBlt(src_, 0, 0, size_.cx, size_.cy, hdc_, 0, 0, SRCCOPY); + SelectObject(hdc_, old_); + DeleteObject(bmp_); + DeleteDC(hdc_); +} + +HDC compatible_dc::get_dc() +{ + return hdc_; +} \ No newline at end of file diff --git a/sln/usb_tools/opt_ui/mem_dc.h b/sln/usb_tools/opt_ui/mem_dc.h new file mode 100644 index 0000000..0c18603 --- /dev/null +++ b/sln/usb_tools/opt_ui/mem_dc.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class compatible_dc +{ + HBITMAP bmp_; + HBITMAP old_; + HDC hdc_; + HDC src_; + SIZE size_; + +public: + compatible_dc(HDC src); + ~compatible_dc(); + +public: + HDC get_dc(void); +}; \ No newline at end of file diff --git a/sln/usb_tools/pch.cpp b/sln/usb_tools/pch.cpp new file mode 100644 index 0000000..b6fb8f4 --- /dev/null +++ b/sln/usb_tools/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: 与预编译标头对应的源文件 + +#include "pch.h" + +// 当使用预编译的头时,需要使用此源文件,编译才能成功。 diff --git a/sln/usb_tools/pch.h b/sln/usb_tools/pch.h new file mode 100644 index 0000000..9660927 --- /dev/null +++ b/sln/usb_tools/pch.h @@ -0,0 +1,13 @@ +// pch.h: 这是预编译标头文件。 +// 下方列出的文件仅编译一次,提高了将来生成的生成性能。 +// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 +// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 +// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。 + +#ifndef PCH_H +#define PCH_H + +// 添加要在此处预编译的标头 +#include "framework.h" + +#endif //PCH_H diff --git a/sln/usb_tools/res/hardware.ico b/sln/usb_tools/res/hardware.ico new file mode 100644 index 0000000..393532b Binary files /dev/null and b/sln/usb_tools/res/hardware.ico differ diff --git a/sln/usb_tools/res/sideshow.ico b/sln/usb_tools/res/sideshow.ico new file mode 100644 index 0000000..682673a Binary files /dev/null and b/sln/usb_tools/res/sideshow.ico differ diff --git a/sln/usb_tools/res/tool-pro.ico b/sln/usb_tools/res/tool-pro.ico new file mode 100644 index 0000000..a3c5bd0 Binary files /dev/null and b/sln/usb_tools/res/tool-pro.ico differ diff --git a/sln/usb_tools/res/usb_tools.ico b/sln/usb_tools/res/usb_tools.ico new file mode 100644 index 0000000..682673a Binary files /dev/null and b/sln/usb_tools/res/usb_tools.ico differ diff --git a/sln/usb_tools/res/usbtools.rc2 b/sln/usb_tools/res/usbtools.rc2 new file mode 100644 index 0000000..24550fc Binary files /dev/null and b/sln/usb_tools/res/usbtools.rc2 differ diff --git a/sln/usb_tools/resource.h b/sln/usb_tools/resource.h new file mode 100644 index 0000000..c427f82 --- /dev/null +++ b/sln/usb_tools/resource.h @@ -0,0 +1,107 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ 生成的包含文件。 +// 供 usbtools.rc 使用 +// +#define IDD_USB_TOOLS_DIALOG 102 +#define IDD_SETTING 105 +#define IDD_AREA 106 +#define IDD_GAMMA 107 +#define IDR_MAINFRAME 128 +#define IDR_MENU1 134 +#define IDD_SCANNER 135 +#define IDD_PAGE 136 +#define IDC_CHECK_AUTO_MON 1000 +#define IDC_BUTTON_REFRESH 1001 +#define IDC_EDIT_PAPER 1001 +#define IDC_TREE_DEVICES 1002 +#define IDC_STATIC_PAPER 1003 +#define IDC_BUTTON_SEND 1005 +#define IDC_BUTTON_SEND_EP0 1006 +#define IDC_BUTTON_RESET_BULK 1007 +#define IDC_LIST_DEVICES 1008 +#define IDC_UNIT 1008 +#define IDC_EDIT_PNP_MSG 1009 +#define IDC_SCHEME 1009 +#define IDC_CHECK_AUTO 1010 +#define IDC_EDIT_DPI 1010 +#define IDC_BUTTON_RECEIVE 1011 +#define IDC_BUTTON_RESET 1011 +#define IDC_EDIT_x 1012 +#define IDC_EDIT_TYPE 1013 +#define IDC_EDIT_y 1013 +#define IDC_EDIT_REQ 1014 +#define IDC_EDIT_W 1014 +#define IDC_EDIT_H 1015 +#define IDC_STATIC_PAINT 1016 +#define IDC_EDIT_LEN 1017 +#define IDC_CHANNEL 1017 +#define IDC_EDIT_INPUT 1018 +#define IDC_EDIT_INDEX2 1019 +#define IDC_EDIT_OUTPUT 1019 +#define IDC_BUTTON_CLEAR 1020 +#define IDC_STATIC_UNIT 1021 +#define IDC_STATIC_DPI 1022 +#define IDC_STATIC_AREA 1023 +#define IDC_STATIC_LEFT 1024 +#define IDC_BUTTON1 1025 +#define IDC_BUTTON_BROWSE_IMG_PATH 1025 +#define IDC_STATIC_TOP 1025 +#define IDC_EDIT_IMG_PATH 1026 +#define IDC_STATIC_W 1026 +#define IDC_EDIT_PROTOCOL_VER 1027 +#define IDC_STATIC_H 1027 +#define IDC_EDIT_BUILK_IN 1028 +#define IDC_STATIC_SCHEME 1028 +#define IDC_EDIT_BULK_OUT 1029 +#define IDC_STATIC_COLOR 1029 +#define IDC_BUTTON_SCAN 1030 +#define IDC_STATIC_INPUT 1030 +#define IDC_EDIT_LOCAL 1031 +#define IDC_STATIC_OUTPUT 1031 +#define IDC_EDIT_REMOTE 1032 +#define IDC_BUTTON_BROWSE_LOCAL 1033 +#define IDC_CHECK_REPEAT 1034 +#define IDC_TAB_OPER 1035 +#define IDC_EDIT_CMD 1036 +#define IDC_EDIT_PARAM 1037 +#define IDC_BUTTON_START_PROG 1038 +#define IDC_EDIT_COUNT 1039 +#define IDC_STATIC_LOCAL 1040 +#define IDC_STATIC_REMOTE 1041 +#define IDC_STATIC_IMG_PATH 1042 +#define IDC_STATIC_CMD 1043 +#define IDC_STATIC_PARAM 1044 +#define IDC_STATIC_COUNT 1045 +#define IDC_EDIT_STATUS 1046 +#define IDC_EDIT_CMD_QUE 1047 +#define IDC_EDIT_SENT_QUE 1048 +#define IDC_EDIT_IND 1049 +#define IDC_EDIT_VAL 1050 +#define IDC_EDIT_DATA 1052 +#define IDC_STATIC_TYPE 1053 +#define IDC_STATIC_REQ 1054 +#define IDC_STATIC_IND 1055 +#define IDC_STATIC_VAL 1056 +#define IDC_STATIC_LEN 1057 +#define IDC_STATIC_DATA 1058 +#define IDC_STATIC_BULK_IN 1059 +#define IDC_STATIC_BULK_OUT 1060 +#define IDC_STATIC_CMD_QUE 1061 +#define IDC_STATIC_SENT_QUE 1062 +#define IDC_STATIC_OPTS 1063 +#define IDC_CHECK_AUTO_OPEN_IMG 1064 +#define IDC_EDIT_LOG_FILE 1065 +#define IDC_COMBO1 1066 +#define IDC_COMBO_BUF_SIZE 1066 +#define ID_TRAY_EXIT 32771 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 138 +#define _APS_NEXT_COMMAND_VALUE 32772 +#define _APS_NEXT_CONTROL_VALUE 1067 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/sln/usb_tools/targetver.h b/sln/usb_tools/targetver.h new file mode 100644 index 0000000..79934a3 --- /dev/null +++ b/sln/usb_tools/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。 + +//如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并 +// 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。 + +#include diff --git a/sln/usb_tools/usb_tools.cpp b/sln/usb_tools/usb_tools.cpp new file mode 100644 index 0000000..a744be9 --- /dev/null +++ b/sln/usb_tools/usb_tools.cpp @@ -0,0 +1,125 @@ + +// usb_tools.cpp: 定义应用程序的类行为。 +// + +#include "pch.h" +#include "framework.h" +#include "usb_tools.h" +#include "usb_toolsDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CusbtoolsApp + +BEGIN_MESSAGE_MAP(CusbtoolsApp, CWinApp) + ON_COMMAND(ID_HELP, &CWinApp::OnHelp) +END_MESSAGE_MAP() + + +// CusbtoolsApp 构造 + +CusbtoolsApp::CusbtoolsApp() +{ + // TODO: 在此处添加构造代码, + // 将所有重要的初始化放置在 InitInstance 中 +} + + +// 唯一的 CusbtoolsApp 对象 + +CusbtoolsApp theApp; + + +// CusbtoolsApp 初始化 +static bool find_instance(void) +{ + // L"task_usb" + HWND hwnd = FindWindowExW(NULL, NULL, L"#32770", L"USB Tools"); + + while (IsWindow(hwnd)) + { + HWND d = (HWND)GetProp(hwnd, L"task_usb"); + if (d == hwnd) + { + PostMessage(hwnd, WM_TRAY_MSG, MAKELPARAM(MAKEWORD('P', 'O'), MAKEWORD('P', 'M')), (LPARAM)WM_LBUTTONDBLCLK); + return true; + } + hwnd = FindWindowExW(NULL, hwnd, L"#32770", L"USB Tools"); + } + + return false; +} + +BOOL CusbtoolsApp::InitInstance() +{ + // 如果一个运行在 Windows XP 上的应用程序清单指定要 + // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式, + //则需要 InitCommonControlsEx()。 否则,将无法创建窗口。 + INITCOMMONCONTROLSEX InitCtrls; + InitCtrls.dwSize = sizeof(InitCtrls); + // 将它设置为包括所有要在应用程序中使用的 + // 公共控件类。 + InitCtrls.dwICC = ICC_WIN95_CLASSES; + InitCommonControlsEx(&InitCtrls); + + CWinApp::InitInstance(); + + + AfxEnableControlContainer(); + + // 创建 shell 管理器,以防对话框包含 + // 任何 shell 树视图控件或 shell 列表视图控件。 + CShellManager *pShellManager = new CShellManager; + + // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题 + CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); + + // 标准初始化 + // 如果未使用这些功能并希望减小 + // 最终可执行文件的大小,则应移除下列 + // 不需要的特定初始化例程 + // 更改用于存储设置的注册表项 + // TODO: 应适当修改该字符串, + // 例如修改为公司或组织名 + SetRegistryKey(_T("应用程序向导生成的本地应用程序")); + + if(!find_instance()) + { + CusbtoolsDlg dlg; + m_pMainWnd = &dlg; + INT_PTR nResponse = dlg.DoModal(); + if (nResponse == IDOK) + { + // TODO: 在此放置处理何时用 + // “确定”来关闭对话框的代码 + } + else if (nResponse == IDCANCEL) + { + // TODO: 在此放置处理何时用 + // “取消”来关闭对话框的代码 + } + else if (nResponse == -1) + { + TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n"); + TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n"); + } + + // 删除上面创建的 shell 管理器。 + if (pShellManager != nullptr) + { + delete pShellManager; + } + } + +#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS) + ControlBarCleanUp(); +#endif + + // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序, + // 而不是启动应用程序的消息泵。 + return FALSE; +} + diff --git a/sln/usb_tools/usb_tools.h b/sln/usb_tools/usb_tools.h new file mode 100644 index 0000000..093b94a --- /dev/null +++ b/sln/usb_tools/usb_tools.h @@ -0,0 +1,32 @@ + +// usb_tools.h: PROJECT_NAME 应用程序的主头文件 +// + +#pragma once + +#ifndef __AFXWIN_H__ + #error "在包含此文件之前包含 'pch.h' 以生成 PCH" +#endif + +#include "resource.h" // 主符号 + + +// CusbtoolsApp: +// 有关此类的实现,请参阅 usb_tools.cpp +// + +class CusbtoolsApp : public CWinApp +{ +public: + CusbtoolsApp(); + +// 重写 +public: + virtual BOOL InitInstance(); + +// 实现 + + DECLARE_MESSAGE_MAP() +}; + +extern CusbtoolsApp theApp; diff --git a/sln/usb_tools/usb_tools.vcxproj b/sln/usb_tools/usb_tools.vcxproj new file mode 100644 index 0000000..5dfaff8 --- /dev/null +++ b/sln/usb_tools/usb_tools.vcxproj @@ -0,0 +1,350 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + {4C912767-88BE-463E-BFFC-AF994A92E425} + MFCProj + usbtools + 10.0 + + + + Application + true + v142 + Unicode + Dynamic + + + Application + false + v142 + true + Unicode + Dynamic + + + Application + true + v142 + Unicode + Dynamic + + + Application + false + v142 + true + Unicode + Dynamic + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)..\..\..\code_device\sdk;$(ProjectDir)..\sdk\include;$(ProjectDir)..\..\..\sdk\include;$(ProjectDir)..\..\device;$(ProjectDir)..\..\..\code_device\hgdriver\3rdparty\opencv\include;$(IncludePath); + $(ProjectDir)..\sdk\lib\$(Configuration);$(ProjectDir)..\..\..\sdk\lib\win\x86\oem\huagao;$(LibraryPath) + + + true + $(ProjectDir)..\..\..\code_device\sdk + $(SolutionDir)sdk\lib\$(Configuration);$(LibraryPath) + + + false + $(ProjectDir)..\..\..\code_device\sdk;$(ProjectDir)..\sdk\include;$(ProjectDir)..\..\..\sdk\include;$(ProjectDir)..\..\device;$(ProjectDir)..\..\..\code_device\hgdriver\3rdparty\opencv\include;$(IncludePath); + $(ProjectDir)..\sdk\lib\$(Configuration);$(LibraryPath) + + + false + $(ProjectDir)..\..\..\code_device\sdk + $(SolutionDir)sdk\lib\$(Configuration);$(LibraryPath) + + + + Use + Level3 + true + WIN32;_WINDOWS;HGSCANNER_EXPORT;PRODUCT_VENDOR="HuaGo";TEST_HGSCANNER;HGSCANNER_EXPORT;PRODUCT_VENDOR="HuaGo";_DEBUG;%(PreprocessorDefinitions);CUSTOM_USBVIEW + pch.h + 4996 + + + Windows + + + false + true + _DEBUG;%(PreprocessorDefinitions) + + + 0x0804 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + true + _WINDOWS;_DEBUG;%(PreprocessorDefinitions);CUSTOM_USBVIEW;_WINSOCK2API_ + pch.h + 4996 + + + Windows + + + false + true + _DEBUG;%(PreprocessorDefinitions) + + + 0x0804 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + true + true + true + WIN32;_WINDOWS;HGSCANNER_EXPORT;PRODUCT_VENDOR="HuaGo";TEST_HGSCANNER;PRODUCT_VENDOR="HuaGo";NDEBUG;%(PreprocessorDefinitions);CUSTOM_USBVIEW;_WINSOCK2API_ + pch.h + MultiThreadedDLL + 4996 + + + Windows + true + true + + + false + true + NDEBUG;%(PreprocessorDefinitions) + + + 0x0804 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + true + true + true + _WINDOWS;NDEBUG;%(PreprocessorDefinitions);CUSTOM_USBVIEW;_WINSOCK2API_ + pch.h + 4996 + + + Windows + true + true + + + false + true + NDEBUG;%(PreprocessorDefinitions) + + + 0x0804 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + + Create + Create + Create + Create + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sln/usb_tools/usb_tools.vcxproj.filters b/sln/usb_tools/usb_tools.vcxproj.filters new file mode 100644 index 0000000..16d824d --- /dev/null +++ b/sln/usb_tools/usb_tools.vcxproj.filters @@ -0,0 +1,228 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {daee46b5-b9e6-48f1-8c51-309ae6f0f1d2} + + + {3ae4886d-05e1-4b81-ad0c-90be56a8f2a4} + + + {97ca5d9c-2dc2-46cf-99a3-26c83a42ba05} + + + {1d14e64c-8b0e-4e0e-b35c-69ee76e726ad} + + + {49600e52-4fc8-41da-ace4-82fd9c1af621} + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + Imports + + + Imports\usbview + + + Imports\usbview + + + Imports\usbview + + + 头文件 + + + opt-ui + + + opt-ui + + + opt-ui + + + opt-ui + + + opt-ui + + + Imports\scanner + + + Imports\scanner + + + Imports\scanner + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports + + + Imports\scanner + + + + + 源文件 + + + 源文件 + + + 源文件 + + + Imports + + + Imports\usbview + + + Imports\usbview + + + 源文件 + + + opt-ui + + + opt-ui + + + opt-ui + + + opt-ui + + + opt-ui + + + Imports\scanner + + + Imports\scanner + + + Imports\scanner + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports\sdk + + + Imports + + + Imports\scanner + + + + + 资源文件 + + + + + 资源文件 + + + + + 资源文件 + + + \ No newline at end of file diff --git a/sln/usb_tools/usb_toolsDlg.cpp b/sln/usb_tools/usb_toolsDlg.cpp new file mode 100644 index 0000000..859f0a1 --- /dev/null +++ b/sln/usb_tools/usb_toolsDlg.cpp @@ -0,0 +1,934 @@ + +// usb_toolsDlg.cpp: 实现文件 +// + +#include "pch.h" +#include "framework.h" +#include "usb_tools.h" +#include "usb_toolsDlg.h" +#include "afxdialogex.h" +#include "DlgScanner.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + +#include +#include +#include + +#include "../../../code_device/hgdriver/hgdev/usb_manager.h" + +// CusbtoolsDlg 对话框 +#pragma warning(disable: 4996) + + +namespace usb +{ + void enum_endpoints(libusb_device* device, std::vector& eps) + { + libusb_device_descriptor desc; + libusb_config_descriptor* conf = NULL; + int ret = libusb_get_device_descriptor(device, &desc); + + if (ret != 0) + { + return; + } + + for (int i = 0; i < (int)desc.bNumConfigurations; ++i) + { + ret = libusb_get_config_descriptor(device, i, &conf); + if (ret != 0) + { + continue; + } + for (int j = 0; j < conf->bNumInterfaces; ++j) + { + for (int k = 0; k < conf->interface[j].num_altsetting; ++k) + { + for (int l = 0; l < conf->interface[j].altsetting[k].bNumEndpoints; ++l) + { + USBEP ep; + + ep.iface = j; + ep.type = conf->interface[j].altsetting[k].endpoint[l].bmAttributes; + ep.addr = conf->interface[j].altsetting[k].endpoint[l].bEndpointAddress; + ep.packet = conf->interface[j].altsetting[k].endpoint[l].wMaxPacketSize; + eps.push_back(std::move(ep)); + } + } + } + libusb_free_config_descriptor(conf); + } + } + struct + { + BYTE type; + const wchar_t* str; + }g_ep_type[] = { {LIBUSB_TRANSFER_TYPE_BULK, L"Bulk"} + , {LIBUSB_TRANSFER_TYPE_BULK_STREAM, L"Bulk Stream"} + , {LIBUSB_TRANSFER_TYPE_CONTROL, L"Control"} + , {LIBUSB_TRANSFER_TYPE_INTERRUPT, L"Interrupt"} + , {LIBUSB_TRANSFER_TYPE_ISOCHRONOUS, L"Isochronous"} + }; + const wchar_t* ep_type(BYTE type) + { + for (auto& v : g_ep_type) + { + if (v.type == type) + return v.str; + } + + return L"Unknown type"; + } + BYTE ep_type(const wchar_t* str) + { + for (auto& v : g_ep_type) + { + if (wcsicmp(v.str, str) == 0) + return v.type; + } + + return -1; + } + BYTE from_hex_str(const wchar_t* str, bool* ok) + { + BYTE v = 0, cnt = 0; + + if (ok) + *ok = false; + while (*str && cnt < 2) + { + if (*str >= L'0' && *str <= L'9') + { + v <<= 4; + v += *str - L'0'; + } + else if (*str >= L'a' && *str <= L'f') + { + v <<= 4; + v += *str - L'a' + 10; + } + else if (*str >= L'A' && *str <= L'F') + { + v <<= 4; + v += *str - L'A' + 10; + } + else + { + break; + } + cnt++; + str++; + } + if (ok) + *ok = cnt == 2; + + return v; + } + + static int LIBUSB_CALL usb_pnp(libusb_context* ctx, + libusb_device* device, + libusb_hotplug_event event, + void* user_data) + { + ONLNSCANNER *pnp = new ONLNSCANNER; + HWND owner = (HWND)user_data; + libusb_device_descriptor desc; + + pnp->dev = libusb_ref_device(device); + libusb_get_device_descriptor(pnp->dev, &desc); + pnp->vid = desc.idVendor; + pnp->pid = desc.idProduct; + pnp->scanner = NULL; + pnp->family = pnp->display_name = ""; + if (!PostMessage(owner, WM_USB_PNP, event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, (LPARAM)pnp)) + { + libusb_unref_device(pnp->dev); + delete pnp; + } + + return 0; + } + static void custom_usb_event_handler(usb_event ev, libusb_device* device, int vid, int pid, int usb_ver_h, int usb_ver_l, bool* retry/*whether */, void* user) + { + ONLNSCANNER* pnp = new ONLNSCANNER; + HWND owner = (HWND)user; + + pnp->dev = libusb_ref_device(device); + pnp->vid = vid; + pnp->pid = pid; + pnp->scanner = NULL; + pnp->family = pnp->display_name = ""; + if (!PostMessage(owner, WM_USB_PNP, ev == USB_EVENT_DEVICE_ARRIVED, (LPARAM)pnp)) + { + utils::to_log(LOG_LEVEL_WARNING, "Lost device PNP: %04X:%04X %s\r\n", vid, pid, ev == USB_EVENT_DEVICE_ARRIVED ? "arrival" : "left"); + libusb_unref_device(pnp->dev); + delete pnp; + } + } + static int transfer(libusb_device_handle* h, BYTE type, BYTE addr, unsigned char* buf, int *len, const wchar_t** desc = NULL, DWORD to = -1) + { + int ret = -1; + const wchar_t* tmp = NULL; + + if (!desc) + desc = &tmp; + + if (type == LIBUSB_TRANSFER_TYPE_BULK) + { + *desc = L"Bulk"; + ret = libusb_bulk_transfer(h, addr, buf, *len, len, to); + } + else if (type == LIBUSB_TRANSFER_TYPE_CONTROL) + { + // ret = libusb_control_transfer() + } + else if (type == LIBUSB_TRANSFER_TYPE_INTERRUPT) + { + *desc = L"Interrupt"; + ret = libusb_interrupt_transfer(h, addr, buf, *len, len, to); + } + + return ret; + } + + std::wstring now(void) + { + std::string cur(std::move(chronograph::now())); + + return std::move(a2u(cur.c_str())); + + //wchar_t buf[40] = { 0 }; + //time_t t = time(NULL); + //tm* lt = localtime(&t); + + //swprintf_s(buf, _countof(buf) - 1, L"%04d-%02d-%02d %02d:%02d:%02d", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); + + //return buf; + } + const wchar_t* error_name(int err, wchar_t* unk) + { +#define RETURN_IF(e) \ + if(err == e) \ + return L###e; + + RETURN_IF(LIBUSB_SUCCESS); + RETURN_IF(LIBUSB_ERROR_IO); + RETURN_IF(LIBUSB_ERROR_INVALID_PARAM); + RETURN_IF(LIBUSB_ERROR_ACCESS); + RETURN_IF(LIBUSB_ERROR_NO_DEVICE); + RETURN_IF(LIBUSB_ERROR_NOT_FOUND); + RETURN_IF(LIBUSB_ERROR_BUSY); + RETURN_IF(LIBUSB_ERROR_TIMEOUT); + RETURN_IF(LIBUSB_ERROR_OVERFLOW); + RETURN_IF(LIBUSB_ERROR_PIPE); + RETURN_IF(LIBUSB_ERROR_INTERRUPTED); + RETURN_IF(LIBUSB_ERROR_NO_MEM); + RETURN_IF(LIBUSB_ERROR_NOT_SUPPORTED); + RETURN_IF(LIBUSB_ERROR_OTHER); + //RETURN_IF(LIBUSB_ERROR_TRY_AGAIN); + + if (unk) + { + swprintf(unk, L"%d", err); + + return unk; + } + + wchar_t buf[20] = { 0 }; + swprintf_s(buf, _countof(buf) - 1, L"%d", err); + + return buf; + } + + INTER_MODULE_CALLBACK(got_wstr) + { + *((std::wstring*)param) += std::wstring((const wchar_t*)data, len / 2); + + return inter_module_data::SET_RESULT_CONTINUE; + } + INTER_MODULE_CALLBACK(got_str) + { + *((std::string*)param) += std::string(data, len); + + return inter_module_data::SET_RESULT_CONTINUE; + } + std::wstring a2u(const char* ansi) + { + std::wstring u(L""); + + coding_util::ansi_2_unicode(ansi, got_wstr, &u); + + return std::move(u); + } + std::string u2a(const wchar_t* unic) + { + std::string a(""); + + coding_util::unicode_2_ansi(unic, got_str, &a); + + return std::move(a); + } + + static void register_tray(HWND owner, HICON icon, bool reg) + { + if(reg) + win_util::add_tray_icon(WM_TRAY_MSG, owner, WM_TRAY_MSG, L"Operate on USB device", icon); + else + win_util::remove_tray_icon(WM_TRAY_MSG, owner); + } +} + + + +CusbtoolsDlg::CusbtoolsDlg(CWnd* pParent /*=nullptr*/) + : CDialogEx(IDD_USB_TOOLS_DIALOG, pParent) +{ + log_file_ = utils::init_log(LOG_TYPE_FILE); + utils::to_log(LOG_LEVEL_DEBUG, "System info: page = %u, mapping-size = %u, disk-cluster-size = %u\r\n", sys_info::page_size, sys_info::page_map_size, sys_info::cluster_size); + + { + //FILE* dst = fopen("D:\\boxroom\\usb-tx-file\\tx\\test.txt", "wb"); + //int pos = 0; + // + //fwrite("T", 1, 100, dst); + //pos = ftell(dst); + //pos = fseek(dst, 0, SEEK_SET); + //pos = fseek(dst, 1024, SEEK_SET); + //fclose(dst); + } + + m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); +} +CusbtoolsDlg::~CusbtoolsDlg() +{ + usb_manager::instance()->register_hotplug(NULL, NULL); + usb_manager::clear(); + remove_pnp_que(); + utils::uninit(); +} + +void CusbtoolsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_LIST_DEVICES, devl_); +} + +BEGIN_MESSAGE_MAP(CusbtoolsDlg, CDialogEx) + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + ON_BN_CLICKED(IDC_CHECK_AUTO_MON, &CusbtoolsDlg::OnBnClickedCheckAutoMon) + ON_MESSAGE(WM_USB_PNP, &CusbtoolsDlg::OnUsbPnp) + ON_MESSAGE(WM_RCV_MSG, &CusbtoolsDlg::OnRcvMsg) + ON_MESSAGE(WM_OPENNING_DLG_CLOSED, &CusbtoolsDlg::OnDevDlgClosed) + ON_MESSAGE(WM_TRAY_MSG, &CusbtoolsDlg::OnTray) + ON_NOTIFY(NM_DBLCLK, IDC_LIST_DEVICES, &CusbtoolsDlg::OnNMDblclkListDevices) + ON_BN_CLICKED(IDC_BUTTON_CLEAR, &CusbtoolsDlg::OnBnClickedButtonClear) + ON_WM_SYSCOMMAND() + ON_COMMAND(ID_TRAY_EXIT, &CusbtoolsDlg::OnTrayExit) + ON_COMMAND_RANGE(ID_TRAY_EXIT + 1, ID_TRAY_EXIT + 100, &CusbtoolsDlg::OnTrayDevice) + ON_BN_CLICKED(IDOK, &CusbtoolsDlg::OnBnClickedOk) + ON_WM_SIZE() +END_MESSAGE_MAP() + + +// CusbtoolsDlg 消息处理程序 +bool show_balloon(int tray_id, HWND main_wnd, const wchar_t* tips, const wchar_t* title, DWORD dwTimeout, HICON hIcon, DWORD dwInfoFlag) +{ + NOTIFYICONDATAW nid = { 0 }; + + nid.cbSize = NOTIFYICONDATA_V2_SIZE; // KEY parameter to show balloon message! + //nid.uVersion = NOTIFYICON_VERSION; + nid.hWnd = main_wnd; + nid.uID = tray_id; + nid.uFlags = /*NIF_INFO |*/ NIF_TIP; + wcscpy_s(nid.szTip, _countof(nid.szTip) - 1, tips); + wcscpy_s(nid.szInfoTitle, _countof(nid.szInfoTitle) - 1, title); + nid.hIcon = hIcon ? hIcon : (HICON)SendMessageW(main_wnd, WM_GETICON, ICON_SMALL, 0); + nid.uTimeout = dwTimeout; + nid.dwInfoFlags = dwInfoFlag; + + return Shell_NotifyIconW(NIM_MODIFY, &nid) == TRUE; +} + +BOOL CusbtoolsDlg::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 + // 执行此操作 + SetIcon(m_hIcon, TRUE); // 设置大图标 + SetIcon(m_hIcon, FALSE); // 设置小图标 + + // TODO: 在此添加额外的初始化代码 + int ind = 0; + devl_.InsertColumn(ind++, TEXT("No."), 0, 51); + devl_.InsertColumn(ind++, TEXT("VID"), 0, 75); + devl_.InsertColumn(ind++, TEXT("PID"), 0, 75); + devl_.SetExtendedStyle(devl_.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_INFOTIP); + + //SetDlgItemInt(IDC_EDIT_TYPE, 0x40); + //SetDlgItemInt(IDC_EDIT_REQ, 0x64); + //SetDlgItemInt(IDC_EDIT_VALUE, 16); + //SetDlgItemInt(IDC_EDIT_INDEX, 0); + //SetDlgItemInt(IDC_EDIT_LEN, 20); + + OnBnClickedCheckAutoMon(); + + tray_ = LoadMenu(NULL, MAKEINTRESOURCE(IDR_MENU1)); + desktop_ = ::FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL); + usb::register_tray(m_hWnd, m_hIcon, true); + + SetPropW(m_hWnd, L"task_usb", m_hWnd); + ::SetDlgItemTextA(m_hWnd, IDC_EDIT_LOG_FILE, log_file_.c_str()); + usb_manager::instance()->register_hotplug(&usb::custom_usb_event_handler, (void*)m_hWnd); + + return TRUE; // 除非将焦点设置到控件,否则返回 TRUE +} + +// 如果向对话框添加最小化按钮,则需要下面的代码 +// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, +// 这将由框架自动完成。 + +void CusbtoolsDlg::OnPaint() +{ + if (IsIconic()) + { + CPaintDC dc(this); // 用于绘制的设备上下文 + + SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); + + // 使图标在工作区矩形中居中 + int cxIcon = GetSystemMetrics(SM_CXICON); + int cyIcon = GetSystemMetrics(SM_CYICON); + CRect rect; + GetClientRect(&rect); + int x = (rect.Width() - cxIcon + 1) / 2; + int y = (rect.Height() - cyIcon + 1) / 2; + + // 绘制图标 + dc.DrawIcon(x, y, m_hIcon); + } + else + { + CDialogEx::OnPaint(); + } +} + +//当用户拖动最小化窗口时系统调用此函数取得光标 +//显示。 +HCURSOR CusbtoolsDlg::OnQueryDragIcon() +{ + return static_cast(m_hIcon); +} + +void CusbtoolsDlg::enable_io_ui(bool enable) +{ + GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(enable); + + + GetDlgItem(IDC_CHECK_AUTO)->EnableWindow(enable); + enable &= ((CButton*)GetDlgItem(IDC_CHECK_AUTO))->GetCheck() != BST_CHECKED; + GetDlgItem(IDC_BUTTON_RECEIVE)->EnableWindow(enable); + //GetDlgItem(IDC_COMBO_EP2)->EnableWindow(enable); + + //OnCbnSelchangeComboEp(); + //GetDlgItem(IDC_EDIT_TYPE)->EnableWindow(enable); + //GetDlgItem(IDC_EDIT_REQ)->EnableWindow(enable); + //GetDlgItem(IDC_EDIT_VALUE)->EnableWindow(enable); + //GetDlgItem(IDC_EDIT_INDEX)->EnableWindow(enable); + //GetDlgItem(IDC_EDIT_LEN)->EnableWindow(enable); +} +void CusbtoolsDlg::add_pnp_que(ONLNSCANNER* pnp) +{ + int vid = pnp->vid, + pid = pnp->pid; + bool add = true; + for (auto& v : pnp_que_) + { + if (v.usb.dev == pnp->dev) + { + libusb_unref_device(pnp->dev); + if (v.dlg) + { + open_usb(v, false); + } + add = false; + break; + } + } + + if (add) + { + SCANNER scnr; + scnr.usb = *pnp; + scnr.dlg = NULL; + scnr.id = InterlockedIncrement(&id_); + pnp_que_.push_back(scnr); + + // add to UI ... + int item = devl_.InsertItem(devl_.GetItemCount(), std::to_wstring(devl_.GetItemCount() + 1).c_str()); + TCHAR text[40] = { 0 }; + + _stprintf(text, TEXT("%04X"), pnp->vid); + devl_.SetItemText(item, 1, text); + + + _stprintf(text, TEXT("%04X"), pnp->pid); + devl_.SetItemText(item, 2, text); + + devl_.SetItemData(item, (DWORD_PTR)scnr.id); + } + + if (!IsWindowVisible()) + { + wchar_t tips[128] = { 0 }; + swprintf_s(tips, _countof(tips) - 1, L"%04X:%04X arrived", vid, pid); + show_balloon(WM_TRAY_MSG, m_hWnd, tips, L"USB", 1000, m_hIcon, NIF_TIP); + } +} +void CusbtoolsDlg::remove_pnp_que(ONLNSCANNER* pnp) +{ + for (size_t i = 0; i < pnp_que_.size(); ++i) + { + if (!pnp || (pnp_que_[i].usb.dev == pnp->dev)) + { + if (IsWindow(devl_.m_hWnd)) + { + for (int j = 0; j < devl_.GetItemCount(); ++j) + { + if (!pnp || devl_.GetItemData(j) == (DWORD_PTR)pnp_que_[i].id) + { + //if (devl_.GetItemState(j, LVIS_SELECTED)) + // enable_io_ui(false); + + if (pnp_que_[i].dlg == NULL) + { + devl_.SetItemData(j, 0); + devl_.DeleteItem(j); + for (; pnp && j < devl_.GetItemCount(); ++j) + devl_.SetItemText(j, 0, std::to_wstring(j + 1).c_str()); + } + if (pnp) + break; + } + } + } + if (pnp_que_[i].dlg && IsWindow(pnp_que_[i].dlg->m_hWnd)) + { + pnp_que_[i].dlg->set_device(NULL); + //if(!pnp) + //{ + // pnp_que_[i].dlg->DestroyWindow(); + // delete pnp_que_[i].dlg; + // pnp_que_[i].dlg = NULL; + //} + } + libusb_unref_device(pnp_que_[i].usb.dev); + close_device(pnp_que_[i]); + if (pnp) + { + pnp_que_.erase(pnp_que_.begin() + i); + break; + } + else + { + if (pnp_que_[i].dlg) + { + delete pnp_que_[i].dlg; + pnp_que_[i].dlg = NULL; + } + } + } + } + + if (IsWindow(m_hWnd) && !IsWindowVisible() && pnp) + { + wchar_t tips[128] = { 0 }; + swprintf_s(tips, _countof(tips) - 1, L"%04X:%04X left", pnp->vid, pnp->pid); + show_balloon(WM_TRAY_MSG, m_hWnd, tips, L"USB", 1000, m_hIcon, NIF_TIP); + } +} +void CusbtoolsDlg::append_log(const wchar_t* log, UINT id) +{ + if (!IsWindow(m_hWnd)) + return; + + int len = ::GetWindowTextLength(GetDlgItem(id)->m_hWnd); + wchar_t* buf = new wchar_t[len + 128]; + libusb_device_descriptor desc; + + ::GetDlgItemTextW(m_hWnd, id, buf, len + 20); + ::SetDlgItemTextW(m_hWnd, id, (std::wstring(buf) + log).c_str()); + while (::SendMessage(GetDlgItem(id)->m_hWnd, EM_SCROLL, SB_PAGEDOWN, 0)); + + delete[] buf; +} +void CusbtoolsDlg::log_pnp(ONLNSCANNER* pnp, bool arrive) +{ + wchar_t buf[128] = { 0 }; + + swprintf_s(buf + lstrlenW(buf), _countof(buf) - 1, L"%s: %04X:%04X %s\r\n", usb::now().c_str(), + pnp->vid, pnp->pid, arrive ? L"Arrive" : L"Left"); + append_log(buf, IDC_EDIT_PNP_MSG); + utils::to_log(LOG_LEVEL_ALL, "%04X:%04X %s\r\n", pnp->vid, pnp->pid, arrive ? "Arrived" : "Left"); +} +bool CusbtoolsDlg::endpoint_from_combo_text(wchar_t* text, BYTE& type, BYTE& addr) +{ + wchar_t* ptr = wcsstr(text, L" "); + bool ret = false; + + if (ptr) + { + *ptr++ = 0; + type = usb::ep_type(text); + if (type != -1) + { + ptr = wcsstr(ptr, L"-"); + if (ptr++) + { + while (*ptr == L' ') ptr++; + addr = usb::from_hex_str(ptr, &ret); + } + } + } + + return ret; +} +void CusbtoolsDlg::close_device(SCANNER& pnp) +{ + wchar_t msg[128] = { 0 }; + + swprintf_s(msg, _countof(msg) - 1, L"%s: %04X:%04X closed.\r\n", usb::now().c_str(), pnp.usb.vid, pnp.usb.pid); + append_log(msg, IDC_EDIT_PNP_MSG); +} +ONLNSCANNER* CusbtoolsDlg::get_cur_device(void) +{ + for (int i = 0; i < devl_.GetItemCount(); ++i) + { + if (devl_.GetItemState(i, LVIS_SELECTED)) + return (ONLNSCANNER*)devl_.GetItemData(i); + } + + return NULL; +} +void CusbtoolsDlg::open_usb(SCANNER& pnp, bool from_list) +{ + wchar_t msg[256] = { 0 }, errb[40] = { 0 }; + int face = -1, ret = 0; + + //* + if (pnp.dlg == NULL) + { + pnp.dlg = new CDlgScanner(this); // new CDlgOpenning(this); + pnp.dlg->Create(IDD_SCANNER); // (IDD_OPENNING); + } + pnp.dlg->set_device(&pnp.usb); + pnp.dlg->ShowWindow(SW_SHOW); + + if (!msg[0]) + swprintf_s(msg, _countof(msg) - 1, L"%s: open %04X:%04X success\r\n", usb::now().c_str(), pnp.usb.vid, pnp.usb.pid); + else if (!from_list) + ::MessageBoxW(m_hWnd, msg, L"Failed", MB_OK | MB_ICONERROR); + + append_log(msg, IDC_EDIT_PNP_MSG); +} + + +LRESULT CusbtoolsDlg::OnUsbPnp(WPARAM wp, LPARAM lp) +{ + ONLNSCANNER *pnp = (ONLNSCANNER*)lp; + + log_pnp(pnp, wp); + if (pnp->pid == 0 && pnp->vid == 0) + { + libusb_unref_device(pnp->dev); + delete pnp; + + return 0; + } + + if (wp) + { + ////for (auto& v : pnp_que_) + ////{ + //// if (v->ctx == pnp->ctx && v->device == pnp->device) + //// { + //// libusb_unref_device(pnp->device); + //// v->arrive = true; + //// delete pnp; + //// pnp = NULL; + //// break; + //// } + ////} + //if (pnp) + add_pnp_que(pnp); + } + else + { + remove_pnp_que(pnp); + // libusb_unref_device(pnp->device); + } + delete pnp; + + return 0; +} +LRESULT CusbtoolsDlg::OnRcvMsg(WPARAM wp, LPARAM lp) +{ + std::wstring* msg = (std::wstring*)lp; + + //append_log(msg->c_str(), IDC_EDIT_REPLY); + delete msg; + + return 0; +} +LRESULT CusbtoolsDlg::OnDevDlgClosed(WPARAM wp, LPARAM lp) +{ + CDlgScanner* dlg = (CDlgScanner*)lp; + //CDlgOpenning* dlg = (CDlgOpenning*)lp; + + for (int i = 0; i < pnp_que_.size(); ++i) + { + if (pnp_que_[i].dlg == dlg) + { + bool online = dlg->is_online(); + pnp_que_[i].dlg = NULL; + close_device(pnp_que_[i]); + if (!online) + { + remove_pnp_que(&pnp_que_[i].usb); + } + break; + } + } + dlg->DestroyWindow(); + delete dlg; + + return 0; +} +LRESULT CusbtoolsDlg::OnTray(WPARAM wp, LPARAM lp) +{ + UINT msg = (UINT)lp; + + if (msg == WM_LBUTTONDBLCLK) + { + ShowWindow(SW_RESTORE); + SetForegroundWindow(); + if (MAKELPARAM(MAKEWORD('P', 'O'), MAKEWORD('P', 'M')) == wp) + { + HWND taskwnd = ::FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL); + if (desktop_ != taskwnd && IsWindow(taskwnd)) + { + usb::register_tray(m_hWnd, m_hIcon, true); + desktop_ = taskwnd; + } + } + } + else if (msg == WM_RBUTTONDOWN) + { + // exit ... + POINT pt = { 0 }; + HMENU hm = GetSubMenu(tray_, 0); + MENUITEMINFOW mi = { 0 }; + + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_DATA; + while (GetMenuItemInfoW(hm, 0, TRUE, &mi) && mi.dwItemData) + DeleteMenu(hm, 0, MF_BYPOSITION); + + for (int i = 0; i < devl_.GetItemCount(); ++i) + { + int tid = (int)devl_.GetItemData(i), vid = 0, pid = 0; + wchar_t id[40] = { 0 }; + bool openned = false; + + for (auto& v : pnp_que_) + { + if (v.id == tid) + { + vid = v.usb.vid; + pid = v.usb.pid; + openned = v.dlg != NULL; + break; + } + } + + swprintf_s(id, _countof(id) - 1, L"%04X:%04X", vid, pid); + InsertMenuW(hm, i, MF_BYPOSITION, ID_TRAY_EXIT + 1 + i, id); + mi.dwItemData = (LONG)tid; + SetMenuItemInfoW(hm, i, TRUE, &mi); + if (openned) + CheckMenuItem(hm, i, MF_CHECKED | MF_BYPOSITION); + } + GetCursorPos(&pt); + TrackPopupMenu(hm, TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, m_hWnd, NULL); + } + + return 0; +} + + +void CusbtoolsDlg::OnBnClickedCheckAutoMon() +{ + // TODO: 在此添加控件通知处理程序代码 + static libusb_hotplug_callback_handle handle = NULL; + + //if (ctx_) + //{ + // libusb_context* cp = ctx_; + + // mon_ = false; + // ctx_ = NULL; + // if (mon_thrd_.get() && mon_thrd_->joinable()) + // mon_thrd_->join(); + // mon_thrd_.reset(); + // libusb_hotplug_deregister_callback(cp, handle); + // libusb_exit(cp); + // handle = NULL; + //} + //else + //{ + // libusb_init(&ctx_); + // int ret = libusb_hotplug_register_callback(ctx_, (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), + // (libusb_hotplug_flag)LIBUSB_HOTPLUG_ENUMERATE, + // LIBUSB_HOTPLUG_MATCH_ANY,//LIBUSB_HOTPLUG_MATCH_ANY + // LIBUSB_HOTPLUG_MATCH_ANY,//LIBUSB_HOTPLUG_MATCH_ANY, + // LIBUSB_HOTPLUG_MATCH_ANY, + // &usb::usb_pnp, + // m_hWnd, + // &handle); + // mon_ = true; + // mon_thrd_.reset(new std::thread(&CusbtoolsDlg::usb_monitor_thread, this)); + //} +} + + + +void CusbtoolsDlg::OnNMDblclkListDevices(NMHDR* pNMHDR, LRESULT* pResult) +{ + LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); + // TODO: 在此添加控件通知处理程序代码 + *pResult = 0; + if (pNMItemActivate->iItem < 0 || pNMItemActivate->iItem >= devl_.GetItemCount()) + return; + + wchar_t msg[256] = { 0 }, unk[40] = { 0 }; + int tid = (int)devl_.GetItemData(pNMItemActivate->iItem); + int face = -1, ret = 0; + SCANNER* pnp = NULL; + + for (auto& v : pnp_que_) + { + if (v.id == tid) + { + pnp = &v; + break; + } + } + if (!pnp) + return; + + if (!pnp->dlg) + { + open_usb(*pnp); + } + else + { + RECT r = { 0 }; + + pnp->dlg->GetWindowRect(&r); + ::SetWindowPos(pnp->dlg->m_hWnd, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOSENDCHANGING | SWP_SHOWWINDOW); + ::SetTimer(pnp->dlg->m_hWnd, WM_TIMER_CANCEL_TOPMOST, 1000, NULL); + } +} + + + + + +void CusbtoolsDlg::OnBnClickedButtonClear() +{ + // TODO: 在此添加控件通知处理程序代码 + SetDlgItemText(IDC_EDIT_PNP_MSG, TEXT("")); +} + + +void CusbtoolsDlg::OnSysCommand(UINT nID, LPARAM lParam) +{ + // TODO: 在此添加消息处理程序代码和/或调用默认值 + if (nID == SC_CLOSE) + { + ShowWindow(SW_MINIMIZE); + ShowWindow(SW_HIDE); + + return; + } + + CDialogEx::OnSysCommand(nID, lParam); +} + + +void CusbtoolsDlg::OnTrayExit() +{ + // TODO: 在此添加命令处理程序代码 + usb::register_tray(m_hWnd, m_hIcon, false); + + PostQuitMessage(0); +} +void CusbtoolsDlg::OnTrayDevice(UINT id) +{ + // TODO: 在此添加命令处理程序代码 + HMENU hm = GetSubMenu(tray_, 0); + MENUITEMINFOW mi = { 0 }; + + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_DATA | MIIM_STATE; + GetMenuItemInfoW(hm, id, FALSE, &mi); + if (mi.dwItemData) + { + SCANNER* pnp = NULL; + + for (auto& v : pnp_que_) + { + if (v.id == (int)mi.dwItemData) + { + pnp = &v; + break; + } + } + if (!pnp) + return; + + if (pnp->dlg) + pnp->dlg->OnBnClickedOk(); + else + open_usb(*pnp, false); + } +} + + +void CusbtoolsDlg::OnBnClickedOk() +{ + // TODO: 在此添加控件通知处理程序代码 + OnTrayExit(); + CDialogEx::OnOK(); +} + + +void CusbtoolsDlg::OnSize(UINT nType, int cx, int cy) +{ + CDialogEx::OnSize(nType, cx, cy); + + // TODO: 在此处添加消息处理程序代码 + //if (cx == 0 && cy == 0 && ::IsWindow(devl_.m_hWnd)) + //{ + // for (int i = 0; i < devl_.GetItemCount(); ++i) + // { + // ONLNSCANNER* pnp = (ONLNSCANNER*)devl_.GetItemData(i); + // if (pnp && pnp->dlg) + // pnp->dlg->ShowWindow(SW_SHOW); + // } + //} +} diff --git a/sln/usb_tools/usb_toolsDlg.h b/sln/usb_tools/usb_toolsDlg.h new file mode 100644 index 0000000..0c4e1bd --- /dev/null +++ b/sln/usb_tools/usb_toolsDlg.h @@ -0,0 +1,75 @@ + +// usb_toolsDlg.h: 头文件 +// + +#pragma once +#include +#include +#include + +#include "DlgScanner.h" + +// CusbtoolsDlg 对话框 +class CusbtoolsDlg : public CDialogEx +{ + typedef struct _scanner + { + DWORD id; + ONLNSCANNER usb; + CDlgScanner* dlg; + }SCANNER; + std::vector pnp_que_; + HMENU tray_; + HWND desktop_; + DWORD id_ = 1; + + void enable_io_ui(bool enable); + void add_pnp_que(ONLNSCANNER* pnp); + void remove_pnp_que(ONLNSCANNER* pnp = NULL); + void append_log(const wchar_t* log, UINT id); + void log_pnp(ONLNSCANNER* pnp, bool arrive); + bool endpoint_from_combo_text(wchar_t* text, BYTE& type, BYTE& addr); + void close_device(SCANNER& pnp); + ONLNSCANNER* get_cur_device(void); + void open_usb(SCANNER& pnp, bool from_list = true); + +// 构造 +public: + CusbtoolsDlg(CWnd* pParent = nullptr); // 标准构造函数 + ~CusbtoolsDlg(); + +// 对话框数据 +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_USB_TOOLS_DIALOG }; +#endif + + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 + + +// 实现 +protected: + HICON m_hIcon; + std::string log_file_; + + // 生成的消息映射函数 + virtual BOOL OnInitDialog(); + afx_msg void OnPaint(); + afx_msg HCURSOR OnQueryDragIcon(); + DECLARE_MESSAGE_MAP() +public: + afx_msg LRESULT OnUsbPnp(WPARAM wp, LPARAM lp); + afx_msg LRESULT OnRcvMsg(WPARAM wp, LPARAM lp); + afx_msg LRESULT OnDevDlgClosed(WPARAM wp, LPARAM lp); + afx_msg LRESULT OnTray(WPARAM wp, LPARAM lp); + afx_msg void OnBnClickedCheckAutoMon(); + CListCtrl devl_; + afx_msg void OnNMDblclkListDevices(NMHDR* pNMHDR, LRESULT* pResult); + CComboBox ep2_; + afx_msg void OnBnClickedButtonClear(); + afx_msg void OnSysCommand(UINT nID, LPARAM lParam); + afx_msg void OnTrayExit(); + afx_msg void OnTrayDevice(UINT id); + afx_msg void OnBnClickedOk(); + afx_msg void OnSize(UINT nType, int cx, int cy); +}; diff --git a/sln/usb_tools/usbtools.rc b/sln/usb_tools/usbtools.rc new file mode 100644 index 0000000..eebfd25 Binary files /dev/null and b/sln/usb_tools/usbtools.rc differ