|  | /* | 
|  | * 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); | 
|  |  | 
|  | if (s_quant) | 
|  | pos = (dd / s_quant); | 
|  | else | 
|  | pos = dd / 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; | 
|  | activeDS.pendingEvent.TWMessage = MSG_XFERREADY; | 
|  | } | 
|  | break; | 
|  | case PSN_QUERYCANCEL: | 
|  | activeDS.pendingEvent.TWMessage = MSG_CLOSEDSREQ; | 
|  | 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 */ |