// DlgIndicator.cpp: 实现文件 // #include "DlgPage.h" #include "resource.h" #include "scanned_img.h" // for local_trans #include "DlgArea.h" #include "DlgGamma.h" #include "gb_json.h" #include "../../code_device/hgsane/sane_hg_mdw.h" // for #include "../../sdk/include/lang/app_language.h" // #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"; volatile bool dlg_base::msg_thread_exited_ = true; dlg_base::dlg_base(HWND parent, UINT idd) : parent_(parent), hwnd_(NULL), idd_(idd), ui_event_notify_(NULL), ui_notify_param_(NULL) , msg_thread_id_(0), destroyed_(false), child_(false), del_at_destroy_(false) { } dlg_base::~dlg_base() { //if (IsWindow(hwnd_)) //{ // //SetPropW(hwnd_, dlg_base::prop_name.c_str(), NULL); // //if (GetCurrentThreadId() == GetWindowThreadProcessId(hwnd_, NULL)) // // DestroyWindow(hwnd_); // //show(false); // //PostMessage(hwnd_, WM_DESTROY, 0, 0); //} //if (!child_) //{ // EnableWindow(parent_, TRUE); // BringWindowToTop(parent_); //} //if (msg_thread_.get()) //{ // PostThreadMessageW(msg_thread_id_, WM_QUIT_MSG_THREAD, 0, 0); // if (msg_thread_->joinable()) // msg_thread_->join(); //} //else //{ // destroyed_ = false; // //PostMessageW(hwnd(), WM_DESTROY, 0, 0); // //while (!destroyed_) // //{ // // MSG msg = { 0 }; // // PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE); // //} //} } BOOL CALLBACK dlg_base::dlg_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nc-winuser-dlgproc: // 尽管对话框过程类似于窗口过程,但它不得调用 DefWindowProc 函数来处理不需要的消息 dlg_base* obj = (dlg_base*)GetPropW(hwnd, dlg_base::prop_name.c_str()); BOOL ret = FALSE; if (msg == WM_INITDIALOG) { obj = (dlg_base*)lp; SetPropW(hwnd, dlg_base::prop_name.c_str(), (HANDLE)obj); obj->hwnd_ = hwnd; obj->child_ = (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD) == WS_CHILD; } else if (msg == WM_NCDESTROY) { SetPropW(hwnd, dlg_base::prop_name.c_str(), NULL); } if (obj) { ret = obj->handle_message(msg, wp, lp); if (msg == WM_INITDIALOG) obj->ready_ = true; else if (msg == WM_CLOSE) { DestroyWindow(hwnd); ret = TRUE; } else if (msg == WM_NCDESTROY) { if (!obj->child_) { EnableWindow(obj->parent_, TRUE); BringWindowToTop(obj->parent_); } obj->destroyed_ = true; if(obj->del_at_destroy_) delete obj; } } return ret; } DWORD WINAPI dlg_base::start_thread(LPVOID lp) { dlg_base::msg_thread_exited_ = false; ((dlg_base*)lp)->thread_pump_message(); dlg_base::msg_thread_exited_ = true; return 0; } void dlg_base::thread_pump_message(void) { msg_thread_id_ = GetCurrentThreadId(); hwnd_ = CreateDialogParamW(g_my_inst, MAKEINTRESOURCE(idd_), parent_, (DLGPROC)&dlg_base::dlg_proc, (LPARAM)this); if (IsWindow(hwnd_)) { MSG msg = { 0 }; BOOL ret = TRUE; HWND hwnd = hwnd_; while ((ret = GetMessageW(&msg, NULL, 0, 0))) { if (ret == -1 || msg.message == WM_QUIT_MSG_THREAD) break; TranslateMessage(&msg); DispatchMessageW(&msg); //if (msg.message == WM_DESTROY) //{ // if (msg.hwnd == hwnd) // break; //} } DestroyWindow(hwnd); //if (del_at_destroy_) // delete this; } } 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); } bool dlg_base::is_message_thread_exited(void) { return dlg_base::msg_thread_exited_; } 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(bool sole_thread) { // InitCommonControls(); if (sole_thread) { HANDLE h = CreateThread(NULL, 0, &dlg_base::start_thread, this, 0, &msg_thread_id_); if (h) { CloseHandle(h); while (!IsWindow(hwnd_)) std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } else hwnd_ = CreateDialogParamW(g_my_inst, MAKEINTRESOURCE(idd_), parent_, (DLGPROC)&dlg_base::dlg_proc, (LPARAM)this); } namespace notify { typedef struct _notify_param { void(__stdcall* ui_event_notify)(int uev, void* sender, void* param); void* param; int ev; void* sender; }NOTIFYPARAM, *LPNOTIFYPARAM; static DWORD WINAPI thread_notify(LPVOID lp) { LPNOTIFYPARAM param = (LPNOTIFYPARAM)lp; param->ui_event_notify(param->ev, param->sender, param->param); delete param; return 0; } static void notify(void(__stdcall* f)(int, void*, void*), int ev, void* sender, void* param) { LPNOTIFYPARAM p = new NOTIFYPARAM; HANDLE h = NULL; p->ui_event_notify = f; p->sender = sender; p->ev = ev; p->param = param; h = CreateThread(NULL, 0, thread_notify, p, 0, NULL); if (h) CloseHandle(h); else delete p; } } void dlg_base::notify_ui_event(int ev) { if (ev == SANE_EVENT_UI_SCAN_COMMAND && ui_event_notify_) ui_event_notify_(ev, this, ui_notify_param_); else notify::notify(ui_event_notify_, ev, this, ui_notify_param_); } gb::sane_config_schm* dlg_base::get_config(bool* create) { gb::sane_config_schm* cfg = NULL; SendMessage(parent_, WM_GET_CONFIG_OBJ, (WPARAM)create, (LPARAM)&cfg); return cfg; } 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) { while (!ready_) { MSG msg = { 0 }; PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE); } 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_); center_window(hwnd_, parent_); } 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, wait_destory = FALSE; 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_) { if (destroyed_) break; else if (!wait_destory) { wait_destory = true; ShowWindow(hwnd(), SW_HIDE); if (enable_parent) { EnableWindow(parent, TRUE); } close(); } } } return modal_exit_; } void dlg_base::quit_modal(int non_zero_code) { // assert ( non_zero_code ); modal_exit_ = non_zero_code; } void dlg_base::close(void) { if(msg_thread_id_) // 独立的消息循环,则退出 PostMessageW(hwnd_, WM_QUIT, 0, 0); else // 共用父窗口消息循环,则关闭窗口。关闭窗口消息由自己处理,不提交到默认过程 PostMessageW(hwnd_, WM_CLOSE, 0, 0); } 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) { del_at_destroy_ = true; size_.cx = size_.cy = 0; pos_.x = 12; pos_.y = 15; create(); tips_wnd_.create(hwnd()); } dlg_page::~dlg_page() { } 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; case WM_DESTROY: for (auto& v : ctrls_) DestroyWindow(v); 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); 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, 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; 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, 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; 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; int x = pos_.x; std::wstring now(local_trans::a2u((char*)cur_val, CP_UTF8)); 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, 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; } 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_); float x = .0f, y = .0f, w = .0f, h = .0f; SANE_Fixed sf; sane_.sane_io_control_api(dev_, IO_CTRL_CODE_GET_PAPER_SIZE, &utf8[0], &size); dlg.set_paper(paper_.c_str(), (float)(size & 0x0ffff), float(size >> 16), dpi_); sane_.sane_control_option_api(dev_, id_custom_left_, SANE_ACTION_GET_VALUE, &sf, NULL); x = (float)SANE_UNFIX(sf); sane_.sane_control_option_api(dev_, id_custom_top_, SANE_ACTION_GET_VALUE, &sf, NULL); y = (float)SANE_UNFIX(sf); sane_.sane_control_option_api(dev_, id_custom_right_, SANE_ACTION_GET_VALUE, &sf, NULL); w = (float)SANE_UNFIX(sf) - x; sane_.sane_control_option_api(dev_, id_custom_bottom_, SANE_ACTION_GET_VALUE, &sf, NULL); h = (float)SANE_UNFIX(sf) - y; dlg.set_area(x, y, w, h); if (dlg.do_modal(parent_) == IDOK) { SANE_Fixed val = SANE_FIX(dlg.x_in_mm()); SANE_Int after = 0; bool created = true; gb::sane_config_schm* cfg = get_config(&created); sane_.sane_control_option_api(dev_, id_custom_left_, SANE_ACTION_SET_VALUE, &val, &after); if (cfg) cfg->config_changed(id_custom_left_, (char*)&val, sizeof(val)); val = SANE_FIX(dlg.y_in_mm()); sane_.sane_control_option_api(dev_, id_custom_top_, SANE_ACTION_SET_VALUE, &val, &after); if (cfg) cfg->config_changed(id_custom_top_, (char*)&val, sizeof(val)); val = SANE_FIX(dlg.x_in_mm() + dlg.w_in_mm()); sane_.sane_control_option_api(dev_, id_custom_right_, SANE_ACTION_SET_VALUE, &val, &after); if (cfg) cfg->config_changed(id_custom_right_, (char*)&val, sizeof(val)); val = SANE_FIX(dlg.y_in_mm() + dlg.h_in_mm()); sane_.sane_control_option_api(dev_, id_custom_bottom_, SANE_ACTION_SET_VALUE, &val, &after); if (cfg) { cfg->config_changed(id_custom_bottom_, (char*)&val, sizeof(val)); cfg->release(); } } 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); gb::sane_config_schm* cfg = get_config(&created); if (cfg) { cfg->config_changed(id_custom_gamma_, (char*)&gamma, sizeof(gamma), true); cfg->release(); } } 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; std::string to_v(hg_sane_middleware::option_value_2_string(type, val)); SANE_Status statu = sane_.sane_control_option_api(dev_, id - dlg_page::dyn_id_base, SANE_ACTION_SET_VALUE, val, &after); bool created = true; gb::sane_config_schm* cfg = get_config(&created); done_ = false; if (cfg) { size_t len = 0; switch (type) { case SANE_TYPE_BOOL: len = sizeof(SANE_Bool); break; case SANE_TYPE_INT: len = sizeof(SANE_Int); break; case SANE_TYPE_FIXED: len = sizeof(SANE_Fixed); break; case SANE_TYPE_STRING: len = lstrlenA((char*)val); break; default: break; } if (len) { if (type == SANE_TYPE_STRING) { std::string def_lang(to_default_language((char*)val, nullptr)); cfg->config_changed(id - dlg_page::dyn_id_base, &def_lang[0], def_lang.length()); } else cfg->config_changed(id - dlg_page::dyn_id_base, (char*)val, len); } cfg->release(); } if (statu == SANE_STATUS_INVAL) { std::wstring attr(local_trans::lang_trans_between_hz936(CONST_STRING_NOT_SUPPORT_SET) + L" \"" + local_trans::a2u(sane_.sane_get_option_descriptor_api(dev_, id - dlg_page::dyn_id_base)->title, CP_UTF8) + L"\" " + local_trans::lang_trans_between_hz936(CONST_STRING_SET_TO_BE) + L": "), attr_v(local_trans::a2u(to_v.c_str(), CP_UTF8)); MessageBoxW(hwnd(), (attr + attr_v).c_str(), local_trans::lang_trans_between_hz936(CONST_STRING_PARAMETER_ERROR).c_str(), MB_OK); set_ctrl_value(wnd, type, val, true); } else { std::string tov(hg_sane_middleware::option_value_2_string(type, val)); if (tov != to_v) { std::wstring attr(local_trans::lang_trans_between_hz936(CONST_STRING_PARAMETER_ORIGIN) + L" \"" + local_trans::a2u(to_v.c_str(), CP_UTF8) + L"\" " + local_trans::lang_trans_between_hz936(CONST_STRING_PARAMETER_INEXACT)), attr_v(local_trans::a2u(tov.c_str(), CP_UTF8)); MessageBoxW(hwnd(), (attr + attr_v).c_str(), local_trans::lang_trans_between_hz936(CONST_STRING_PARAMETER_ADJUSTED).c_str(), MB_OK); } 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 { 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, text_ind = 0; wchar_t cls_name[40] = { 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; HWND host = ctrls_[ind]; GetClassNameW(host, cls_name, _countof(cls_name) - 1); if (IS_COMBOX(cls_name)) { if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST) { SendMessage(host, CB_RESETCONTENT, 0, 0); for (int i = 0; desc->constraint.string_list[i]; ++i) { std::wstring text(local_trans::a2u(desc->constraint.string_list[i], CP_UTF8)); SendMessageW(host, CB_ADDSTRING, 0, (LPARAM)text.c_str()); } } else if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST) { SendMessage(host, CB_RESETCONTENT, 0, 0); for (int i = 0; i < desc->constraint.word_list[0]; ++i) { SendMessageW(host, CB_ADDSTRING, 0, (LPARAM)std::to_wstring(desc->constraint.word_list[1 + i]).c_str()); } } } else if (IS_TRACKBAR(cls_name)) { if (desc->constraint_type == SANE_CONSTRAINT_RANGE) { if (desc->type == SANE_TYPE_FIXED) { double lower = SANE_UNFIX(desc->constraint.range->min), upper = SANE_UNFIX(desc->constraint.range->max); SendMessage(host, TBM_SETRANGEMIN, 1, (LPARAM)int(lower * 100.0f + .5f)); SendMessage(host, TBM_SETRANGEMAX, 1, (LPARAM)int(upper * 100.0f + .5f)); } else { int lower = desc->constraint.range->min, upper = desc->constraint.range->max; SendMessage(host, TBM_SETRANGEMIN, 1, (LPARAM)int(lower * 1.0f + .5f)); SendMessage(host, TBM_SETRANGEMAX, 1, (LPARAM)int(upper * 1.0f + .5f)); } } } else if (IS_STATIC_TEXT(cls_name)) { if (desc->constraint_type == SANE_CONSTRAINT_RANGE) { if (text_ind == 1) { wchar_t t[20] = { 0 }; if (desc->type == SANE_TYPE_FIXED) swprintf_s(t, _countof(t) - 1, L"%.2f", SANE_UNFIX(desc->constraint.range->min)); else swprintf_s(t, _countof(t) - 1, L"%d", desc->constraint.range->min); SetWindowTextW(host, t); } else if(text_ind == 2) { wchar_t t[20] = { 0 }; if (desc->type == SANE_TYPE_FIXED) swprintf_s(t, _countof(t) - 1, L"%.2f", SANE_UNFIX(desc->constraint.range->max)); else swprintf_s(t, _countof(t) - 1, L"%d", desc->constraint.range->max); SetWindowTextW(host, t); } } text_ind++; } set_ctrl_value(ctrls_[ind], desc->type, cur_val, true); EnableWindow(ctrls_[ind], (desc->cap & SANE_CAP_INACTIVE) != SANE_CAP_INACTIVE); 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 &= (desc->cap & SANE_CAP_INACTIVE) != SANE_CAP_INACTIVE; 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; 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; 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); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //