diff --git a/sane/DlgPage.cpp b/sane/DlgPage.cpp index 1d3fd56..de53806 100644 --- a/sane/DlgPage.cpp +++ b/sane/DlgPage.cpp @@ -5,6 +5,15 @@ #include "resource.h" #include "scanned_img.h" // for local_trans +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +#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_TRACKBAR(cls) IS_STR_EQUAL(cls, TRACKBAR_CLASSW) +#define IS_UPDOWN_ARROW(cls) IS_STR_EQUAL(cls, UPDOWN_CLASSW) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // dlg_base 对话框 extern HMODULE g_my_inst; @@ -122,86 +131,635 @@ HFONT dlg_base::get_font(void) { return (HFONT)SendMessage(hwnd_, WM_GETFONT, 0, 0); } +int dlg_base::get_string_width(const wchar_t* str) +{ + HDC hdc = GetWindowDC(hwnd_); + SIZE size = { 0 }; + + GetTextExtentPointW(hdc, str, lstrlenW(str), &size); + ReleaseDC(hwnd_, hdc); + + return size.cx; +} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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 = 12; +int dlg_page::gap_y = 15; +int dlg_page::spin_w = 8; -dlg_page::dlg_page(HWND parent, const wchar_t* name) : dlg_base(parent, IDD_PAGE), name_(name ? name : L""), ctrl_id_(0) +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) { size_.cx = size_.cy = 0; + pos_.x = 12; + pos_.y = 8; create(); - refresh_font(); } dlg_page::~dlg_page() -{} +{ + for (auto& v : ctrls_) + DestroyWindow(v); +} BOOL dlg_page::handle_message(UINT msg, WPARAM wp, LPARAM lp) { - return FALSE; + 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; + default: + ret = FALSE; + } + + return ret; } void dlg_page::on_font_changed(void) -{ - refresh_font(); -} -void dlg_page::refresh_font(void) { HFONT font = get_font(); LOGFONTW lf = { 0 }; - - GetObjectW(font, sizeof(lf), &lf); - font_size_.cx = lf.lfWidth; - font_size_.cy = lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight; } -bool dlg_page::create_control_bool(int sn, const SANE_Option_Descriptor* desc, void* cur_val) -{ - std::wstring title(local_trans::a2u(desc->title, CP_UTF8)); - bool ret = true; - bool now = *(SANE_Bool*)cur_val == SANE_TRUE ? true : false; - int w = title.length() * font_size_.cx + 18, - h = font_size_.cy; - HWND wnd = CreateWindowW(L"Button", L"check", WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX, pos_.x, pos_.y, w, h, hwnd(), NULL, g_my_inst, NULL); - - pos_.y += h + dlg_page::gap_y; - if (size_.cx < w) - size_.cx = w; - if (size_.cy < pos_.y) - size_.cy = pos_.y; - if (desc->cap & SANE_CAP_INACTIVE) - EnableWindow(wnd, FALSE); +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 = (upper - lower + step - 1) / step; + size->cx += 100; + size->cx /= 200; + size->cx *= 100; + if (size->cx < 100) + size->cx = 100; + if (size->cx > 300) + size->cx = 300; + 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 < 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 < 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 ret; + return wnd; } -bool dlg_page::create_control_int(int sn, const SANE_Option_Descriptor* desc, void* cur_val) +HWND dlg_page::create_control_int(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) { - bool ret = true; + // 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 = 100; + 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_CHANGE || + IS_COMBOX(cls) && code != CBN_SELCHANGE) + { + return; + } + + control_action((HWND)ctrl); +} +BOOL dlg_page::on_notify(int ctrl_id, LPNMHDR pnmh) +{ + wchar_t cls[128] = { 0 }; + + GetClassNameW((HWND)pnmh->hwndFrom, cls, _countof(cls) - 1); + if (IS_TRACKBAR(cls)) + { + if (pnmh->code != NM_RELEASEDCAPTURE && + pnmh->code != NM_CUSTOMDRAW) + 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; + } + + 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 + 2]; + + GetWindowTextW(ctrl, buf, len); + buf[len] = 0; + 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) + { + std::string utf8(local_trans::u2a(buf, CP_UTF8)); + ret = new char[len + 2]; + if (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; } -bool dlg_page::create_control_float(int sn, const SANE_Option_Descriptor* desc, void* cur_val) +void dlg_page::set_ctrl_value(HWND ctrl, SANE_Value_Type type, void* val, bool only_me, bool skip_ctrl) { - bool ret = true; + if (only_me) + { + bool finish = done_; + wchar_t cls[128] = { 0 }; + int(__cdecl * cmpstr)(const wchar_t*, const wchar_t*) = wcscmp; - return ret; + GetClassNameW(ctrl, cls, _countof(cls) - 1); + done_ = false; + if (cmpstr(cls, WC_BUTTONW) == 0) + { + if (type == SANE_TYPE_BOOL) + SendMessage(ctrl, BM_SETCHECK, *(SANE_Bool*)val == SANE_TRUE ? (WPARAM)BST_CHECKED : (WPARAM)BST_UNCHECKED, 0); + } + else if (cmpstr(cls, TRACKBAR_CLASSW) == 0) + { + 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 (cmpstr(cls, WC_EDITW) == 0 || cmpstr(cls, WC_COMBOBOXW) == 0) + { + 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 + SendMessageW(ctrl, CB_SELECTSTRING, 0, (LPARAM)text.c_str()); + } + else if (cmpstr(cls, UPDOWN_CLASSW) == 0) + { + 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 < ctrls_.size(); ++ind) + { + if (GetWindowLong(ctrls_[ind], GWL_ID) == id) + break; + } + for (; ind < 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); + } + } } -bool dlg_page::create_control_string(int sn, const SANE_Option_Descriptor* desc, void* cur_val) +void dlg_page::free_ctrl_value(void* val) { - bool ret = true; - - return ret; + if (val) + { + char* v = (char*)val; + delete[] v; + } } -bool dlg_page::create_control_button(int sn, const SANE_Option_Descriptor* desc, void* cur_val) +int dlg_page::find_control_ind(HWND wnd) { - bool ret = true; + int ind = -1; - return ret; + for (int i = 0; i < 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); + done_ = false; + set_ctrl_value(wnd, type, val, false, !(after || statu)); + done_ = true; + if (after || statu) + 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; } bool dlg_page::add_control(int sn, const SANE_Option_Descriptor* desc, void* cur_val) @@ -211,7 +769,7 @@ bool dlg_page::add_control(int sn, const SANE_Option_Descriptor* desc, void* cur struct { int sane_type; - bool(dlg_page::* func)(int, const SANE_Option_Descriptor*, void*); + 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} @@ -222,13 +780,40 @@ bool dlg_page::add_control(int sn, const SANE_Option_Descriptor* desc, void* cur { if (creat[i].sane_type == desc->type) { - ret = (this->*creat[i].func)(sn, desc, cur_val); + 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) + { + 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 < ctrls_.size(); ++pos) + EnableWindow(ctrls_[pos], FALSE); + } + if (size_.cx < pos_.x + text.cx) + size_.cx = pos_.x + text.cx; + pos_.y += text.cy + dlg_page::gap_y; + } break; } } + size_.cy = pos_.y; return ret; } +void dlg_page::add_control_done(void) +{ + done_ = true; +} SIZE dlg_page::desired_size(void) { return size_; @@ -241,6 +826,32 @@ 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 < ctrls_.size(); ++ind) + { + if (GetWindowLong(ctrls_[ind], GWL_ID) == sn) + { + found = true; + break; + } + } + done_ = false; + for (; ind < ctrls_.size(); ++ind) + { + if (GetWindowLong(ctrls_[ind], GWL_ID) != sn) + break; + set_ctrl_value(ctrls_[ind], desc->type, cur_val, true); + EnableWindow(ctrls_[ind], (desc->cap & SANE_CAP_INACTIVE) != SANE_CAP_INACTIVE); + } + done_ = true; + + return found; +} const wchar_t* dlg_page::name(void) { return name_.c_str(); diff --git a/sane/DlgPage.h b/sane/DlgPage.h index 1a9a82e..2482a71 100644 --- a/sane/DlgPage.h +++ b/sane/DlgPage.h @@ -40,37 +40,63 @@ public: HWND get_item(UINT id); BOOL set_font(HFONT font); HFONT get_font(void); + int get_string_width(const wchar_t* str); }; class dlg_page : public dlg_base { std::wstring name_; SIZE size_; - SIZE font_size_; UINT ctrl_id_; POINT pos_; + SANEAPI sane_; + SANE_Handle dev_; + bool done_; + std::vector ctrls_; + 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; BOOL handle_message(UINT msg, WPARAM wp, LPARAM lp) override; void on_font_changed(void) override; - void refresh_font(void); - bool create_control_bool(int sn, const SANE_Option_Descriptor* desc, void* cur_val); - bool create_control_int(int sn, const SANE_Option_Descriptor* desc, void* cur_val); - bool create_control_float(int sn, const SANE_Option_Descriptor* desc, void* cur_val); - bool create_control_string(int sn, const SANE_Option_Descriptor* desc, void* cur_val); - bool create_control_button(int sn, const SANE_Option_Descriptor* desc, void* cur_val); + + 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); public: - dlg_page(HWND parent, const wchar_t* name); + 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); }; diff --git a/sane/DlgSetting.cpp b/sane/DlgSetting.cpp index 4b4db74..bc7911d 100644 --- a/sane/DlgSetting.cpp +++ b/sane/DlgSetting.cpp @@ -53,7 +53,7 @@ BOOL dlg_setting::handle_message(UINT msg, WPARAM wp, LPARAM lp) handle_command(HIWORD(wp), LOWORD(wp), (HWND)lp); break; case WM_NOTIFY: - on_notify((int)wp, (LPNMHDR)lp); + ret = on_notify((int)wp, (LPNMHDR)lp); break; case WM_REFRESH_OPTION: refresh_controls((int)wp); @@ -73,11 +73,14 @@ void dlg_setting::handle_command(WORD code, WORD id, HANDLE ctrl) } 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_); } } void dlg_setting::notify_over(void) @@ -99,7 +102,10 @@ void dlg_setting::on_init_dialog(void) if (desc->type == SANE_TYPE_GROUP) { if (page) + { dlg_base::get_max_size(size, page->desired_size()); + page->add_control_done(); + } page = add_tab(desc->title); } else if (page) @@ -115,14 +121,23 @@ void dlg_setting::on_init_dialog(void) else if(desc->type == SANE_TYPE_BUTTON) { if (cmp_sane_opt(OPTION_TITLE_BZ, desc->title)) + { ShowWindow(GetDlgItem(hwnd_, IDC_BUTTON_HELP), SW_SHOW); - if (cmp_sane_opt(OPTION_TITLE_HFMRSZ, desc->title)) + id_help_ = sn - 1; + } + else if (cmp_sane_opt(OPTION_TITLE_HFMRSZ, desc->title)) + { 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) + { dlg_base::get_max_size(size, page->desired_size()); + page->add_control_done(); + } if (size.cx || size.cy || IsWindow(tab_)) { @@ -136,7 +151,7 @@ void dlg_setting::on_init_dialog(void) GetWindowRect(tab_, &r); y = r.bottom - r.top; size.cy += y; - r.right = r.left + rme.right - rme.left; + r.right = r.left + size.cx; screen_2_client(&r); MoveWindow(tab_, r.left, r.top, r.right - r.left, y, TRUE); } @@ -174,9 +189,19 @@ void dlg_setting::on_init_dialog(void) select_page(0); UpdateWindow(hwnd()); } -void dlg_setting::on_notify(int ctrl_id, LPNMHDR pnmh) +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) { @@ -190,7 +215,7 @@ int dlg_setting::get_tab_count(void) dlg_page* dlg_setting::add_tab(const char* utf8_title) { std::wstring title(local_trans::a2u(utf8_title, CP_UTF8)); - dlg_page *page = new dlg_page(hwnd(), title.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 }; @@ -198,7 +223,12 @@ dlg_page* dlg_setting::add_tab(const char* utf8_title) page->set_font(font); if (!IsWindow(tab_)) { - tab_ = CreateWindowW(L"SysTabControl32", L"pages", WS_CHILD | WS_VISIBLE, 0, 0, 100, lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, hwnd(), NULL, g_my_inst, NULL); + 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); SendMessage(tab_, WM_SETFONT, (WPARAM)SendMessage(get_item(IDOK), WM_GETFONT, 0, 0), 1); SetWindowLong(tab_, GWL_ID, 1234); ShowWindow(tab_, SW_SHOW); @@ -271,7 +301,24 @@ dlg_page* dlg_setting::select_page(int index) } 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) + { + 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++); + } } void dlg_setting::set_quit_notify(void(__stdcall* notify)(bool, void*), void* param) diff --git a/sane/DlgSetting.h b/sane/DlgSetting.h index dab540d..2aa2db9 100644 --- a/sane/DlgSetting.h +++ b/sane/DlgSetting.h @@ -23,6 +23,8 @@ class dlg_setting : public dlg_base unsigned int papers_; unsigned int images_; bool err_; + int id_help_; + int id_restore_; HWND tab_; @@ -36,7 +38,7 @@ class dlg_setting : public dlg_base void handle_command(WORD code, WORD id, HANDLE ctrl); void notify_over(void); void on_init_dialog(void); - void on_notify(int ctrl_id, LPNMHDR pnmh); + 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); diff --git a/sane/sane.rc b/sane/sane.rc index dab6080..169396a 100644 --- a/sane/sane.rc +++ b/sane/sane.rc @@ -74,7 +74,7 @@ BEGIN PUSHBUTTON "",IDC_BUTTON_HELP,167,7,48,14,NOT WS_VISIBLE END -IDD_PAGE DIALOGEX 0, 0, 257, 133 +IDD_PAGE DIALOGEX 0, 0, 258, 133 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN @@ -108,7 +108,7 @@ BEGIN IDD_PAGE, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 250 + RIGHTMARGIN, 251 TOPMARGIN, 7 BOTTOMMARGIN, 126 END