| /* | |
| * TWAIN32 Options UI | |
| * | |
| * Copyright 2006 CodeWeavers, Aric Stewart | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include <stdlib.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #define NONAMELESSUNION | |
| #define NONAMELESSSTRUCT | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winuser.h" | |
| #include "winnls.h" | |
| #include "wingdi.h" | |
| #include "prsht.h" | |
| #include "twain.h" | |
| #include "sane_i.h" | |
| #include "wine/debug.h" | |
| #include "resource.h" | |
| #include "wine/unicode.h" | |
| #ifdef SONAME_LIBSANE | |
| WINE_DEFAULT_DEBUG_CHANNEL(twain); | |
| #define ID_BASE 0x100 | |
| #define ID_EDIT_BASE 0x1000 | |
| #define ID_STATIC_BASE 0x2000 | |
| static INT_PTR CALLBACK DialogProc (HWND , UINT , WPARAM , LPARAM ); | |
| static INT CALLBACK PropSheetProc(HWND, UINT,LPARAM); | |
| static int create_leading_static(HDC hdc, LPCSTR text, | |
| LPDLGITEMTEMPLATEW* template_out, int y, int id) | |
| { | |
| LPDLGITEMTEMPLATEW tpl = NULL; | |
| INT len; | |
| SIZE size; | |
| LPBYTE ptr; | |
| LONG base; | |
| *template_out = NULL; | |
| if (!text) | |
| return 0; | |
| base = GetDialogBaseUnits(); | |
| len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0); | |
| len *= sizeof(WCHAR); | |
| len += sizeof(DLGITEMTEMPLATE); | |
| len += 3*sizeof(WORD); | |
| tpl = HeapAlloc(GetProcessHeap(),0,len); | |
| tpl->style=WS_VISIBLE; | |
| tpl->dwExtendedStyle = 0; | |
| tpl->x = 4; | |
| tpl->y = y; | |
| tpl->id = ID_BASE; | |
| GetTextExtentPoint32A(hdc,text,lstrlenA(text),&size); | |
| tpl->cx = MulDiv(size.cx,4,LOWORD(base)); | |
| tpl->cy = MulDiv(size.cy,8,HIWORD(base)) * 2; | |
| ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE); | |
| *(LPWORD)ptr = 0xffff; | |
| ptr += sizeof(WORD); | |
| *(LPWORD)ptr = 0x0082; | |
| ptr += sizeof(WORD); | |
| ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR); | |
| *(LPWORD)ptr = 0x0000; | |
| *template_out = tpl; | |
| return len; | |
| } | |
| static int create_trailing_edit(HDC hdc, LPDLGITEMTEMPLATEW* template_out, int id, | |
| int y, LPCSTR text,BOOL is_int) | |
| { | |
| LPDLGITEMTEMPLATEW tpl = NULL; | |
| INT len; | |
| LPBYTE ptr; | |
| SIZE size; | |
| LONG base; | |
| static const char int_base[] = "0000 xxx"; | |
| static const char float_base[] = "0000.0000 xxx"; | |
| base = GetDialogBaseUnits(); | |
| len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0); | |
| len *= sizeof(WCHAR); | |
| len += sizeof(DLGITEMTEMPLATE); | |
| len += 3*sizeof(WORD); | |
| tpl = HeapAlloc(GetProcessHeap(),0,len); | |
| tpl->style=WS_VISIBLE|ES_READONLY|WS_BORDER; | |
| tpl->dwExtendedStyle = 0; | |
| tpl->x = 1; | |
| tpl->y = y; | |
| tpl->id = id; | |
| if (is_int) | |
| GetTextExtentPoint32A(hdc,int_base,lstrlenA(int_base),&size); | |
| else | |
| GetTextExtentPoint32A(hdc,float_base,lstrlenA(float_base),&size); | |
| tpl->cx = MulDiv(size.cx*2,4,LOWORD(base)); | |
| tpl->cy = MulDiv(size.cy,8,HIWORD(base)) * 2; | |
| ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE); | |
| *(LPWORD)ptr = 0xffff; | |
| ptr += sizeof(WORD); | |
| *(LPWORD)ptr = 0x0081; | |
| ptr += sizeof(WORD); | |
| ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR); | |
| *(LPWORD)ptr = 0x0000; | |
| *template_out = tpl; | |
| return len; | |
| } | |
| static int create_item(HDC hdc, const SANE_Option_Descriptor *opt, | |
| INT id, LPDLGITEMTEMPLATEW *template_out, int y, int *cx, int* count) | |
| { | |
| LPDLGITEMTEMPLATEW tpl = NULL,rc = NULL; | |
| WORD class = 0xffff; | |
| DWORD styles = WS_VISIBLE; | |
| LPBYTE ptr = NULL; | |
| LPDLGITEMTEMPLATEW lead_static = NULL; | |
| LPDLGITEMTEMPLATEW trail_edit = NULL; | |
| DWORD leading_len = 0; | |
| DWORD trail_len = 0; | |
| DWORD local_len = 0; | |
| LPCSTR title = NULL; | |
| CHAR buffer[255]; | |
| int padding = 0; | |
| int padding2 = 0; | |
| int base_x = 0; | |
| LONG base; | |
| int ctl_cx = 0; | |
| SIZE size; | |
| GetTextExtentPoint32A(hdc,"X",1,&size); | |
| base = GetDialogBaseUnits(); | |
| base_x = MulDiv(size.cx,4,LOWORD(base)); | |
| if (opt->type == SANE_TYPE_BOOL) | |
| { | |
| class = 0x0080; /* Button */ | |
| styles |= BS_AUTOCHECKBOX; | |
| local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0); | |
| local_len *= sizeof(WCHAR); | |
| title = opt->title; | |
| } | |
| else if (opt->type == SANE_TYPE_INT) | |
| { | |
| SANE_Int i; | |
| psane_control_option(activeDS.deviceHandle, id-ID_BASE, | |
| SANE_ACTION_GET_VALUE, &i,NULL); | |
| sprintf(buffer,"%i",i); | |
| if (opt->constraint_type == SANE_CONSTRAINT_NONE) | |
| { | |
| class = 0x0081; /* Edit*/ | |
| styles |= ES_NUMBER; | |
| title = buffer; | |
| local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0); | |
| local_len *= sizeof(WCHAR); | |
| } | |
| else if (opt->constraint_type == SANE_CONSTRAINT_RANGE) | |
| { | |
| class = 0x0084; /* scroll */ | |
| ctl_cx = 10 * base_x; | |
| trail_len += create_trailing_edit(hdc, &trail_edit, id + | |
| ID_EDIT_BASE, y,buffer,TRUE); | |
| } | |
| else | |
| { | |
| class= 0x0085; /* Combo */ | |
| ctl_cx = 10 * base_x; | |
| styles |= CBS_DROPDOWNLIST; | |
| } | |
| leading_len += create_leading_static(hdc, opt->title, &lead_static, y, | |
| id+ID_STATIC_BASE); | |
| } | |
| else if (opt->type == SANE_TYPE_FIXED) | |
| { | |
| SANE_Fixed *i; | |
| double dd; | |
| i = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word)); | |
| psane_control_option(activeDS.deviceHandle, id-ID_BASE, | |
| SANE_ACTION_GET_VALUE, i, NULL); | |
| dd = SANE_UNFIX(*i); | |
| sprintf(buffer,"%f",dd); | |
| HeapFree(GetProcessHeap(),0,i); | |
| if (opt->constraint_type == SANE_CONSTRAINT_NONE) | |
| { | |
| class = 0x0081; /* Edit */ | |
| title = buffer; | |
| local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0); | |
| local_len *= sizeof(WCHAR); | |
| } | |
| else if (opt->constraint_type == SANE_CONSTRAINT_RANGE) | |
| { | |
| class= 0x0084; /* scroll */ | |
| ctl_cx = 10 * base_x; | |
| trail_len += create_trailing_edit(hdc, &trail_edit, id + | |
| ID_EDIT_BASE, y,buffer,FALSE); | |
| } | |
| else | |
| { | |
| class= 0x0085; /* Combo */ | |
| ctl_cx = 10 * base_x; | |
| styles |= CBS_DROPDOWNLIST; | |
| } | |
| leading_len += create_leading_static(hdc, opt->title, &lead_static, y, | |
| id+ID_STATIC_BASE); | |
| } | |
| else if (opt->type == SANE_TYPE_STRING) | |
| { | |
| if (opt->constraint_type == SANE_CONSTRAINT_NONE) | |
| { | |
| class = 0x0081; /* Edit*/ | |
| } | |
| else | |
| { | |
| class= 0x0085; /* Combo */ | |
| ctl_cx = opt->size * base_x; | |
| styles |= CBS_DROPDOWNLIST; | |
| } | |
| leading_len += create_leading_static(hdc, opt->title, &lead_static, y, | |
| id+ID_STATIC_BASE); | |
| psane_control_option(activeDS.deviceHandle, id-ID_BASE, | |
| SANE_ACTION_GET_VALUE, buffer,NULL); | |
| title = buffer; | |
| local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0); | |
| local_len *= sizeof(WCHAR); | |
| } | |
| else if (opt->type == SANE_TYPE_BUTTON) | |
| { | |
| class = 0x0080; /* Button */ | |
| local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0); | |
| local_len *= sizeof(WCHAR); | |
| title = opt->title; | |
| } | |
| else if (opt->type == SANE_TYPE_GROUP) | |
| { | |
| class = 0x0080; /* Button */ | |
| styles |= BS_GROUPBOX; | |
| local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0); | |
| local_len *= sizeof(WCHAR); | |
| title = opt->title; | |
| } | |
| local_len += sizeof(DLGITEMTEMPLATE); | |
| if (title) | |
| local_len += 3*sizeof(WORD); | |
| else | |
| local_len += 4*sizeof(WORD); | |
| if (lead_static) | |
| { | |
| padding = leading_len % sizeof(DWORD); | |
| rc = HeapReAlloc(GetProcessHeap(),0,lead_static,leading_len+local_len + padding); | |
| tpl = (LPDLGITEMTEMPLATEW)((LPBYTE)rc + leading_len + padding); | |
| } | |
| else | |
| rc = tpl = HeapAlloc(GetProcessHeap(),0,local_len); | |
| tpl->style=styles; | |
| tpl->dwExtendedStyle = 0; | |
| if (lead_static) | |
| tpl->x = lead_static->x + lead_static->cx + 1; | |
| else if (opt->type == SANE_TYPE_GROUP) | |
| tpl->x = 2; | |
| else | |
| tpl->x = 4; | |
| tpl->y = y; | |
| tpl->id = id; | |
| if (title) | |
| { | |
| GetTextExtentPoint32A(hdc,title,lstrlenA(title),&size); | |
| tpl->cx = size.cx; | |
| tpl->cy = size.cy; | |
| } | |
| else | |
| { | |
| if (lead_static) | |
| tpl->cy = lead_static->cy; | |
| else | |
| tpl->cy = 15; | |
| if (!ctl_cx) | |
| ctl_cx = 15; | |
| tpl->cx = ctl_cx; | |
| } | |
| ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE); | |
| *(LPWORD)ptr = 0xffff; | |
| ptr += sizeof(WORD); | |
| *(LPWORD)ptr = class; | |
| ptr += sizeof(WORD); | |
| if (title) | |
| { | |
| ptr += MultiByteToWideChar(CP_ACP,0,title,-1,(LPWSTR)ptr,local_len) * sizeof(WCHAR); | |
| } | |
| else | |
| { | |
| *(LPWORD)ptr = 0x0000; | |
| ptr += sizeof(WORD); | |
| } | |
| *((LPWORD)ptr) = 0x0000; | |
| ptr += sizeof(WORD); | |
| if (trail_edit) | |
| { | |
| trail_edit->x = tpl->cx + tpl->x + 2; | |
| *cx = trail_edit->x + trail_edit->cx; | |
| padding2 = (leading_len + local_len + padding)% sizeof(DWORD); | |
| rc = HeapReAlloc(GetProcessHeap(),0,rc,leading_len+local_len + padding | |
| +padding2+trail_len); | |
| memcpy(((LPBYTE)rc) + leading_len + local_len + padding + padding2, | |
| trail_edit,trail_len); | |
| } | |
| else | |
| *cx = tpl->cx + tpl->x; | |
| *template_out = rc; | |
| if (leading_len) | |
| *count = 2; | |
| else | |
| *count = 1; | |
| if (trail_edit) | |
| *count+=1; | |
| return leading_len + local_len + padding + padding2 + trail_len; | |
| } | |
| static LPDLGTEMPLATEW create_options_page(HDC hdc, int *from_index, | |
| SANE_Int optcount, BOOL split_tabs) | |
| { | |
| int i; | |
| INT y = 2; | |
| LPDLGTEMPLATEW tpl = NULL; | |
| LPBYTE all_controls = NULL; | |
| DWORD control_len = 0; | |
| int max_cx = 0; | |
| int group_max_cx = 0; | |
| LPBYTE ptr; | |
| int group_offset = -1; | |
| INT control_count = 0; | |
| for (i = *from_index; i < optcount; i++) | |
| { | |
| LPDLGITEMTEMPLATEW item_tpl = NULL; | |
| const SANE_Option_Descriptor *opt; | |
| int len; | |
| int padding = 0; | |
| int x; | |
| int count; | |
| int hold_for_group = 0; | |
| opt = psane_get_option_descriptor(activeDS.deviceHandle, i); | |
| if (!opt) | |
| continue; | |
| if (opt->type == SANE_TYPE_GROUP && split_tabs) | |
| { | |
| if (control_len > 0) | |
| { | |
| *from_index = i - 1; | |
| goto exit; | |
| } | |
| else | |
| { | |
| *from_index = i; | |
| return NULL; | |
| } | |
| } | |
| if (!SANE_OPTION_IS_ACTIVE (opt->cap)) | |
| continue; | |
| len = create_item(hdc, opt, ID_BASE + i, &item_tpl, y, &x, &count); | |
| control_count += count; | |
| if (!len) | |
| { | |
| continue; | |
| } | |
| hold_for_group = y; | |
| y+= item_tpl->cy + 1; | |
| max_cx = max(max_cx, x + 2); | |
| group_max_cx = max(group_max_cx, x ); | |
| padding = len % sizeof(DWORD); | |
| if (all_controls) | |
| { | |
| LPBYTE newone; | |
| newone = HeapReAlloc(GetProcessHeap(),0,all_controls, | |
| control_len + len + padding); | |
| all_controls = newone; | |
| memcpy(all_controls+control_len,item_tpl,len); | |
| memset(all_controls+control_len+len,0xca,padding); | |
| HeapFree(GetProcessHeap(),0,item_tpl); | |
| } | |
| else | |
| { | |
| if (!padding) | |
| { | |
| all_controls = (LPBYTE)item_tpl; | |
| } | |
| else | |
| { | |
| all_controls = HeapAlloc(GetProcessHeap(),0,len + padding); | |
| memcpy(all_controls,item_tpl,len); | |
| memset(all_controls+len,0xcb,padding); | |
| HeapFree(GetProcessHeap(),0,item_tpl); | |
| } | |
| } | |
| if (opt->type == SANE_TYPE_GROUP) | |
| { | |
| if (group_offset == -1) | |
| { | |
| group_offset = control_len; | |
| group_max_cx = 0; | |
| } | |
| else | |
| { | |
| LPDLGITEMTEMPLATEW group = | |
| (LPDLGITEMTEMPLATEW)(all_controls+group_offset); | |
| group->cy = hold_for_group - group->y; | |
| group->cx = group_max_cx; | |
| group = (LPDLGITEMTEMPLATEW)(all_controls+control_len); | |
| group->y += 2; | |
| y+=2; | |
| group_max_cx = 0; | |
| group_offset = control_len; | |
| } | |
| } | |
| control_len += len + padding; | |
| } | |
| if ( group_offset && !split_tabs ) | |
| { | |
| LPDLGITEMTEMPLATEW group = | |
| (LPDLGITEMTEMPLATEW)(all_controls+group_offset); | |
| group->cy = y - group->y; | |
| group->cx = group_max_cx; | |
| y+=2; | |
| } | |
| *from_index = i-1; | |
| exit: | |
| tpl = HeapAlloc(GetProcessHeap(),0,sizeof(DLGTEMPLATE) + 3*sizeof(WORD) + | |
| control_len); | |
| tpl->style = WS_VISIBLE | WS_OVERLAPPEDWINDOW; | |
| tpl->dwExtendedStyle = 0; | |
| tpl->cdit = control_count; | |
| tpl->x = 0; | |
| tpl->y = 0; | |
| tpl->cx = max_cx + 10; | |
| tpl->cy = y + 10; | |
| ptr = (LPBYTE)tpl + sizeof(DLGTEMPLATE); | |
| *(LPWORD)ptr = 0x0000; | |
| ptr+=sizeof(WORD); | |
| *(LPWORD)ptr = 0x0000; | |
| ptr+=sizeof(WORD); | |
| *(LPWORD)ptr = 0x0000; | |
| ptr+=sizeof(WORD); | |
| memcpy(ptr,all_controls,control_len); | |
| HeapFree(GetProcessHeap(),0,all_controls); | |
| return tpl; | |
| } | |
| BOOL DoScannerUI(void) | |
| { | |
| HDC hdc; | |
| PROPSHEETPAGEW psp[10]; | |
| int page_count= 0; | |
| PROPSHEETHEADERW psh; | |
| int index = 1; | |
| SANE_Status rc; | |
| SANE_Int optcount; | |
| UINT psrc; | |
| LPWSTR szCaption; | |
| DWORD len; | |
| hdc = GetDC(0); | |
| memset(&psp,0,sizeof(psp)); | |
| rc = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE, | |
| &optcount, NULL); | |
| if (rc != SANE_STATUS_GOOD) | |
| { | |
| ERR("Unable to read number of options\n"); | |
| return FALSE; | |
| } | |
| while (index < optcount) | |
| { | |
| const SANE_Option_Descriptor *opt; | |
| psp[page_count].u.pResource = create_options_page(hdc, &index, | |
| optcount, TRUE); | |
| opt = psane_get_option_descriptor(activeDS.deviceHandle, index); | |
| if (opt->type == SANE_TYPE_GROUP) | |
| { | |
| LPWSTR title = NULL; | |
| INT len; | |
| len = MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0); | |
| title = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR)); | |
| MultiByteToWideChar(CP_ACP,0,opt->title,-1,title,len); | |
| psp[page_count].pszTitle = title; | |
| } | |
| if (psp[page_count].u.pResource) | |
| { | |
| psp[page_count].dwSize = sizeof(PROPSHEETPAGEW); | |
| psp[page_count].dwFlags = PSP_DLGINDIRECT | PSP_USETITLE; | |
| psp[page_count].hInstance = SANE_instance; | |
| psp[page_count].pfnDlgProc = DialogProc; | |
| psp[page_count].lParam = (LPARAM)&activeDS; | |
| page_count ++; | |
| } | |
| index ++; | |
| } | |
| len = lstrlenA(activeDS.identity.Manufacturer) | |
| + lstrlenA(activeDS.identity.ProductName) + 2; | |
| szCaption = HeapAlloc(GetProcessHeap(),0,len *sizeof(WCHAR)); | |
| MultiByteToWideChar(CP_ACP,0,activeDS.identity.Manufacturer,-1, | |
| szCaption,len); | |
| szCaption[lstrlenA(activeDS.identity.Manufacturer)] = ' '; | |
| MultiByteToWideChar(CP_ACP,0,activeDS.identity.ProductName,-1, | |
| &szCaption[lstrlenA(activeDS.identity.Manufacturer)+1],len); | |
| psh.dwSize = sizeof(PROPSHEETHEADERW); | |
| psh.dwFlags = PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK; | |
| psh.hwndParent = activeDS.hwndOwner; | |
| psh.hInstance = SANE_instance; | |
| psh.u.pszIcon = 0; | |
| psh.pszCaption = szCaption; | |
| psh.nPages = page_count; | |
| psh.u2.nStartPage = 0; | |
| psh.u3.ppsp = (LPCPROPSHEETPAGEW) &psp; | |
| psh.pfnCallback = PropSheetProc; | |
| psrc = PropertySheetW(&psh); | |
| for(index = 0; index < page_count; index ++) | |
| { | |
| HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].u.pResource); | |
| HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].pszTitle); | |
| } | |
| HeapFree(GetProcessHeap(),0,szCaption); | |
| if (psrc == IDOK) | |
| return TRUE; | |
| else | |
| return FALSE; | |
| } | |
| static void UpdateRelevantEdit(HWND hwnd, const SANE_Option_Descriptor *opt, | |
| int index, int position) | |
| { | |
| WCHAR buffer[244]; | |
| HWND edit_w; | |
| int len; | |
| if (opt->type == SANE_TYPE_INT) | |
| { | |
| static const WCHAR formatW[] = {'%','i',0}; | |
| INT si; | |
| if (opt->constraint.range->quant) | |
| si = position * opt->constraint.range->quant; | |
| else | |
| si = position; | |
| len = sprintfW( buffer, formatW, si ); | |
| } | |
| else if (opt->type == SANE_TYPE_FIXED) | |
| { | |
| static const WCHAR formatW[] = {'%','f',0}; | |
| double s_quant, dd; | |
| s_quant = SANE_UNFIX(opt->constraint.range->quant); | |
| if (s_quant) | |
| dd = position * s_quant; | |
| else | |
| dd = position * 0.01; | |
| len = sprintfW( buffer, formatW, dd ); | |
| } | |
| else return; | |
| buffer[len++] = ' '; | |
| LoadStringW( SANE_instance, opt->unit, buffer + len, sizeof(buffer)/sizeof(WCHAR) - len ); | |
| edit_w = GetDlgItem(hwnd,index+ID_BASE+ID_EDIT_BASE); | |
| if (edit_w) SetWindowTextW(edit_w,buffer); | |
| } | |
| static BOOL UpdateSaneScrollOption( | |
| const SANE_Option_Descriptor *opt, int index, DWORD position) | |
| { | |
| SANE_Status rc = SANE_STATUS_GOOD; | |
| SANE_Int result = 0; | |
| if (opt->type == SANE_TYPE_INT) | |
| { | |
| SANE_Int si; | |
| if (opt->constraint.range->quant) | |
| si = position * opt->constraint.range->quant; | |
| else | |
| si = position; | |
| rc = psane_control_option (activeDS.deviceHandle,index, | |
| SANE_ACTION_SET_VALUE, &si, &result); | |
| } | |
| else if (opt->type == SANE_TYPE_FIXED) | |
| { | |
| double s_quant, dd; | |
| SANE_Fixed *sf; | |
| s_quant = SANE_UNFIX(opt->constraint.range->quant); | |
| if (s_quant) | |
| dd = position * s_quant; | |
| else | |
| dd = position * 0.01; | |
| sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word)); | |
| *sf = SANE_FIX(dd); | |
| rc = psane_control_option (activeDS.deviceHandle,index, | |
| SANE_ACTION_SET_VALUE, sf, &result); | |
| HeapFree(GetProcessHeap(),0,sf); | |
| } | |
| if(rc == SANE_STATUS_GOOD) | |
| { | |
| if (result & SANE_INFO_RELOAD_OPTIONS || | |
| result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| static BOOL UpdateSaneBoolOption(int index, BOOL position) | |
| { | |
| SANE_Status rc = SANE_STATUS_GOOD; | |
| SANE_Int result = 0; | |
| SANE_Bool si; | |
| si = position; | |
| rc = psane_control_option (activeDS.deviceHandle,index, | |
| SANE_ACTION_SET_VALUE, &si, &result); | |
| if(rc == SANE_STATUS_GOOD) | |
| { | |
| if (result & SANE_INFO_RELOAD_OPTIONS || | |
| result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| static BOOL UpdateSaneStringOption(int index, SANE_String value) | |
| { | |
| SANE_Status rc = SANE_STATUS_GOOD; | |
| SANE_Int result = 0; | |
| rc = psane_control_option (activeDS.deviceHandle,index, | |
| SANE_ACTION_SET_VALUE, value, &result); | |
| if(rc == SANE_STATUS_GOOD) | |
| { | |
| if (result & SANE_INFO_RELOAD_OPTIONS || | |
| result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| static INT_PTR InitializeDialog(HWND hwnd) | |
| { | |
| SANE_Status rc; | |
| SANE_Int optcount; | |
| HWND control; | |
| int i; | |
| rc = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE, | |
| &optcount, NULL); | |
| if (rc != SANE_STATUS_GOOD) | |
| { | |
| ERR("Unable to read number of options\n"); | |
| return FALSE; | |
| } | |
| for ( i = 1; i < optcount; i++) | |
| { | |
| const SANE_Option_Descriptor *opt; | |
| control = GetDlgItem(hwnd,i+ID_BASE); | |
| if (!control) | |
| continue; | |
| opt = psane_get_option_descriptor(activeDS.deviceHandle, i); | |
| TRACE("%i %s %i %i\n",i,opt->title,opt->type,opt->constraint_type); | |
| if (!SANE_OPTION_IS_ACTIVE(opt->cap)) | |
| EnableWindow(control,FALSE); | |
| else | |
| EnableWindow(control,TRUE); | |
| SendMessageA(control,CB_RESETCONTENT,0,0); | |
| /* initialize values */ | |
| if (opt->type == SANE_TYPE_STRING && opt->constraint_type != | |
| SANE_CONSTRAINT_NONE) | |
| { | |
| int j = 0; | |
| CHAR buffer[255]; | |
| while (opt->constraint.string_list[j]!=NULL) | |
| { | |
| SendMessageA(control,CB_ADDSTRING,0, | |
| (LPARAM)opt->constraint.string_list[j]); | |
| j++; | |
| } | |
| psane_control_option(activeDS.deviceHandle, i, SANE_ACTION_GET_VALUE, buffer,NULL); | |
| SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer); | |
| } | |
| else if (opt->type == SANE_TYPE_BOOL) | |
| { | |
| SANE_Bool b; | |
| psane_control_option(activeDS.deviceHandle, i, | |
| SANE_ACTION_GET_VALUE, &b, NULL); | |
| if (b) | |
| SendMessageA(control,BM_SETCHECK,BST_CHECKED,0); | |
| } | |
| else if (opt->constraint_type == SANE_CONSTRAINT_RANGE) | |
| { | |
| if (opt->type == SANE_TYPE_INT) | |
| { | |
| SANE_Int si; | |
| int min,max; | |
| min = (SANE_Int)opt->constraint.range->min / | |
| (((SANE_Int)opt->constraint.range->quant)? | |
| (SANE_Int)opt->constraint.range->quant:1); | |
| max = (SANE_Int)opt->constraint.range->max / | |
| (((SANE_Int)opt->constraint.range->quant) | |
| ?(SANE_Int)opt->constraint.range->quant:1); | |
| SendMessageA(control,SBM_SETRANGE,min,max); | |
| psane_control_option(activeDS.deviceHandle, i, | |
| SANE_ACTION_GET_VALUE, &si,NULL); | |
| if (opt->constraint.range->quant) | |
| si = si / opt->constraint.range->quant; | |
| SendMessageW(control,SBM_SETPOS, si, TRUE); | |
| UpdateRelevantEdit(hwnd, opt, i, si); | |
| } | |
| else if (opt->type == SANE_TYPE_FIXED) | |
| { | |
| SANE_Fixed *sf; | |
| double dd; | |
| double s_min,s_max,s_quant; | |
| INT pos; | |
| int min,max; | |
| s_min = SANE_UNFIX(opt->constraint.range->min); | |
| s_max = SANE_UNFIX(opt->constraint.range->max); | |
| s_quant = SANE_UNFIX(opt->constraint.range->quant); | |
| if (s_quant) | |
| { | |
| min = (s_min / s_quant); | |
| max = (s_max / s_quant); | |
| } | |
| else | |
| { | |
| min = s_min / 0.01; | |
| max = s_max / 0.01; | |
| } | |
| SendMessageA(control,SBM_SETRANGE,min,max); | |
| sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word)); | |
| psane_control_option(activeDS.deviceHandle, i, | |
| SANE_ACTION_GET_VALUE, sf,NULL); | |
| dd = SANE_UNFIX(*sf); | |
| HeapFree(GetProcessHeap(),0,sf); | |
| /* Note that conversion of float -> SANE_Fixed is lossy; | |
| * and when you truncate it into an integer, you can get | |
| * unfortunate results. This calculation attempts | |
| * to mitigate that harm */ | |
| if (s_quant) | |
| pos = ((dd + (s_quant/2.0)) / s_quant); | |
| else | |
| pos = (dd + 0.005) / 0.01; | |
| SendMessageW(control, SBM_SETPOS, pos, TRUE); | |
| UpdateRelevantEdit(hwnd, opt, i, pos); | |
| } | |
| } | |
| } | |
| return TRUE; | |
| } | |
| static INT_PTR ProcessScroll(HWND hwnd, WPARAM wParam, LPARAM lParam) | |
| { | |
| int index; | |
| const SANE_Option_Descriptor *opt; | |
| WORD scroll; | |
| DWORD position; | |
| index = GetDlgCtrlID((HWND)lParam)- ID_BASE; | |
| if (index < 0) | |
| return FALSE; | |
| opt = psane_get_option_descriptor(activeDS.deviceHandle, index); | |
| if (!opt) | |
| return FALSE; | |
| scroll = LOWORD(wParam); | |
| switch (scroll) | |
| { | |
| case SB_THUMBTRACK: | |
| case SB_THUMBPOSITION: | |
| { | |
| SCROLLINFO si; | |
| si.cbSize = sizeof(SCROLLINFO); | |
| si.fMask = SIF_TRACKPOS; | |
| GetScrollInfo((HWND)lParam,SB_CTL, &si); | |
| position = si.nTrackPos; | |
| } | |
| break; | |
| case SB_LEFT: | |
| case SB_LINELEFT: | |
| case SB_PAGELEFT: | |
| position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0); | |
| position--; | |
| break; | |
| case SB_RIGHT: | |
| case SB_LINERIGHT: | |
| case SB_PAGERIGHT: | |
| position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0); | |
| position++; | |
| break; | |
| default: | |
| position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0); | |
| } | |
| SendMessageW((HWND)lParam,SBM_SETPOS, position, TRUE); | |
| position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0); | |
| UpdateRelevantEdit(hwnd, opt, index, position); | |
| if (UpdateSaneScrollOption(opt, index, position)) | |
| InitializeDialog(hwnd); | |
| return TRUE; | |
| } | |
| static void ButtonClicked(HWND hwnd, INT id, HWND control) | |
| { | |
| int index; | |
| const SANE_Option_Descriptor *opt; | |
| index = id - ID_BASE; | |
| if (index < 0) | |
| return; | |
| opt = psane_get_option_descriptor(activeDS.deviceHandle, index); | |
| if (!opt) | |
| return; | |
| if (opt->type == SANE_TYPE_BOOL) | |
| { | |
| BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED; | |
| if (UpdateSaneBoolOption(index, r)) | |
| InitializeDialog(hwnd); | |
| } | |
| } | |
| static void ComboChanged(HWND hwnd, INT id, HWND control) | |
| { | |
| int index; | |
| int selection; | |
| int len; | |
| const SANE_Option_Descriptor *opt; | |
| SANE_String value; | |
| index = id - ID_BASE; | |
| if (index < 0) | |
| return; | |
| opt = psane_get_option_descriptor(activeDS.deviceHandle, index); | |
| if (!opt) | |
| return; | |
| selection = SendMessageW(control,CB_GETCURSEL,0,0); | |
| len = SendMessageW(control,CB_GETLBTEXTLEN,selection,0); | |
| len++; | |
| value = HeapAlloc(GetProcessHeap(),0,len); | |
| SendMessageA(control,CB_GETLBTEXT,selection,(LPARAM)value); | |
| if (opt->type == SANE_TYPE_STRING) | |
| { | |
| if (UpdateSaneStringOption(index, value)) | |
| InitializeDialog(hwnd); | |
| } | |
| } | |
| static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| return InitializeDialog(hwndDlg); | |
| case WM_HSCROLL: | |
| return ProcessScroll(hwndDlg, wParam, lParam); | |
| case WM_NOTIFY: | |
| { | |
| LPPSHNOTIFY psn = (LPPSHNOTIFY)lParam; | |
| switch (((NMHDR*)lParam)->code) | |
| { | |
| case PSN_APPLY: | |
| if (psn->lParam == TRUE) | |
| { | |
| activeDS.currentState = 6; | |
| if (activeDS.windowMessage) | |
| PostMessageA(activeDS.hwndOwner, activeDS.windowMessage, MSG_XFERREADY, 0); | |
| } | |
| break; | |
| case PSN_QUERYCANCEL: | |
| if (activeDS.windowMessage) | |
| PostMessageA(activeDS.hwndOwner, activeDS.windowMessage, MSG_CLOSEDSREQ, 0); | |
| break; | |
| case PSN_SETACTIVE: | |
| InitializeDialog(hwndDlg); | |
| break; | |
| } | |
| } | |
| case WM_COMMAND: | |
| { | |
| switch (HIWORD(wParam)) | |
| { | |
| case BN_CLICKED: | |
| ButtonClicked(hwndDlg,LOWORD(wParam), | |
| (HWND)lParam); | |
| break; | |
| case CBN_SELCHANGE: | |
| ComboChanged(hwndDlg,LOWORD(wParam), | |
| (HWND)lParam); | |
| } | |
| } | |
| } | |
| return FALSE; | |
| } | |
| static int CALLBACK PropSheetProc(HWND hwnd, UINT msg, LPARAM lParam) | |
| { | |
| if (msg == PSCB_INITIALIZED) | |
| { | |
| /* rename OK button to Scan */ | |
| HWND scan = GetDlgItem(hwnd,IDOK); | |
| SetWindowTextA(scan,"Scan"); | |
| } | |
| return TRUE; | |
| } | |
| static INT_PTR CALLBACK ScanningProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| return FALSE; | |
| } | |
| HWND ScanningDialogBox(HWND dialog, LONG progress) | |
| { | |
| if (!dialog) | |
| dialog = CreateDialogW(SANE_instance, | |
| (LPWSTR)MAKEINTRESOURCE(IDD_DIALOG1), NULL, ScanningProc); | |
| if (progress == -1) | |
| { | |
| EndDialog(dialog,0); | |
| return NULL; | |
| } | |
| RedrawWindow(dialog,NULL,NULL, | |
| RDW_INTERNALPAINT|RDW_UPDATENOW|RDW_ALLCHILDREN); | |
| return dialog; | |
| } | |
| #else /* SONAME_LIBSANE */ | |
| BOOL DoScannerUI(void) | |
| { | |
| return FALSE; | |
| } | |
| #endif /* SONAME_LIBSANE */ |